2026-09-13 15:04:01 -05:00
|
|
|
"""Bounded evidence contracts for dynamically discovered provider models."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPTS = Path(__file__).parents[2] / "services" / "hermes" / "scripts"
|
|
|
|
|
sys.path.insert(0, str(SCRIPTS))
|
|
|
|
|
|
|
|
|
|
import model_capability_evaluator as evaluator # noqa: E402
|
|
|
|
|
from hermes_model_routing import Catalog, write_routing_catalog # noqa: E402
|
2026-09-13 15:46:39 -05:00
|
|
|
from model_evaluation_evidence import ( # noqa: E402
|
|
|
|
|
TRANSIENT_RETRY_SECONDS,
|
|
|
|
|
accepted_outcomes,
|
|
|
|
|
load_store,
|
|
|
|
|
retry_due,
|
|
|
|
|
unavailable_record,
|
|
|
|
|
)
|
2026-09-13 15:04:01 -05:00
|
|
|
from routing_catalog import load_catalog # noqa: E402
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Replies:
|
|
|
|
|
"""Record literal broker requests and return exact deterministic answers."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, role="advanced") -> None:
|
|
|
|
|
self.answers = iter(evaluator._probe_cases(role))
|
|
|
|
|
self.calls: list[tuple[str, str, str, str]] = []
|
|
|
|
|
|
|
|
|
|
def invoke(self, provider: str, model: str, effort: str, prompt: str):
|
|
|
|
|
self.calls.append((provider, model, effort, prompt))
|
|
|
|
|
_kind, _prompt, decision, invariants, checks = next(self.answers)
|
|
|
|
|
return evaluator.ProbeReply(
|
|
|
|
|
text=json.dumps(
|
|
|
|
|
{"decision": decision, "invariants": sorted(invariants), "checks": sorted(checks)}
|
|
|
|
|
),
|
|
|
|
|
input_tokens=3,
|
|
|
|
|
output_tokens=4,
|
|
|
|
|
latency_ms=9,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Unavailable:
|
|
|
|
|
"""Simulate a broker condition that must remain pending, not poor quality."""
|
|
|
|
|
|
|
|
|
|
def __init__(self) -> None:
|
|
|
|
|
self.calls = 0
|
|
|
|
|
|
|
|
|
|
def invoke(self, *_args):
|
|
|
|
|
self.calls += 1
|
|
|
|
|
raise evaluator.ProbeTransportError("timeout")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def catalog(*models: str) -> dict:
|
|
|
|
|
"""Return a live catalog whose models are unknown to reviewed policy."""
|
|
|
|
|
return {
|
|
|
|
|
"providers": {
|
|
|
|
|
"codex": {
|
|
|
|
|
"live": True,
|
|
|
|
|
"models": list(models),
|
|
|
|
|
"model_metadata": {
|
|
|
|
|
model: {
|
|
|
|
|
"description": "Built for difficult complex tasks",
|
|
|
|
|
"supported_reasoning_efforts": ["high", "xhigh"],
|
|
|
|
|
}
|
|
|
|
|
for model in models
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_eval_uses_literal_model_and_persists_only_sanitized_results(tmp_path):
|
|
|
|
|
transport = Replies()
|
|
|
|
|
path = tmp_path / "model-evaluations.json"
|
|
|
|
|
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora"), evidence_path=path, transport=transport, now=100
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
record = result["evaluations"]["codex"]["gpt-9-aurora"]
|
|
|
|
|
assert record["result"] == "pass"
|
|
|
|
|
assert record["role_fit"] == "verified"
|
|
|
|
|
assert record["proposed_role"] == "advanced"
|
|
|
|
|
assert record["attempted_efforts"] == ["high"]
|
|
|
|
|
assert record["tokens"] == {"input": 6, "output": 8, "total": 14}
|
|
|
|
|
assert record["latency_ms"] == 18
|
|
|
|
|
assert "prompt" not in record and "response" not in record
|
|
|
|
|
assert [call[:3] for call in transport.calls] == [
|
|
|
|
|
("codex", "gpt-9-aurora", "high"),
|
|
|
|
|
("codex", "gpt-9-aurora", "high"),
|
|
|
|
|
]
|
|
|
|
|
assert all("auto" not in call[1] and "route/" not in call[1] for call in transport.calls)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_success_is_cached_by_metadata_fingerprint_and_eval_version(tmp_path):
|
|
|
|
|
path = tmp_path / "model-evaluations.json"
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora"), evidence_path=path, transport=Replies(), now=100
|
|
|
|
|
)
|
|
|
|
|
cached = Replies()
|
|
|
|
|
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora"), evidence_path=path, transport=cached, now=101
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert cached.calls == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_admitted_catalog_refresh_keeps_a_verified_evaluation_cached(tmp_path):
|
|
|
|
|
"""Derived admission metadata cannot invalidate the original fingerprint."""
|
|
|
|
|
path = tmp_path / "catalog.json"
|
|
|
|
|
metadata = {
|
|
|
|
|
"gpt-9-aurora": {
|
|
|
|
|
"description": "Built for difficult complex tasks",
|
|
|
|
|
"supported_reasoning_efforts": ["high", "xhigh"],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
codex = Catalog("openai-codex", ["gpt-5.6-sol", "gpt-9-aurora"], True, True, "connected", metadata)
|
|
|
|
|
claude = Catalog("anthropic", [], True, True, "connected")
|
|
|
|
|
write_routing_catalog(path, codex, claude)
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
load_catalog(path), evidence_path=path.with_name("model-evaluations.json"),
|
|
|
|
|
transport=Replies(), now=100,
|
|
|
|
|
)
|
|
|
|
|
admitted = write_routing_catalog(path, codex, claude)
|
|
|
|
|
assert "gpt-9-aurora" in admitted["providers"]["codex"]["capability_pools"]["advanced"]
|
|
|
|
|
cached = Replies()
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
load_catalog(path), evidence_path=path.with_name("model-evaluations.json"),
|
|
|
|
|
transport=cached, now=101,
|
|
|
|
|
)
|
|
|
|
|
assert cached.calls == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_stale_success_is_not_reused_when_provider_metadata_changes(tmp_path):
|
|
|
|
|
path = tmp_path / "model-evaluations.json"
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora"), evidence_path=path, transport=Replies(), now=100
|
|
|
|
|
)
|
|
|
|
|
changed = {
|
|
|
|
|
"providers": {
|
|
|
|
|
"codex": {
|
|
|
|
|
"live": True,
|
|
|
|
|
"models": ["gpt-9-aurora"],
|
|
|
|
|
"model_metadata": {
|
|
|
|
|
"gpt-9-aurora": {
|
|
|
|
|
"description": "The frontier most capable state of the art model",
|
|
|
|
|
"supported_reasoning_efforts": ["xhigh"],
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
refreshed = Replies("frontier")
|
|
|
|
|
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
changed, evidence_path=path, transport=refreshed, now=101
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert len(refreshed.calls) == 2
|
|
|
|
|
assert result["evaluations"]["codex"]["gpt-9-aurora"]["proposed_role"] == "frontier"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_refresh_is_capped_and_transport_failure_is_not_quality_failure(tmp_path):
|
|
|
|
|
path = tmp_path / "model-evaluations.json"
|
|
|
|
|
unavailable = Unavailable()
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-a", "gpt-9-b", "gpt-9-c"),
|
|
|
|
|
evidence_path=path,
|
|
|
|
|
transport=unavailable,
|
|
|
|
|
now=100,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
records = result["evaluations"]["codex"]
|
|
|
|
|
assert unavailable.calls == 2
|
|
|
|
|
assert set(records) == {"gpt-9-a", "gpt-9-b"}
|
|
|
|
|
assert all(record["result"] == "unavailable" for record in records.values())
|
|
|
|
|
assert all(record["role_fit"] == "pending" for record in records.values())
|
|
|
|
|
assert all(record["failure_class"] == "timeout" for record in records.values())
|
|
|
|
|
retry = Unavailable()
|
|
|
|
|
evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-a", "gpt-9-b"), evidence_path=path, transport=retry, now=101
|
|
|
|
|
)
|
|
|
|
|
assert retry.calls == 0
|
|
|
|
|
|
|
|
|
|
|
2026-09-13 15:46:39 -05:00
|
|
|
def test_loopback_transport_failure_retries_before_the_next_hourly_refresh():
|
|
|
|
|
"""A broker startup race does not suppress a future model for six hours."""
|
|
|
|
|
record = unavailable_record(
|
|
|
|
|
provider="claude",
|
|
|
|
|
model="unknown",
|
|
|
|
|
fingerprint="fingerprint",
|
|
|
|
|
role="advanced",
|
|
|
|
|
efforts=["high"],
|
|
|
|
|
failure_class="network",
|
|
|
|
|
now=100,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert record["retry_after"] == 100 + TRANSIENT_RETRY_SECONDS
|
|
|
|
|
assert retry_due(record, 100 + TRANSIENT_RETRY_SECONDS - 1) is False
|
|
|
|
|
assert retry_due(record, 100 + TRANSIENT_RETRY_SECONDS) is True
|
|
|
|
|
|
|
|
|
|
|
2026-09-13 15:04:01 -05:00
|
|
|
def test_only_verified_acceptance_outcomes_are_retained(tmp_path):
|
|
|
|
|
path = tmp_path / "model-evaluations.json"
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora"),
|
|
|
|
|
evidence_path=path,
|
|
|
|
|
transport=Replies(),
|
|
|
|
|
now=100,
|
|
|
|
|
observed_outcomes=(
|
|
|
|
|
{"provider": "codex", "model": "gpt-9-aurora", "evidence_kind": "acceptance", "verified": True, "accepted": True, "evidence_id": "run-1"},
|
|
|
|
|
{"provider": "codex", "model": "gpt-9-aurora", "evidence_kind": "acceptance", "verified": False, "accepted": False, "evidence_id": "untrusted"},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert result["accepted_outcomes"] == [
|
|
|
|
|
{"provider": "codex", "model": "gpt-9-aurora", "accepted": True, "evidence_id": "run-1"}
|
|
|
|
|
]
|
|
|
|
|
record = load_store(path)["evaluations"]["codex:gpt-9-aurora"]
|
|
|
|
|
assert record["observed_acceptance"] == {"accepted": True, "evidence_id": "run-1"}
|
|
|
|
|
assert accepted_outcomes(({"provider": "codex", "model": "auto", "verified": True, "accepted": True},)) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_provider_description_and_supported_effort_levels_form_a_candidate(tmp_path):
|
|
|
|
|
transport = Replies("frontier")
|
|
|
|
|
document = {
|
|
|
|
|
"providers": {
|
|
|
|
|
"codex": {
|
|
|
|
|
"live": True,
|
|
|
|
|
"models": ["gpt-10-vertex"],
|
|
|
|
|
"model_metadata": {
|
|
|
|
|
"gpt-10-vertex": {
|
|
|
|
|
"description": "Our frontier model for the most capable state of the art reasoning",
|
|
|
|
|
"supportedEffortLevels": [{"level": "xhigh"}],
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
document, evidence_path=tmp_path / "evidence.json", transport=transport, now=100
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
record = result["evaluations"]["codex"]["gpt-10-vertex"]
|
|
|
|
|
assert record["proposed_role"] == "frontier"
|
|
|
|
|
assert record["attempted_efforts"] == ["xhigh"]
|
|
|
|
|
assert record["result"] == "pass"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_malformed_output_and_reviewed_policy_records_do_not_promote(tmp_path):
|
|
|
|
|
class Malformed:
|
|
|
|
|
def invoke(self, *_args):
|
|
|
|
|
return evaluator.ProbeReply(text="[]")
|
|
|
|
|
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
catalog("gpt-9-aurora", "gpt-5.6-sol"),
|
|
|
|
|
evidence_path=tmp_path / "evidence.json",
|
|
|
|
|
transport=Malformed(),
|
|
|
|
|
now=100,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
record = result["evaluations"]["codex"]["gpt-9-aurora"]
|
|
|
|
|
assert record["result"] == "unavailable"
|
|
|
|
|
assert record["role_fit"] == "pending"
|
|
|
|
|
assert record["failure_class"] == "invalid_response"
|
|
|
|
|
assert "gpt-5.6-sol" not in result["evaluations"]["codex"]
|
|
|
|
|
|
|
|
|
|
|
2026-09-13 15:46:39 -05:00
|
|
|
def test_reviewed_native_claude_alias_does_not_spend_an_evaluation(tmp_path):
|
|
|
|
|
"""A first-party alias resolving to reviewed Opus is already classified."""
|
|
|
|
|
document = {
|
|
|
|
|
"providers": {
|
|
|
|
|
"claude": {
|
|
|
|
|
"live": True,
|
|
|
|
|
"models": ["opus[1m]"],
|
|
|
|
|
"model_metadata": {
|
|
|
|
|
"opus[1m]": {
|
|
|
|
|
"resolvedModel": "claude-opus-5[1m]",
|
|
|
|
|
"supportsEffort": True,
|
|
|
|
|
"supportedEffortLevels": ["high", "xhigh"],
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
transport = Unavailable()
|
|
|
|
|
|
|
|
|
|
result = evaluator.evaluate_catalog_candidates(
|
|
|
|
|
document, evidence_path=tmp_path / "evidence.json", transport=transport, now=100
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert transport.calls == 0
|
|
|
|
|
assert result["evaluations"]["claude"] == {}
|
|
|
|
|
|
|
|
|
|
|
2026-09-13 15:04:01 -05:00
|
|
|
def test_native_broker_envelopes_keep_literal_models_and_account_usage(monkeypatch):
|
|
|
|
|
class Response:
|
|
|
|
|
def __init__(self, body):
|
|
|
|
|
self.body = body
|
|
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, *_args):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def read(self, _limit):
|
|
|
|
|
return self.body
|
|
|
|
|
|
|
|
|
|
seen = []
|
|
|
|
|
codex_body = json.dumps(
|
|
|
|
|
{
|
|
|
|
|
"output": [{"content": [{"type": "output_text", "text": '{"answer":"ok"}'}]}],
|
|
|
|
|
"usage": {"input_tokens": 7, "output_tokens": 999},
|
|
|
|
|
}
|
|
|
|
|
).encode()
|
|
|
|
|
claude_body = json.dumps(
|
|
|
|
|
{
|
|
|
|
|
"content": [{"type": "text", "text": '{"answer":"ok"}'}],
|
|
|
|
|
"usage": {"input_tokens": 8, "output_tokens": 9},
|
|
|
|
|
}
|
|
|
|
|
).encode()
|
|
|
|
|
|
|
|
|
|
def fake_urlopen(request, timeout):
|
|
|
|
|
seen.append((request, timeout))
|
|
|
|
|
return Response(codex_body if "responses" in request.full_url else claude_body)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(evaluator, "urlopen", fake_urlopen)
|
|
|
|
|
transport = evaluator.BrokerHttpTransport(key="private")
|
|
|
|
|
codex = transport.invoke("codex", "gpt-10-vertex", "xhigh", "probe")
|
|
|
|
|
claude = transport.invoke("claude", "claude-next", "high", "probe")
|
|
|
|
|
|
|
|
|
|
assert codex.output_tokens == 999 # Usage is recorded, never treated as a hard cutoff.
|
|
|
|
|
assert claude.input_tokens == 8
|
|
|
|
|
codex_payload = json.loads(seen[0][0].data)
|
|
|
|
|
claude_payload = json.loads(seen[1][0].data)
|
|
|
|
|
assert codex_payload["model"] == "gpt-10-vertex"
|
|
|
|
|
assert codex_payload["reasoning"] == {"effort": "xhigh"}
|
|
|
|
|
assert claude_payload["model"] == "claude-next"
|
|
|
|
|
assert claude_payload["output_config"] == {"effort": "high"}
|
|
|
|
|
assert all(request.get_header("Authorization") == "Bearer private" for request, _ in seen)
|
|
|
|
|
assert all(timeout == evaluator.REQUEST_TIMEOUT_SECONDS for _, timeout in seen)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_clipped_broker_body_is_inconclusive_not_a_quality_mismatch(monkeypatch):
|
|
|
|
|
class Response:
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, *_args):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def read(self, limit):
|
|
|
|
|
return b"x" * limit
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(evaluator, "urlopen", lambda *_args, **_kwargs: Response())
|
|
|
|
|
transport = evaluator.BrokerHttpTransport(key="private")
|
|
|
|
|
|
|
|
|
|
with pytest.raises(evaluator.ProbeTransportError, match="truncated"):
|
|
|
|
|
transport.invoke("codex", "gpt-10-vertex", "high", "probe")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_probe_score_requires_correct_finite_ids_and_rejects_unsafe_extras():
|
|
|
|
|
kind, _prompt, decision, invariants, checks = evaluator._probe_cases("advanced")[0]
|
|
|
|
|
good = json.dumps({"decision": decision, "invariants": sorted(invariants), "checks": sorted(checks)})
|
|
|
|
|
wrong = json.dumps({"decision": "D2", "invariants": sorted(invariants), "checks": sorted(checks)})
|
|
|
|
|
unsafe_extra = json.dumps(
|
|
|
|
|
{"decision": decision, "invariants": sorted(invariants | {"I3"}), "checks": sorted(checks)}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert evaluator._score(kind, good, decision, invariants, checks) is True
|
|
|
|
|
assert evaluator._score(kind, wrong, decision, invariants, checks) is False
|
|
|
|
|
assert evaluator._score(kind, unsafe_extra, decision, invariants, checks) is False
|
|
|
|
|
assert evaluator._score(kind, "[]", decision, invariants, checks) is None
|