Download tests/plugins/memory/test_openviking_optional_peer.py from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/tests/plugins/memory/test_openviking_optional_peer.py
- Command line
-
hf download hf://SaylorTwift/hermes-agent/tests/plugins/memory/test_openviking_optional_peer.py
-
curl -L -o test_openviking_optional_peer.py https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/tests/plugins/memory/test_openviking_optional_peer.py
12.7 kB
| """Optional peer identity must agree across setup, requests and memory writes.""" | |
| import json | |
| import os | |
| import threading | |
| from http.server import BaseHTTPRequestHandler, HTTPServer | |
| from pathlib import Path | |
| import pytest | |
| import yaml | |
| import plugins.memory.openviking as ov | |
| def isolated_config(tmp_path, monkeypatch): | |
| monkeypatch.setattr(Path, "home", lambda: tmp_path) | |
| monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes")) | |
| for key in (*ov._OPENVIKING_ENV_KEYS, "OPENVIKING_CLI_CONFIG_FILE"): | |
| # Track absent keys too, so setup's direct environment writes are undone. | |
| monkeypatch.setenv(key, "") | |
| monkeypatch.delenv(key, raising=False) | |
| def test_configured_peer_routing_is_preserved(tmp_path, monkeypatch, source, peer): | |
| config = {} | |
| if source == "env": | |
| monkeypatch.setenv("OPENVIKING_AGENT", peer) | |
| elif source == "yaml": | |
| config["agent"] = peer | |
| else: | |
| path = tmp_path / "ovcli.conf" | |
| path.write_text( | |
| json.dumps({"url": "http://localhost:1933", source: peer}), encoding="utf-8" | |
| ) | |
| config = {"use_ovcli_config": True, "ovcli_config_path": str(path)} | |
| settings = ov._resolve_connection_settings(config) | |
| client = ov._VikingClient("http://localhost:1933", agent=settings["agent"]) | |
| monkeypatch.setattr(client, "get", lambda *a, **kw: {"result": {"user": "alice"}}) | |
| provider = ov.OpenVikingMemoryProvider() | |
| uri = provider._build_memory_uri("preferences", client=client) | |
| assert settings["agent"] == peer | |
| assert client._headers().get("X-OpenViking-Actor-Peer", "") == peer | |
| prefix = f"peers/{peer}/" if peer else "" | |
| assert uri.startswith(f"viking://user/alice/{prefix}memories/preferences/mem_") | |
| def test_unconfigured_client_and_schema_do_not_supply_a_peer(): | |
| settings = ov._resolve_connection_settings({}) | |
| client = ov._VikingClient("http://localhost:1933") | |
| schema = { | |
| field["key"]: field | |
| for field in ov.OpenVikingMemoryProvider().get_config_schema() | |
| } | |
| assert settings["agent"] == "" | |
| assert schema["agent"]["default"] == "" | |
| assert "X-OpenViking-Actor-Peer" not in client._headers() | |
| assert "X-OpenViking-Actor-Peer" not in client._multipart_headers() | |
| def test_linked_profile_status_only_shows_a_configured_peer(tmp_path, peer): | |
| path = tmp_path / "ovcli.conf" | |
| path.write_text( | |
| json.dumps({"url": "http://localhost:1933", "actor_peer_id": peer}), | |
| encoding="utf-8", | |
| ) | |
| display = ov.OpenVikingMemoryProvider().get_status_config({ | |
| "use_ovcli_config": True, | |
| "ovcli_config_path": str(path), | |
| }) | |
| if peer: | |
| assert display["agent"] == peer | |
| else: | |
| assert "agent" not in display | |
| def test_memory_uri_uses_captured_peer_even_when_empty(monkeypatch, peer): | |
| client = ov._VikingClient("http://localhost:1933", agent=peer) | |
| monkeypatch.setattr(client, "get", lambda *a, **kw: {"result": {"user": "alice"}}) | |
| provider = ov.OpenVikingMemoryProvider() | |
| provider._agent = "later-peer" | |
| uri = provider._build_memory_uri("preferences", client=client) | |
| prefix = f"peers/{peer}/" if peer else "" | |
| assert uri.startswith(f"viking://user/alice/{prefix}memories/preferences/mem_") | |
| assert "later-peer" not in uri | |
| def test_new_setup_does_not_ask_for_or_save_peer( | |
| tmp_path, | |
| monkeypatch, | |
| save_to_store, | |
| credential, | |
| stale_env, | |
| ): | |
| from hermes_cli import memory_setup | |
| home = tmp_path / "hermes" | |
| home.mkdir() | |
| (home / ".env").write_text( | |
| "OPENVIKING_AGENT=old-peer\nOTHER_KEY=keep\n", encoding="utf-8" | |
| ) | |
| config = {"memory": {"openviking": {"agent": "old-peer", "recall_limit": 9}}} | |
| if stale_env: | |
| for key in ov._OPENVIKING_ENV_KEYS: | |
| monkeypatch.setenv( | |
| key, "old-peer" if key == "OPENVIKING_AGENT" else "old-value" | |
| ) | |
| monkeypatch.setenv("OTHER_KEY", "keep") | |
| validations = [] | |
| def validate(values, **kwargs): | |
| validations.append(dict(values)) | |
| role = ( | |
| "root" | |
| if credential == "root" | |
| else "user" | |
| if values.get("api_key") | |
| else None | |
| ) | |
| return True, "", role | |
| def prompt(label, default=None, secret=False): | |
| values = { | |
| "OpenViking server URL": "http://localhost:1933", | |
| "OpenViking user API key": "test-user-key", | |
| "OpenViking root API key": "test-root-key", | |
| "OpenViking API key": "test-service-key", | |
| "OpenViking account": "account", | |
| "OpenViking user": "alice", | |
| "OpenViking profile name": "personal", | |
| } | |
| assert label in values, f"Unexpected setup question: {label}" | |
| return values[label] | |
| def select(title, options, **kwargs): | |
| choices = { | |
| " OpenViking connection": 0 if credential == "service" else 1, | |
| " OpenViking credential": {"dev": 2, "user": 0, "root": 1}.get( | |
| credential, 0 | |
| ), | |
| " Save OpenViking config": int(save_to_store), | |
| } | |
| assert title in choices, f"Unexpected setup menu: {title}" | |
| return choices[title] | |
| monkeypatch.setattr(memory_setup, "_prompt", prompt) | |
| monkeypatch.setattr(memory_setup, "_curses_select", select) | |
| monkeypatch.setattr(ov, "_validate_openviking_reachability", lambda *a: (True, "")) | |
| monkeypatch.setattr(ov, "_validate_openviking_setup_values", validate) | |
| ov.OpenVikingMemoryProvider().post_setup(str(home), config) | |
| assert validations | |
| assert all(values["agent"] == "" for values in validations) | |
| assert "OPENVIKING_AGENT" not in (home / ".env").read_text(encoding="utf-8") | |
| assert "OTHER_KEY=keep" in (home / ".env").read_text(encoding="utf-8") | |
| saved_config = ov._load_hermes_openviking_config() | |
| assert saved_config["recall_limit"] == 9 | |
| settings = ov._resolve_connection_settings(saved_config) | |
| assert settings["agent"] == "" | |
| assert settings == { | |
| key: validations[-1][key] | |
| for key in ("endpoint", "api_key", "account", "user", "agent") | |
| } | |
| assert "OPENVIKING_AGENT" not in os.environ | |
| assert os.environ["OTHER_KEY"] == "keep" | |
| if save_to_store: | |
| saved = json.loads( | |
| Path(saved_config["ovcli_config_path"]).read_text(encoding="utf-8") | |
| ) | |
| assert "actor_peer_id" not in saved | |
| assert "agent_id" not in saved | |
| def test_hermes_only_save_uses_the_same_clean_values_in_file_and_process( | |
| tmp_path, peer | |
| ): | |
| from dotenv import dotenv_values | |
| env_path = tmp_path / ".env" | |
| ov._setup._save_hermes_only_config( | |
| config={"memory": {}}, | |
| provider_config={}, | |
| env_path=env_path, | |
| values={ | |
| "endpoint": "http://localhost:29333", | |
| "api_key": "test\r\n-key\x00", | |
| "agent": peer, | |
| }, | |
| ) | |
| expected = { | |
| "OPENVIKING_ENDPOINT": "http://localhost:29333", | |
| "OPENVIKING_API_KEY": "test-key", | |
| } | |
| if peer: | |
| expected["OPENVIKING_AGENT"] = peer | |
| assert dict(dotenv_values(env_path)) == expected | |
| assert { | |
| key: os.environ[key] for key in ov._OPENVIKING_ENV_KEYS if key in os.environ | |
| } == expected | |
| def test_hermes_only_save_failure_leaves_process_environment_unchanged( | |
| tmp_path, monkeypatch | |
| ): | |
| for key in ov._OPENVIKING_ENV_KEYS: | |
| monkeypatch.setenv(key, "old-value") | |
| def fail_write(*args, **kwargs): | |
| raise OSError("test write failure") | |
| monkeypatch.setattr(ov, "_write_env_vars", fail_write) | |
| with pytest.raises(OSError, match="test write failure"): | |
| ov._setup._save_hermes_only_config( | |
| config={"memory": {}}, | |
| provider_config={}, | |
| env_path=tmp_path / ".env", | |
| values={"endpoint": "http://localhost:29333", "api_key": "test-key"}, | |
| ) | |
| assert all(os.environ[key] == "old-value" for key in ov._OPENVIKING_ENV_KEYS) | |
| def test_wire_requests_keep_writes_and_session_messages_in_the_selected_scope( | |
| tmp_path, | |
| monkeypatch, | |
| peer, | |
| ): | |
| records = [] | |
| class Handler(BaseHTTPRequestHandler): | |
| def log_message(self, *args): | |
| pass | |
| def respond(self, payload): | |
| body = json.dumps(payload).encode() | |
| self.send_response(200) | |
| self.send_header("Content-Type", "application/json") | |
| self.send_header("Content-Length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def do_GET(self): | |
| if self.path == "/health": | |
| self.respond({"status": "ok", "healthy": True, "version": "test"}) | |
| elif self.path == "/api/v1/system/status": | |
| self.respond({"result": {"user": "alice"}}) | |
| else: | |
| self.send_error(404) | |
| def do_POST(self): | |
| payload = json.loads(self.rfile.read(int(self.headers["Content-Length"]))) | |
| records.append((self.path, dict(self.headers), payload)) | |
| self.respond({"status": "ok", "result": {"written_bytes": 10}}) | |
| server = HTTPServer(("127.0.0.1", 0), Handler) | |
| thread = threading.Thread(target=server.serve_forever, daemon=True) | |
| thread.start() | |
| home = tmp_path / "hermes" | |
| home.mkdir() | |
| provider_config = {"endpoint": f"http://127.0.0.1:{server.server_port}"} | |
| if peer: | |
| provider_config["agent"] = peer | |
| (home / "config.yaml").write_text( | |
| yaml.safe_dump({ | |
| "memory": {"provider": "openviking", "openviking": provider_config} | |
| }), | |
| encoding="utf-8", | |
| ) | |
| provider = ov.OpenVikingMemoryProvider() | |
| try: | |
| provider.initialize("peer-test", hermes_home=str(home)) | |
| assert provider._client is not None | |
| result = json.loads( | |
| provider.handle_tool_call("viking_remember", {"content": "I like tea"}) | |
| ) | |
| assert result["status"] == "submitted" | |
| provider.on_memory_write("add", "user", "I like coffee") | |
| provider.sync_turn("hello", "hi", session_id="peer-test") | |
| assert provider._drain_writers("peer-test", timeout=5.0) | |
| provider.sync_turn( | |
| "next", | |
| "reply", | |
| session_id="peer-test", | |
| messages=[ | |
| {"role": "user", "content": "next"}, | |
| {"role": "assistant", "content": "reply"}, | |
| ], | |
| ) | |
| assert provider._drain_writers("peer-test", timeout=5.0) | |
| provider.on_session_end([]) | |
| finally: | |
| provider.shutdown() | |
| server.shutdown() | |
| server.server_close() | |
| thread.join(timeout=3.0) | |
| assert records | |
| for _path, headers, _payload in records: | |
| if peer: | |
| assert headers["X-OpenViking-Actor-Peer"] == peer | |
| else: | |
| assert "X-OpenViking-Actor-Peer" not in headers | |
| writes = [ | |
| payload for path, _, payload in records if path == "/api/v1/content/write" | |
| ] | |
| prefix = f"peers/{peer}/" if peer else "" | |
| assert {write["content"] for write in writes} == {"I like coffee"} | |
| assert all( | |
| write["uri"].startswith(f"viking://user/alice/{prefix}memories/") | |
| for write in writes | |
| ) | |
| remember_messages = [ | |
| (path, payload) | |
| for path, _, payload in records | |
| if path.startswith("/api/v1/sessions/hermes-remember-") | |
| and path.endswith("/messages") | |
| ] | |
| assert len(remember_messages) == 1 | |
| remember_path, remember_message = remember_messages[0] | |
| remember_session = remember_path.removesuffix("/messages") | |
| assert remember_message == { | |
| "role": "user", | |
| "parts": [{"type": "text", "text": "I like tea"}], | |
| } | |
| assert any(path == f"{remember_session}/commit" for path, _, _ in records) | |
| batches = [ | |
| payload["messages"] | |
| for path, _, payload in records | |
| if path.endswith("/messages/batch") | |
| ] | |
| assert len(batches) == 2 | |
| for batch in batches: | |
| assert "peer_id" not in batch[0] | |
| if peer: | |
| assert batch[1]["peer_id"] == peer | |
| else: | |
| assert "peer_id" not in batch[1] | |
| assert any(path.endswith("/commit") for path, _, _ in records) | |