blindspot / eval /tests /test_common.py
yashwrites's picture
Blindspot: initial Space deploy
54f0281 verified
Raw History Blame Contribute Delete
7.32 kB
"""Cost estimator math, cache keys, cache store, bootstrap CI helper."""
from __future__ import annotations
import math
import numpy as np
import pytest
from eval.common import (
CI,
BudgetExceeded,
CallPlan,
ResponseCache,
SpendTracker,
bootstrap_ci,
bootstrap_paired_diff,
cache_key,
check_budget,
estimate_cost,
fmt_pct,
image_tokens,
parse_price_overrides,
price_for,
stable_seed,
text_tokens,
usage_cost,
wilson_ci,
)
from eval.llm import estimate_request_input_tokens, png_dims, request_key, strip_images
PRICES = {"m-small": (2.0, 10.0), "m-big": (4.0, 20.0)}
# ------------------------------------------------------------------------------------------------ cost
def test_estimate_cost_math() -> None:
est = estimate_cost(
[CallPlan("a", "m-small", 100, 2000, 500, 1000), CallPlan("b", "m-big", 10, 1000, 100, 400)], PRICES
)
# a: 100 × (2000×2 + 500×10)/1e6 = 0.9 ; worst 100 × (4000 + 10000)/1e6 = 1.4
# b: 10 × (1000×4 + 100×20)/1e6 = 0.06 ; worst 10 × (4000 + 8000)/1e6 = 0.12
assert est.expected_usd == pytest.approx(0.96)
assert est.worst_usd == pytest.approx(1.52)
assert "**0.96**" in est.table()
def test_zero_calls_cost_nothing_even_for_unknown_model() -> None:
assert estimate_cost([CallPlan("x", "unknown-model", 0, 10, 10, 10)], PRICES).expected_usd == 0.0
def test_unknown_model_price_refuses() -> None:
with pytest.raises(SystemExit):
price_for("not-a-model", PRICES)
def test_price_override_parsing() -> None:
p = parse_price_overrides(["claude-sonnet-5-5=1.5,7.5", "new-model=3,9"])
assert p["claude-sonnet-5-5"] == (1.5, 7.5)
assert p["new-model"] == (3.0, 9.0)
assert p["claude-opus-5-5"] == (4.0, 20.0) # default table retained
def test_check_budget_refuses_live_but_not_dry() -> None:
est = estimate_cost([CallPlan("a", "m-small", 1000, 2000, 500, 1000)], PRICES) # $9.00
with pytest.raises(SystemExit):
check_budget(est, 5.0, dry_run=False)
check_budget(est, 5.0, dry_run=True) # prints, never raises
check_budget(est, 10.0, dry_run=False)
def test_usage_cost_and_tracker_hard_stop() -> None:
assert usage_cost("m-small", 1_000_000, 0, PRICES) == pytest.approx(2.0)
assert usage_cost("m-small", 0, 0, PRICES, cache_read_tokens=1_000_000) == pytest.approx(0.2)
assert usage_cost("m-small", 0, 0, PRICES, cache_write_tokens=1_000_000) == pytest.approx(2.5)
t = SpendTracker(0.01, PRICES)
t.add("m-small", {"input_tokens": 1000, "output_tokens": 500}) # 0.002 + 0.005
t.check()
t.add("m-small", {"input_tokens": 1000, "output_tokens": 500})
with pytest.raises(BudgetExceeded):
t.check()
def test_token_heuristics() -> None:
assert image_tokens(1024, 1024) == math.ceil(1024 * 1024 / 750)
assert image_tokens(3000, 3000) == image_tokens(1568, 1568) # long edge scaled to 1568
assert text_tokens("x" * 35) == 10
def test_request_input_tokens_reads_png_dims() -> None:
import base64
import cv2
ok, buf = cv2.imencode(".png", np.zeros((300, 200), np.uint8))
b64 = base64.standard_b64encode(buf.tobytes()).decode()
assert png_dims(b64) == (200, 300)
kw = {
"model": "m",
"messages": [
{
"role": "user",
"content": [{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": b64}}],
}
],
}
assert estimate_request_input_tokens(kw, 9999) >= image_tokens(200, 300)
assert estimate_request_input_tokens(kw, 9999) < 9999
# ------------------------------------------------------------------------------------------------ cache
def test_cache_key_deterministic_and_sensitive() -> None:
k = cache_key("m", {"text": "hi", "schema": {"a": 1}}, ["h1"])
assert k == cache_key("m", {"schema": {"a": 1}, "text": "hi"}, ["h1"]) # key order irrelevant
assert len(k) == 64
assert k != cache_key("m2", {"text": "hi", "schema": {"a": 1}}, ["h1"])
assert k != cache_key("m", {"text": "hi!", "schema": {"a": 1}}, ["h1"])
assert k != cache_key("m", {"text": "hi", "schema": {"a": 1}}, ["h2"])
def test_request_key_uses_image_hash_not_bytes() -> None:
import base64
a = base64.standard_b64encode(b"\x89PNG-a").decode()
b = base64.standard_b64encode(b"\x89PNG-b").decode()
def kw(data: str) -> dict:
return {
"model": "m",
"max_tokens": 10,
"messages": [
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": data}},
{"type": "text", "text": "q"},
],
}
],
}
assert request_key(kw(a)) == request_key(kw(a))
assert request_key(kw(a)) != request_key(kw(b))
stripped, hashes = strip_images(kw(a))
assert len(hashes) == 1 and "data" not in stripped["messages"][0]["content"][0]["source"]
def test_cache_roundtrip_and_namespaces(tmp_path) -> None: # noqa: ANN001
live, mock = ResponseCache(tmp_path, "x/live"), ResponseCache(tmp_path, "x/mock")
mock.put("ab" * 32, {"parsed": {"v": 1}, "mock": True})
assert mock.get("ab" * 32)["parsed"] == {"v": 1}
assert live.get("ab" * 32) is None # mock outputs are never visible as live ones
assert ("ab" * 32) in mock
live.put("cd" * 32, {"model": "m", "mock": False, "usage": {"output_tokens": 300}})
live.put("ef" * 32, {"model": "m", "mock": False, "usage": {"output_tokens": 500}})
assert live.observed_output_tokens("m") == pytest.approx(400)
assert mock.observed_output_tokens("m") is None
def test_stable_seed_is_process_independent() -> None:
assert stable_seed(0, "cxd_1") == stable_seed(0, "cxd_1")
assert stable_seed(0, "cxd_1") != stable_seed(1, "cxd_1")
# ------------------------------------------------------------------------------------------------ bootstrap
def test_bootstrap_ci_basic() -> None:
vals = [0, 1] * 50
ci = bootstrap_ci(vals, n_boot=1000, seed=0)
assert ci.point == pytest.approx(0.5) and ci.n == 100
assert ci.lo < 0.5 < ci.hi
assert 0.38 < ci.lo < 0.45 and 0.55 < ci.hi < 0.62 # ≈ ±1.96·sqrt(.25/100)
assert bootstrap_ci(vals, seed=0) == bootstrap_ci(vals, seed=0) # seeded
def test_bootstrap_ci_edge_cases() -> None:
assert bootstrap_ci([]).n == 0 and math.isnan(bootstrap_ci([]).point)
one = bootstrap_ci([0.7])
assert (one.point, one.lo, one.hi, one.n) == (0.7, 0.7, 0.7, 1)
assert bootstrap_ci([1, None, float("nan"), 0]).n == 2
const = bootstrap_ci([1.0] * 20)
assert const.lo == const.hi == 1.0
assert "Wilson" in fmt_pct(const) # collapsed bootstrap → Wilson interval added
med = bootstrap_ci([1, 2, 3, 100], stat="median", seed=0)
assert med.point == 2.5
def test_paired_diff_and_wilson() -> None:
d = bootstrap_paired_diff([1, 1, 1, 0], [0, 0, 1, 0], seed=0)
assert d.point == pytest.approx(0.5) and d.n == 4
assert bootstrap_paired_diff([1, None], [0, 0]).n == 1
lo, hi = wilson_ci(0, 20)
assert lo == 0.0 and 0.1 < hi < 0.2
assert isinstance(fmt_pct(CI(0.5, 0.4, 0.6, 10)), str)