testsAndMisc/python_pkg/token_audit/tests/test_pricing.py

71 lines
2.1 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
import pytest
from python_pkg.token_audit import pricing
from python_pkg.token_audit.model import Turn
BOOK = pricing.PriceBook(
models={
"claude-opus-5": pricing.Price(input=5.0, output=25.0, cache_read=0.5),
"claude-opus-5-5": pricing.Price(input=4.0, output=20.0, cache_read=0.2),
},
default="claude-opus-5",
)
def _turn(usage: dict[str, int], model: str = "claude-opus-5") -> Turn:
return Turn(usage=usage, context=0, model=model, is_sidechain=False)
def test_for_model_exact_prefix_and_default() -> None:
assert BOOK.for_model("claude-opus-5").input == 5.0
# Longest matching known id wins over a shorter prefix.
assert BOOK.for_model("claude-opus-5-5-2026").input == 4.0
assert BOOK.for_model("mystery").input == 5.0
def test_load_reads_shipped_table() -> None:
book = pricing.load()
assert book.default in book.models
assert book.for_model("claude-sonnet-5-5").cache_read == 0.2
def test_load_custom_path(tmp_path: Path) -> None:
path = tmp_path / "p.json"
path.write_text(
json.dumps(
{
"default": "m",
"models": {"m": {"input": 1, "output": 2, "cache_read": 3}},
},
),
encoding="utf-8",
)
book = pricing.load(path)
assert book.models["m"] == pricing.Price(1.0, 2.0, 3.0)
def test_usd_without_split_prices_creation_as_5m() -> None:
usage = {"input_tokens": 1_000_000, "cache_creation_input_tokens": 1_000_000}
assert pricing.usd(_turn(usage), BOOK) == pytest.approx(5.0 + 1.25 * 5.0)
def test_usd_with_split_prices_1h_at_double() -> None:
usage = {
"cache_creation_input_tokens": 1_000_000,
"cache_creation_1h": 400_000,
"cache_creation_5m": 600_000,
"cache_read_input_tokens": 1_000_000,
"output_tokens": 1_000_000,
}
expected = 0.4 * 2.0 * 5.0 + 0.6 * 1.25 * 5.0 + 0.5 + 25.0
assert pricing.usd(_turn(usage), BOOK) == pytest.approx(expected)
def test_usd_zero_for_empty_usage() -> None:
assert pricing.usd(_turn({}), BOOK) == 0.0