hermes-agent/tests/plugins/test_nemo_relay_plugin.py
Teknium 6b81590c55
test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions
Systematic prune per AGENTS.md test policy, one pass over every major
test tree (gateway, hermes_cli, tools, agent, run_agent, plugins, cli,
cron, tui_gateway, honcho/openviking, root-level):

- DELETE: source-reading tests (read_text/getsource on prod files),
  change-detector tests (exact catalog counts, model-name snapshots,
  config version literals), mock-echo tests (assert a mock returns what
  it was told), assertion-free/trivial tests, near-duplicate
  parametrizations (boundaries + one representative kept), async/sync
  twin duplicates, cosmetic within-file variations.
- KEEP (mandatory): security/redaction/approval guards, message-role
  alternation invariants, prompt-caching/deterministic-call-id
  invariants, issue-number regression tests (deduped), E2E tests.
- 6 test files deleted outright (script-style/no-assert or fully
  redundant); conftest.py, fakes/, fixtures/ untouched.
- tests/acp/conftest.py added: autouse fixture stubs the live
  models.dev/GitHub/Copilot/Anthropic inventory fetches that ACP server
  tests performed on every session create — test_server.py 147s → 3.4s,
  and the tests are now genuinely hermetic.
- Sleep-based slowness shrunk where safe (codex_ttfb_watchdog,
  compression_concurrent_fork, etc.); no wall-clock assertion tightened.

Verification: full hermetic suite via scripts/run_tests.sh —
2439 files, 31,130 tests passed, 0 failed, 0 flaky retries, 315s wall
(baseline: 583s wall, 13,564s subprocess CPU).
2026-07-29 13:10:23 -07:00

484 lines
16 KiB
Python

"""Tests for the bundled observability/nemo_relay plugin."""
from __future__ import annotations
import asyncio
import contextvars
import gc
import importlib
import json
import sys
import warnings
from pathlib import Path
from types import SimpleNamespace
import pytest
import yaml
from hermes_cli import lifecycle, plugins as plugin_api
from hermes_cli.observability import relay_runtime, relay_shared_metrics
from hermes_cli.plugins import PluginManager
REPO_ROOT = Path(__file__).resolve().parents[2]
PLUGIN_DIR = REPO_ROOT / "plugins" / "observability" / "nemo_relay"
class _FakeNemoRelay:
def __init__(self):
self.events = []
self._callbacks = {}
self._llm_starts = {}
self._scope_serial = 0
self._scope_context = contextvars.ContextVar(
"fake_nemo_relay_scope", default=None
)
self.ScopeType = SimpleNamespace(Agent="agent", Function="function")
self.scope = SimpleNamespace(
push=self._scope_push,
pop=self._scope_pop,
event=self._scope_event,
)
self.llm = SimpleNamespace(
call=self._llm_call,
call_end=self._llm_call_end,
execute=self._llm_execute,
)
self.tools = SimpleNamespace(
call=self._tool_call,
call_end=self._tool_call_end,
execute=self._tool_execute,
request_intercepts=self._tool_request_intercepts,
)
self.plugin = SimpleNamespace(
initialize=self._plugin_initialize,
clear=self._plugin_clear,
initialize_with_dynamic_plugins=self._plugin_initialize_with_dynamic,
)
self.subscribers = SimpleNamespace(
register=self._register_subscriber,
deregister=self._deregister_subscriber,
flush=self._flush_subscribers,
)
self.LLMRequest = _FakeLLMRequest
self.AtofExporterConfig = _FakeAtofExporterConfig
self.AtofExporterMode = SimpleNamespace(Append="append", Overwrite="overwrite")
self.AtofExporter = self._make_atof_exporter
self.AtifExporter = self._make_atif_exporter
self.get_scope_stack = self._get_scope_stack
def _scope_push(self, name, scope_type, **kwargs):
self._scope_serial += 1
handle = ("scope", name, self._scope_serial)
self._scope_context.set(handle)
self.events.append(("scope.push", name, scope_type, kwargs))
return handle
def _scope_pop(self, handle, **kwargs):
self.events.append(("scope.pop", handle, kwargs))
def _scope_event(self, name, **kwargs):
self.events.append(("scope.event", name, kwargs))
def _get_scope_stack(self):
current = self._scope_context.get()
self.events.append(("scope.sync", current))
return current
def _llm_call(self, name, request, **kwargs):
handle = ("llm", name)
self._llm_starts[handle] = kwargs
self.events.append(("llm.call", name, request.content, kwargs))
return handle
def _llm_call_end(self, handle, response, **kwargs):
self.events.append(("llm.call_end", handle, response, kwargs))
start = self._llm_starts.pop(handle, {})
event = SimpleNamespace(
kind="scope",
category="llm",
name=handle[1],
scope_category="end",
category_profile={"model_name": start.get("model_name")},
metadata={
**(start.get("metadata") or {}),
**(kwargs.get("metadata") or {}),
"otel.status_code": "OK",
},
data=response,
)
for callback in list(self._callbacks.values()):
callback(event)
def _llm_execute(self, name, request, func, **kwargs):
self.events.append(("llm.execute.start", name, request.content, kwargs))
handle = self._llm_call(name, request, **kwargs)
result = func(_FakeLLMRequest(request.headers, {"intercepted": True, **request.content}))
self._llm_call_end(
handle,
result,
**{key: value for key, value in kwargs.items() if key != "handle"},
)
self.events.append(("llm.execute.end", name, result, kwargs))
return result
def _tool_call(self, name, args, **kwargs):
handle = ("tool", name)
self.events.append(("tool.call", name, args, kwargs))
return handle
def _tool_call_end(self, handle, result, **kwargs):
self.events.append(("tool.call_end", handle, result, kwargs))
def _tool_execute(self, name, args, func, **kwargs):
self.events.append(("tool.execute.start", name, args, kwargs))
handle = self._tool_call(name, args, **kwargs)
result = func(args)
self._tool_call_end(
handle,
result,
**{key: value for key, value in kwargs.items() if key != "handle"},
)
self.events.append(("tool.execute.end", name, result, kwargs))
return result
def _tool_request_intercepts(self, name, args):
self.events.append(("tool.request_intercepts", name, args))
return {"intercepted": True, **args}
def _make_atof_exporter(self, config):
return _FakeAtofExporter(self.events, config)
def _make_atif_exporter(self, session_id, agent_name, agent_version, **kwargs):
return _FakeAtifExporter(self.events, session_id, agent_name, agent_version, kwargs)
async def _plugin_initialize(self, config):
self.events.append(("plugin.initialize", config))
return {"diagnostics": []}
async def _plugin_clear(self):
self.events.append(("plugin.clear",))
async def _plugin_initialize_with_dynamic(self, config, dynamic_plugins):
self.events.append(("plugin.activate_dynamic", config, dynamic_plugins))
return _FakePluginActivation(self.events)
def _register_subscriber(self, name, callback):
self._callbacks[name] = callback
self.events.append(("subscribers.register", name))
def _deregister_subscriber(self, name):
self._callbacks.pop(name, None)
self.events.append(("subscribers.deregister", name))
def _flush_subscribers(self):
self.events.append(("subscribers.flush",))
class _FakePluginActivation:
def __init__(self, events):
self.events = events
self.report = {"diagnostics": []}
async def close(self):
self.events.append(("plugin.activation.close",))
class _FakeLLMRequest:
def __init__(self, headers, content):
self.headers = headers
self.content = content
class _FakeAtofExporterConfig:
def __init__(self):
self.output_directory = ""
self.filename = "events.jsonl"
self.mode = "append"
class _FakeAtofExporter:
def __init__(self, events, config):
self.events = events
self.config = config
def register(self, name):
self.events.append(("atof.register", name, self.config.output_directory, self.config.filename))
def deregister(self, name):
self.events.append(("atof.deregister", name, self.config.output_directory, self.config.filename))
return True
class _FakeAtifExporter:
def __init__(self, events, session_id, agent_name, agent_version, kwargs):
self.events = events
self.session_id = session_id
self.agent_name = agent_name
self.agent_version = agent_version
self.kwargs = kwargs
def register(self, name):
self.events.append(("atif.register", name, self.session_id))
def deregister(self, name):
self.events.append(("atif.deregister", name, self.session_id))
return True
def export_json(self):
self.events.append(("atif.export", self.session_id))
return json.dumps({"session_id": self.session_id, "agent_name": self.agent_name})
def _fresh_plugin(monkeypatch, fake):
existing = sys.modules.get("plugins.observability.nemo_relay")
if existing is not None:
existing.reset_for_tests()
relay_shared_metrics._reset_for_tests()
relay_runtime._reset_for_tests()
monkeypatch.setattr(relay_runtime, "_load_nemo_relay", lambda: fake)
monkeypatch.setitem(sys.modules, "nemo_relay", fake)
sys.modules.pop("plugins.observability.nemo_relay", None)
plugin = importlib.import_module("plugins.observability.nemo_relay")
plugin.reset_for_tests()
return plugin
def _enable_dynamic_plugin(tmp_path, monkeypatch) -> Path:
plugins_toml = tmp_path / "plugins.toml"
plugins_toml.write_text(
f"""
version = 1
[[dynamic_plugins]]
plugin_id = "fixture"
kind = "rust_dynamic"
manifest_ref = "{(tmp_path / "fixture" / "relay-plugin.toml").as_posix()}"
[dynamic_plugins.config]
mode = "test"
""",
encoding="utf-8",
)
monkeypatch.setenv("HERMES_NEMO_RELAY_PLUGINS_TOML", str(plugins_toml))
return plugins_toml
def test_manifest_fields():
data = yaml.safe_load((PLUGIN_DIR / "plugin.yaml").read_text())
assert data["name"] == "nemo_relay"
assert set(data["hooks"]) == {
"on_session_start",
"on_session_end",
"on_session_finalize",
"on_session_reset",
"pre_llm_call",
"post_llm_call",
"pre_approval_request",
"post_approval_response",
"subagent_start",
"subagent_stop",
}
def test_nemo_relay_plugin_is_discoverable_as_bundled_plugin(tmp_path, monkeypatch):
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes_test"))
manager = PluginManager()
manager.discover_and_load()
loaded = manager._plugins["observability/nemo_relay"]
assert loaded.manifest.name == "nemo_relay"
assert loaded.manifest.source == "bundled"
assert not loaded.enabled
def test_shared_metrics_and_rich_plugin_share_one_core_session(
tmp_path,
monkeypatch,
):
from agent import relay_llm
fake = _FakeNemoRelay()
hermes_home = tmp_path / "hermes-home"
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
monkeypatch.setenv("HERMES_NEMO_RELAY_ATIF_ENABLED", "1")
monkeypatch.setenv(
"HERMES_NEMO_RELAY_ATIF_OUTPUT_DIRECTORY", str(tmp_path / "atif")
)
monkeypatch.setattr(
"hermes_cli.config.read_raw_config_readonly",
lambda: {"telemetry": {"shared_metrics": {"enabled": True}}},
)
plugin = _fresh_plugin(monkeypatch, fake)
manager = PluginManager()
class _Context:
def register_hook(self, name, callback):
manager._hooks.setdefault(name, []).append(callback)
plugin.register(_Context())
monkeypatch.setattr(plugin_api, "_plugin_manager", manager)
event = {
"session_id": "s1",
"task_id": "t1",
"api_request_id": "api-1",
"provider": "anthropic",
"model": "claude-sonnet",
"platform": "cli",
}
coordinator = relay_runtime.SESSION_COORDINATOR
lease = coordinator.acquire_conversation(
profile_key=relay_runtime.current_profile_key(),
session_id="s1",
platform="cli",
model=event["model"],
)
lifecycle.invoke_hook("on_session_start", **event)
turn = coordinator.begin_turn(
lease,
turn_id="turn-1",
task_id="t1",
)
lifecycle.invoke_hook(
"pre_api_request",
**event,
request={"body": {"messages": [{"role": "user", "content": "hi"}]}},
)
relay_llm.execute(
{"messages": [{"role": "user", "content": "hi"}]},
lambda _request: {
"assistant_message": {"role": "assistant", "content": "hello"}
},
session_id="s1",
name="anthropic",
model_name="claude-sonnet",
metadata={"api_request_id": "api-1", "api_mode": "custom"},
)
lifecycle.invoke_hook(
"post_api_request",
**event,
response={"assistant_message": {"role": "assistant", "content": "hello"}},
)
coordinator.end_turn(turn, outcome="success")
coordinator.release_conversation(lease)
lifecycle.finalize_session(session_id="s1")
session_pushes = [
item
for item in fake.events
if item[0] == "scope.push" and item[1] == relay_runtime.SESSION_SCOPE
]
assert len(session_pushes) == 1
register_metrics = next(
index
for index, item in enumerate(fake.events)
if item[0] == "subscribers.register"
and item[1].startswith("hermes.nemo_relay.shared_metrics.")
)
register_atif = next(
index for index, item in enumerate(fake.events) if item[0] == "atif.register"
)
open_session = fake.events.index(session_pushes[0])
assert register_metrics < register_atif < open_session
plugin_runtime = plugin._get_runtime()
assert plugin_runtime is not None
assert not plugin_runtime.sessions
assert relay_runtime.get_session_handle("s1") is None
packages = list(
(hermes_home / "telemetry" / "shared_metrics" / "outbox").glob("*.json")
)
assert len(packages) == 1
package = json.loads(packages[0].read_text(encoding="utf-8"))
assert package["metrics"][0]["name"] == "hermes.model_call.count"
assert package["metrics"][0]["value"] == 1
assert (tmp_path / "atif" / "hermes-atif-s1.json").exists()
def test_real_binding_shares_plugin_configuration_across_two_profiles(
tmp_path,
monkeypatch,
):
relay = pytest.importorskip("nemo_relay")
if getattr(relay, "_native", None) is None:
pytest.skip("NeMo Relay native binding is unavailable on this platform")
plugin = _fresh_plugin(monkeypatch, relay)
original_initialize = relay.plugin.initialize
original_clear = relay.plugin.clear
original_clear()
initialize_calls = []
clear_calls = 0
async def _initialize(config):
initialize_calls.append(config)
return await original_initialize(config)
def _clear():
nonlocal clear_calls
clear_calls += 1
return original_clear()
monkeypatch.setattr(relay.plugin, "initialize", _initialize)
monkeypatch.setattr(relay.plugin, "clear", _clear)
monkeypatch.setattr(
plugin,
"_load_settings",
lambda: plugin._Settings(plugins_config={"version": 1}),
)
profile_a = str(tmp_path / "profile-a")
profile_b = str(tmp_path / "profile-b")
host_a = relay_runtime.RelayRuntime(relay=relay, profile_key=profile_a)
host_b = relay_runtime.RelayRuntime(relay=relay, profile_key=profile_b)
try:
runtime_a = plugin._get_runtime(profile_key=profile_a, host=host_a)
runtime_b = plugin._get_runtime(profile_key=profile_b, host=host_b)
assert runtime_a is not None
assert runtime_b is not None
runtime_a.ensure_session({"session_id": "session-a"})
runtime_b.ensure_session({"session_id": "session-b"})
assert initialize_calls == [{"version": 1}]
assert relay.plugin.report() is not None
runtime_a.close_session({"session_id": "session-a"})
assert clear_calls == 0
assert relay.plugin.report() is not None
assert runtime_b.host.get_session("session-b") is not None
runtime_b.close_session({"session_id": "session-b"})
assert clear_calls == 1
assert relay.plugin.report() is None
finally:
plugin.reset_for_tests()
host_a.shutdown()
host_b.shutdown()
original_clear()
def test_relay_tool_request_rewrite_precedes_hermes_authorization_boundary(
tmp_path,
monkeypatch,
):
from hermes_cli.middleware import apply_tool_request_middleware
fake = _FakeNemoRelay()
plugin = _fresh_plugin(monkeypatch, fake)
_enable_dynamic_plugin(tmp_path, monkeypatch)
plugin.on_session_start(session_id="s1")
result = apply_tool_request_middleware(
"fixture-tool",
{"value": 1},
session_id="s1",
tool_call_id="tool-1",
)
assert result.payload == {"intercepted": True, "value": 1}
assert result.trace[0] == {"source": "nemo_relay"}