From 0b59a9837eb818e3e6a33e0b12ceafa2e5a04571 Mon Sep 17 00:00:00 2001 From: bytebrujo Date: Fri, 4 Sep 2026 15:21:42 +0800 Subject: [PATCH] data: widen coverage from 12 models to 25 Adds seven Claude models (Fable 5, Opus 4.8, 4.7, 4.6, 4.5, Sonnet 4.6, 4.5) and six Gemini models (3.7 Flash, 3.6 Flash, 3.5 Flash, 3.5 Flash-Lite, 2.5 Flash, 2.5 Flash-Lite). The registry previously omitted models people actually use, which is a poor answer to "what does this cost". Prices, display names and lifecycle state come from the parsers running over the committed fixtures, so what is recorded is exactly what the scraper will assert tomorrow. Context windows, max output and modalities are not published in a scrapeable table by either provider, so they were read from each model's own page and are cited in `sources`. Also fills in release dates for the four Claude models that already had records but carried `released: null`, since each model page publishes one. Two Claude snapshots carry aliases (claude-opus-4-5, claude-sonnet-4-5) so the pricing page's display name and the deprecation page's dated id fold into one record. New invariant in test/data.test.js: every tracked id must have a record and every record must be tracked. Adding an id without seeding the record would otherwise turn the next scrape red, because normalize() will not invent a context window. That test caught exactly that mistake while writing this. Untracked models still reported on catalog pages drop from 13 to 6 (Anthropic) and 46 to 40 (Google); the remainder are image, video, audio, embedding and robotics models with no per-token price, plus the invitation-only Mythos models. Co-Authored-By: Claude Opus 5 (1M context) --- AGENTS.md | 5 + data/anthropic.json | 291 +++++++++++++++++++++++++++++++- data/google.json | 251 +++++++++++++++++++++++++++ scrapers/providers/anthropic.js | 7 + scrapers/providers/google.js | 6 + test/data.test.js | 35 ++++ 6 files changed, 588 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4b14710..81cd66e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -106,3 +106,8 @@ files themselves. above were found by executing that step and are fixed. It will fire on the next genuine upstream change, or by the 30-day `last_verified` re-stamp at the latest. +- 2026-09-04 — Coverage is a closed loop, enforced by a test: every id in a + provider's `tracked` list must have a record in `data/`, and every record must + be tracked. Adding an id without seeding the record turns the next scrape red, + because context windows and modalities are not scrapeable and `normalize()` + will not invent them. Seed the record in the same PR. diff --git a/data/anthropic.json b/data/anthropic.json index 3a2459e..8b0d610 100644 --- a/data/anthropic.json +++ b/data/anthropic.json @@ -1,6 +1,45 @@ { "schema_version": "1.0.0", "models": [ + { + "id": "anthropic:claude-fable-5", + "provider": "anthropic", + "model_id": "claude-fable-5", + "display_name": "Claude Fable 5", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 1000000, + "max_output_tokens": 128000, + "pricing": { + "currency": "USD", + "input_per_mtok": 10, + "output_per_mtok": 50, + "cached_input_per_mtok": 1, + "batch_input_per_mtok": 5, + "batch_output_per_mtok": 25 + }, + "dates": { + "released": "2026-06-09", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/fable-5/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Retirement committed not sooner than 2027-06-09. Cache reads are the usual 10% of base input, unlike Claude Fable 5.1." + }, { "id": "anthropic:claude-fable-5-1", "provider": "anthropic", @@ -28,14 +67,15 @@ "batch_output_per_mtok": 25 }, "dates": { - "released": null, + "released": "2026-09-01", "deprecated": null, "retired": null }, "sources": [ "https://platform.claude.com/docs/en/about-claude/model-deprecations", "https://platform.claude.com/docs/en/about-claude/models/overview", - "https://platform.claude.com/docs/en/about-claude/pricing" + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/fable-5-1/overview" ], "last_verified": "2026-09-04", "notes": "Retirement committed not sooner than 2027-09-01. Cache reads are 2.5% of base input on this model, not the usual 10%." @@ -74,11 +114,168 @@ "sources": [ "https://platform.claude.com/docs/en/about-claude/model-deprecations", "https://platform.claude.com/docs/en/about-claude/models/overview", - "https://platform.claude.com/docs/en/about-claude/pricing" + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/haiku-4-5/overview" ], "last_verified": "2026-09-04", "notes": "Alias claude-haiku-4-5 resolves to this snapshot. Retirement committed not sooner than 2026-10-15." }, + { + "id": "anthropic:claude-opus-4-5-20251101", + "provider": "anthropic", + "model_id": "claude-opus-4-5-20251101", + "display_name": "Claude Opus 4.5", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 200000, + "max_output_tokens": 64000, + "pricing": { + "currency": "USD", + "input_per_mtok": 5, + "output_per_mtok": 25, + "cached_input_per_mtok": 0.5, + "batch_input_per_mtok": 2.5, + "batch_output_per_mtok": 12.5 + }, + "dates": { + "released": "2025-11-24", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/opus-4-5/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Alias claude-opus-4-5 resolves to this snapshot. Retirement committed not sooner than 2026-11-24." + }, + { + "id": "anthropic:claude-opus-4-6", + "provider": "anthropic", + "model_id": "claude-opus-4-6", + "display_name": "Claude Opus 4.6", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 1000000, + "max_output_tokens": 128000, + "pricing": { + "currency": "USD", + "input_per_mtok": 5, + "output_per_mtok": 25, + "cached_input_per_mtok": 0.5, + "batch_input_per_mtok": 2.5, + "batch_output_per_mtok": 12.5 + }, + "dates": { + "released": "2026-02-05", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/opus-4-6/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Retirement committed not sooner than 2027-02-05." + }, + { + "id": "anthropic:claude-opus-4-7", + "provider": "anthropic", + "model_id": "claude-opus-4-7", + "display_name": "Claude Opus 4.7", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 1000000, + "max_output_tokens": 128000, + "pricing": { + "currency": "USD", + "input_per_mtok": 5, + "output_per_mtok": 25, + "cached_input_per_mtok": 0.5, + "batch_input_per_mtok": 2.5, + "batch_output_per_mtok": 12.5 + }, + "dates": { + "released": "2026-04-16", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/opus-4-7/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Retirement committed not sooner than 2027-04-16. First model on the current tokenizer, which produces about 30% more tokens for the same text than earlier models." + }, + { + "id": "anthropic:claude-opus-4-8", + "provider": "anthropic", + "model_id": "claude-opus-4-8", + "display_name": "Claude Opus 4.8", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 1000000, + "max_output_tokens": 128000, + "pricing": { + "currency": "USD", + "input_per_mtok": 5, + "output_per_mtok": 25, + "cached_input_per_mtok": 0.5, + "batch_input_per_mtok": 2.5, + "batch_output_per_mtok": 12.5 + }, + "dates": { + "released": "2026-05-28", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/opus-4-8/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Retirement committed not sooner than 2027-05-28. Supports 300K output tokens on the Batch API with a beta header." + }, { "id": "anthropic:claude-opus-5", "provider": "anthropic", @@ -106,18 +303,97 @@ "batch_output_per_mtok": 12.5 }, "dates": { - "released": null, + "released": "2026-07-24", "deprecated": null, "retired": null }, "sources": [ "https://platform.claude.com/docs/en/about-claude/model-deprecations", "https://platform.claude.com/docs/en/about-claude/models/overview", - "https://platform.claude.com/docs/en/about-claude/pricing" + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/opus-5/overview" ], "last_verified": "2026-09-04", "notes": "Retirement committed not sooner than 2027-07-24. cached_input_per_mtok is the cache-read (hit) price; 5m/1h cache writes cost 1.25x/2x base input." }, + { + "id": "anthropic:claude-sonnet-4-5-20250929", + "provider": "anthropic", + "model_id": "claude-sonnet-4-5-20250929", + "display_name": "Claude Sonnet 4.5", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 200000, + "max_output_tokens": 64000, + "pricing": { + "currency": "USD", + "input_per_mtok": 3, + "output_per_mtok": 15, + "cached_input_per_mtok": 0.3, + "batch_input_per_mtok": 1.5, + "batch_output_per_mtok": 7.5 + }, + "dates": { + "released": "2025-09-29", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/sonnet-4-5/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Alias claude-sonnet-4-5 resolves to this snapshot. Retirement committed not sooner than 2026-09-29." + }, + { + "id": "anthropic:claude-sonnet-4-6", + "provider": "anthropic", + "model_id": "claude-sonnet-4-6", + "display_name": "Claude Sonnet 4.6", + "family": "claude", + "status": "available", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "context_window": 1000000, + "max_output_tokens": 128000, + "pricing": { + "currency": "USD", + "input_per_mtok": 3, + "output_per_mtok": 15, + "cached_input_per_mtok": 0.3, + "batch_input_per_mtok": 1.5, + "batch_output_per_mtok": 7.5 + }, + "dates": { + "released": "2026-02-17", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://platform.claude.com/docs/en/about-claude/model-deprecations", + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/sonnet-4-6/overview" + ], + "last_verified": "2026-09-04", + "notes": "Legacy. Retirement committed not sooner than 2027-02-17." + }, { "id": "anthropic:claude-sonnet-5", "provider": "anthropic", @@ -145,14 +421,15 @@ "batch_output_per_mtok": 5 }, "dates": { - "released": null, + "released": "2026-06-30", "deprecated": null, "retired": null }, "sources": [ "https://platform.claude.com/docs/en/about-claude/model-deprecations", "https://platform.claude.com/docs/en/about-claude/models/overview", - "https://platform.claude.com/docs/en/about-claude/pricing" + "https://platform.claude.com/docs/en/about-claude/pricing", + "https://platform.claude.com/docs/en/models/sonnet-5/overview" ], "last_verified": "2026-09-04", "notes": "Retirement committed not sooner than 2027-06-30. Introductory $2/$10 pricing was made permanent; the planned 2026-09-01 increase was cancelled." diff --git a/data/google.json b/data/google.json index 22bd9f4..54e3e0c 100644 --- a/data/google.json +++ b/data/google.json @@ -34,6 +34,90 @@ "last_verified": "2026-09-04", "notes": "Shut down 2026-06-01; provider's recommended replacement is gemini-3.6-flash. Deprecation announcement date is not published on the deprecations page." }, + { + "id": "google:gemini-2.5-flash", + "provider": "google", + "model_id": "gemini-2.5-flash", + "display_name": "Gemini 2.5 Flash", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 0.3, + "output_per_mtok": 2.5, + "cached_input_per_mtok": 0.03, + "batch_input_per_mtok": 0.15, + "batch_output_per_mtok": 1.25 + }, + "dates": { + "released": "2025-06-17", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-2-5-flash", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04", + "notes": "Input price shown is the text, image and video rate; audio input is billed higher." + }, + { + "id": "google:gemini-2.5-flash-lite", + "provider": "google", + "model_id": "gemini-2.5-flash-lite", + "display_name": "Gemini 2.5 Flash-Lite", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 0.1, + "output_per_mtok": 0.4, + "cached_input_per_mtok": 0.01, + "batch_input_per_mtok": 0.05, + "batch_output_per_mtok": 0.2 + }, + "dates": { + "released": "2025-07-22", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-2-5-flash-lite", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04", + "notes": "Input price shown is the text, image and video rate; audio input is billed higher." + }, { "id": "google:gemini-2.5-pro", "provider": "google", @@ -76,6 +160,172 @@ "last_verified": "2026-09-04", "notes": "Prices shown are the <=200K-token prompt tier. Prompts over 200K tokens: $2.50 input / $15.00 output per MTok. No shutdown date announced." }, + { + "id": "google:gemini-3.5-flash", + "provider": "google", + "model_id": "gemini-3.5-flash", + "display_name": "Gemini 3.5 Flash", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 1.5, + "output_per_mtok": 9, + "cached_input_per_mtok": 0.15, + "batch_input_per_mtok": 0.75, + "batch_output_per_mtok": 4.5 + }, + "dates": { + "released": "2026-05-19", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-3-5-flash", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04" + }, + { + "id": "google:gemini-3.5-flash-lite", + "provider": "google", + "model_id": "gemini-3.5-flash-lite", + "display_name": "Gemini 3.5 Flash-Lite", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 0.3, + "output_per_mtok": 2.5, + "cached_input_per_mtok": 0.03, + "batch_input_per_mtok": 0.15, + "batch_output_per_mtok": 1.25 + }, + "dates": { + "released": "2026-07-21", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-3-5-flash-lite", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04" + }, + { + "id": "google:gemini-3.6-flash", + "provider": "google", + "model_id": "gemini-3.6-flash", + "display_name": "Gemini 3.6 Flash", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 0.75, + "output_per_mtok": 3.75, + "cached_input_per_mtok": 0.075, + "batch_input_per_mtok": 0.375, + "batch_output_per_mtok": 1.875 + }, + "dates": { + "released": "2026-07-21", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-3-6-flash", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04", + "notes": "Paid tier. Provider states these prices apply through 2026-12-31 and double on 2027-01-01." + }, + { + "id": "google:gemini-3.7-flash", + "provider": "google", + "model_id": "gemini-3.7-flash", + "display_name": "Gemini 3.7 Flash", + "family": "gemini", + "status": "available", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "context_window": 1048576, + "max_output_tokens": 65536, + "pricing": { + "currency": "USD", + "input_per_mtok": 0.75, + "output_per_mtok": 3.75, + "cached_input_per_mtok": 0.075, + "batch_input_per_mtok": 0.375, + "batch_output_per_mtok": 1.875 + }, + "dates": { + "released": "2026-08-13", + "deprecated": null, + "retired": null + }, + "sources": [ + "https://ai.google.dev/gemini-api/docs/deprecations", + "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-3-7-flash", + "https://ai.google.dev/gemini-api/docs/pricing" + ], + "last_verified": "2026-09-04", + "notes": "Paid tier. Provider states these prices apply through 2026-12-31 and double on 2027-01-01." + }, { "id": "google:gemini-3.8-flash", "provider": "google", @@ -112,6 +362,7 @@ "sources": [ "https://ai.google.dev/gemini-api/docs/deprecations", "https://ai.google.dev/gemini-api/docs/models", + "https://ai.google.dev/gemini-api/docs/models/gemini-3-8-flash", "https://ai.google.dev/gemini-api/docs/pricing" ], "last_verified": "2026-09-04", diff --git a/scrapers/providers/anthropic.js b/scrapers/providers/anthropic.js index f7b94a6..45632c8 100644 --- a/scrapers/providers/anthropic.js +++ b/scrapers/providers/anthropic.js @@ -52,8 +52,15 @@ export const sources = [ // run.js as an untracked model rather than silently dropped or half-populated. export const tracked = [ { model_id: "claude-fable-5-1" }, + { model_id: "claude-fable-5" }, { model_id: "claude-opus-5" }, + { model_id: "claude-opus-4-8" }, + { model_id: "claude-opus-4-7" }, + { model_id: "claude-opus-4-6" }, + { model_id: "claude-opus-4-5-20251101", aliases: ["claude-opus-4-5"] }, { model_id: "claude-sonnet-5" }, + { model_id: "claude-sonnet-4-6" }, + { model_id: "claude-sonnet-4-5-20250929", aliases: ["claude-sonnet-4-5"] }, { model_id: "claude-haiku-4-5-20251001", aliases: ["claude-haiku-4-5"] }, ]; diff --git a/scrapers/providers/google.js b/scrapers/providers/google.js index f466738..d04ce4c 100644 --- a/scrapers/providers/google.js +++ b/scrapers/providers/google.js @@ -36,7 +36,13 @@ export const sources = [ export const tracked = [ { model_id: "gemini-3.8-flash" }, + { model_id: "gemini-3.7-flash" }, + { model_id: "gemini-3.6-flash" }, + { model_id: "gemini-3.5-flash" }, + { model_id: "gemini-3.5-flash-lite" }, { model_id: "gemini-2.5-pro" }, + { model_id: "gemini-2.5-flash" }, + { model_id: "gemini-2.5-flash-lite" }, { model_id: "gemini-2.0-flash" }, ]; diff --git a/test/data.test.js b/test/data.test.js index f7c4280..fb210ac 100644 --- a/test/data.test.js +++ b/test/data.test.js @@ -1,6 +1,9 @@ // Cross-file invariants (build spec §4.3). import { test } from "node:test"; import assert from "node:assert/strict"; +import { readdirSync } from "node:fs"; +import { join, dirname, resolve } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { listDataFiles, readDataFile } from "../scrapers/lib/schema.js"; // Allowed hostnames for `sources`, per provider. Extend deliberately. @@ -104,3 +107,35 @@ test("retired models have no pricing, priced models have positive prices", () => } } }); + +// Coverage must be a closed loop: a model named in a provider's `tracked` list +// has to exist in data/, because context windows and modalities are not +// scrapeable and normalize() will not invent them. Without this, adding an id +// to `tracked` and forgetting to seed the record turns the next scrape red. +const providersDir = resolve( + dirname(fileURLToPath(import.meta.url)), + "..", + "scrapers", + "providers", +); + +test("every tracked model has a record, and every record is tracked", async () => { + for (const file of readdirSync(providersDir).filter((f) => f.endsWith(".js"))) { + const mod = await import(pathToFileURL(join(providersDir, file)).href); + const recorded = new Set(all.filter((m) => m.provider === mod.provider).map((m) => m.model_id)); + const trackedIds = new Set(mod.tracked.map((t) => t.model_id)); + + for (const id of trackedIds) { + assert.ok( + recorded.has(id), + `${mod.provider} tracks ${id} but data/${mod.provider}.json has no record`, + ); + } + for (const id of recorded) { + assert.ok( + trackedIds.has(id), + `data/${mod.provider}.json records ${id} but the module does not track it`, + ); + } + } +});