Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions plugins/memory/honcho/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,14 +880,28 @@ def _fetch_peer_card(self, peer_id: str) -> list[str]:

return []

def _fetch_peer_context(self, peer_id: str, search_query: str | None = None) -> dict[str, Any]:
"""Fetch representation + peer card directly from a peer object."""
def _fetch_peer_context(
self,
peer_id: str,
search_query: str | None = None,
target_id: str | None = None,
) -> dict[str, Any]:
"""Fetch representation + peer card directly from a peer object.

When ``target_id`` is provided, fetch context from the observer peer's
perspective about that target peer (observer -> observed).
"""
peer = self._get_or_create_peer(peer_id)
representation = ""
card: list[str] = []

try:
ctx = peer.context(search_query=search_query) if search_query else peer.context()
kwargs: dict[str, Any] = {}
if target_id:
kwargs["target"] = target_id
if search_query:
kwargs["search_query"] = search_query
ctx = peer.context(**kwargs) if kwargs else peer.context()
representation = (
getattr(ctx, "representation", None)
or getattr(ctx, "peer_representation", None)
Expand All @@ -899,7 +913,15 @@ def _fetch_peer_context(self, peer_id: str, search_query: str | None = None) ->

if not representation:
try:
representation = peer.representation() or ""
if target_id:
ctx = peer.context(target=target_id)
representation = (
getattr(ctx, "representation", None)
or getattr(ctx, "peer_representation", None)
or ""
)
else:
representation = peer.representation() or ""
except Exception as e:
logger.debug("Direct peer.representation() failed for '%s': %s", peer_id, e)

Expand Down Expand Up @@ -950,7 +972,21 @@ def search_context(self, session_key: str, query: str, max_tokens: int = 800) ->
return ""

try:
ctx = self._fetch_peer_context(session.user_peer_id, search_query=query)
# Align retrieval direction with how conclusions are stored.
# - directional mode (default): assistant observes user
# - unified mode: user self-observes
if self._ai_observe_others:
observer_peer_id = session.assistant_peer_id
target_id = session.user_peer_id
else:
observer_peer_id = session.user_peer_id
target_id = None

ctx = self._fetch_peer_context(
observer_peer_id,
search_query=query,
target_id=target_id,
)
parts = []
if ctx["representation"]:
parts.append(ctx["representation"])
Expand Down
28 changes: 23 additions & 5 deletions tests/honcho_plugin/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,38 @@ def test_get_peer_card_uses_direct_peer_lookup(self):
assert mgr.get_peer_card(session.key) == ["Name: Robert"]
user_peer.get_card.assert_called_once_with()

def test_search_context_uses_peer_context_response(self):
def test_search_context_uses_assistant_perspective_with_target(self):
mgr, session = self._make_cached_manager()
user_peer = MagicMock()
user_peer.context.return_value = SimpleNamespace(
assistant_peer = MagicMock()
assistant_peer.context.return_value = SimpleNamespace(
representation="Robert runs neuralancer",
peer_card=["Location: Melbourne"],
)
mgr._get_or_create_peer = MagicMock(return_value=user_peer)
mgr._get_or_create_peer = MagicMock(return_value=assistant_peer)

result = mgr.search_context(session.key, "neuralancer")

assert "Robert runs neuralancer" in result
assert "- Location: Melbourne" in result
user_peer.context.assert_called_once_with(search_query="neuralancer")
assistant_peer.context.assert_called_once_with(
target="robert",
search_query="neuralancer",
)

def test_search_context_unified_mode_uses_user_self_context(self):
mgr, session = self._make_cached_manager()
mgr._ai_observe_others = False
user_peer = MagicMock()
user_peer.context.return_value = SimpleNamespace(
representation="Unified self context",
peer_card=["Name: Robert"],
)
mgr._get_or_create_peer = MagicMock(return_value=user_peer)

result = mgr.search_context(session.key, "self")

assert "Unified self context" in result
user_peer.context.assert_called_once_with(search_query="self")

def test_get_prefetch_context_fetches_user_and_ai_from_peer_api(self):
mgr, session = self._make_cached_manager()
Expand Down