Files
6krrt/tests/test_load_candidates.py
adlee-was-taken c4ebc4be37 feat: thread capability gates into routing and add local vision fallback
chat_completions reads capabilities once and threads them into both passes of the re-route; 422s name vision/json-mode; a pinned model that can't satisfy the request gets a clear 422. The local_vision fallback (off by default) answers image requests that find no cloud vision candidate, rejecting remote image URLs as an SSRF guard and refusing empty answers rather than returning a 200. estimate_prompt_tokens now counts tool definitions.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-23 12:18:13 -04:00

192 lines
6.9 KiB
Python

"""Tests for dispatcher.load_candidates — how measured data reaches scoring.
Every behaviour here was found by a wrong routing decision rather than by
reasoning, so each gets explicit coverage:
- scoring reads the provider's ATTRIBUTED figures, because the attribution
ratio turned out to be a stable per-model property (750x between models,
~1.8x within one) rather than the noise it resembles up close
- the median, not the mean, because a few models still throw 50-90x outliers
- carbon reported as a static fallback is not data and must not rank anything
"""
import sqlite3
from pathlib import Path
import pytest
from dispatcher import (
FALLBACK_CARBON_SOURCE,
SEED_CATEGORY,
USD_PER_KWH,
gross_energy_kwh,
load_candidates,
)
ROOT = Path(__file__).resolve().parent.parent
SCHEMA_SQL = (ROOT / "schema.sql").read_text()
# 1000 W for 3.6 s = 1e-3 kWh, so the arithmetic below stays readable.
WATTS = 1000.0
SECONDS = 3.6
GROSS_KWH = 1e-3
@pytest.fixture
def db(tmp_path):
conn = sqlite3.connect(tmp_path / "test.db")
conn.row_factory = sqlite3.Row
conn.executescript(SCHEMA_SQL)
conn.execute(
"""
INSERT INTO models (model_id, provider, tier, availability, last_updated)
VALUES ('m', 'neuralwatt', 2, 'active', '2026-08-12T00:00:00+00:00')
"""
)
conn.commit()
yield conn
conn.close()
def _observe(conn, *, cost=1.0, carbon=37.0, source="agent_cache", category=SEED_CATEGORY):
conn.execute(
"""
INSERT INTO energy_observations (
model_id, provider, task_category, cost_usd,
carbon_g_co2eq, carbon_source, observed_at
) VALUES ('m', 'neuralwatt', ?, ?, ?, ?, '2026-08-12T00:00:00+00:00')
""",
(category, cost, carbon, source),
)
conn.commit()
def _only(conn):
return load_candidates(conn, "coding_general")[0]
# --- attribution is signal, not noise -------------------------------------
def test_gross_energy_identity_holds():
# The decomposition behind the finding: billed = power x duration x
# attribution. 1260.9 W x 1.523 s x 0.0158 = 8.43e-06 kWh, and the API
# reported 8.438e-06. gross_energy_kwh is a diagnostic on that identity.
assert gross_energy_kwh(1000.0, 3.6) == pytest.approx(1e-3)
assert gross_energy_kwh(1260.9, 1.523) * 0.0158 == pytest.approx(8.43e-06, rel=1e-2)
def test_scoring_reads_billed_cost_not_pre_attribution_energy(db):
# Given: a model whose billed cost is far below its share of pool gross,
# because many concurrent requests share its GPUs — deepseek-v4-flash
# bills ~1000x under gross. That discount is real money, so it must reach
# the score rather than being normalized away.
_observe(db, cost=1.154e-06)
_observe(db, cost=1.154e-06)
assert _only(db)["cost"] == pytest.approx(1.154e-06)
# --- median, not mean -----------------------------------------------------
def test_cost_uses_the_median_so_one_spike_cannot_dominate(db):
# Given: kimi-k3's real shape — ordinary samples plus a ~90x outlier
for c in (1.0, 1.0, 1.0, 1.0, 90.0):
_observe(db, cost=c)
assert _only(db)["cost"] == 1.0
def test_eco_uses_the_median_too(db):
for carbon in (1.0, 2.0, 3.0, 4.0, 500.0):
_observe(db, cost=1.0, carbon=carbon)
assert _only(db)["eco"] == 3.0
# --- fabricated carbon is not data ----------------------------------------
def test_static_fallback_carbon_is_excluded(db):
# Given: the glm-5.2-fast case. NeuralWatt could not resolve live grid
# data and substituted 475.0 while still reporting grid_id 'FI'.
for _ in range(3):
_observe(db, cost=1.0, carbon=8.5e-03, source=FALLBACK_CARBON_SOURCE)
row = _only(db)
# Then: no eco figure at all rather than a placeholder one. scoring turns
# that into a neutral 0.5, which is honest; ranking a model against a
# constant nobody measured would not be.
assert row["eco"] is None
# Cost is unaffected — it comes from the billing block, not the estimate.
assert row["cost"] == 1.0
def test_measured_carbon_survives_alongside_fallback_rows(db):
_observe(db, cost=1.0, carbon=100.0, source=FALLBACK_CARBON_SOURCE)
_observe(db, cost=1.0, carbon=2.0)
_observe(db, cost=1.0, carbon=4.0)
assert _only(db)["eco"] == 3.0
# --- scope ----------------------------------------------------------------
def test_only_reference_workload_observations_are_used(db):
# Given: organic traffic, whose energy tracks request shape far more than
# model efficiency (19x spread on one model), alongside the sweep
_observe(db, cost=1.0)
_observe(db, cost=1.0)
_observe(db, cost=999.0, category="coding_general")
assert _only(db)["cost"] == 1.0
def test_unswept_model_has_no_measurements(db):
row = _only(db)
assert row["cost"] is None
assert row["eco"] is None
assert row["samples"] == 0
# --- context estimation ---------------------------------------------------
def test_context_estimate_is_conservative_for_agent_traffic():
# Real measurement: opencode sent a prompt that tokenized to 106,158 while
# the chars/4 estimate put it under 94,196 — so it was routed to a model
# that could not hold it within its effective window. Agent traffic is
# code, JSON and tool schemas, which pack denser than prose.
from dispatcher import estimate_prompt_tokens
dense = "x" * 376_784 # the char count implied by that failure
# At the old divisor of 4 this estimated 94,196 and passed the filter.
assert estimate_prompt_tokens([{"role": "user", "content": dense}]) > 94_196
def test_context_estimate_counts_every_message():
from dispatcher import estimate_prompt_tokens
msgs = [{"role": "user", "content": "a" * 300}, {"role": "assistant", "content": "b" * 300}]
assert estimate_prompt_tokens(msgs) == 200
def test_context_estimate_reads_multimodal_text_parts():
from dispatcher import estimate_prompt_tokens
msgs = [{"role": "user", "content": [
{"type": "text", "text": "c" * 300},
{"type": "image_url", "image_url": {"url": "data:..."}},
]}]
assert estimate_prompt_tokens(msgs) == 100
def test_context_estimate_counts_tool_definitions():
# upstream_body = {**body, "model": target} forwards `tools` to the
# provider verbatim on every call, so it counts against the same context
# window and has to count here too. Omitting it undercounts every
# tool-carrying request -- nearly all agent traffic -- which is exactly
# how qwen3.6-35b got a live 400 for "too long ... even after
# compaction" while the router's own estimate said it fit.
from dispatcher import estimate_prompt_tokens
msgs = [{"role": "user", "content": "a" * 300}]
tools = [{"type": "function", "function": {
"name": "read_file",
"parameters": {"type": "object", "properties": {"path": {"type": "string"}}},
}}]
without_tools = estimate_prompt_tokens(msgs)
with_tools = estimate_prompt_tokens(msgs, tools=tools)
assert with_tools > without_tools