diff --git a/evalboard/lib/pricing.ts b/evalboard/lib/pricing.ts index 988828c0..20db9c0a 100644 --- a/evalboard/lib/pricing.ts +++ b/evalboard/lib/pricing.ts @@ -24,6 +24,9 @@ export const PRICING: Record = { // alias must never inherit the older tier. Undated aliases each need their // own key: resolvePricing's fallback only strips a trailing date (dated → // undated), it cannot invent one. + // Fable 5.1 prices cache hits at 0.025x input, not the 0.1x every other + // Claude model uses. Fable 5 pays $1 on the identical $10 base. + "claude-fable-5-1": p(10, 50, 12.5, 0.25), "claude-fable-5": p(10, 50, 12.5, 1), "claude-opus-5": p(5, 25, 6.25, 0.5), "claude-opus-4-8": p(5, 25, 6.25, 0.5), @@ -34,7 +37,8 @@ export const PRICING: Record = { "claude-opus-4-1": p(15, 75, 18.75, 1.5), "claude-opus-4": p(15, 75, 18.75, 1.5), "claude-opus-4-20250514": p(15, 75, 18.75, 1.5), - "claude-sonnet-5": p(3, 15, 3.75, 0.3), + // $2/$10, not the $3/$15 that 4.6 and earlier pay. + "claude-sonnet-5": p(2, 10, 2.5, 0.2), "claude-sonnet-4-6": p(3, 15, 3.75, 0.3), "claude-sonnet-4-5": p(3, 15, 3.75, 0.3), "claude-sonnet-4-5-20250929": p(3, 15, 3.75, 0.3), @@ -64,8 +68,8 @@ export const PRICING: Record = { "gpt-5.2-codex": p(1.75, 14, 1.75, 0.175), "gpt-5.4": p(2.5, 15, 2.5, 0.25), "gpt-5.5": p(5, 30, 5, 0.5), - // Terra and Luna repriced 2026-07-30 (-20% / -80%); post-cut rates. - "gpt-5.6-sol": p(5, 30, 5, 0.5), + // Sol's rate is promotional through at least 2026-11-21. + "gpt-5.6-sol": p(4, 20, 4, 0.4), "gpt-5.6-terra": p(2, 12, 2, 0.2), "gpt-5.6-luna": p(0.2, 1.2, 0.2, 0.02), // Google Gemini (AntigravityAgent). Gemini bills no separate cache-write @@ -75,6 +79,10 @@ export const PRICING: Record = { "gemini-3-pro-preview": p(2, 12, 2, 0.2), "gemini-3.1-pro-preview": p(2, 12, 2, 0.2), "gemini-3.1-pro-preview-customtools": p(2, 12, 2, 0.2), + // 3.6 / 3.7 / 3.8 Flash share one rate card. List rates; Google is + // discounting all three by half through 2026-12-31. + "gemini-3.8-flash": p(1.5, 7.5, 1.5, 0.15), + "gemini-3.7-flash": p(1.5, 7.5, 1.5, 0.15), "gemini-3.6-flash": p(1.5, 7.5, 1.5, 0.15), "gemini-3.5-flash": p(1.5, 9, 1.5, 0.15), "gemini-3.5-flash-lite": p(0.3, 2.5, 0.3, 0.03), diff --git a/src/coder_eval/pricing.py b/src/coder_eval/pricing.py index 88af853a..4289ff40 100644 --- a/src/coder_eval/pricing.py +++ b/src/coder_eval/pricing.py @@ -2,9 +2,11 @@ Anthropic/OpenAI/Google built-in rates; plugins contribute additional rates via ``register_pricing()``. Prices are per million tokens (MTok). -Sources: https://claude.com/pricing#api, https://developers.openai.com/api/docs/pricing, -https://ai.google.dev/gemini-api/docs/pricing (all verified 2026-07-29; -the GPT-5.6 rows re-verified 2026-08-09 after the 2026-07-30 Terra/Luna cut). +Sources: https://platform.claude.com/docs/en/about-claude/pricing, +https://developers.openai.com/api/docs/pricing, +https://ai.google.dev/gemini-api/docs/pricing, and OpenRouter's live +``/api/v1/models`` (every row re-verified 2026-09-03, except the Bedrock +open-weight block: AWS publishes no eu-north-1 figures for those three). """ from collections.abc import Iterable @@ -21,9 +23,12 @@ class ModelPricing: cache_read_per_mtok: float # prompt caching read -# Official vendor rate cards, verified 2026-07-29 (GPT-5.6 rows: 2026-08-09). +# Official vendor rate cards, verified 2026-09-03. # Key: CLI model name (before gateway mapping) _PRICING: dict[str, ModelPricing] = { + # Fable 5.1 (and Mythos 5.1) price cache hits at 0.025x input, not the 0.1x + # every other Claude model uses. Fable 5 pays $1 on the identical $10 base. + "claude-fable-5-1": ModelPricing(10.0, 50.0, 12.50, 0.25), "claude-fable-5": ModelPricing(10.0, 50.0, 12.50, 1.0), # Opus 4.5 and later dropped to $5/$25; 4.1 and 4 keep the old $15/$75. The # version boundary is the price boundary: a newer Opus is not the dearer one. @@ -36,10 +41,9 @@ class ModelPricing: "claude-opus-4-1": ModelPricing(15.0, 75.0, 18.75, 1.50), "claude-opus-4": ModelPricing(15.0, 75.0, 18.75, 1.50), "claude-opus-4-20250514": ModelPricing(15.0, 75.0, 18.75, 1.50), - # Standard $3/$15, not the $2/$10 promo running through 2026-08-31: a static - # table cannot express a window, and overstating for a few weeks beats - # understating indefinitely after it lapses. - "claude-sonnet-5": ModelPricing(3.0, 15.0, 3.75, 0.30), + # $2/$10, NOT the $3/$15 that Sonnet 4.6 and earlier pay. Do not copy the + # 4.x row onto it. + "claude-sonnet-5": ModelPricing(2.0, 10.0, 2.50, 0.20), "claude-sonnet-4-6": ModelPricing(3.0, 15.0, 3.75, 0.30), "claude-sonnet-4-5": ModelPricing(3.0, 15.0, 3.75, 0.30), "claude-sonnet-4-5-20250929": ModelPricing(3.0, 15.0, 3.75, 0.30), @@ -80,12 +84,10 @@ class ModelPricing: "gpt-5.4-mini": ModelPricing(0.75, 4.5, 0.75, 0.075), "gpt-5.4-nano": ModelPricing(0.20, 1.25, 0.20, 0.02), # GPT-5.6: sol flagship / terra balanced (Codex default) / luna economy. - # Terra and Luna were REPRICED on 2026-07-30 (-20% and -80%); these are the - # post-cut rates. The pre-cut $2.50/$15 and $1.00/$6 are what a historical run - # was actually billed, but this table is a single current-rate card with no - # notion of an effective date — so old runs re-price low, the same tradeoff - # the Sonnet promo comment above already accepts. - "gpt-5.6-sol": ModelPricing(5.0, 30.0, 5.0, 0.50), + # This table is a single current-rate card with no notion of an effective + # date, so a repriced model makes historical runs re-price at today's rate. + # Sol's rate is promotional through at least 2026-11-21; re-check then. + "gpt-5.6-sol": ModelPricing(4.0, 20.0, 4.0, 0.40), "gpt-5.6-terra": ModelPricing(2.0, 12.0, 2.0, 0.20), "gpt-5.6-luna": ModelPricing(0.20, 1.20, 0.20, 0.02), # Google Gemini (AntigravityAgent, via the Gemini Developer API), keyed on the @@ -93,6 +95,10 @@ class ModelPricing: # cache_write == input (unused: the agent maps cache_creation_tokens to 0). # CAVEAT: Pro's >200K-token tier costs more ($4/$18, $0.40 cached), so a # very-large-context run reads low. + # 3.6 / 3.7 / 3.8 Flash share one rate card. These are list rates; Google is + # discounting all three by half through 2026-12-31. + "gemini-3.8-flash": ModelPricing(1.5, 7.5, 1.5, 0.15), + "gemini-3.7-flash": ModelPricing(1.5, 7.5, 1.5, 0.15), "gemini-3.6-flash": ModelPricing(1.5, 7.5, 1.5, 0.15), "gemini-3.5-flash": ModelPricing(1.5, 9.0, 1.5, 0.15), "gemini-3.5-flash-lite": ModelPricing(0.30, 2.5, 0.30, 0.03), @@ -113,10 +119,14 @@ class ModelPricing: "moonshotai.kimi-k2.5": ModelPricing(0.72, 3.6, 0.72, 0.0), # OpenRouter models. These providers cache prefixes implicitly (no # cache_control, no write fee), so cache-creation is priced at input (unused) - # and cache-read at OpenRouter's published input_cache_read rate. + # and cache-read at OpenRouter's published input_cache_read rate, read from + # the live /api/v1/models catalogue. Headline rates only: OpenRouter routes + # per request, so the real bill depends on the provider a call lands on — + # which is why the litellm path captures actual per-call cost proxy-side and + # overrides these (litellm_cost.apply_actual_cost). Static fallback. "moonshotai/kimi-k3": ModelPricing(3.0, 15.0, 3.0, 0.30), - "z-ai/glm-5.2": ModelPricing(0.7168, 2.2528, 0.7168, 0.13312), - "deepseek/deepseek-v4-pro": ModelPricing(0.435, 0.87, 0.435, 0.003625), + "z-ai/glm-5.2": ModelPricing(0.966, 3.036, 0.966, 0.1932), + "deepseek/deepseek-v4-pro": ModelPricing(1.030776, 2.061552, 1.030776, 0.085898), } diff --git a/tests/test_antigravity_agent.py b/tests/test_antigravity_agent.py index cc780e59..b4b6b837 100644 --- a/tests/test_antigravity_agent.py +++ b/tests/test_antigravity_agent.py @@ -213,6 +213,8 @@ def test_tool_name_map_covers_core_builtins(): "gemini-3.1-pro-preview", "gemini-3.1-pro-preview-customtools", "gemini-3-pro-preview", + "gemini-3.8-flash", + "gemini-3.7-flash", "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", diff --git a/tests/test_cost_accounting_paths.py b/tests/test_cost_accounting_paths.py index 98ac1b6f..e81c227b 100644 --- a/tests/test_cost_accounting_paths.py +++ b/tests/test_cost_accounting_paths.py @@ -259,7 +259,7 @@ def test_simulator_prices_at_the_route_model_not_the_subject(self): """UserSimulator pins model=None, so it bills at BEDROCK_MODEL. A task that pins ``agent.model`` would otherwise mis-bill every simulated - row. Here the subject is sonnet-5 ($3/$15) while the route is haiku-4.5 + row. Here the subject is sonnet-5 ($2/$10) while the route is haiku-4.5 ($1/$5), so the simulator must cost the haiku rate. """ result = self._simulated(bedrock_model="claude-haiku-4-5-20251001") @@ -269,8 +269,8 @@ def test_simulator_prices_at_the_route_model_not_the_subject(self): def test_simulator_falls_back_to_the_subject_model_off_bedrock(self): """A non-Bedrock route names no model on the record; the subject's is the best available.""" result = self._simulated() - # sonnet-5 at $3/$15 per MTok. - assert eval_result_to_task_dict(result)["simulator_cost_usd"] == pytest.approx(3.0 + 1.5) + # sonnet-5 at $2/$10 per MTok: 1M uncached input + 100K output. + assert eval_result_to_task_dict(result)["simulator_cost_usd"] == pytest.approx(2.0 + 1.0) def test_single_shot_row_has_no_simulator_cost(self): result = _result([_turn(1, TokenUsage(uncached_input_tokens=10, output_tokens=1, total_cost_usd=0.1))])