Files
6krrt/tests/test_poller_parsing.py
adlee-was-taken 8ae5c2f3f6 fix: knobs that validated and did nothing
The previous commit made config strict so a WRONG key fails at load. That
guards typos; it says nothing about keys that are spelled correctly, parse
cleanly, and are read by nobody. Four of those:

`context.per_model_overrides` was the worst, because config.yaml shipped a
worked example for it -- follow that example and the model's effective window
does not move. It is now typed (ContextOverride, not a bare dict, so a typo
INSIDE an override is an error too) and read by
poller.ModelRow.effective_context_window. Both keys fall back independently,
compared with `is not None` rather than `or`: an override of 0 reserve tokens
must mean zero, and present-but-falsy defaults are a trap evals/tasks.yaml
deliberately tests models on.

`logging.log_path` named "router.log", which nothing ever wrote -- the
dispatcher prints to stderr and systemd hands that to the journal. Deleted,
with a note in config.yaml saying where the logs actually are.

`tiers` is documented as labels "for logging/dashboards" and reached neither.
/health is the dashboard surface, so it reports them now.

`objective.quality_tolerance: 0` is a fifth of the same family, arriving from
the other direction: the validator accepts [0, 1) and `band()` divided by it,
so a legitimate setting -- "never trade quality for cost" -- turned every
request into a 500 on a config that had loaded cleanly. 0 now means no band:
strict quality ordering, cost breaking only exact ties.

And `python config.py`, the setup step documented in README.md and CLAUDE.md,
printed cfg.weights -- replaced by cfg.objective some commits ago. It said
"Config loaded OK" and then died with AttributeError, which is a bad look for
the one command whose entire job is to prove the config is fine. The summary
is now a function so a test pins the attribute names against the next rename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSkcSD2Jzkxo1Kw27ecfXJ
2026-08-22 20:57:50 -04:00

192 lines
6.9 KiB
Python

"""Tests for poller.py's pure catalog-parsing helpers.
The NeuralWatt catalog encodes serving class in the model id and access
gating only in prose, so both are parsed rather than read from a field.
Cases below are taken from the live catalog.
"""
import pytest
from poller import parse_access_level, parse_base_model_id, parse_serving_class
# --- serving class --------------------------------------------------------
@pytest.mark.parametrize(
"model_id,expected",
[
# base rows take the schema defaults
("gemma-4-31b", ("standard", "default", "full")),
("kimi-k3", ("standard", "default", "full")),
# one dimension at a time
("kimi-k3-fast", ("standard", "reduced", "full")),
("kimi-k3-flex", ("flex", "default", "full")),
("glm-5.2-short", ("standard", "default", "short")),
# the dimensions are orthogonal and combine freely
("glm-5.2-short-fast", ("standard", "reduced", "short")),
("glm-5.2-short-flex", ("flex", "default", "short")),
("glm-5.2-short-fast-flex", ("flex", "reduced", "short")),
],
)
def test_parse_serving_class(model_id, expected):
assert parse_serving_class(model_id) == expected
def test_flash_is_not_read_as_fast():
# Given: a base model whose name ends in 'flash' — a near-miss for the
# '-fast' suffix that a substring match would misclassify, wrongly
# demoting the cheapest model in the catalog out of tier 1
# Then: whole-segment matching leaves it a plain standard row
assert parse_serving_class("deepseek-v4-flash") == ("standard", "default", "full")
assert parse_serving_class("deepseek-v4-flash-flex") == ("flex", "default", "full")
def test_namespaced_and_mixed_case_ids():
# Given: the HF-style duplicate row, which carries a namespace and caps
assert parse_serving_class("deepseek-ai/DeepSeek-V4-Flash") == (
"standard",
"default",
"full",
)
def test_suffix_only_id_is_not_stripped_to_nothing():
# Given: a degenerate id consisting solely of a suffix token, the loop
# must leave at least one segment rather than consuming the whole id
assert parse_serving_class("fast") == ("standard", "default", "full")
# --- access level ---------------------------------------------------------
@pytest.mark.parametrize(
"display_name,description,expected",
[
("GLM-5.2 (short)", "... Private preview (grant-gated).", "preview"),
("GLM-5.2", "Private GLM-5.2 test canary", "canary"),
# gating can appear in the display name rather than the description
("DeepSeek V4 Flash 0731 (Canary)", "1M context window.", "canary"),
("Gemma 4 31B", "Google Gemma 4 31B — multimodal with tool calling.", "public"),
(None, None, "public"),
],
)
def test_parse_access_level(display_name, description, expected):
assert parse_access_level(display_name, description) == expected
def test_preview_takes_precedence_over_canary():
# Given: prose mentioning both, the more restrictive label wins
assert parse_access_level("x", "Private preview (grant-gated) canary") == "preview"
# --- model family ---------------------------------------------------------
@pytest.mark.parametrize(
"model_id,expected",
[
("gemma-4-31b", "gemma-4-31b"),
("kimi-k3", "kimi-k3"),
("kimi-k3-fast", "kimi-k3"),
("kimi-k3-flex", "kimi-k3"),
# every serving dimension collapses, in any combination
("glm-5.2-short-fast-flex", "glm-5.2"),
("glm-5.2-short", "glm-5.2"),
# the HF-style duplicate row folds into the same family
("deepseek-ai/DeepSeek-V4-Flash", "deepseek-v4-flash"),
# ...without 'flash' being mistaken for the '-fast' suffix
("deepseek-v4-flash", "deepseek-v4-flash"),
("deepseek-v4-flash-flex", "deepseek-v4-flash"),
],
)
def test_parse_base_model_id(model_id, expected):
assert parse_base_model_id(model_id) == expected
def test_family_never_collapses_to_empty():
# A degenerate id made only of suffix tokens must keep a segment, or every
# such row would share one meaningless family
assert parse_base_model_id("flex") == "flex"
# --- per-model context overrides ------------------------------------------
#
# The knob validated and was never read, while config.yaml shipped a worked
# example for it. Anyone who followed that example got silence.
def _row(**overrides):
from poller import ModelRow
fields = dict(
model_id="qwen3.6-35b", provider="neuralwatt", base_model_id="qwen3.6-35b",
display_name=None, cost_per_1m_prompt=None, cost_per_1m_completion=None,
cost_per_1m_prompt_cached=None, context_window=200_000,
max_output_tokens=None, supports_tools=True, supports_json_mode=True,
supports_vision=False, supports_reasoning=True,
reasoning_default_enabled=True, latency_class="standard",
reasoning_mode="default", context_variant="full", access_level="public",
pricing_tbd=False, deprecated=False,
)
fields.update(overrides)
return ModelRow(**fields)
def _cfg(overrides=None):
import copy
import yaml
from config import RouterConfig
with open("config.yaml") as fh:
raw = copy.deepcopy(yaml.safe_load(fh))
raw["context"]["per_model_overrides"] = overrides or {}
return RouterConfig(**raw)
def test_the_global_safety_factor_applies_without_an_override():
# 200000 * 0.75 - 4096
assert _row().effective_context_window(_cfg()) == 145_904
def test_a_per_model_safety_factor_is_actually_applied():
cfg = _cfg({"qwen3.6-35b": {"safety_factor": 0.85}})
# 200000 * 0.85 - 4096
assert _row().effective_context_window(cfg) == 165_904
def test_an_override_for_another_model_does_not_leak():
cfg = _cfg({"kimi-k3": {"safety_factor": 0.85}})
assert _row().effective_context_window(cfg) == 145_904
def test_a_zero_output_reserve_means_zero_not_unset():
"""Present-but-falsy: `or` would silently fall through to 4096."""
cfg = _cfg({"qwen3.6-35b": {"output_reserve_tokens": 0}})
assert _row().effective_context_window(cfg) == 150_000
def test_an_override_reserve_beats_the_models_advertised_ceiling():
cfg = _cfg({"qwen3.6-35b": {"output_reserve_tokens": 8192}})
assert _row(max_output_tokens=16384).effective_context_window(cfg) == 141_808
def test_each_key_falls_back_independently():
cfg = _cfg({"qwen3.6-35b": {"safety_factor": 0.85}})
# factor overridden, reserve still the global default
assert _row().effective_context_window(cfg) == 165_904
def test_a_typo_inside_an_override_is_an_error():
"""The point of typing it: StrictModel has to reach inside the block."""
with pytest.raises(Exception, match="safety_factr|Extra inputs"):
_cfg({"qwen3.6-35b": {"safety_factr": 0.85}})
def test_an_out_of_range_override_is_an_error():
with pytest.raises(Exception, match="safety_factor"):
_cfg({"qwen3.6-35b": {"safety_factor": 1.5}})