diff --git a/plugins/memory/honcho/__init__.py b/plugins/memory/honcho/__init__.py index ae29bff16311..10d8a9c1f55a 100644 --- a/plugins/memory/honcho/__init__.py +++ b/plugins/memory/honcho/__init__.py @@ -754,9 +754,12 @@ class HonchoMemoryProvider(MemoryProvider): def _fetch_base() -> None: try: - _ctx_holder["ctx"] = self._manager.get_prefetch_context( + ctx = self._manager.get_prefetch_context( self._session_key, query or None ) or {} + _ctx_holder["ctx"] = ctx + if ctx: + self._manager.set_context_result(self._session_key, ctx) except Exception as e: logger.debug("Honcho first-turn base context failed: %s", e) @@ -773,6 +776,7 @@ class HonchoMemoryProvider(MemoryProvider): _bt.join(timeout=_base_wait) _ctx = _ctx_holder.get("ctx") if _ctx: + self._manager.pop_context_result(self._session_key) formatted = self._format_first_turn_context(_ctx) if formatted: with self._base_context_lock: @@ -1248,7 +1252,9 @@ class HonchoMemoryProvider(MemoryProvider): # nothing. logger.debug("Honcho dialectic depth %d: pass %d has no non-empty prior — " "falling back to base prompt", self._dialectic_depth, i) - prompt = self._build_dialectic_prompt(0, prior_results, is_cold) + prompt = rewritten_query or self._build_dialectic_prompt( + 0, prior_results, is_cold + ) else: prompt = self._build_dialectic_prompt(i, prior_results, is_cold) diff --git a/tests/honcho_plugin/test_query_rewrite.py b/tests/honcho_plugin/test_query_rewrite.py index 95b9498d0b70..afd50a160463 100644 --- a/tests/honcho_plugin/test_query_rewrite.py +++ b/tests/honcho_plugin/test_query_rewrite.py @@ -147,6 +147,17 @@ def test_query_rewriter_runs_once_for_a_multi_pass_dialectic_cycle(): assert provider._manager.dialectic_query.call_count == 2 +def test_empty_first_pass_retries_with_rewritten_query(): + rewritten = "What prior deployment decisions did the user make?" + provider = _provider(lambda message: rewritten, depth=2) + provider._manager.dialectic_query.side_effect = ["", "grounded synthesis"] + + provider._run_dialectic_depth("What should we deploy?") + + prompts = [call.args[1] for call in provider._manager.dialectic_query.call_args_list] + assert prompts == [rewritten, rewritten] + + def test_session_prewarm_can_skip_query_rewrite(): rewriter = MagicMock(return_value="unused") provider = _provider(rewriter) diff --git a/tests/honcho_plugin/test_session.py b/tests/honcho_plugin/test_session.py index 9114c8bd03c8..8633ffc01758 100644 --- a/tests/honcho_plugin/test_session.py +++ b/tests/honcho_plugin/test_session.py @@ -1115,6 +1115,45 @@ class TestBaseContextSummary: formatted = provider._format_first_turn_context(ctx) assert "Session Summary" not in formatted + def test_timed_out_first_turn_context_surfaces_next_turn(self): + import threading + import time + + ready = threading.Event() + cached = {} + manager = MagicMock() + + def get_context(*args, **kwargs): + ready.wait(timeout=1) + return {"representation": "late user context", "card": ""} + + manager.get_prefetch_context.side_effect = get_context + manager.set_context_result.side_effect = ( + lambda session_key, result: cached.__setitem__(session_key, result) + ) + manager.pop_context_result.side_effect = ( + lambda session_key: cached.pop(session_key, {}) + ) + + provider = HonchoMemoryProvider() + provider._manager = manager + provider._config = SimpleNamespace(timeout=0.01, context_tokens=0) + provider._session_key = "test" + provider._session_initialized = True + provider._recall_mode = "context" + provider._turn_count = 1 + provider._last_dialectic_turn = 0 + + assert provider.prefetch("first question") == "" + ready.set() + + deadline = time.monotonic() + 1 + while "test" not in cached and time.monotonic() < deadline: + time.sleep(0.01) + + provider._turn_count = 2 + assert "late user context" in provider.prefetch("follow-up question") + class TestDialecticDepth: """Tests for the dialecticDepth multi-pass system."""