Neither id is in LiteLLM yet, so litellm-snapshot.json had no row for
them (285 sessions / 5,446 calls priced at $0 on one real corpus,
flagged during #1075 verification). Add explicit snapshot rows sourced
from the pattern every prior Codex-suffixed id LiteLLM does carry
follows: it bills identically to its bare-model sibling of the same
generation (gpt-5-codex == gpt-5, gpt-5.1-codex == gpt-5.1-codex-max ==
gpt-5.1, gpt-5.2-codex == gpt-5.2, gpt-5.3-codex == gpt-5.3, verified
against the live model_prices_and_context_window.json) - both new ids
get the exact gpt-5.6 tuple rather than an invented rate.
getModelCosts already resolved both ids to the correct rate through
the `gpt-5.6` prefix fallback before this, so a fresh parse or a warm
session-cache read (codeburn models included) was already pricing
these calls correctly. The daily cache is the one place that does not
self-heal: it has no per-provider invalidation, so a day finalized
before a `gpt-5.6` snapshot row existed at all keeps that $0 forever.
Raising MIN_SUPPORTED_VERSION (v23 -> v24) forces the one-time
re-derivation, a lossless no-op for days already correct - the
mechanism itself is generic and already covered by
daily-cache-version-rederivation.test.ts. #1056 also claims 24 on its
own branch; whichever lands second takes the next number.
Closes#1077
Mythos 5 ($10/$50) is not in LiteLLM or the models.dev/OpenRouter gap-fill
yet (Fable is), so dropping the manual patch left it priced at $0. Add it to
MANUAL_ENTRIES + the snapshot and map the "Mythos 5" display name.
Keep model pricing automatic instead of hand-coding new models. The bundler
now layers three sources in priority order: LiteLLM (broad list prices),
hand-curated MANUAL_ENTRIES overrides, then a separate last-resort fallback
file gap-filled from models.dev first-party makers (official direct prices)
and OpenRouter (resale backstop). New models such as MiniMax-M3 ($0.6/$2.4)
now price correctly with no per-model code.
The fallback is written to its own pricing-fallback.json and consulted only
case-insensitively as the final step in getModelCosts, so a reseller variant
name can never shadow a canonical or aliased match.
Fixes surfaced while building and verifying this:
- Alias precedence: LiteLLM ships snowflake/claude-4-opus ($5), which the
bundler strips to a bare claude-4-opus key that shadowed the curated alias
to claude-opus-4 ($15 official). An explicit alias for a bare name now wins
over a coincidental stripped reseller key; the prefixed gateway price is
still returned for the fully-qualified id.
- Zero-stub guard: LiteLLM [0,0] price stubs (e.g. GigaChat-2-Max) are
excluded from the case-insensitive index so a case-mismatched query stays
null and keeps firing the unknown-model warning instead of silently
reporting $0.
- Negative-sentinel guard: OpenRouter returns -1 for variable/BYOK-priced
models. The bundler now rejects any non-positive rate pair (and strips the
sentinel from cache fields) so a negative per-token cost can never ship and
subtract from spend totals.
Bundler hardening: bareKey strips @pin and date suffixes to match the runtime
canonical form, seen-set dedupes on both full and bare key shapes, and it logs
MANUAL_ENTRIES now covered upstream plus models.dev allowlist drift. Extracted
buildCosts so the cache-cost heuristics live in one place. Added a data-hygiene
test that fails CI if a rebundle reintroduces negative, free, or unreachable
fallback entries.
New Claude releases no longer need a hand-maintained SHORT_NAMES entry or
FAST_MULTIPLIERS row. Display names are derived from the claude-<family>-<major>-<minor>
id, and the fast-mode multiplier rides along as a 5th element in the LiteLLM
snapshot tuple (provider_specific_entry.fast). Fixes#420: claude-opus-4-8 gets
its own line and correct pricing instead of falling into the Opus 4 bucket.