Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion scripts/lib/model-prices.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_comment": "USD per 1M tokens. Hand-edited in a PR. A model absent here keeps its token counts and reports usd_estimated: null — see scripts/lib/token-cost.mjs. Sourced 2026-07-31 (#1197 review, finding I6; gemini-3.5-flash added #1197 re-review, finding B, from run 30647253368 — the run's largest consumer, 13 calls / 14,690 tokens, previously unpriced; the three gemini-*-lite rows added #1211 review round 2) from Anthropic's published pricing (platform.claude.com/docs/en/pricing) and Google's public Gemini pricing page — re-verify before trusting a number more than a few months old. A model id absent EXACTLY is also matched by substring against every key here (both directions — a dated/preview/latest id contains its family key, a short alias is contained by a longer one), longest key wins when more than one matches, gated on the leftover being recognizable version/alias noise (a date, `-latest`, `-preview`, `-search-preview`) rather than a different product's name — see token-cost.mjs's isAllowedSuffix() — so a rotated `-preview`/`-latest`/dated id does not need its own row unless its rate actually differs from its family's, but a TIER id (Lite, Mini, Nano, Pro, ...) always needs its own row: the substring pass deliberately refuses to guess a tier's price from its non-tier sibling. A model's value is either a flat rate ({inputPerMillion, outputPerMillion} — the common case) or a dated-bands array ([{since, inputPerMillion, outputPerMillion}, ...], see claude-sonnet-5 below) when the rate has actually changed over time; `--summarize` selects the band effective on the run's own date (RUN_DATE), never the newest by default. gemini-flash-latest and gemini-3.5-flash are priced the same as gemini-2.5-flash — the Flash tier's pricing has stayed stable across recent dot-releases and gemini-flash-latest resolves to this exact class on Langflow 1.12.0.dev10 (measured: the trace's modelName is the bare alias, not a dated id) — re-check these two rows if either moves to pricing distinct from 2.5-flash. Same reasoning extends to the Lite tier: gemini-2.5-flash-lite, gemini-3.5-flash-lite and gemini-flash-lite-latest are all priced at Google's published Gemini 2.5 Flash-Lite rate ($0.10/$0.40 per 1M, ai.google.dev/gemini-api/docs/pricing, sourced 2026-07-31) — re-check if 3.5's Lite tier is ever priced separately from 2.5's. EVERY entry (and every band of a dated entry) also carries `provider` \u2014 openai | anthropic | google, the vocabulary the weekday rotation in scripts/select-daily-model-target.mjs uses, plus `azure` for a model reached through an Azure AI Foundry deployment rather than through one of the rotated provider keys. `azure` is not a rotation value and never will be: the Foundry spec runs on every lane, outside the rotation. The field names WHO BILLED the tokens, which is what the QA Platform groups spend by \u2014 writing `openai` for a Foundry deployment would fold its spend into the same provider row as the real openai lanes, which are different accounts. It is written down rather than derived from the id prefix on purpose: the resolver here matches ids by substring in BOTH directions, so a prefix rule would put a second, different notion of identity on the same data. It has TWO consumers: scripts/sync-model-prices.mjs projects this file into the QA Platform's e2e_model_prices table, which carries provider as a column on each (price_key, since) row; and since #1300 `--summarize` reads it too, for the by_provider rollup on each token-history line (token-cost.mjs's resolveProvider). Note what this field can and cannot say, because the by_provider figure inherits it: it is keyed by model NAME, exactly like the rate, so the caveat on the gpt-5-mini row below — this row prices a NAME, and a different model deployed under that name is priced wrong with no gap to show it — applies to the provider just as it applies to the dollars. A genuine OpenAI call to an id this table declares `azure` is booked to azure.",
"_comment": "USD per 1M tokens. Hand-edited in a PR. A model absent here keeps its token counts and reports usd_estimated: null — see scripts/lib/token-cost.mjs. Sourced 2026-07-31 (#1197 review, finding I6; gemini-3.5-flash added #1197 re-review, finding B, from run 30647253368 — the run's largest consumer, 13 calls / 14,690 tokens, previously unpriced; the three gemini-*-lite rows added #1211 review round 2) from Anthropic's published pricing (platform.claude.com/docs/en/pricing) and Google's public Gemini pricing page — re-verify before trusting a number more than a few months old. A model id absent EXACTLY is also matched by PREFIX against every key here — not by substring-anywhere, which this comment claimed until 2026-08-07 while the code always did `longer.startsWith(shorter)`; the conclusions below are unaffected because every suffix this targets is appended, never inserted — in both directions (a dated/preview/latest id EXTENDS its family key, a short alias is EXTENDED BY a longer one), longest key wins when more than one matches, gated on the leftover being recognizable version/alias noise (a date, `-latest`, `-preview`, `-search-preview`) rather than a different product's name — see token-cost.mjs's isAllowedSuffix() — so a rotated `-preview`/`-latest`/dated id does not need its own row unless its rate actually differs from its family's, but a TIER id (Lite, Mini, Nano, Pro, ...) always needs its own row: the substring pass deliberately refuses to guess a tier's price from its non-tier sibling. A model's value is either a flat rate ({inputPerMillion, outputPerMillion} — the common case) or a dated-bands array ([{since, inputPerMillion, outputPerMillion}, ...], see claude-sonnet-5 below) when the rate has actually changed over time; `--summarize` selects the band effective on the run's own date (RUN_DATE), never the newest by default. gemini-flash-latest and gemini-3.5-flash are priced the same as gemini-2.5-flash — the Flash tier's pricing has stayed stable across recent dot-releases and gemini-flash-latest resolves to this exact class on Langflow 1.12.0.dev10 (measured: the trace's modelName is the bare alias, not a dated id) — re-check these two rows if either moves to pricing distinct from 2.5-flash. Same reasoning extends to the Lite tier: gemini-2.5-flash-lite, gemini-3.5-flash-lite and gemini-flash-lite-latest are all priced at Google's published Gemini 2.5 Flash-Lite rate ($0.10/$0.40 per 1M, ai.google.dev/gemini-api/docs/pricing, sourced 2026-07-31) — re-check if 3.5's Lite tier is ever priced separately from 2.5's. EVERY entry (and every band of a dated entry) also carries `provider` \u2014 openai | anthropic | google, the vocabulary the weekday rotation in scripts/select-daily-model-target.mjs uses, plus `azure` for a model reached through an Azure AI Foundry deployment rather than through one of the rotated provider keys. `azure` is not a rotation value and never will be: the Foundry spec runs on every lane, outside the rotation. The field names WHO BILLED the tokens, which is what the QA Platform groups spend by \u2014 writing `openai` for a Foundry deployment would fold its spend into the same provider row as the real openai lanes, which are different accounts. It is written down rather than derived from the id prefix on purpose: the resolver here already matches ids by PREFIX in BOTH directions, so deriving the provider from the id's leading characters would put a second, different notion of identity on the same data. It has TWO consumers: scripts/sync-model-prices.mjs projects this file into the QA Platform's e2e_model_prices table, which carries provider as a column on each (price_key, since) row; and since #1300 `--summarize` reads it too, for the by_provider rollup on each token-history line (token-cost.mjs's resolveProvider). Note what this field can and cannot say, because the by_provider figure inherits it: it is keyed by model NAME, exactly like the rate, so the caveat on the gpt-5-mini row below — this row prices a NAME, and a different model deployed under that name is priced wrong with no gap to show it — applies to the provider just as it applies to the dollars. A genuine OpenAI call to an id this table declares `azure` is booked to azure.",
"gpt-4o-mini": { "provider": "openai", "inputPerMillion": 0.15, "outputPerMillion": 0.6 },
"gpt-4o": { "provider": "openai", "inputPerMillion": 2.5, "outputPerMillion": 10.0 },
"gpt-5-mini": {
Expand All @@ -8,6 +8,12 @@
"inputPerMillion": 0.25,
"outputPerMillion": 2.0
},
"gpt-5.6-sol": {
"_comment": "Verified 2026-08-07 against OpenAI's published pricing ($5.00/$30.00 per MTok, developers.openai.com/api/docs/pricing) and cross-checked against an independent aggregator. STANDARD SYNC TIER: batch is $2.50/$15.00 and fast mode $10.00/$60.00, and this file holds one pair per key, so a spec that ever routes this model through batch or fast mode is priced wrong on an axis the schema cannot express. Cached input is $0.50/MTok and there is no cached-input concept either, so this entry OVER-prices a cache-heavy run — the opposite direction from every other approximation downstream, which fails toward a floor. One aggregator also claimed a >272K-input tier at $10/$45; OpenAI's own page did not show it, so it is not encoded. Reported by run 31163810520 (2026-08-07, 3 calls / 2,304 tokens) from tests-automations/regression/core-functionality/llm-agents/agent-structured-output.spec.ts and .../agent-context-id-continuity.spec.ts, where it left the whole run priced as a floor. Adding it here prices FUTURE runs only — those three fact rows froze price_key NULL at capture and need quality-platform's migration 20260807030000.",
"provider": "openai",
"inputPerMillion": 5,
"outputPerMillion": 30
},
"claude-sonnet-4-6": { "provider": "anthropic", "inputPerMillion": 3.0, "outputPerMillion": 15.0 },
"claude-sonnet-5": [
{
Expand Down