hermes-agent/tests/agent/test_memory_write_bridge.py
Teknium b1b20270c4 refactor(memory): move write-mirror gating behind MemoryManager interface
The success/staged gating and op-expansion for mirroring built-in memory
writes to external providers lived in a standalone agent/memory_write_bridge.py
helper called inline from two core call sites (tool_executor.py,
agent_runtime_helpers.py). That left the mirror decision-making in the agent
loop, outside the memory-provider interface.

Fold it into a new MemoryManager.notify_memory_tool_write() entry point: the
loop now hands over the raw tool result + args and a metadata callback, and the
manager decides whether/what to mirror. Both core call sites collapse to a
single call; the orphan module is removed. No MemoryProvider ABC change.

Tests rewritten as behavior tests against the manager method.
2026-06-22 07:00:42 -07:00

145 lines
4.4 KiB
Python

"""Behavior tests for the built-in memory → external provider bridge.
The bridge lives behind the MemoryManager interface
(``MemoryManager.notify_memory_tool_write``): the agent loop hands over the raw
built-in memory tool result + args, and the manager decides whether/what to
mirror to external providers. These tests drive that method with a fake
external provider and assert which ``on_memory_write`` calls land.
"""
import json
import pytest
from agent.memory_manager import MemoryManager
from agent.memory_provider import MemoryProvider
class _RecordingProvider(MemoryProvider):
"""Minimal external provider that records on_memory_write calls."""
def __init__(self) -> None:
self.calls = []
@property
def name(self) -> str:
return "recording"
def is_available(self) -> bool:
return True
def initialize(self, session_id: str, **kwargs) -> None:
pass
def get_tool_schemas(self):
return []
def shutdown(self) -> None:
pass
def on_memory_write(self, action, target, content, metadata=None):
self.calls.append({
"action": action,
"target": target,
"content": content,
"metadata": dict(metadata or {}),
})
def _manager_with_provider():
mgr = MemoryManager()
provider = _RecordingProvider()
mgr.add_provider(provider)
return mgr, provider
def test_notifies_remove_with_old_text_after_success():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": True}),
{"action": "remove", "target": "memory", "old_text": "stale preference entry"},
)
assert provider.calls == [
{
"action": "remove",
"target": "memory",
"content": "",
"metadata": {"old_text": "stale preference entry"},
}
]
def test_skips_failed_memory_write():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": False, "error": "No entry matched"}),
{"action": "remove", "target": "memory", "old_text": "stale preference entry"},
)
assert provider.calls == []
def test_skips_staged_memory_write():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": True, "staged": True, "pending_id": "abc123"}),
{"action": "remove", "target": "memory", "old_text": "stale preference entry"},
)
assert provider.calls == []
@pytest.mark.parametrize("tool_result", [None, [], object(), "not-json"])
def test_skips_unrecognized_tool_result_shape(tool_result):
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
tool_result,
{"action": "add", "target": "memory", "content": "new fact"},
)
assert provider.calls == []
def test_preserves_old_text_for_replace_and_remove_batch():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": True}),
{
"target": "user",
"operations": [
{"action": "replace", "old_text": "old preference", "content": "updated"},
{"action": "remove", "old_text": "obsolete preference"},
{"action": "add", "content": "new fact"},
],
},
)
assert provider.calls == [
{"action": "replace", "target": "user", "content": "updated",
"metadata": {"old_text": "old preference"}},
{"action": "remove", "target": "user", "content": "",
"metadata": {"old_text": "obsolete preference"}},
{"action": "add", "target": "user", "content": "new fact", "metadata": {}},
]
def test_non_mutating_actions_are_not_mirrored():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": True}),
{"action": "read", "target": "memory"},
)
assert provider.calls == []
def test_build_metadata_callback_is_merged_per_op():
mgr, provider = _manager_with_provider()
mgr.notify_memory_tool_write(
json.dumps({"success": True}),
{"action": "add", "target": "memory", "content": "fact"},
build_metadata=lambda: {"session_id": "s1", "tool_name": "memory"},
)
assert provider.calls == [
{
"action": "add",
"target": "memory",
"content": "fact",
"metadata": {"session_id": "s1", "tool_name": "memory"},
}
]