Add Memory Gateway agent plugin
This commit is contained in:
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PLUGIN_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PLUGIN_ROOT))
|
||||
|
||||
from memory_gateway_plugin.output import dumps_safe, short_id, summarize_data
|
||||
|
||||
|
||||
USER_ID = "test_user_memory_gateway_plugin"
|
||||
AGENT_ID = "test_hermes_memory_gateway_plugin"
|
||||
WORKSPACE_ID = "test_workspace_memory_gateway_plugin"
|
||||
SESSION_ID = "test_session_memory_gateway_plugin_interactive_002"
|
||||
PROMPT = "Please remember this integration test preference: Memory Gateway plugin should store summarized episodes, not raw transcripts."
|
||||
|
||||
|
||||
def _request(method: str, url: str, payload: dict[str, Any] | None = None, api_key: str = "") -> dict[str, Any]:
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if api_key:
|
||||
headers["X-API-Key"] = api_key
|
||||
body = None if payload is None else json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as response:
|
||||
raw = response.read().decode("utf-8")
|
||||
return {"ok": True, "status_code": getattr(response, "status", 200), "data": json.loads(raw) if raw else {}}
|
||||
except urllib.error.HTTPError as exc:
|
||||
return {"ok": False, "status_code": exc.code, "error": str(exc.reason)[:300]}
|
||||
except Exception as exc:
|
||||
return {"ok": False, "status_code": None, "error": str(exc)[:300]}
|
||||
|
||||
|
||||
def _gateway_url() -> str:
|
||||
return os.environ.get("MEMORY_GATEWAY_URL", "http://127.0.0.1:1934").rstrip("/")
|
||||
|
||||
|
||||
def _api_key() -> str:
|
||||
return os.environ.get("MEMORY_GATEWAY_API_KEY", "")
|
||||
|
||||
|
||||
def _audit_counts() -> dict[str, int]:
|
||||
result = _request("GET", _gateway_url() + "/v1/audit?limit=1000", api_key=_api_key())
|
||||
rows = result.get("data") or []
|
||||
actions = {"memory_search": 0, "append_episode": 0, "commit_session": 0}
|
||||
for row in rows:
|
||||
if row.get("actor_user_id") != USER_ID or row.get("actor_agent_id") != AGENT_ID:
|
||||
continue
|
||||
action = row.get("action")
|
||||
if action in actions:
|
||||
actions[action] += 1
|
||||
return actions
|
||||
|
||||
|
||||
def _run_cmd(args: list[str], timeout: int = 20, env: dict[str, str] | None = None) -> dict[str, Any]:
|
||||
try:
|
||||
completed = subprocess.run(args, capture_output=True, text=True, timeout=timeout, env=env, check=False)
|
||||
return {"ok": completed.returncode == 0, "returncode": completed.returncode, "stdout_chars": len(completed.stdout), "stderr_chars": len(completed.stderr)}
|
||||
except FileNotFoundError:
|
||||
return {"ok": False, "returncode": None, "error": "command_not_found"}
|
||||
except subprocess.TimeoutExpired:
|
||||
return {"ok": False, "returncode": None, "error": "timeout"}
|
||||
|
||||
|
||||
def _manual_instructions(reason: str) -> dict[str, Any]:
|
||||
return {
|
||||
"mode": "manual",
|
||||
"reason": reason,
|
||||
"commands": [
|
||||
"hermes plugins list",
|
||||
"hermes tools list",
|
||||
"MEMORY_GATEWAY_URL=http://127.0.0.1:1934 MEMORY_GATEWAY_DEFAULT_USER_ID=test_user_memory_gateway_plugin MEMORY_GATEWAY_DEFAULT_AGENT_ID=test_hermes_memory_gateway_plugin MEMORY_GATEWAY_DEFAULT_WORKSPACE_ID=test_workspace_memory_gateway_plugin MEMORY_GATEWAY_AUTO_COMMIT_SESSION=false hermes chat -Q -q 'Please remember this integration test preference: Memory Gateway plugin should store summarized episodes, not raw transcripts.' --source memory-gateway-plugin-test --toolsets memory_gateway",
|
||||
"python plugins/memory-gateway-agent/scripts/hermes_interactive_session_check.py",
|
||||
],
|
||||
"expected": [
|
||||
"Gateway audit memory_search count increases for the test user/agent.",
|
||||
"Gateway audit append_episode count increases for the test user/agent.",
|
||||
"commit_session count does not increase while MEMORY_GATEWAY_AUTO_COMMIT_SESSION=false.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def run() -> dict[str, Any]:
|
||||
hermes = shutil.which("hermes") or "/home/tom/.local/bin/hermes"
|
||||
plugin_list = _run_cmd([hermes, "plugins", "list"], timeout=10)
|
||||
tools_list = _run_cmd([hermes, "tools", "list"], timeout=10)
|
||||
health = _request("GET", _gateway_url() + "/health", api_key=_api_key())
|
||||
if not health.get("ok"):
|
||||
return {"ok": False, "mode": "blocked", "plugin_list": plugin_list, "tools_list": tools_list, "gateway_health": {"ok": False, "status_code": health.get("status_code"), "error": health.get("error")}, "manual": _manual_instructions("gateway_unhealthy")}
|
||||
|
||||
before = _audit_counts()
|
||||
env = os.environ.copy()
|
||||
env.update(
|
||||
{
|
||||
"MEMORY_GATEWAY_URL": _gateway_url(),
|
||||
"MEMORY_GATEWAY_DEFAULT_USER_ID": USER_ID,
|
||||
"MEMORY_GATEWAY_DEFAULT_AGENT_ID": AGENT_ID,
|
||||
"MEMORY_GATEWAY_DEFAULT_WORKSPACE_ID": WORKSPACE_ID,
|
||||
"MEMORY_GATEWAY_AUTO_SEARCH": "true",
|
||||
"MEMORY_GATEWAY_AUTO_APPEND_EPISODE": "true",
|
||||
"MEMORY_GATEWAY_AUTO_COMMIT_SESSION": os.environ.get("MEMORY_GATEWAY_AUTO_COMMIT_SESSION", "false"),
|
||||
}
|
||||
)
|
||||
chat = _run_cmd(
|
||||
[hermes, "chat", "-Q", "-q", PROMPT, "--source", "memory-gateway-plugin-test", "--toolsets", "memory_gateway"],
|
||||
timeout=int(os.environ.get("MEMORY_GATEWAY_PLUGIN_CHAT_TIMEOUT", "180")),
|
||||
env=env,
|
||||
)
|
||||
after = _audit_counts()
|
||||
delta = {key: after.get(key, 0) - before.get(key, 0) for key in before}
|
||||
auto_commit = env["MEMORY_GATEWAY_AUTO_COMMIT_SESSION"].strip().lower() in {"1", "true", "yes", "on"}
|
||||
expected_commit = delta.get("commit_session", 0) > 0 if auto_commit else delta.get("commit_session", 0) == 0
|
||||
passed = chat.get("ok") and delta.get("memory_search", 0) > 0 and delta.get("append_episode", 0) > 0 and expected_commit
|
||||
return {
|
||||
"ok": bool(passed),
|
||||
"mode": "auto" if chat.get("ok") else "manual",
|
||||
"plugin_list": plugin_list,
|
||||
"tools_list": tools_list,
|
||||
"gateway_health": {"ok": True, "status_code": health.get("status_code"), "data": summarize_data(health.get("data"))},
|
||||
"chat": chat,
|
||||
"auto_commit": auto_commit,
|
||||
"audit_before": before,
|
||||
"audit_after": after,
|
||||
"audit_delta": delta,
|
||||
"test_identity": {"user_id": USER_ID, "agent_id": AGENT_ID, "workspace_id": WORKSPACE_ID, "session_id": short_id(SESSION_ID)},
|
||||
"manual": None if chat.get("ok") else _manual_instructions(chat.get("error", "chat_command_failed")),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
result = run()
|
||||
print(dumps_safe(result))
|
||||
return 0 if result.get("ok") else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user