Change: model-price churn is now one committed command instead of a scratch
script. uv run models refresh pulls OpenRouter prices and Artificial Analysis
intelligence scores, surgically updates model-pricing.yaml, and prints the
exact diff. uv run models drift renders the same comparison without writing;
CI runs it as an advisory warning on trusted post-merge pushes because an external vendor
price change must not block unrelated work, and pull-request code must never receive the
Artificial Analysis API key.
rank, what-if, and
automatic seat re-pointing remain Stage 2 or explicitly out of scope.OpenRouter /api/v1/models ── per-token price × 1,000,000 ─┐
├─ compare existing tracked rows
Artificial Analysis ── max INT per normalized model slug ─┘
│
├─ models drift → readable diff + non-zero on divergence; NO write
└─ models refresh → same diff, then surgical scalar-line rewrite
The writer deliberately does not use yaml.safe_dump. It rewrites only targeted
numeric scalar lines, so the file header, ordering, comments, and hand-written
structured_note provenance survive byte-for-byte.
tokens_per_text_ratio defaults to 1.0. Claude 4.7+ rows carry
1.30, so refresh and drift show the number a seat reviewer actually compares:
| Model | List input $/M tokens | Ratio | Effective $/M of the same text |
|---|---|---|---|
| Claude Sonnet 5 | 2.00 | 1.30 | 2.60 |
| Claude Opus 5 / Opus 4.8 / Opus 4.7 | 5.00 | 1.30 | 6.50 |
| Claude Fable 5 | 10.00 | 1.30 | 13.00 |
Runtime token-cost functions are intentionally unchanged: providers already return the
actual tokenizer-produced token count. Applying 1.30 there would double-count
the tax. The ratio exists only for same-document-text comparison in model operations.
| Pricing key shape | OpenRouter prices? | AA score? |
|---|---|---|
vendor/model — gateway | Yes, exact pinned id only | Yes, max per slug |
vendor:model — provider direct | No | Yes, route-independent model score |
bedrock:… | No | No — row untouched |
OpenRouter ids beginning with ~, and rows with non-positive prompt/completion
prices, are ignored. Prices use decimal rounding to six places, preventing values such as
0.09999999999999999 from creating permanent drift.
uv run models refresh → corrected live gateway price/cache drift and AA scores → preserved every structured_note and the file header uv run models refresh → model-pricing.yaml is current (no drift). → exit 0 uv run models drift → model-pricing.yaml is current (no drift). → exit 0 uv run pytest tests/test_model_ops.py -q → 14 passed uv run ruff check scripts/model_ops.py tests/test_model_ops.py → All checks passed! uv run ruff check agents/ tests/ scripts/ → All checks passed! uv run pytest tests -q → 319 passed
rank and what-if — Stage 2.