104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
"""Tests for routing the Hindsight memory builtins through runner-local dispatch.
|
|
|
|
Without runner-local dispatch a wrapped harness's (claude-sdk / codex / …) call
|
|
to hindsight_retain falls through to the harness, which has no such tool, and
|
|
silently no-ops. These lock in that the tools dispatch locally, are relayed to
|
|
native harnesses, and resolve the bank from the threaded agent identity.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from omnigent.runner.tool_dispatch import (
|
|
_ALL_LOCAL_TOOLS,
|
|
_HINDSIGHT_TOOLS,
|
|
_NATIVE_RELAY_BUILTIN_TOOLS,
|
|
_execute_hindsight_tool,
|
|
should_dispatch_locally,
|
|
)
|
|
|
|
|
|
def _spec(config: dict[str, str], name: str = "hindsight_retain") -> SimpleNamespace:
|
|
return SimpleNamespace(
|
|
executor=SimpleNamespace(model="claude-opus-4-8"),
|
|
tools=SimpleNamespace(builtins=[SimpleNamespace(name=name, config=config)]),
|
|
)
|
|
|
|
|
|
def test_hindsight_tool_set_is_exactly_the_three() -> None:
|
|
assert set(_HINDSIGHT_TOOLS) == {"hindsight_retain", "hindsight_recall", "hindsight_reflect"}
|
|
|
|
|
|
@pytest.mark.parametrize("name", ["hindsight_retain", "hindsight_recall", "hindsight_reflect"])
|
|
def test_hindsight_tools_are_runner_local(name: str) -> None:
|
|
assert name in _ALL_LOCAL_TOOLS
|
|
assert should_dispatch_locally(name) is True
|
|
|
|
|
|
@pytest.mark.parametrize("name", ["hindsight_retain", "hindsight_recall", "hindsight_reflect"])
|
|
def test_hindsight_tools_relayed_to_native_harnesses(name: str) -> None:
|
|
# Unlike web_search, native harnesses have no built-in memory of their own.
|
|
assert name in _NATIVE_RELAY_BUILTIN_TOOLS
|
|
|
|
|
|
def test_retain_dispatch_uses_agent_id_as_bank() -> None:
|
|
"""With no config bank_id, the bank defaults to the threaded agent_id."""
|
|
client = MagicMock()
|
|
spec = _spec({"api_key": "hsk_test"})
|
|
with patch("hindsight_client.Hindsight", return_value=client):
|
|
result = asyncio.run(
|
|
_execute_hindsight_tool(
|
|
{"content": "DuckDB is my favorite db"},
|
|
tool_name="hindsight_retain",
|
|
agent_spec=spec,
|
|
conversation_id="conv_1",
|
|
agent_id="ag_remy",
|
|
)
|
|
)
|
|
assert result == "Stored to long-term memory."
|
|
assert client.retain.call_args.kwargs["bank_id"] == "ag_remy"
|
|
assert client.retain.call_args.kwargs["content"] == "DuckDB is my favorite db"
|
|
|
|
|
|
def test_recall_dispatch_honors_config_bank_id() -> None:
|
|
"""An explicit config bank_id overrides the agent_id default."""
|
|
client = MagicMock()
|
|
response = MagicMock()
|
|
response.results = [MagicMock(text="DuckDB is my favorite db")]
|
|
client.recall.return_value = response
|
|
spec = _spec({"api_key": "hsk_test", "bank_id": "remy-ui-test"}, name="hindsight_recall")
|
|
with patch("hindsight_client.Hindsight", return_value=client):
|
|
result = asyncio.run(
|
|
_execute_hindsight_tool(
|
|
{"query": "favorite db"},
|
|
tool_name="hindsight_recall",
|
|
agent_spec=spec,
|
|
conversation_id="conv_1",
|
|
agent_id="ag_remy",
|
|
)
|
|
)
|
|
assert "DuckDB" in result
|
|
assert client.recall.call_args.kwargs["bank_id"] == "remy-ui-test"
|
|
|
|
|
|
def test_dispatch_missing_api_key_returns_error() -> None:
|
|
spec = _spec({}) # no api_key
|
|
result = asyncio.run(
|
|
_execute_hindsight_tool(
|
|
{"content": "x"},
|
|
tool_name="hindsight_retain",
|
|
agent_spec=spec,
|
|
agent_id="ag_remy",
|
|
)
|
|
)
|
|
assert "api_key" in result.lower()
|
|
# never echoes the success string when it couldn't store
|
|
assert result != "Stored to long-term memory."
|
|
json.dumps(result) # result is a plain string
|