refactor(honcho): tighten provider tool handlers and session peer-config sync
This commit is contained in:
@@ -7,6 +7,7 @@ Config chain: $HERMES_HOME/honcho.json -> ~/.honcho/config.json -> env vars.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
@@ -400,16 +401,14 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
ctx_holder: dict[str, dict] = {}
|
||||
|
||||
def _fetch_base() -> None:
|
||||
ctx = self._manager.get_prefetch_context(self._session_key, query or None) or {}
|
||||
ctx_holder["ctx"] = ctx
|
||||
ctx_holder["ctx"] = ctx = self._manager.get_prefetch_context(self._session_key, query or None) or {}
|
||||
if ctx:
|
||||
self._manager.set_context_result(self._session_key, ctx)
|
||||
|
||||
bt = self._spawn_write(_fetch_base, "honcho-base-first", "Honcho first-turn base context failed: %s")
|
||||
base_wait = max(0.0, first_turn_base_deadline - time.monotonic()) if first_turn_base_deadline is not None else 0.0
|
||||
bt.join(timeout=base_wait)
|
||||
ctx = ctx_holder.get("ctx")
|
||||
if ctx:
|
||||
if ctx := ctx_holder.get("ctx"):
|
||||
self._manager.pop_context_result(self._session_key)
|
||||
return _adopt(ctx)
|
||||
if bt.is_alive():
|
||||
@@ -428,12 +427,9 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
|
||||
dia_wait = self._first_turn_wait(self._FIRST_TURN_DIALECTIC_CAP)
|
||||
if not self._thread_is_live():
|
||||
self._spawn_dialectic(
|
||||
query, thread_name="honcho-prefetch-first", fired_at=self._turn_count,
|
||||
log_label="first-turn dialectic",
|
||||
)
|
||||
live = self._prefetch_thread
|
||||
if live is not None:
|
||||
self._spawn_dialectic(query, thread_name="honcho-prefetch-first", fired_at=self._turn_count,
|
||||
log_label="first-turn dialectic")
|
||||
if (live := self._prefetch_thread) is not None:
|
||||
live.join(timeout=dia_wait)
|
||||
if self._prefetch_thread and self._prefetch_thread.is_alive():
|
||||
logger.debug("Honcho first-turn dialectic still running after %.1fs — will surface on next turn", dia_wait)
|
||||
@@ -681,16 +677,13 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
|
||||
def _tool_profile(self, args: dict) -> str:
|
||||
peer = args.get("peer", "user")
|
||||
card_update = args.get("card")
|
||||
if card_update:
|
||||
if card_update := args.get("card"):
|
||||
result = self._manager.set_peer_card(self._session_key, card_update, peer=peer)
|
||||
if result is None:
|
||||
return tool_error("Failed to update peer card.")
|
||||
return json.dumps({"result": f"Peer card updated ({len(result)} facts).", "card": result})
|
||||
card = self._manager.get_peer_card(self._session_key, peer=peer)
|
||||
if not card:
|
||||
return json.dumps(self._empty_profile_hint(peer))
|
||||
return json.dumps({"result": card})
|
||||
return json.dumps({"result": card} if card else self._empty_profile_hint(peer))
|
||||
|
||||
def _tool_search(self, args: dict) -> str:
|
||||
query = (args.get("query") or "").strip()
|
||||
@@ -734,9 +727,8 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
parts = [f"## {header}\n{ctx[key]}"
|
||||
for key, header in (("summary", "Summary"), ("representation", "Representation"), ("card", "Card"))
|
||||
if ctx.get(key)]
|
||||
if ctx.get("recent_messages"):
|
||||
msg_str = "\n".join(f" [{m['role']}] {m['content'][:200]}" for m in ctx["recent_messages"][-5:])
|
||||
parts.append(f"## Recent messages\n{msg_str}")
|
||||
if recent := ctx.get("recent_messages"):
|
||||
parts.append("## Recent messages\n" + "\n".join(f" [{m['role']}] {m['content'][:200]}" for m in recent[-5:]))
|
||||
return json.dumps({"result": "\n\n".join(parts) or "No context available."})
|
||||
|
||||
def _tool_conclude(self, args: dict) -> str:
|
||||
@@ -751,8 +743,7 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
return tool_error("query is only valid when list is true.")
|
||||
|
||||
if list_mode:
|
||||
conclusions = self._manager.list_conclusions(self._session_key, query=query or None, peer=peer)
|
||||
return json.dumps({"conclusions": conclusions})
|
||||
return json.dumps({"conclusions": self._manager.list_conclusions(self._session_key, query=query or None, peer=peer)})
|
||||
if delete_id:
|
||||
if self._manager.delete_conclusion(self._session_key, delete_id, peer=peer):
|
||||
return json.dumps({"result": f"Conclusion {delete_id} deleted."})
|
||||
@@ -779,13 +770,11 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
if self._init_thread and self._init_thread.is_alive():
|
||||
return tool_error("Honcho session is still initializing; try again shortly.")
|
||||
if not self._ensure_session():
|
||||
if self._init_auth_failure:
|
||||
return tool_error(f"Honcho memory authentication failed: {self._init_auth_failure}")
|
||||
return tool_error("Honcho session could not be initialized.")
|
||||
return tool_error(f"Honcho memory authentication failed: {self._init_auth_failure}"
|
||||
if self._init_auth_failure else "Honcho session could not be initialized.")
|
||||
if not self._manager or not self._session_key:
|
||||
return tool_error("Honcho is not active for this session.")
|
||||
handler = self._TOOL_HANDLERS.get(tool_name)
|
||||
if handler is None:
|
||||
if (handler := self._TOOL_HANDLERS.get(tool_name)) is None:
|
||||
return tool_error(f"Unknown tool: {tool_name}")
|
||||
try:
|
||||
return handler(self, args)
|
||||
@@ -804,15 +793,13 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider):
|
||||
manager = self._manager
|
||||
if not manager or (self._init_thread and self._init_thread.is_alive() and not self._session_initialized):
|
||||
return
|
||||
try:
|
||||
# saveMessages: false skips persistence, but the async-writer thread must still
|
||||
# be joined so daemon threads aren't left blocked in httpx I/O at interpreter exit.
|
||||
# saveMessages: false skips persistence, but the async-writer thread must still
|
||||
# be joined so daemon threads aren't left blocked in httpx I/O at interpreter exit.
|
||||
with contextlib.suppress(Exception):
|
||||
if getattr(self._config, "save_messages", True):
|
||||
manager.shutdown() # flush_all() + join the writer
|
||||
else:
|
||||
manager.stop_async_writer()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def register(ctx) -> None:
|
||||
|
||||
@@ -145,7 +145,8 @@ class HonchoSessionManager(SessionAuthMixin, SessionPeersMixin, SessionContextMi
|
||||
for kind, peer in peers
|
||||
]
|
||||
self._authed_call("session peer setup", lambda: self._sdk_session(session_id).add_peers(peer_entries))
|
||||
try:
|
||||
|
||||
def _adopt_server_config() -> None:
|
||||
server_cfgs = self._authed_call(
|
||||
"peer configuration read",
|
||||
lambda: [self._sdk_session(session_id).get_peer_configuration(peer) for _, peer in peers],
|
||||
@@ -157,10 +158,9 @@ class HonchoSessionManager(SessionAuthMixin, SessionPeersMixin, SessionContextMi
|
||||
setattr(self, f"_{kind}_{field_name}", value)
|
||||
logger.debug("Honcho observation synced from server: user(me=%s,others=%s) ai(me=%s,others=%s)",
|
||||
self._user_observe_me, self._user_observe_others, self._ai_observe_me, self._ai_observe_others)
|
||||
except HonchoAuthError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.debug("Honcho get_peer_configuration failed (using local config): %s", e)
|
||||
|
||||
self._guarded(_adopt_server_config, None, logging.DEBUG,
|
||||
"Honcho get_peer_configuration failed (using local config): %s")
|
||||
except HonchoAuthError:
|
||||
return False
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user