Files
2026-07-13 13:12:33 +08:00

547 lines
18 KiB
Python

"""Unit tests for the capability_gate routing stage and its catalog facts.
The gate acts only on DEFINITE catalog signals: an unknown model, a
synthesized entry, or the anthropic/ollama flag-gated empty capabilities
must produce no action ("never act on ignorance"). The composed default —
no capability data at all — is byte-identical to the pre-gate pipeline and
is pinned separately by ``test_routing_policy_parity.py``.
All model/tier/provider names are synthetic dummy data; the catalog is
populated in-process (no network, no credentials).
"""
from __future__ import annotations
import asyncio
import copy
from collections.abc import Iterator
from types import SimpleNamespace
import pytest
from opensquilla.engine.pipeline import TurnContext
from opensquilla.engine.routing import (
CapabilityGateAction,
PolicyInputs,
RoutingDecision,
RoutingPolicyEngine,
TierCapability,
capability_gate,
record_capability_gate_trail,
)
from opensquilla.engine.steps import squilla_router as squilla_router_step
from opensquilla.engine.steps.squilla_router import _tier_capability_facts
from opensquilla.gateway.config import GatewayConfig
from opensquilla.provider.model_catalog import ModelCatalog, set_shared_catalog
VALID_TIERS = ["c0", "c1", "c2", "c3"]
TIERS = {
"c0": {"model": "dummy-nano-1"},
"c1": {"model": "dummy-mini-1"},
"c2": {"model": "dummy-pro-1"},
"c3": {"model": "dummy-max-1"},
}
def caps(**overrides: TierCapability) -> dict[str, TierCapability]:
facts = {name: TierCapability() for name in VALID_TIERS}
facts.update(overrides)
return facts
# ---------------------------------------------------------------------------
# vision walk-up
# ---------------------------------------------------------------------------
def test_vision_walk_up_to_nearest_vision_tier() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(supports_vision=False),
c2=TierCapability(supports_vision=True),
c3=TierCapability(supports_vision=True),
),
turn_has_image=True,
material_tokens=100,
)
assert result.tier == "c2" # nearest definite vision tier, not the top
assert result.actions == (CapabilityGateAction("vision_walk_up", "c0", "c2"),)
def test_vision_unknown_caps_no_op() -> None:
# supports_vision=None covers unknown models, synthesized entries, and
# the anthropic/ollama flag-gated empty capabilities: no action.
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(c2=TierCapability(supports_vision=True)),
turn_has_image=True,
material_tokens=100,
)
assert (result.tier, result.actions) == ("c0", ())
def test_vision_no_definite_target_no_op() -> None:
# The working tier definitely lacks vision but no tier above is
# definitely vision-capable: moving anyway would be acting on ignorance.
result = capability_gate(
"c1",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c1=TierCapability(supports_vision=False),
c2=TierCapability(supports_vision=False),
),
turn_has_image=True,
material_tokens=100,
)
assert (result.tier, result.actions) == ("c1", ())
def test_vision_rule_requires_image_turn() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(supports_vision=False),
c2=TierCapability(supports_vision=True),
),
turn_has_image=False,
material_tokens=100,
)
assert (result.tier, result.actions) == ("c0", ())
# ---------------------------------------------------------------------------
# context walk-up
# ---------------------------------------------------------------------------
def test_context_walk_up_to_first_fitting_tier() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(context_window=8_000),
c2=TierCapability(context_window=200_000),
),
turn_has_image=False,
material_tokens=30_000,
)
assert result.tier == "c2" # c1's window is unknown, so it cannot qualify
assert result.actions == (CapabilityGateAction("context_walk_up", "c0", "c2"),)
def test_context_unknown_window_no_op() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(c2=TierCapability(context_window=200_000)),
turn_has_image=False,
material_tokens=500_000,
)
assert (result.tier, result.actions) == ("c0", ())
def test_context_fitting_window_no_op() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(c0=TierCapability(context_window=64_000)),
turn_has_image=False,
material_tokens=30_000,
)
assert (result.tier, result.actions) == ("c0", ())
def test_context_top_tier_saturation_when_nothing_fits() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(context_window=8_000),
c3=TierCapability(context_window=100_000),
),
turn_has_image=False,
material_tokens=500_000,
)
assert result.tier == "c3"
assert result.actions == (CapabilityGateAction("context_walk_up", "c0", "c3"),)
def test_context_already_top_tier_no_op() -> None:
result = capability_gate(
"c3",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(c3=TierCapability(context_window=8_000)),
turn_has_image=False,
material_tokens=30_000,
)
assert (result.tier, result.actions) == ("c3", ())
def test_vision_then_context_walk_up_compose() -> None:
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(supports_vision=False),
c1=TierCapability(supports_vision=True, context_window=8_000),
c3=TierCapability(supports_vision=True, context_window=200_000),
),
turn_has_image=True,
material_tokens=30_000,
)
assert result.tier == "c3"
assert result.actions == (
CapabilityGateAction("vision_walk_up", "c0", "c1"),
CapabilityGateAction("context_walk_up", "c1", "c3"),
)
# ---------------------------------------------------------------------------
# no-signal defaults + trail recording
# ---------------------------------------------------------------------------
def test_no_capability_data_is_strict_no_op() -> None:
for tier_capabilities in (None, {}):
result = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=tier_capabilities,
turn_has_image=True,
material_tokens=500_000,
)
assert (result.tier, result.actions) == ("c0", ())
def test_unknown_tier_is_no_op() -> None:
result = capability_gate(
"zz",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(c2=TierCapability(supports_vision=True)),
turn_has_image=True,
material_tokens=100,
)
assert (result.tier, result.actions) == ("zz", ())
def test_trail_recorded_only_when_gate_acted() -> None:
extra: dict = {}
acted = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=caps(
c0=TierCapability(context_window=8_000),
c2=TierCapability(context_window=200_000),
),
turn_has_image=False,
material_tokens=30_000,
)
record_capability_gate_trail(extra, acted)
assert extra["capability_gate_applied"] is True
assert extra["routing_trail"] == [
{
"stage": "capability_gate",
"rule": "context_walk_up",
"from_tier": "c0",
"to_tier": "c2",
}
]
untouched: dict = {}
idle = capability_gate(
"c0",
valid_tiers=VALID_TIERS,
tier_capabilities=None,
turn_has_image=True,
material_tokens=500_000,
)
record_capability_gate_trail(untouched, idle)
assert untouched == {} # no keys at all on a no-op turn
# ---------------------------------------------------------------------------
# engine composition
# ---------------------------------------------------------------------------
def _engine_inputs(**overrides: object) -> PolicyInputs:
values: dict = {
"decision": RoutingDecision(
tier="c0", model="dummy-nano-1", confidence=0.9, source="v4_phase3"
),
"message": "please summarize this short note",
"router_cfg": SimpleNamespace(
default_tier="c1",
confidence_threshold=0.5,
confidence_high_tier_margin=0.05,
complaint_upgrade_enabled=True,
complaint_upgrade_steps=1,
complaint_upgrade_max_chars=160,
kv_cache_anti_downgrade_enabled=True,
kv_cache_anti_downgrade_window_seconds=600,
),
"tiers": TIERS,
"valid_tiers": VALID_TIERS,
"routing_history": None,
"extra": {},
"thinking_mode": "T1",
"prompt_policy": "P1",
"history_strategy": True,
"material_estimated_tokens": 100,
"context_window_tokens": 200_000,
"now": 10_000.0,
}
values.update(overrides)
return PolicyInputs(**values)
def test_engine_gate_runs_before_bind_and_reconciles() -> None:
extra: dict = {}
result = RoutingPolicyEngine().run(
_engine_inputs(
decision=RoutingDecision(
tier="c1", model="dummy-mini-1", confidence=0.9, source="v4_phase3"
),
extra=extra,
turn_has_image=True,
tier_capabilities=caps(
c1=TierCapability(supports_vision=False),
c2=TierCapability(supports_vision=True),
),
)
)
assert result.decision.tier == "c2"
assert result.decision.model == "dummy-pro-1" # bind rebound to the gated tier
assert extra["final_tier"] == "c2"
assert extra["final_route_class"] == "R2"
assert extra["capability_gate_applied"] is True
assert extra["routing_trail"] == [
{
"stage": "capability_gate",
"rule": "vision_walk_up",
"from_tier": "c1",
"to_tier": "c2",
}
]
assert result.thinking_mode == "T2" # reconciled against the gated final tier
def test_engine_default_inputs_match_pre_gate_pipeline() -> None:
baseline_extra: dict = {}
baseline = RoutingPolicyEngine().run(_engine_inputs(extra=baseline_extra))
gated_extra: dict = {}
gated = RoutingPolicyEngine().run(
_engine_inputs(
extra=gated_extra,
turn_has_image=False,
tier_capabilities=None,
)
)
assert gated.decision == baseline.decision
assert gated_extra == baseline_extra
assert "capability_gate_applied" not in gated_extra
assert "routing_trail" not in gated_extra
# ---------------------------------------------------------------------------
# catalog facts gathering (never act on ignorance, at the source)
# ---------------------------------------------------------------------------
@pytest.fixture()
def facts_catalog() -> Iterator[ModelCatalog]:
catalog = ModelCatalog()
catalog._populate_from_data(
[
{
"id": "dummy-vis-live-1",
"context_length": 200_000,
"supported_parameters": ["tools"],
"architecture": {"input_modalities": ["text", "image"]},
},
{
"id": "dummy-small-live-1",
"context_length": 8_000,
"supported_parameters": ["reasoning"],
"architecture": {"input_modalities": ["text"]},
},
]
)
catalog.set_user_overrides(
{"anthroprov/dummy-anthropic-x": {"supports_vision": True}}
)
set_shared_catalog(catalog)
try:
yield catalog
finally:
set_shared_catalog(None)
def test_facts_definite_signals_from_catalog(facts_catalog: ModelCatalog) -> None:
tiers = {
"c0": {"model": "dummy-small-live-1"},
"c1": {"model": "dummy-mini-unknown-1"},
"c2": {"model": "dummy-vis-live-1"},
}
facts = _tier_capability_facts(tiers, ["c0", "c1", "c2"], "mainprov")
assert facts["c0"] == TierCapability(supports_vision=False, context_window=8_000)
assert facts["c2"] == TierCapability(supports_vision=True, context_window=200_000)
def test_facts_synthesized_entry_gives_no_signal(facts_catalog: ModelCatalog) -> None:
facts = _tier_capability_facts(
{"c1": {"model": "dummy-mini-unknown-1"}}, ["c1"], "mainprov"
)
assert facts["c1"] == TierCapability(supports_vision=None, context_window=None)
def test_facts_user_override_window_counts_as_definite(facts_catalog: ModelCatalog) -> None:
# An operator-declared [models.*] context_window is knowledge, not an
# estimate: the gate may act on it exactly like a catalog-sourced window.
facts_catalog.set_user_overrides(
{"mainprov/dummy-pinned-window-1": {"context_window": 64_000}}
)
facts = _tier_capability_facts(
{"c1": {"model": "dummy-pinned-window-1"}}, ["c1"], "mainprov"
)
assert facts["c1"].context_window == 64_000
def test_facts_anthropic_flag_gated_caps_stay_unknown(facts_catalog: ModelCatalog) -> None:
# The user-override layer claims vision for this model. Under a normal
# provider that is a definite signal; under anthropic the capabilities
# are flag-gated to the empty ModelCapabilities() for one release, so
# the gate must treat the same claim as unknown.
overridden = _tier_capability_facts(
{"c2": {"model": "dummy-anthropic-x", "provider": "anthroprov"}},
["c2"],
"mainprov",
)
assert overridden["c2"].supports_vision is True
anthropic_facts = _tier_capability_facts(
{"c2": {"model": "dummy-anthropic-x", "provider": "anthropic"}},
["c2"],
"mainprov",
)
assert anthropic_facts["c2"] == TierCapability(
supports_vision=None, context_window=None
)
def test_facts_blank_model_gives_no_signal(facts_catalog: ModelCatalog) -> None:
facts = _tier_capability_facts({"c0": {"model": ""}}, ["c0"], "mainprov")
assert facts["c0"] == TierCapability()
# ---------------------------------------------------------------------------
# step-level: context walk-up through apply_squilla_router
# ---------------------------------------------------------------------------
class _FixedStrategy:
def __init__(self, tier: str, confidence: float, extra: dict) -> None:
self.tier = tier
self.confidence = confidence
self.extra = extra
async def classify(
self,
message: str,
valid_tiers: list[str],
routing_history: list[dict] | None = None,
) -> tuple[str, float, str, dict]:
return self.tier, self.confidence, "v4_phase3", copy.deepcopy(self.extra)
def _run_step(
config: GatewayConfig,
strategy: object,
*,
message: str = "please summarize this short note",
metadata: dict | None = None,
) -> TurnContext:
sr = squilla_router_step
sr._history_store.clear()
sr._strategy = None
sr._strategy_key = None
ctx = TurnContext(
message=message,
session_key="agent:capgate:main",
config=config,
provider=None,
model=config.llm.model,
tool_defs=[],
system_prompt="system",
attachments=[],
metadata=metadata or {},
)
original = sr._get_strategy
sr._get_strategy = lambda _config: strategy # type: ignore[assignment]
try:
asyncio.run(sr.apply_squilla_router(ctx))
finally:
sr._get_strategy = original # type: ignore[assignment]
sr._history_store.clear()
return ctx
def _gate_config() -> GatewayConfig:
config = GatewayConfig()
config.llm.provider = "mainprov"
config.llm.model = "dummy-base-model"
config.squilla_router.enabled = True
config.squilla_router.rollout_phase = "full"
config.squilla_router.tiers = {
"c0": {"model": "dummy-small-live-1"},
"c1": {"model": "dummy-mini-unknown-1"},
"c2": {"model": "dummy-vis-live-1"},
"c3": {"model": "dummy-max-unknown-1"},
}
return config
def test_step_context_walk_up_end_to_end(facts_catalog: ModelCatalog) -> None:
# 10k material tokens: above c0's definite 8k window, below the 25k
# large-context floor — only the capability gate can move this turn.
ctx = _run_step(
_gate_config(),
_FixedStrategy("c0", 0.9, {"route_class": "R0", "thinking_mode": "T0"}),
metadata={"material_estimated_tokens": 10_000},
)
assert ctx.metadata["routed_tier"] == "c2"
assert ctx.metadata["routed_model"] == "dummy-vis-live-1"
assert ctx.metadata["routing_extra"]["capability_gate_applied"] is True
assert ctx.metadata["routing_extra"]["routing_trail"] == [
{
"stage": "capability_gate",
"rule": "context_walk_up",
"from_tier": "c0",
"to_tier": "c2",
}
]
assert ctx.metadata["routing_extra"]["final_tier"] == "c2"
def test_step_no_gate_keys_when_catalog_unknown(facts_catalog: ModelCatalog) -> None:
config = _gate_config()
config.squilla_router.tiers = {
"c0": {"model": "dummy-unknown-a"},
"c1": {"model": "dummy-unknown-b"},
"c2": {"model": "dummy-unknown-c"},
"c3": {"model": "dummy-unknown-d"},
}
ctx = _run_step(
config,
_FixedStrategy("c0", 0.9, {"route_class": "R0", "thinking_mode": "T0"}),
metadata={"material_estimated_tokens": 10_000},
)
assert ctx.metadata["routed_tier"] == "c0"
assert "capability_gate_applied" not in ctx.metadata["routing_extra"]
assert "routing_trail" not in ctx.metadata["routing_extra"]