Update memory system skill and plugin
This commit is contained in:
245
tests/test_hermes_memory_system_plugin.py
Normal file
245
tests/test_hermes_memory_system_plugin.py
Normal file
@ -0,0 +1,245 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
HERMES_ROOT = Path("/home/tom/hermes-agent")
|
||||
PLUGIN_PATH = Path(__file__).resolve().parents[1] / "plugins" / "memory" / "memory_system" / "__init__.py"
|
||||
|
||||
|
||||
def load_plugin_module():
|
||||
if str(HERMES_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(HERMES_ROOT))
|
||||
spec = importlib.util.spec_from_file_location("memory_system_plugin", PLUGIN_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, commit_status="success"):
|
||||
self.posts = []
|
||||
self.gets = []
|
||||
self.commit_status = commit_status
|
||||
|
||||
def post(self, path, payload=None):
|
||||
self.posts.append((path, payload or {}))
|
||||
if path == "/memory-system/search":
|
||||
return {
|
||||
"status": "success",
|
||||
"items": [{"source": "openviking", "content": "likes latte", "score": 0.9}],
|
||||
}
|
||||
if path.endswith("/commit"):
|
||||
return {"status": self.commit_status}
|
||||
return {"status": "success"}
|
||||
|
||||
def get(self, path):
|
||||
self.gets.append(path)
|
||||
return {"status": "success", "profile": {"coffee": "latte"}}
|
||||
|
||||
|
||||
def make_provider():
|
||||
module = load_plugin_module()
|
||||
provider = module.MemorySystemMemoryProvider()
|
||||
provider._client = FakeClient()
|
||||
provider._endpoint = "http://127.0.0.1:1934"
|
||||
provider._user_id = "user-1"
|
||||
provider._session_id = "session-1"
|
||||
return provider
|
||||
|
||||
|
||||
def wait_for_sync(provider):
|
||||
thread = provider._sync_thread
|
||||
if thread and thread.is_alive():
|
||||
thread.join(timeout=2.0)
|
||||
|
||||
|
||||
def test_initialize_loads_config_from_hermes_env_file(tmp_path, monkeypatch):
|
||||
module = load_plugin_module()
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
env_file = hermes_home / ".env"
|
||||
env_file.write_text(
|
||||
"\n".join(
|
||||
[
|
||||
"MEMORY_SYSTEM_ENDPOINT=http://127.0.0.1:1934",
|
||||
"MEMORY_SYSTEM_USER_ID=file-user",
|
||||
"MEMORY_SYSTEM_COMMIT_EVERY_TURNS=3",
|
||||
"MEMORY_SYSTEM_COMMIT_INTERVAL_SECONDS=60",
|
||||
"MEMORY_SYSTEM_TIMEOUT_SECONDS=123",
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
for key in list(os.environ):
|
||||
if key.startswith("MEMORY_SYSTEM_"):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
|
||||
class InitClient(FakeClient):
|
||||
def __init__(self, endpoint, api_key="", timeout=0):
|
||||
super().__init__()
|
||||
self.endpoint = endpoint
|
||||
self.api_key = api_key
|
||||
self.timeout = timeout
|
||||
|
||||
def health(self):
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(module, "_MemorySystemClient", InitClient)
|
||||
provider = module.MemorySystemMemoryProvider()
|
||||
|
||||
provider.initialize("session-1", hermes_home=str(hermes_home))
|
||||
|
||||
assert provider._endpoint == "http://127.0.0.1:1934"
|
||||
assert provider._user_id == "file-user"
|
||||
assert provider._commit_every_turns == 3
|
||||
assert provider._commit_interval_seconds == 60
|
||||
assert provider._timeout == 123
|
||||
assert provider._client.endpoint == "http://127.0.0.1:1934"
|
||||
assert provider._client.timeout == 123
|
||||
|
||||
|
||||
def test_sync_turn_posts_user_and_assistant_messages():
|
||||
provider = make_provider()
|
||||
|
||||
provider.sync_turn("hello", "hi there")
|
||||
wait_for_sync(provider)
|
||||
|
||||
assert provider._client.posts == [
|
||||
(
|
||||
"/memory-system/messages",
|
||||
{
|
||||
"user_id": "user-1",
|
||||
"session_id": "session-1",
|
||||
"user_message": "hello",
|
||||
"assistant_message": "hi there",
|
||||
"metadata": {"source": "hermes", "provider": "memory_system"},
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def test_on_session_end_commits_after_turn_sync():
|
||||
provider = make_provider()
|
||||
provider.sync_turn("hello", "hi there")
|
||||
|
||||
provider.on_session_end([])
|
||||
|
||||
assert provider._client.posts[-1] == (
|
||||
"/memory-system/sessions/session-1/commit",
|
||||
{"user_id": "user-1"},
|
||||
)
|
||||
|
||||
|
||||
def test_sync_turn_commits_every_configured_turns():
|
||||
provider = make_provider()
|
||||
provider._commit_every_turns = 2
|
||||
provider._commit_interval_seconds = 0
|
||||
|
||||
provider.sync_turn("turn 1", "reply 1")
|
||||
wait_for_sync(provider)
|
||||
provider.sync_turn("turn 2", "reply 2")
|
||||
wait_for_sync(provider)
|
||||
|
||||
assert provider._client.posts[-1] == (
|
||||
"/memory-system/sessions/session-1/commit",
|
||||
{"user_id": "user-1"},
|
||||
)
|
||||
assert provider._last_commit_turn == 2
|
||||
|
||||
|
||||
def test_partial_commit_does_not_mark_turns_committed():
|
||||
provider = make_provider()
|
||||
provider._client = FakeClient(commit_status="partial_success")
|
||||
provider._turn_count = 2
|
||||
|
||||
response = provider._commit_session("session-1")
|
||||
|
||||
assert response["status"] == "partial_success"
|
||||
assert provider._last_commit_turn == 0
|
||||
|
||||
|
||||
def test_on_session_end_skips_when_periodic_commit_is_current():
|
||||
provider = make_provider()
|
||||
provider._commit_every_turns = 1
|
||||
provider._commit_interval_seconds = 0
|
||||
provider.sync_turn("hello", "hi there")
|
||||
wait_for_sync(provider)
|
||||
|
||||
provider.on_session_end([])
|
||||
|
||||
commit_posts = [
|
||||
post for post in provider._client.posts if post[0] == "/memory-system/sessions/session-1/commit"
|
||||
]
|
||||
assert len(commit_posts) == 1
|
||||
|
||||
|
||||
def test_search_tool_uses_memory_system_api():
|
||||
provider = make_provider()
|
||||
|
||||
result = json.loads(provider.handle_tool_call("memory_system_search", {"query": "coffee", "limit": 3}))
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert result["items"][0]["content"] == "likes latte"
|
||||
assert provider._client.posts[-1] == (
|
||||
"/memory-system/search",
|
||||
{
|
||||
"user_id": "user-1",
|
||||
"session_id": "session-1",
|
||||
"query": "coffee",
|
||||
"use_llm": False,
|
||||
"limit": 3,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_profile_tool_reads_user_profile():
|
||||
provider = make_provider()
|
||||
|
||||
result = json.loads(provider.handle_tool_call("memory_system_profile", {}))
|
||||
|
||||
assert result["profile"] == {"coffee": "latte"}
|
||||
assert provider._client.gets == ["/memory-system/users/user-1/profile"]
|
||||
|
||||
|
||||
def test_remember_tool_writes_and_commits():
|
||||
provider = make_provider()
|
||||
|
||||
result = json.loads(provider.handle_tool_call("memory_system_remember", {"content": "likes latte"}))
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert provider._client.posts == [
|
||||
(
|
||||
"/memory-system/messages",
|
||||
{
|
||||
"user_id": "user-1",
|
||||
"session_id": "session-1",
|
||||
"user_message": "likes latte",
|
||||
"metadata": {"source": "hermes", "provider": "memory_system"},
|
||||
},
|
||||
),
|
||||
(
|
||||
"/memory-system/sessions/session-1/commit",
|
||||
{"user_id": "user-1"},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def test_register_adds_provider():
|
||||
module = load_plugin_module()
|
||||
|
||||
class Ctx:
|
||||
def __init__(self):
|
||||
self.providers = []
|
||||
|
||||
def register_memory_provider(self, provider):
|
||||
self.providers.append(provider)
|
||||
|
||||
ctx = Ctx()
|
||||
module.register(ctx)
|
||||
|
||||
assert len(ctx.providers) == 1
|
||||
assert ctx.providers[0].name == "memory_system"
|
||||
Reference in New Issue
Block a user