atlas-iac/testing/tests/test_hermes_voice_route_preflight.py

416 lines
13 KiB
Python

"""Release gates for bounded, advisory-only voice route preflight."""
from __future__ import annotations
import importlib.util
import json
import sys
import threading
import time
from http.server import ThreadingHTTPServer
from types import SimpleNamespace
from urllib.error import HTTPError
from urllib.request import Request, urlopen
import pytest
from testing.tests.test_hermes_chat_support import ROOT
HERMES = ROOT / "services" / "hermes"
def _load_runtime(monkeypatch):
"""Load the sibling runtime modules without requiring httpx in image CI."""
monkeypatch.setitem(sys.modules, "httpx", SimpleNamespace())
preflight_path = HERMES / "scripts" / "voice_route_preflight.py"
spec = importlib.util.spec_from_file_location("voice_route_preflight", preflight_path)
assert spec and spec.loader
preflight = importlib.util.module_from_spec(spec)
spec.loader.exec_module(preflight)
monkeypatch.setitem(sys.modules, "voice_route_preflight", preflight)
broker_path = HERMES / "scripts" / "classifier_broker.py"
spec = importlib.util.spec_from_file_location("classifier_broker", broker_path)
assert spec and spec.loader
broker = importlib.util.module_from_spec(spec)
spec.loader.exec_module(broker)
return preflight, broker
@pytest.mark.parametrize(
"payload",
[
{},
{"turn_id": "bad/id", "revision": 1, "transcript": "long enough text"},
{"turn_id": "nonce-1", "revision": True, "transcript": "long enough text"},
{"turn_id": "nonce-1", "revision": 0, "transcript": "long enough text"},
{"turn_id": "nonce-1", "revision": 1, "transcript": None},
{"turn_id": "nonce-1", "revision": 1, "transcript": "short"},
{"turn_id": "nonce-1", "revision": 1, "transcript": "x" * 513},
],
)
def test_preflight_input_bounds_fail_closed(monkeypatch, payload):
preflight, _ = _load_runtime(monkeypatch)
with pytest.raises(ValueError):
preflight.validate_request(payload)
def test_preflight_is_same_14b_model_and_never_hosts_a_foreground_turn(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
payload = preflight.inference_payload("Please compare these two safe approaches")
assert preflight.MODEL == "qwen2.5:14b-instruct-q4_0"
assert payload["model"] == preflight.MODEL
assert payload["stream"] is True
assert payload["max_tokens"] <= 16
assert payload["temperature"] == 0
assert "tools" not in payload
assert "provider" not in payload
assert preflight.TIMEOUT_SECONDS <= 0.75
def test_preflight_is_once_per_random_browser_turn_and_authority_preempts(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
cancel = coordinator.begin("8f" * 16 + "-1-1")
assert cancel is not None
class Resource:
closed = False
def close(self):
self.closed = True
client = Resource()
response = Resource()
assert coordinator.register(cancel, client=client, response=response)
started = time.monotonic()
coordinator.begin_authoritative()
assert time.monotonic() - started < 0.1
assert cancel.is_set()
assert client.closed and response.closed
assert coordinator.begin("9e" * 16 + "-1-1") is None
coordinator.end_authoritative()
coordinator.end()
# A completed browser turn cannot spend another 750 ms on a later partial.
assert coordinator.begin("8f" * 16 + "-1-1") is None
def test_coordinator_prunes_seen_turns_and_tolerates_close_failures(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
now = time.monotonic()
coordinator.seen = {f"turn-{index}": now for index in range(300)}
coordinator._prune(now)
assert len(coordinator.seen) == preflight.MAX_SEEN_TURNS
class BrokenResource:
def close(self):
raise RuntimeError("close failed")
cancel = coordinator.begin("fresh-turn")
assert cancel is not None
assert coordinator.register(cancel, client=BrokenResource(), response=BrokenResource())
coordinator.begin_authoritative()
assert cancel.is_set()
coordinator.end_authoritative()
coordinator.end_authoritative()
coordinator.end()
unrelated = threading.Event()
assert coordinator.register(unrelated) is False
assert unrelated.is_set()
idle = preflight.Coordinator()
idle.begin_authoritative()
idle.end_authoritative()
def _stream_with_fake_httpx(
monkeypatch,
preflight,
coordinator,
lines,
*,
fail_build=False,
close_raises=False,
preempt_on_send=False,
):
"""Drive the streamed parser and cleanup branches without network I/O."""
class Response:
def raise_for_status(self):
return None
def iter_lines(self):
yield from lines
def close(self):
if close_raises:
raise RuntimeError("response close failed")
response = Response()
class Client:
def __init__(self, timeout):
self.timeout = timeout
def build_request(self, *args, **kwargs):
if fail_build:
raise RuntimeError("build failed")
return (args, kwargs)
def send(self, request, stream=False):
assert stream is True
if preempt_on_send:
coordinator.begin_authoritative()
return response
def close(self):
if close_raises:
raise RuntimeError("client close failed")
monkeypatch.setattr(
preflight,
"httpx",
SimpleNamespace(Timeout=lambda *args, **kwargs: (args, kwargs), Client=Client),
)
cancel = coordinator.begin("stream-" + str(time.monotonic_ns()))
assert cancel is not None
result = coordinator.stream("This provisional request is stable", cancel)
if preempt_on_send:
coordinator.end_authoritative()
coordinator.end()
return result
def test_stream_parser_accepts_only_valid_bounded_json(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
content = json.dumps({"tier": "deep"})
lines = [
"ignored",
"data: not-json",
'data: {"choices":[]}',
'data: {"choices":[{"delta":{"content":7}}]}',
"data: "
+ json.dumps({"choices": [{"delta": {"content": content}}]}),
"data: [DONE]",
]
assert _stream_with_fake_httpx(monkeypatch, preflight, coordinator, lines) == "deep"
assert 'outcome="success"} 1' in coordinator.metrics()
invalid = preflight.Coordinator()
lines = [
"data: "
+ json.dumps(
{"choices": [{"delta": {"content": json.dumps({"tier": "hosted"})}}]}
)
]
assert _stream_with_fake_httpx(monkeypatch, preflight, invalid, lines) == ""
assert 'outcome="failure"} 1' in invalid.metrics()
def test_stream_fails_closed_on_oversize_preemption_and_cleanup_errors(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
oversized = preflight.Coordinator()
line = "data: " + json.dumps({"choices": [{"delta": {"content": "x" * 129}}]})
assert _stream_with_fake_httpx(monkeypatch, preflight, oversized, [line]) == ""
assert 'outcome="cancelled"} 1' in oversized.metrics()
preempted = preflight.Coordinator()
assert (
_stream_with_fake_httpx(
monkeypatch, preflight, preempted, [], preempt_on_send=True
)
== ""
)
assert 'outcome="cancelled"} 1' in preempted.metrics()
failed = preflight.Coordinator()
assert (
_stream_with_fake_httpx(
monkeypatch,
preflight,
failed,
[],
fail_build=True,
close_raises=True,
)
== ""
)
assert 'outcome="failure"} 1' in failed.metrics()
def test_stream_rejects_a_cancelled_reservation_before_network(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
cancel = coordinator.begin("cancel-before-network")
assert cancel is not None
cancel.set()
closed = {"value": False}
class Client:
def __init__(self, timeout):
self.timeout = timeout
def close(self):
closed["value"] = True
monkeypatch.setattr(
preflight,
"httpx",
SimpleNamespace(Timeout=lambda *args, **kwargs: None, Client=Client),
)
assert coordinator.stream("This request will be cancelled", cancel) == ""
assert closed["value"] is True
assert 'outcome="cancelled"} 1' in coordinator.metrics()
coordinator.end()
def test_absolute_deadline_closes_a_stalled_stream(monkeypatch):
preflight, _ = _load_runtime(monkeypatch)
monkeypatch.setattr(preflight, "TIMEOUT_SECONDS", 0.05)
closed = threading.Event()
class Response:
def raise_for_status(self):
return None
def iter_lines(self):
closed.wait(1)
if False:
yield ""
def close(self):
closed.set()
response = Response()
class Client:
def __init__(self, timeout):
self.timeout = timeout
def build_request(self, *args, **kwargs):
return (args, kwargs)
def send(self, request, stream=False):
assert stream is True
return response
def close(self):
response.close()
monkeypatch.setattr(
preflight,
"httpx",
SimpleNamespace(Timeout=lambda *args, **kwargs: (args, kwargs), Client=Client),
)
coordinator = preflight.Coordinator()
cancel = coordinator.begin("ab" * 16 + "-2-1")
assert cancel is not None
started = time.monotonic()
assert coordinator.stream("This provisional request is stable", cancel) == ""
elapsed = time.monotonic() - started
coordinator.end()
assert elapsed < 0.25
assert 'outcome="timeout"} 1' in coordinator.metrics()
def test_preflight_is_warm_only_and_final_payload_semantics_never_change(monkeypatch):
preflight, broker = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
authoritative = {
"model": "qwen2.5:14b-instruct-q4_0",
"messages": [
{"role": "system", "content": "normal routing contract"},
{"role": "user", "content": "Please compare the two deployments"},
],
"response_format": {"type": "json_object"},
}
before = broker.compact_payload(authoritative)
cancel = coordinator.begin("aa" * 16 + "-2-1")
assert cancel is not None
coordinator.end()
after = broker.compact_payload(authoritative)
assert before == after
assert not hasattr(coordinator, "cache")
assert not hasattr(coordinator, "remember")
assert not hasattr(coordinator, "take_hint")
assert not hasattr(preflight, "add_advisory_hint")
def _post(server, payload):
request = Request(
f"http://127.0.0.1:{server.server_port}/voice/route-preflight",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
with urlopen(request, timeout=2) as response:
return response.status, response.headers, response.read()
def test_handler_returns_exact_sanitized_binding_and_204_when_busy(monkeypatch):
preflight, broker = _load_runtime(monkeypatch)
coordinator = preflight.Coordinator()
monkeypatch.setattr(broker, "VOICE_PREFLIGHT", coordinator)
monkeypatch.setattr(coordinator, "stream", lambda transcript, cancel: "balanced")
server = ThreadingHTTPServer(("127.0.0.1", 0), broker.VoiceHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
payload = {
"turn_id": "cd" * 16 + "-3-1",
"revision": 4,
"transcript": "Private words that must never leave this boundary",
}
try:
status, headers, raw = _post(server, payload)
result = json.loads(raw)
assert status == 200
assert headers["Cache-Control"] == "no-store"
assert result == {
"turn_id": payload["turn_id"],
"revision": 4,
"tier": "balanced",
"target": "atlas/auto/balanced",
"advisory": True,
}
assert b"Private words" not in raw
status, headers, raw = _post(server, payload)
assert status == 204
assert headers["Cache-Control"] == "no-store"
assert raw == b""
finally:
server.shutdown()
server.server_close()
thread.join(timeout=2)
def test_public_and_authoritative_listeners_have_disjoint_route_surfaces(monkeypatch):
_, broker = _load_runtime(monkeypatch)
def post_status(handler, path):
server = ThreadingHTTPServer(("127.0.0.1", 0), handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
request = Request(
f"http://127.0.0.1:{server.server_port}{path}",
data=b"{}",
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urlopen(request, timeout=2) as response:
return response.status
except HTTPError as error:
return error.code
finally:
server.shutdown()
server.server_close()
thread.join(timeout=2)
assert post_status(broker.Handler, "/voice/route-preflight") == 404
assert post_status(broker.VoiceHandler, "/v1/chat/completions") == 404