116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
"""Wire-contract freeze for streamed session event payloads.
|
|
|
|
``session.event.router_decision`` drives the router HUD (tier pill,
|
|
tier-shift highlight, scanner popover) and ``session.event`` ensemble
|
|
progress frames reveal ensemble members mid-turn, on every client surface.
|
|
Their key names are a public protocol contract (see CLAUDE.md: public RPC
|
|
field names are stable).
|
|
|
|
- Renaming or removing any frozen key is a contract break and must fail here.
|
|
- Adding a key requires deliberately extending the frozen sets in this file —
|
|
that friction is the point: wire additions should be a conscious decision.
|
|
|
|
The shapes are frozen at the pure payload builders in channel_dispatch
|
|
(``_router_decision_payload`` / ``_ensemble_progress_payload``) over fully
|
|
synthetic engine events — no gateway, channel, or network involved.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from opensquilla.engine.types import EnsembleProgressEvent, RouterDecisionEvent
|
|
from opensquilla.gateway.channel_dispatch import (
|
|
_ensemble_progress_payload,
|
|
_router_decision_payload,
|
|
)
|
|
|
|
ROUTER_DECISION_KEYS = frozenset(
|
|
{
|
|
"tier",
|
|
"tier_index",
|
|
"model",
|
|
"baseline_model",
|
|
"source",
|
|
"confidence",
|
|
"probs",
|
|
"savings_pct",
|
|
"fallback",
|
|
"thinking_mode",
|
|
"prompt_policy",
|
|
"routing_applied",
|
|
"rollout_phase",
|
|
"context_window",
|
|
}
|
|
)
|
|
|
|
ENSEMBLE_PROGRESS_KEYS = frozenset(
|
|
{
|
|
"event_type",
|
|
"proposer_index",
|
|
"proposer_label",
|
|
"proposer_model",
|
|
"proposer_provider",
|
|
"sample_index",
|
|
"elapsed_ms",
|
|
"input_tokens",
|
|
"output_tokens",
|
|
"cost_usd",
|
|
"error",
|
|
}
|
|
)
|
|
|
|
|
|
def _synthetic_router_decision() -> RouterDecisionEvent:
|
|
return RouterDecisionEvent(
|
|
tier="c1",
|
|
tier_index=1,
|
|
model="test-provider/test-model",
|
|
baseline_model="test-provider/test-baseline",
|
|
source="classifier",
|
|
confidence=0.87,
|
|
probs=[0.05, 0.87, 0.06, 0.02],
|
|
savings_pct=0.42,
|
|
fallback=False,
|
|
thinking_mode="medium",
|
|
prompt_policy="tiered",
|
|
routing_applied=True,
|
|
rollout_phase="full",
|
|
context_window=128_000,
|
|
)
|
|
|
|
|
|
def test_router_decision_payload_keys_are_frozen() -> None:
|
|
payload = _router_decision_payload(_synthetic_router_decision())
|
|
assert set(payload) == ROUTER_DECISION_KEYS
|
|
|
|
|
|
def test_router_decision_payload_values_pass_through() -> None:
|
|
# Values must arrive verbatim; the HUD renders tier/model/probs raw and
|
|
# keys tier styling off the canonical c0-c3 strings.
|
|
payload = _router_decision_payload(_synthetic_router_decision())
|
|
assert payload["tier"] == "c1"
|
|
assert payload["tier_index"] == 1
|
|
assert payload["model"] == "test-provider/test-model"
|
|
assert payload["baseline_model"] == "test-provider/test-baseline"
|
|
assert payload["probs"] == [0.05, 0.87, 0.06, 0.02]
|
|
assert payload["context_window"] == 128_000
|
|
|
|
|
|
def test_ensemble_progress_payload_keys_are_frozen() -> None:
|
|
event = EnsembleProgressEvent(
|
|
event_type="proposer_done",
|
|
proposer_index=2,
|
|
proposer_label="proposer-3",
|
|
proposer_model="test-provider/test-model",
|
|
proposer_provider="test-provider",
|
|
sample_index=1,
|
|
elapsed_ms=1500,
|
|
input_tokens=1200,
|
|
output_tokens=340,
|
|
cost_usd=0.0021,
|
|
error="",
|
|
)
|
|
payload = _ensemble_progress_payload(event)
|
|
assert set(payload) == ENSEMBLE_PROGRESS_KEYS
|
|
assert payload["event_type"] == "proposer_done"
|
|
assert payload["proposer_model"] == "test-provider/test-model"
|