"""Contracts for Agent Hermes automatic route selection.""" from __future__ import annotations import importlib.util import sys from pathlib import Path SOURCE = ( Path(__file__).parents[2] / "services/hermes/plugins/auto-router/__init__.py" ) SPEC = importlib.util.spec_from_file_location("hermes_auto_router", SOURCE) assert SPEC and SPEC.loader router = importlib.util.module_from_spec(SPEC) sys.modules[SPEC.name] = router SPEC.loader.exec_module(router) def _status() -> dict: return { "providers": { "openai-codex": {"connected": True}, "anthropic": {"connected": True}, }, "routes": { "codex-low": [ "openai-codex/gpt-5.6-luna", "anthropic/claude-haiku-4-5-20251001", ], "codex-medium": [ "openai-codex/gpt-5.6-terra", "anthropic/claude-sonnet-5", ], "claude-medium": [ "anthropic/claude-sonnet-5", "openai-codex/gpt-5.6-terra", ], "claude-xhigh": [ "anthropic/claude-opus-5", "openai-codex/gpt-5.6-sol", ], }, } def test_heuristics_keep_simple_questions_cheap_and_risky_work_capped(): simple = router.heuristic_decision("Who is the current provider?") risky = router.heuristic_decision("Migrate production Vault credentials safely") assert (simple.shape, simple.effort, simple.provider) == ( "question", "low", "codex", ) assert (risky.shape, risky.effort, risky.provider) == ( "review", "xhigh", "claude", ) def test_jetson_decision_cannot_lower_explicit_implementation(monkeypatch): monkeypatch.setattr( router, "jetson_decision", lambda text: router.Decision( "question", "low", "claude", "jetson", "test", 42 ), ) decision = router.classify_task("Implement and test the new API handler") assert decision.shape == "implementation" assert decision.provider == "codex" assert decision.effort == "medium" assert decision.classifier == "jetson" def test_route_uses_managed_models_and_connected_provider_fallback(): status = _status() decision = router.Decision( "question", "low", "codex", "heuristic", "short question" ) plan = router.select_route(status, decision) assert plan["profile"] == "codex-low" assert plan["model"] == "gpt-5.6-luna" status["providers"]["openai-codex"]["connected"] = False status["routes"]["claude-low"] = [ "anthropic/claude-haiku-4-5-20251001", "openai-codex/gpt-5.6-luna", ] fallback = router.select_route(status, decision) assert fallback["profile"] == "claude-low" assert fallback["provider"] == "anthropic" def test_local_classifier_rejects_unbounded_or_unknown_values(): assert router._validated_local_decision( {"shape": "review", "effort": "max", "provider": "claude"}, 1 ) is None assert router._validated_local_decision( {"shape": "review", "effort": "xhigh", "provider": "claude"}, 1 ).effort == "xhigh" def test_manual_policy_is_reapplied_on_every_non_command_turn(monkeypatch): monkeypatch.setattr( router, "_current_policy", lambda: { "mode": "manual", "manual": {"provider": "claude", "effort": "medium", "model": ""}, }, ) monkeypatch.setattr(router, "_load_json", lambda path: _status()) plans = [] monkeypatch.setattr(router, "_apply_route", lambda ctx, agent, plan: plans.append(plan)) monkeypatch.setattr(router, "_record_plan", lambda policy, plan: None) class Agent: def _emit_status(self, message): self.message = message agent = Agent() router._pre_turn_route(object(), agent=agent, user_message="Continue the task") assert plans[0]["profile"] == "claude-medium" assert plans[0]["model"] == "claude-sonnet-5" assert agent.message.startswith("MANUAL")