Skip to content

chore(pricing): refresh the bundled snapshot; a refresh can no longer drop a model's pricing - #1483

Merged
iamtoruk merged 4 commits into
getagentseal:mainfrom
ozymandiashh:chore/pricing-snapshot-refresh
Sep 19, 2026
Merged

iamtoruk merged 4 commits into
getagentseal:mainfrom
ozymandiashh:chore/pricing-snapshot-refresh

Conversation

@ozymandiashh

Copy link
Copy Markdown
Collaborator

Split out of #1478 per review: the pure snapshot refresh lands on its own, because it moves money that has nothing to do with the tier mechanism and the mechanism is inert without fresh data anyway.

What's in the data

  • 5,938 primary entries: 1,210 added, 298 repriced vs the 0.9.24 bundle. Among the additions, upstream shipped the Grok Build family (grok-build-latest, grok-build-0.1 and routes) — which makes grok-4.6-build priceable, so the Grok parser's authoritative-model rule now prefers the real modelUsage id (pin updated with the reasoning in place).
  • Four entries are genuinely gone upstream (the two friendliai llama-3.1 rows, bare and prefixed). Seven more are newly excluded by the bundler's per-token-output filter (twelvelabs embedding and gpt-image-2 rows that publish no per-token output cost) — a filter rule change, not upstream deletions, called out as such in the changelog.
  • Fallback: 51 from the gap-fill plus 9 carried forward, 203 → 60.

Two bundler rules from the review, both fixing real damage this regen exposed

  1. Completeness wins over first-write for vendor-prefixed aliases. Upstream added nebius/MiniMaxAI/MiniMax-M3 with no cache-read rate, and pass 2 handed the stripped alias to it on first write — the publisher's entry carrying the published 6e-8 cache-read was silently replaced, buildCosts fell back to input × 0.1 (3e-8), and 1,574 calls repriced by −$2.99. Now a sparser alias cannot displace a richer entry for the same model.
  2. A refresh can no longer leave a model without pricing. Previously-priced fallback entries carry forward when neither the new primary nor the gap-fill covers their key under the exact or vendor-prefixed form the resolution pipeline actually queries. Nine models would otherwise have lost all pricing: qwen3.5-plus, mercury-2.5-preview, inkling-small:batch, nex-n2-mini, nex-n2-pro, gpt-latest, gemma-4-31b-it:batch, gpt-oss-20b:batch, grok-latest. Coverage deliberately excludes date-stripped matches: qwen/qwen3.5-plus-20260420 shares the bare name but does not answer the undated query at runtime.

Verification

  • npm run bundle-litellm twice over (idempotent carry), MiniMax-M3 cache-read verified back at 6e-8, all nine carried keys present.
  • Root tsc --noEmit clean; affected suites green (models, pricing-fallback-data, grok pipeline with the updated pin, codex, parser c4, budget).

Regenerated from live LiteLLM/models.dev/OpenRouter: 5,938 primary
entries (1,210 added, 11 removed, 298 repriced vs the 0.9.24 bundle)
and 51 fallback entries; 394 primary entries carry a tier slot. The
Grok Build family upstream is what makes grok-4.6-build priceable,
shifting the Grok parser's authoritative-model pick to the real
modelUsage id (pin updated with the reasoning in the test).
… drop a model's pricing

Regenerated from live LiteLLM/models.dev/OpenRouter: 5,938 primary
entries (1,210 added, 298 repriced vs the 0.9.24 bundle), fallback 51
from the gap-fill plus 9 carried forward. Upstream actually removed
four entries (friendliai llama-3.1 bare and prefixed); seven more are
excluded by the per-token-output filter, which is a rule change and is
called out as one.

Two bundler rules from review:

- vendor-prefixed aliases no longer displace a richer entry for the
  same model: completeness (published cache slots) wins over
  first-write, so the new nebius/MiniMaxAI/MiniMax-M3 without
  cache-read rates cannot replace the publisher's entry that carries
  them;
- previously-priced fallback entries carry forward when neither the
  new primary nor the gap-fill covers their key exactly or
  vendor-prefixed - the form the resolution pipeline queries. Nine
  models would otherwise have lost all pricing; a dated variant
  sharing the bare name does not count as coverage because it does
  not answer the undated query.

The Grok Build family upstream makes grok-4.6-build priceable, so the
Grok parser's authoritative-model pick prefers the real modelUsage id
(pin updated with the reasoning in place).

@iamtoruk iamtoruk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snapshot regeneration itself is faithful. The top models by spend match upstream exactly, and no model in real use is dropped. The dollar impact on the maintainer's own data is negligible. But two things must change before this can land, because they cost other users money or hide a regression.

  1. Guard 2 coverage is wrong and drops pricing for 96 models. The bundler's coverage check uses Object.keys(snapshot).some(k => k.endsWith('/' + key)), which treats a bare id like grok-latest as covered by a vendor-prefixed key such as ~x-ai/grok-latest. But the runtime resolver getModelCosts in src/models.ts never tries <vendor>/<id> for a bare id, so those fallback entries are dropped and the models return null (unpriced). This is not just grok-latest: 96 of the 145 removed fallback keys price on main and return null on this PR, including claude-opus-4.8, claude-fable-5.1, the claude/gpt/gemini/kimi "-latest" aliases, and the batch variants. Fix: drop the endsWith('/' + key) clause or check against what the runtime resolver actually reaches, then regenerate the fallback so those entries stay priced.

  2. The grok-parser-pipeline test edit masks a regression. That test now mocks grok-4.6-build to null, so the change it pins is grok-latest losing its price and falling back to grok-build rates, not "grok-4.6-build prices through the Grok Build family" as the new comment says. Revert that test edit. Once fix 1 is in, grok-latest keeps its own price and the test should not need changing.

  3. The completeness-wins guard is not rate-neutral, so please either make it so or own the reprices. Beyond preventing a dropped price, it changes 43 input/output rates and 34 cache rates by itself, on the same upstream data, just by choosing a different row. Some of these are large, for example grok-3 from 3/15 to 1.25/2.5, grok-4 the same, mistral-large-latest from 8/24 to 0.5/1.5, deepseek-v3 from 1.14/4.56 to 0.27/1.1. These may well be the correct rows, but the PR presents the guards as hardening, not repricing. Either restrict the guard to replace only when input and output are identical (pure completeness), or keep the reprices and list them in the PR body and changelog so they are a deliberate, reviewed change.

One more thing: the finalized daily cache is not re-derived by a refresh, and there is no cache version bump here. So installed users only see any of this on days the cache is re-derived. Online, the live price feed wins anyway, so the bundle is only a gap-fill for offline or unpriced cases.

This is close, and the regeneration is good work. It just needs the coverage clause fixed, the test reverted, and the guard reprices owned or removed.

…uard, regenerated bundle (getagentseal#1483)

- coveredByKey is exact-key only: the resolver never reaches vendor/<id> for
  a bare query, so the old endsWith clause dropped 96 priced fallback ids.
- Pass 2 stripped writes are strictly slot-filling: a richer upstream row may
  only fill missing cache slots when input/output rates are identical, never
  re-price a filled slot (was re-pricing 43 input/output + 34 cache rates).
- Carry now rescues dropped primary rows as well as fallback entries (twelve
  ids this cycle), so a refresh cannot leave a previously-priced model null.
- Regenerated: 5,942 primary + 212 fallback; 159 carried; zero coverage
  regression and zero guard-caused rate changes vs upstream/main, verified by
  resolving every id from either bundle through the real getModelCosts.
…ntseal#1483)

Upstream LiteLLM now publishes a distinct z-ai/glm-5.3 rate, so the
prefixed spelling no longer equals the glm-5p2 sibling. Assert against
the snapshot's own row (the gpt-5.6-codex pattern) so the test survives
later upstream reprices and the z-ai/ -> zai/ namespace rename.
@ozymandiashh

Copy link
Copy Markdown
Collaborator Author

All three addressed on e69f12b0; re-measured rather than quoted.

1 — coverage is exact-key-only, and the loss it already caused is repaired. The endsWith('/'+key) clause is gone, with a comment stating the real reason (the resolver never reaches a vendor/<id> key for a bare query). Fixing the clause alone wasn't enough: this PR's first regen had already run under the broken rule and dropped main's fallback from 203 to 60, so the carry inherited a lossy baseline. The carry now seeds from the main∪HEAD union of both data files and also rescues previous primary rows upstream itself dropped or renamed (this cycle: gpt-image-2-2026-04-21 + its azure form, the Bedrock marengo embeds, the friendliai llama-3.1 rows — 12 ids) into the fallback tier, primary still consulted first. Verified at the resolver level with the real getModelCosts over the 6,154-id union of both trees' bundles: your 96 reproduce exactly on the previous head, and zero ids that price on main price null on this one. Bundle is now 5,942 primary + 212 fallback.

2 — test reverted byte-identical to main and passes 6/6 unmodified; grok-latest keeps its own price, so the original assertion needs nothing.

3 — the guard is now strictly slot-fill (your option A). Pass 2 replaces a stripped key only when input and output are identical and every slot the existing entry already carries keeps its value — it fills null slots, never re-prices a filled one. grok-3 and grok-4 are back at 3/15, mistral-large-latest back at 8/24. Rate-neutrality verified two ways: on today's identical fetched JSON, main's pass-2 vs this one — 0 input/output changes, 0 cache changes, 62 null-slot fills; and end-to-end, every remaining rate diff vs main is attributable to source data (181 same-key upstream reprices, 313 new upstream rows), none to the guard or the carry.

Two things disclosed rather than smuggled:

  • One test amendment, its own commit (e69f12b0): upstream now ships z-ai/glm-5.3 with a distinct rate, so tests/models.test.ts's sibling-equality assertion is false for any honest refresh. It now asserts the snapshot's own row (the gpt-5.6-codex pattern), which also survives the z-ai/zai/ rename live upstream already shows. Full suite after it: 4,277 passed, 5 skipped, 0 failed.
  • A caveat on the 12 carried ids: they price under their bare and vendor-prefixed spellings but not under router-prefixed ones (omniroute:…, cp/…) — pre-existing fallback-tier behavior, identical for all 203 fallback-only ids on main.

On your cache note — agreed, and deliberate: this PR doesn't bump DAILY_CACHE_VERSION; the stacked #1478 does (33 → 34), so once both land, a re-derivation picks up the corrected bundle exactly when the tier rates arrive too.

@iamtoruk
iamtoruk merged commit 3bb72ad into getagentseal:main Sep 19, 2026
26 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants