135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
"""Honcho session summaries must not inject model reasoning (#97639)."""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from plugins.memory.honcho.session import HonchoSession, HonchoSessionManager
|
|
from plugins.memory.honcho.session_context import usable_honcho_summary
|
|
|
|
|
|
CONTAMINATED = """I need to create a thorough, comprehensive summary of this conversation.
|
|
The instruction says to focus on capturing key facts...
|
|
First, let me review what I have in the previous summary...
|
|
</think>
|
|
Alice prefers dark roast coffee.
|
|
"""
|
|
|
|
PLANNING_ONLY = """I need to create a thorough, comprehensive summary of this conversation.
|
|
The instruction says to focus on capturing key facts...
|
|
First, let me review what I have in the previous summary...
|
|
"""
|
|
|
|
|
|
def test_usable_summary_keeps_text_after_think_close() -> None:
|
|
out = usable_honcho_summary(CONTAMINATED)
|
|
assert out is not None
|
|
assert "Alice prefers dark roast coffee." in out
|
|
assert "</think>" not in out.lower()
|
|
assert "I need to create a thorough" not in out
|
|
|
|
|
|
def test_usable_summary_drops_planning_preamble_without_body() -> None:
|
|
assert usable_honcho_summary(PLANNING_ONLY) is None
|
|
|
|
|
|
def test_usable_summary_drops_closed_think_block() -> None:
|
|
raw = "<think>plan the summary</think>\nBob uses vim."
|
|
out = usable_honcho_summary(raw)
|
|
assert out == "Bob uses vim."
|
|
|
|
|
|
def test_usable_summary_keeps_clean_text() -> None:
|
|
assert usable_honcho_summary("Uses Python 3.11.") == "Uses Python 3.11."
|
|
|
|
|
|
class _FakeSummary:
|
|
def __init__(self, content: str) -> None:
|
|
self.content = content
|
|
|
|
|
|
class _FakeContext:
|
|
def __init__(self, content: str) -> None:
|
|
self.summary = _FakeSummary(content)
|
|
self.peer_representation = "representation"
|
|
self.peer_card = ["fact"]
|
|
self.messages = []
|
|
|
|
|
|
class _RecordingHonchoSession:
|
|
def __init__(self, content: str) -> None:
|
|
self.content = content
|
|
|
|
def context(self, **kwargs):
|
|
return _FakeContext(self.content)
|
|
|
|
|
|
def _manager_with_summary(content: str):
|
|
cfg = SimpleNamespace(
|
|
write_frequency="turn",
|
|
dialectic_reasoning_level="low",
|
|
dialectic_dynamic=True,
|
|
dialectic_max_chars=600,
|
|
observation_mode="directional",
|
|
user_observe_me=True,
|
|
user_observe_others=True,
|
|
ai_observe_me=True,
|
|
ai_observe_others=True,
|
|
message_max_chars=25000,
|
|
dialectic_max_input_chars=10000,
|
|
)
|
|
mgr = HonchoSessionManager(honcho=SimpleNamespace(), config=cfg)
|
|
session = HonchoSession(
|
|
key="test-session",
|
|
user_peer_id="chris",
|
|
assistant_peer_id="hermes",
|
|
honcho_session_id="test-session",
|
|
)
|
|
fake = _RecordingHonchoSession(content)
|
|
mgr._cache[session.key] = session
|
|
mgr._sessions_cache[session.honcho_session_id] = fake
|
|
return mgr
|
|
|
|
|
|
def test_prefetch_omits_planning_only_summary() -> None:
|
|
mgr = _manager_with_summary(PLANNING_ONLY)
|
|
mgr._fetch_peer_context = lambda *a, **k: {
|
|
"representation": "representation",
|
|
"card": ["fact"],
|
|
}
|
|
mgr._resolve_observer_target = lambda *a, **k: ("hermes", "chris")
|
|
result = mgr.get_prefetch_context("test-session")
|
|
assert "summary" not in result
|
|
assert result.get("representation") == "representation"
|
|
|
|
|
|
def test_prefetch_keeps_body_after_think_close() -> None:
|
|
mgr = _manager_with_summary(CONTAMINATED)
|
|
mgr._fetch_peer_context = lambda *a, **k: {
|
|
"representation": "representation",
|
|
"card": ["fact"],
|
|
}
|
|
mgr._resolve_observer_target = lambda *a, **k: ("hermes", "chris")
|
|
result = mgr.get_prefetch_context("test-session")
|
|
assert result["summary"] == "Alice prefers dark roast coffee."
|
|
|
|
|
|
def test_session_context_omits_planning_only_summary() -> None:
|
|
mgr = _manager_with_summary(PLANNING_ONLY)
|
|
result = mgr.get_session_context("test-session")
|
|
assert "summary" not in result
|
|
assert result.get("representation") == "representation"
|
|
|
|
|
|
def test_format_first_turn_omits_contaminated_summary() -> None:
|
|
from plugins.memory.honcho import HonchoMemoryProvider
|
|
|
|
plugin = HonchoMemoryProvider()
|
|
out = plugin._format_first_turn_context(
|
|
{
|
|
"summary": PLANNING_ONLY,
|
|
"card": "Likes espresso.",
|
|
}
|
|
)
|
|
assert "Likes espresso." in out
|
|
assert "Session Summary" not in out
|
|
assert "I need to create a thorough" not in out
|