SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
56.7 KB · 604 lines python
Raw Blame History
1#!/usr/bin/env python32"""Build the Anthropic models catalogue + pricing fragments and the models doc.34Inputs (offline): sources/anthropic/pages/** (facts transcribed below with their canonical URL),5                  sources/anthropic/models-api-raw.json (GET /v1/models, sanitized),6                  tmp-live/anthropic-models/summary.json (scripts/verify_anthropic_models.py output, optional).7Outputs: generated/fragments/models/anthropic-models.json8         generated/fragments/pricing/anthropic-pricing.json9         docs/models/anthropic-models.md10Re-run after refreshing the sources; the doc matrices are derived from the same records as the JSON.11"""12from __future__ import annotations1314import json15from pathlib import Path1617ROOT = Path(__file__).resolve().parents[1]18RAW = ROOT / "sources" / "anthropic" / "models-api-raw.json"19SUMMARY = ROOT / "tmp-live" / "anthropic-models" / "summary.json"20OUT_MODELS = ROOT / "generated" / "fragments" / "models" / "anthropic-models.json"21OUT_PRICING = ROOT / "generated" / "fragments" / "pricing" / "anthropic-pricing.json"22OUT_DOC = ROOT / "docs" / "models" / "anthropic-models.md"2324RETRIEVED = "2026-09-18"25LAST_VERIFIED = "2026-09-18"26D = "https://platform.claude.com/docs/en/"272829def src(*paths: str) -> list[dict]:30    return [{"url": D + p, "retrieved_at": RETRIEVED} for p in paths]313233# --------------------------------------------------------------------------------------------34# Static facts transcribed from the docs (models/<slug>/overview, pricing, deprecations, tool pages)35# --------------------------------------------------------------------------------------------36# generation buckets used by capability rules37GEN5 = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5", "claude-mythos-5", "claude-opus-5", "claude-sonnet-5"}38GEN47P = GEN5 | {"claude-opus-4-8", "claude-opus-4-7", "claude-mythos-preview"}          # new tokenizer, sampling params rejected39GEN46P = GEN47P | {"claude-opus-4-6", "claude-sonnet-4-6"}                                   # 1M default, inference_geo, compaction40GEN45 = {"claude-opus-4-5-20251101", "claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001"}41CURRENT = GEN46P | GEN4542TOOLSET_MODELS = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5", "claude-mythos-5", "claude-opus-5", "claude-sonnet-5", "claude-opus-4-8"}43COMPUTER_20251124 = TOOLSET_MODELS | {"claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-opus-4-5-20251101"}44COMPUTER_20250124 = {"claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001", "claude-opus-4-1-20250805", "claude-sonnet-4-20250514", "claude-opus-4-20250514"}45TASK_BUDGET_MODELS = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5", "claude-mythos-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7"}46FAST_MODE_MODELS = {"claude-opus-5", "claude-opus-4-8"}47BATCH_300K = {"claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-5", "claude-sonnet-4-6"}48NO_PRIORITY_TIER = {"claude-fable-5-1", "claude-mythos-5-1", "claude-mythos-5", "claude-mythos-preview", "claude-opus-5", "claude-sonnet-5"}49MID_CONV_SYSTEM = {"claude-fable-5", "claude-mythos-5", "claude-opus-4-8", "claude-opus-5", "claude-fable-5-1", "claude-mythos-5-1"}50MID_CONV_TOOL_CHANGES = {"claude-fable-5", "claude-mythos-5", "claude-opus-4-8", "claude-opus-5"}51PER_MESSAGE_EFFORT = {"claude-fable-5-1", "claude-mythos-5-1", "claude-opus-5"}52ALWAYS_ON_THINKING = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5", "claude-mythos-5"}53PROGRESS_UPDATES = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5"}54RETENTION_30D = {"claude-fable-5-1", "claude-mythos-5-1", "claude-fable-5", "claude-mythos-5"}55CACHE_MIN = {"claude-fable-5-1": 512, "claude-mythos-5-1": 512, "claude-opus-5": 512, "claude-fable-5": 512, "claude-mythos-5": 512,56             "claude-mythos-preview": 2048, "claude-opus-4-7": 2048, "claude-opus-4-6": 4096, "claude-opus-4-5-20251101": 4096,57             "claude-opus-4-8": 1024, "claude-sonnet-5": 1024, "claude-sonnet-4-6": 1024, "claude-sonnet-4-5-20250929": 1024,58             "claude-opus-4-1-20250805": 1024, "claude-opus-4-20250514": 1024, "claude-sonnet-4-20250514": 1024,59             "claude-haiku-4-5-20251001": 4096, "claude-3-5-haiku-20241022": 2048}60TOOL_SYS_PROMPT_TOKENS = {  # pricing page "Tool use system prompt token count": (auto/none, any/tool)61    "claude-opus-5": (286, 406), "claude-opus-4-8": (290, 410), "claude-opus-4-7": (675, 804), "claude-opus-4-6": (497, 589),62    "claude-opus-4-5-20251101": (496, 588), "claude-opus-4-1-20250805": (313, 315), "claude-opus-4-20250514": (313, 315),63    "claude-sonnet-5": (354, 474), "claude-sonnet-4-6": (497, 589), "claude-sonnet-4-5-20250929": (496, 588),64    "claude-sonnet-4-20250514": (313, 315), "claude-haiku-4-5-20251001": (496, 588), "claude-3-5-haiku-20241022": (264, 355)}65RATE_LIMIT_CLASS = {"claude-fable-5-1": "fable-5.x (shared Fable 5.1 + Fable 5)", "claude-fable-5": "fable-5.x (shared Fable 5.1 + Fable 5)",66                    "claude-mythos-5-1": "mythos-5.x (shared Mythos 5.1 + Mythos 5; same terms as Fable)", "claude-mythos-5": "mythos-5.x (shared Mythos 5.1 + Mythos 5; same terms as Fable)",67                    "claude-opus-5": "opus-5 (own bucket)", "claude-opus-4-8": "opus-4.x (shared 4.8/4.7/4.6/4.5)", "claude-opus-4-7": "opus-4.x (shared 4.8/4.7/4.6/4.5)",68                    "claude-opus-4-6": "opus-4.x (shared 4.8/4.7/4.6/4.5)", "claude-opus-4-5-20251101": "opus-4.x (shared 4.8/4.7/4.6/4.5)",69                    "claude-sonnet-5": "sonnet-5 (own bucket)", "claude-sonnet-4-6": "sonnet-4.x (shared 4.6/4.5)", "claude-sonnet-4-5-20250929": "sonnet-4.x (shared 4.6/4.5)",70                    "claude-haiku-4-5-20251001": "haiku-4.5", "claude-3-5-haiku-20241022": "haiku-3.5 (retired on Claude API; counts cache reads toward ITPM)"}7172# id, display, family, generation, slug, released (docs), reliable cutoff, training cutoff, ctx, max_out, thinking, default effort,73# effort levels, retirement, bedrock ids, vertex, foundry, cp-aws, pricing (in,out,5m,1h,read), latency, lifecycle, alias74MODELS: list[dict] = [75    dict(id="claude-fable-5-1", display="Claude Fable 5.1", family="Fable", gen="5.1", slug="fable-5-1", released="2026-09-01",76         cutoff=("Jun 2026", "Jun 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (always on)", effort_default="high",77         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-09-01",78         bedrock=["anthropic.claude-fable-5-1"], vertex="claude-fable-5-1", foundry="claude-fable-5-1", cpaws="claude-fable-5-1",79         price=(10, 50, 12.5, 20, 0.25), latency="Slower", lifecycle="Active (latest)", alias=None,80         desc="For demanding reasoning and long-horizon agentic work"),81    dict(id="claude-mythos-5-1", display="Claude Mythos 5.1", family="Mythos", gen="5.1", slug="mythos-5-1", released="2026-09-01",82         cutoff=("Jun 2026", "Jun 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (always on)", effort_default="high",83         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-09-01",84         bedrock=["anthropic.claude-mythos-5-1"], vertex="claude-mythos-5-1", foundry="claude-mythos-5-1", cpaws=None,85         price=(10, 50, 12.5, 20, 0.25), latency="Slower", lifecycle="Active (invite only, Project Glasswing)", alias=None,86         desc="Invite-only frontier model (Project Glasswing)"),87    dict(id="claude-fable-5", display="Claude Fable 5", family="Fable", gen="5", slug="fable-5", released="2026-06-09",88         cutoff=("Jan 2026", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (always on)", effort_default="high",89         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-06-09",90         bedrock=["anthropic.claude-fable-5"], vertex="claude-fable-5", foundry="claude-fable-5", cpaws="claude-fable-5",91         price=(10, 50, 12.5, 20, 1.0), latency=None, lifecycle="Active (legacy)", alias=None,92         desc="Most capable widely released model at launch; access paused then restored 2026-07-01"),93    dict(id="claude-mythos-5", display="Claude Mythos 5", family="Mythos", gen="5", slug="mythos-5", released="2026-06-09",94         cutoff=("Jan 2026", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (always on)", effort_default="high",95         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-06-09",96         bedrock=["anthropic.claude-mythos-5"], vertex="claude-mythos-5", foundry="claude-mythos-5", cpaws=None,97         price=(10, 50, 12.5, 20, 1.0), latency=None, lifecycle="Active (invite only, Project Glasswing)", alias=None,98         desc="Invite-only frontier model (Project Glasswing)"),99    dict(id="claude-mythos-preview", display="Claude Mythos Preview", family="Mythos", gen="preview", slug=None, released="2026-04-07",100         cutoff=(None, None), ctx=1_000_000, max_out=128_000, thinking="adaptive", effort_default="high",101         effort=["low", "medium", "high", "max"], retirement="to be announced (deprecated 2026-06-09)",102         bedrock=["anthropic.claude-mythos-preview"], vertex=None, foundry=None, cpaws=None,103         price=None, latency=None, lifecycle="Deprecated (invite only)", alias=None,104         desc="Gated research preview for defensive cybersecurity (Project Glasswing); migrate to claude-mythos-5"),105    dict(id="claude-opus-5", display="Claude Opus 5", family="Opus", gen="5", slug="opus-5", released="2026-07-24",106         cutoff=("May 2026", "May 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (on by default)", effort_default="high",107         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-07-24",108         bedrock=["anthropic.claude-opus-5"], vertex="claude-opus-5", foundry="claude-opus-5", cpaws="claude-opus-5",109         price=(5, 25, 6.25, 10, 0.5), latency="Moderate", lifecycle="Active (latest)", alias=None,110         desc="For complex agentic coding and enterprise work"),111    dict(id="claude-opus-4-8", display="Claude Opus 4.8", family="Opus", gen="4.8", slug="opus-4-8", released="2026-05-28",112         cutoff=("Jan 2026", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive", effort_default="high",113         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-05-28",114         bedrock=["anthropic.claude-opus-4-8"], vertex="claude-opus-4-8", foundry="claude-opus-4-8", cpaws="claude-opus-4-8",115         price=(5, 25, 6.25, 10, 0.5), latency=None, lifecycle="Active (legacy)", alias=None, desc="Opus 4.x line; same tools/features as Opus 4.7"),116    dict(id="claude-opus-4-7", display="Claude Opus 4.7", family="Opus", gen="4.7", slug="opus-4-7", released="2026-04-16",117         cutoff=("Jan 2026", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive", effort_default="high",118         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-04-16",119         bedrock=["anthropic.claude-opus-4-7"], vertex="claude-opus-4-7", foundry="claude-opus-4-7", cpaws="claude-opus-4-7",120         price=(5, 25, 6.25, 10, 0.5), latency=None, lifecycle="Active (legacy)", alias=None, desc="Introduced the new tokenizer (+~30% tokens), xhigh effort, high-res vision"),121    dict(id="claude-opus-4-6", display="Claude Opus 4.6", family="Opus", gen="4.6", slug="opus-4-6", released="2026-02-05",122         cutoff=("May 2025", "Aug 2025"), ctx=1_000_000, max_out=128_000, thinking="adaptive (manual extended deprecated)", effort_default="high",123         effort=["low", "medium", "high", "max"], retirement="not sooner than 2027-02-05",124         bedrock=["anthropic.claude-opus-4-6-v1"], vertex="claude-opus-4-6", foundry="claude-opus-4-6", cpaws="claude-opus-4-6",125         price=(5, 25, 6.25, 10, 0.5), latency=None, lifecycle="Active (legacy)", alias=None, desc="First dateless ID; last Bedrock ID with -v1 suffix"),126    dict(id="claude-opus-4-5-20251101", display="Claude Opus 4.5", family="Opus", gen="4.5", slug="opus-4-5", released="2025-11-24",127         cutoff=("May 2025", "Aug 2025"), ctx=200_000, max_out=64_000, thinking="extended (manual)", effort_default="high",128         effort=["low", "medium", "high"], retirement="not sooner than 2026-11-24",129         bedrock=["anthropic.claude-opus-4-5-20251101-v1:0"], vertex="claude-opus-4-5@20251101", foundry="claude-opus-4-5", cpaws="claude-opus-4-5",130         price=(5, 25, 6.25, 10, 0.5), latency=None, lifecycle="Active (legacy)", alias="claude-opus-4-5", desc="Last dated Opus snapshot"),131    dict(id="claude-sonnet-5", display="Claude Sonnet 5", family="Sonnet", gen="5", slug="sonnet-5", released="2026-06-30",132         cutoff=("Jan 2026", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (on by default)", effort_default="high",133         effort=["low", "medium", "high", "xhigh", "max"], retirement="not sooner than 2027-06-30",134         bedrock=["anthropic.claude-sonnet-5"], vertex="claude-sonnet-5", foundry="claude-sonnet-5", cpaws="claude-sonnet-5",135         price=(2, 10, 2.5, 4, 0.2), latency="Fast", lifecycle="Active (latest)", alias=None, desc="The best combination of speed and intelligence"),136    dict(id="claude-sonnet-4-6", display="Claude Sonnet 4.6", family="Sonnet", gen="4.6", slug="sonnet-4-6", released="2026-02-17",137         cutoff=("Aug 2025", "Jan 2026"), ctx=1_000_000, max_out=128_000, thinking="adaptive (manual extended deprecated)", effort_default="high",138         effort=["low", "medium", "high", "max"], retirement="not sooner than 2027-02-17",139         bedrock=["anthropic.claude-sonnet-4-6"], vertex="claude-sonnet-4-6", foundry="claude-sonnet-4-6", cpaws="claude-sonnet-4-6",140         price=(3, 15, 3.75, 6, 0.3), latency=None, lifecycle="Active (legacy)", alias=None, desc="First Sonnet with dateless ID"),141    dict(id="claude-sonnet-4-5-20250929", display="Claude Sonnet 4.5", family="Sonnet", gen="4.5", slug="sonnet-4-5", released="2025-09-29",142         cutoff=("Jan 2025", "Jul 2025"), ctx=200_000, max_out=64_000, thinking="extended (manual)", effort_default=None,143         effort=[], retirement="not sooner than 2026-09-29",144         bedrock=["anthropic.claude-sonnet-4-5-20250929-v1:0"], vertex="claude-sonnet-4-5@20250929", foundry="claude-sonnet-4-5", cpaws="claude-sonnet-4-5",145         price=(3, 15, 3.75, 6, 0.3), latency=None, lifecycle="Active (legacy)", alias="claude-sonnet-4-5", desc="1M context beta retired for this model on 2026-04-30"),146    dict(id="claude-haiku-4-5-20251001", display="Claude Haiku 4.5", family="Haiku", gen="4.5", slug="haiku-4-5", released="2025-10-15",147         cutoff=("Feb 2025", "Jul 2025"), ctx=200_000, max_out=64_000, thinking="extended (manual)", effort_default=None,148         effort=[], retirement="not sooner than 2026-10-15",149         bedrock=["anthropic.claude-haiku-4-5", "anthropic.claude-haiku-4-5-20251001-v1:0"], vertex="claude-haiku-4-5@20251001", foundry="claude-haiku-4-5", cpaws="claude-haiku-4-5",150         price=(1, 5, 1.25, 2, 0.1), latency="Fastest", lifecycle="Active (latest)", alias="claude-haiku-4-5", desc="The fastest model with near-frontier intelligence"),151]152153# Retired / legacy models (deprecations page + release notes). pricing only where the current pricing page still lists it.154LEGACY: list[dict] = [155    dict(id="claude-opus-4-1-20250805", display="Claude Opus 4.1", family="Opus", gen="4.1", released="2025-08-05", deprecated="2026-06-05", retired="2026-08-05",156         replacement="claude-opus-4-8", price=(15, 75, 18.75, 30, 1.5), ctx=200_000, max_out=32_000, alias="claude-opus-4-1",157         bedrock=["anthropic.claude-opus-4-1-20250805-v1:0"], vertex="claude-opus-4-1@20250805", cloud_note="retired on Claude API; still listed (deprecated) on Bedrock and Google Cloud"),158    dict(id="claude-opus-4-20250514", display="Claude Opus 4", family="Opus", gen="4", released="2025-05-22", deprecated="2026-04-14", retired="2026-06-15",159         replacement="claude-opus-4-8", price=(15, 75, 18.75, 30, 1.5), ctx=200_000, max_out=32_000, alias="claude-opus-4-0",160         bedrock=["anthropic.claude-opus-4-20250514-v1:0"], vertex="claude-opus-4@20250514", cloud_note="retired on Claude API and Bedrock; still listed (deprecated) on Google Cloud"),161    dict(id="claude-sonnet-4-20250514", display="Claude Sonnet 4", family="Sonnet", gen="4", released="2025-05-22", deprecated="2026-04-14", retired="2026-06-15",162         replacement="claude-sonnet-4-6", price=(3, 15, 3.75, 6, 0.3), ctx=200_000, max_out=64_000, alias="claude-sonnet-4-0",163         bedrock=["anthropic.claude-sonnet-4-20250514-v1:0"], vertex="claude-sonnet-4@20250514", cloud_note="retired on Claude API; still listed (deprecated) on Bedrock and Google Cloud; 1M context beta retired 2026-04-30"),164    dict(id="claude-3-7-sonnet-20250219", display="Claude Sonnet 3.7", family="Sonnet", gen="3.7", released="2025-02-24", deprecated="2025-10-28", retired="2026-02-19",165         replacement="claude-sonnet-4-6", price=None, ctx=200_000, max_out=None, alias="claude-3-7-sonnet-latest",166         bedrock=["anthropic.claude-3-7-sonnet-20250219-v1:0"], vertex=None, cloud_note="retired everywhere per Bedrock table"),167    dict(id="claude-3-5-haiku-20241022", display="Claude Haiku 3.5", family="Haiku", gen="3.5", released="2024-11-04", deprecated="2025-12-19", retired="2026-02-19",168         replacement="claude-haiku-4-5-20251001", price=(0.8, 4, 1.0, 1.6, 0.08), ctx=200_000, max_out=None, alias="claude-3-5-haiku-latest",169         bedrock=["anthropic.claude-3-5-haiku-20241022-v1:0"], vertex="claude-3-5-haiku@20241022", cloud_note="retired on Claude API; still listed (deprecated) on Bedrock and Google Cloud; counts cache reads toward ITPM"),170    dict(id="claude-3-haiku-20240307", display="Claude Haiku 3", family="Haiku", gen="3", released="2024-03-07", deprecated="2026-02-19", retired="2026-04-20",171         replacement="claude-haiku-4-5-20251001", price=None, ctx=200_000, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None),172    dict(id="claude-3-5-sonnet-20241022", display="Claude Sonnet 3.5 (Oct 2024)", family="Sonnet", gen="3.5", released="2024-10-22", deprecated="2025-08-13", retired="2025-10-28",173         replacement="claude-sonnet-4-6", price=None, ctx=200_000, max_out=None, alias="claude-3-5-sonnet-latest", bedrock=None, vertex=None, cloud_note=None),174    dict(id="claude-3-5-sonnet-20240620", display="Claude Sonnet 3.5 (Jun 2024)", family="Sonnet", gen="3.5", released="2024-06-20", deprecated="2025-08-13", retired="2025-10-28",175         replacement="claude-sonnet-4-6", price=None, ctx=200_000, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None),176    dict(id="claude-3-opus-20240229", display="Claude Opus 3", family="Opus", gen="3", released="2024-02-29", deprecated="2025-06-30", retired="2026-01-05",177         replacement="claude-opus-4-8", price=None, ctx=200_000, max_out=None, alias="claude-3-opus-latest", bedrock=None, vertex=None, cloud_note="researchers may request access via External Researcher Access Program"),178    dict(id="claude-3-sonnet-20240229", display="Claude Sonnet 3", family="Sonnet", gen="3", released="2024-02-29", deprecated="2025-01-21", retired="2025-07-21",179         replacement="claude-sonnet-4-6", price=None, ctx=200_000, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None),180    dict(id="claude-2.1", display="Claude 2.1", family="Claude 2", gen="2.1", released=None, deprecated="2025-01-21", retired="2025-07-21", replacement="claude-opus-4-8", price=None, ctx=200_000, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None),181    dict(id="claude-2.0", display="Claude 2.0", family="Claude 2", gen="2.0", released=None, deprecated="2025-01-21", retired="2025-07-21", replacement="claude-opus-4-8", price=None, ctx=None, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None),182] + [dict(id=i, display=i, family="Claude 1 / Instant", gen=i.split("-")[-1], released=None, deprecated="2024-09-04", retired="2024-11-06", replacement="claude-haiku-4-5-20251001",183          price=None, ctx=None, max_out=None, alias=None, bedrock=None, vertex=None, cloud_note=None)184     for i in ["claude-1.0", "claude-1.1", "claude-1.2", "claude-1.3", "claude-instant-1.0", "claude-instant-1.1", "claude-instant-1.2"]]185186187def load_live() -> tuple[dict[str, dict], dict]:188    live = {m["id"]: m for m in json.loads(RAW.read_text())["data"]} if RAW.exists() else {}189    summary = json.loads(SUMMARY.read_text()) if SUMMARY.exists() else {}190    return live, summary191192193def cap(v: bool | str | None) -> bool | str:194    return "unknown" if v is None else v195196197def capabilities(m: dict, live: dict | None) -> dict:198    i = m["id"]199    lc = (live or {}).get("capabilities") or {}200    eff = lc.get("effort", {})201    th = lc.get("thinking", {}).get("types", {})202    cm = lc.get("context_management", {})203    haiku = i == "claude-haiku-4-5-20251001"204    c = {205        "text_input": True, "vision": True, "pdf_input": cap(lc.get("pdf_input", {}).get("supported")) if lc else True,206        "high_resolution_vision_2576px": i in GEN47P,207        "tool_use": True, "parallel_tool_use": True,208        "tool_choice_any_or_tool": i not in {"claude-fable-5-1", "claude-mythos-5-1"},209        "strict_tool_use": True, "structured_outputs_json": cap(lc.get("structured_outputs", {}).get("supported")) if lc else (i in GEN46P | GEN45),210        "fine_grained_tool_streaming": True,211        "thinking_extended_manual_budget": cap(th.get("enabled", {}).get("supported")) if lc else (i in GEN45 or i in {"claude-opus-4-6", "claude-sonnet-4-6"}),212        "thinking_adaptive": cap(th.get("adaptive", {}).get("supported")) if lc else (i in GEN46P),213        "thinking_always_on": i in ALWAYS_ON_THINKING,214        "thinking_on_by_default": i in GEN5,215        "thinking_can_be_disabled": (i not in ALWAYS_ON_THINKING) if i in CURRENT else "unknown",216        "interleaved_thinking": False if haiku else (True if i in GEN46P else "beta header interleaved-thinking-2025-05-14 with manual thinking"),217        "thinking_display_omitted_default": i in GEN47P,218        "progress_updates_between_tool_calls": i in PROGRESS_UPDATES,219        "effort_parameter": cap(eff.get("supported")) if lc else bool(m.get("effort")),220        "effort_levels": [lv for lv in ["low", "medium", "high", "xhigh", "max"] if eff.get(lv, {}).get("supported")] if lc else list(m.get("effort") or []),221        "effort_default": m.get("effort_default"),222        "per_message_effort_beta": i in PER_MESSAGE_EFFORT,223        "fast_mode_speed_fast": i in FAST_MODE_MODELS,224        "prompt_caching": True, "prompt_caching_1h_ttl": True, "automatic_caching_top_level_cache_control": True,225        "min_cacheable_tokens": CACHE_MIN.get(i),226        "batch_api": cap(lc.get("batch", {}).get("supported")) if lc else True,227        "batch_extended_output_300k_beta": i in BATCH_300K,228        "citations": cap(lc.get("citations", {}).get("supported")) if lc else True,229        "files_api": True,230        "web_search": True, "web_search_dynamic_filtering": i in GEN46P, "web_fetch": True, "web_fetch_dynamic_filtering": i in GEN46P,231        "code_execution": cap(lc.get("code_execution", {}).get("supported")) if lc else (i in CURRENT),232        "code_execution_docs": i in CURRENT,233        "programmatic_tool_calling": i in CURRENT and not haiku,234        "computer_use": i in (TOOLSET_MODELS | COMPUTER_20251124 | COMPUTER_20250124),235        "computer_use_toolset_ga": i in TOOLSET_MODELS,236        "browser_use": i in TOOLSET_MODELS,237        "memory_tool": True, "text_editor_tool": True, "bash_tool": True,238        "tool_search": i in CURRENT, "advisor_tool": i in CURRENT and i != "claude-mythos-preview",239        "mcp_connector": True, "agent_skills": i in CURRENT,240        "context_editing_clear_tool_uses": cap(cm.get("clear_tool_uses_20250919", {}).get("supported")) if lc else True,241        "context_editing_clear_thinking": cap(cm.get("clear_thinking_20251015", {}).get("supported")) if lc else True,242        "compaction_server_side": cap(cm.get("compact_20260112", {}).get("supported")) if lc else (i in GEN46P),243        "compact_on_demand_beta": i in GEN46P,244        "context_1m_default_no_beta": i in GEN46P,245        "inference_geo_data_residency": i in GEN46P,246        "mid_conversation_system_messages": i in MID_CONV_SYSTEM,247        "mid_conversation_tool_changes_beta": i in MID_CONV_TOOL_CHANGES,248        "task_budgets_beta": i in TASK_BUDGET_MODELS,249        "sampling_params_temperature_top_p_top_k": "rejected with 400 when non-default" if i in GEN47P else "accepted (incompatible with thinking on)",250        "assistant_prefill": False if (i in ALWAYS_ON_THINKING or i == "claude-opus-4-6" or i in GEN5) else "unknown",251        "priority_tier": i not in NO_PRIORITY_TIER,252        "zero_data_retention_eligible": (i not in RETENTION_30D) if i in CURRENT else "unknown",253        "tokenizer": "new tokenizer (Claude 4.7+, ~30% more tokens)" if i in GEN47P else "previous tokenizer",254        "multilingual": True,255        "latency_class": m.get("latency"),256        "output_modalities": ["text"],257    }258    return c259260261def tools_for(m: dict) -> list[dict]:262    i = m["id"]263    t: list[dict] = []264265    def add(type_: str, category: str, beta: str | None = None, note: str | None = None):266        d = {"type": type_, "category": category, "beta_header": beta}267        if note:268            d["note"] = note269        t.append(d)270271    for v in ["web_search_20260318", "web_search_20260209", "web_search_20250305"]:272        if v == "web_search_20250305" or i in GEN46P:273            add(v, "server", None, None if v == "web_search_20250305" else "dynamic filtering (code execution) on 4.6+")274    for v in ["web_fetch_20260318", "web_fetch_20260309", "web_fetch_20260209", "web_fetch_20250910"]:275        if v == "web_fetch_20250910" or i in GEN46P:276            add(v, "server")277    if i in CURRENT:278        for v in ["code_execution_20260521", "code_execution_20260120", "code_execution_20250825"]:279            add(v, "server", None, "Haiku 4.5: no programmatic tool calling / REPL persistence" if i == "claude-haiku-4-5-20251001" and v != "code_execution_20250825" else None)280        add("tool_search_tool_regex_20251119", "server"); add("tool_search_tool_bm25_20251119", "server")281        if i != "claude-mythos-preview":282            add("advisor_20260301", "server", "advisor-tool-2026-03-01")283    add("mcp_toolset", "mcp", "mcp-client-2025-11-20")284    add("memory_20250818", "client")285    add("bash_20250124", "client")286    add("text_editor_20250728", "client")287    if i in TOOLSET_MODELS:288        add("computer_toolset_20260801", "client"); add("browser_toolset_20260801", "client")289    if i in COMPUTER_20251124:290        add("computer_20251124", "client", "computer-use-2025-11-24")291    if i in COMPUTER_20250124:292        add("computer_20250124", "client", "computer-use-2025-01-24")293    return t294295296def pricing_block(p: tuple | None, i: str) -> dict | None:297    if not p:298        return None299    inp, out, w5, w1, rd = p300    d = {"currency": "USD", "unit": "per 1M tokens", "input": inp, "output": out, "cache_write_5m": w5, "cache_write_1h": w1, "cache_read": rd,301         "batch_input": round(inp / 2, 4), "batch_output": round(out / 2, 4), "batch_discount": 0.5,302         "long_context_over_200k": "standard pricing (no premium) for 1M-context models" if i in GEN46P else "n/a (200k context)",303         "inference_geo_us_multiplier": 1.1 if i in GEN46P else None,304         "priority_tier": "same token prices; capacity commitment, no longer sold" if i not in NO_PRIORITY_TIER else "not supported",305         "cache_read_multiplier": round(rd / inp, 4)}306    if i in FAST_MODE_MODELS:307        d["fast_mode"] = {"input": 10, "output": 50, "note": "speed: fast, research preview, Claude API only; caching/data-residency multipliers stack; not on Batch"}308    if i in TOOL_SYS_PROMPT_TOKENS:309        a, b = TOOL_SYS_PROMPT_TOKENS[i]310        d["tool_use_system_prompt_tokens"] = {"auto_or_none": a, "any_or_tool": b}311    return d312313314def build() -> tuple[list[dict], list[dict]]:315    live, summary = load_live()316    get_model = summary.get("get_model", {})317    msgs = summary.get("messages", {})318    verified_ts = summary.get("retrieved_at", LAST_VERIFIED)319    records: list[dict] = []320321    for m in MODELS:322        i = m["id"]323        lv = live.get(i)324        gm = get_model.get(i, {})325        status: list[str] = ["DOCUMENTED"]326        if lv:327            status.append("LIVE_VERIFIED")328        elif gm.get("status") == 404:329            status.append("ACCOUNT_RESTRICTED")330        if m["lifecycle"].startswith("Deprecated"):331            status.append("DEPRECATED")332        if "legacy" in m["lifecycle"]:333            status.append("LEGACY")334        if "invite" in m["lifecycle"] or i == "claude-mythos-preview":335            status.append("PREVIEW")336        ver = {"method": "live_api" if (lv or gm) else "docs_only", "verified_at": verified_ts,337               "result": "success" if lv else ("restricted" if gm.get("status") == 404 else "n/a"),338               "http_status": gm.get("status"), "request_note": f"GET /v1/models/{i}" + (f" -> {gm.get('status')}" if gm else " (not probed)")}339        if i in msgs:340            ver["messages_probe"] = {"http_status": msgs[i]["status"], "usage": msgs[i]["body"].get("usage"), "stop_reason": msgs[i]["body"].get("stop_reason")}341        discrepancies = []342        if lv:343            if lv["created_at"][:10] != m["released"]:344                discrepancies.append(f"live created_at {lv['created_at'][:10]} != documented release {m['released']}")345            if lv.get("max_input_tokens") != m["ctx"]:346                discrepancies.append(f"live max_input_tokens {lv.get('max_input_tokens')} != documented context window {m['ctx']}")347            if lv.get("max_tokens") != m["max_out"]:348                discrepancies.append(f"live max_tokens {lv.get('max_tokens')} != documented max output {m['max_out']}")349            if i == "claude-haiku-4-5-20251001" and not lv["capabilities"]["code_execution"]["supported"]:350                discrepancies.append("live capabilities.code_execution.supported=false while code-execution-tool docs list claude-haiku-4-5-20251001 as supported (without PTC/REPL persistence)")351        rec = {352            "provider": "anthropic", "id": i, "display_name": m["display"], "kind": "snapshot",353            "aliases": [m["alias"]] if m["alias"] else [], "snapshots": [i], "family": m["family"], "generation": m["gen"],354            "description": m["desc"], "status": status, "lifecycle_docs": m["lifecycle"],355            "release_date": m["released"], "created_at_live": lv["created_at"] if lv else None,356            "knowledge_cutoff": {"reliable": m["cutoff"][0], "training_data": m["cutoff"][1]},357            "context_window": m["ctx"], "context_window_note": "1M default, no beta header, standard pricing" if i in GEN46P else "200k",358            "max_output": m["max_out"], "max_output_batch_beta": 300_000 if i in BATCH_300K else None,359            "live_max_input_tokens": lv.get("max_input_tokens") if lv else None, "live_max_tokens": lv.get("max_tokens") if lv else None,360            "modalities": {"input": ["text", "image", "pdf"], "output": ["text"]},361            "thinking": m["thinking"],362            "capabilities": capabilities(m, lv), "live_capabilities": lv.get("capabilities") if lv else None,363            "tools": tools_for(m),364            "endpoints": ["POST /v1/messages", "POST /v1/messages/count_tokens", "POST /v1/messages/batches", "GET /v1/models/{model_id}"]365                         + (["Managed Agents (/v1/agents, /v1/sessions) — see managed-agents docs for per-model support"] if i in CURRENT else []),366            "pricing": pricing_block(m["price"], i),367            "rate_limits": {"class": RATE_LIMIT_CLASS.get(i), "ref": "generated/fragments/rate-limits/anthropic-rate-limits.json", "doc": D + "api/rate-limits"},368            "beta_headers": sorted({t["beta_header"] for t in tools_for(m) if t["beta_header"]} | ({"fast-mode-2026-02-01"} if i in FAST_MODE_MODELS else set())369                                   | ({"output-300k-2026-03-24"} if i in BATCH_300K else set()) | ({"task-budgets-2026-03-13"} if i in TASK_BUDGET_MODELS else set())370                                   | ({"compact-2026-01-12", "compact-2026-09-04"} if i in GEN46P else set()) | ({"mid-conversation-output-config-2026-07-01"} if i in PER_MESSAGE_EFFORT else set())371                                   | ({"mid-conversation-tool-changes-2026-07-01"} if i in MID_CONV_TOOL_CHANGES else set()) | ({"thinking-display-updates-2026-08-18"} if i in PROGRESS_UPDATES else set())372                                   | ({"interleaved-thinking-2025-05-14"} if i in GEN45 and i != "claude-haiku-4-5-20251001" else set())),373            "restrictions": {"data_retention": "30-day retention required; not available under ZDR unless authorized" if i in RETENTION_30D else None,374                             "access": "invite only (Project Glasswing)" if m["family"] == "Mythos" else None,375                             "tool_choice": "any/tool return 400" if i in {"claude-fable-5-1", "claude-mythos-5-1"} else None,376                             "thinking_disable": "not supported" if i in ALWAYS_ON_THINKING else ("only at effort high or below (400 at xhigh/max)" if i == "claude-opus-5" else None)},377            "availability": {"account": "available" if lv else ("not visible to our key (404)" if gm.get("status") == 404 else "unknown"),378                             "platforms": ["Claude API"] + (["Amazon Bedrock"] if m["bedrock"] else []) + (["Google Cloud (Vertex AI)"] if m["vertex"] else [])379                                          + (["Microsoft Foundry"] if m["foundry"] else []) + (["Claude Platform on AWS"] if m["cpaws"] else []),380                             "cloud_ids": {"bedrock": m["bedrock"], "vertex": m["vertex"], "foundry": m["foundry"], "claude_platform_on_aws": m["cpaws"]},381                             "regions": "Claude API is global by default; inference_geo us/global on 4.6+; Bedrock/Vertex regional endpoints +10%"},382            "deprecation": {"state": m["lifecycle"], "deprecated_on": "2026-06-09" if i == "claude-mythos-preview" else None, "retirement": m["retirement"],383                            "replacement": "claude-mythos-5" if i == "claude-mythos-preview" else None},384            "discrepancies_live_vs_docs": discrepancies,385            "last_verified": LAST_VERIFIED, "verification": ver,386            "sources": src("models/overview", f"models/{m['slug']}/overview" if m["slug"] else "about-claude/model-deprecations", "about-claude/pricing",387                           "about-claude/model-deprecations", "api/models/list", "build-with-claude/thinking", "build-with-claude/effort",388                           "agents-and-tools/tool-use/tool-reference", "build-with-claude/prompt-caching", "build-with-claude/context-windows"),389        }390        records.append(rec)391        # alias record392        if m["alias"]:393            ga = get_model.get(m["alias"], {})394            records.append({395                "provider": "anthropic", "id": m["alias"], "display_name": m["display"] + " (alias)", "kind": "alias", "resolves_to": i,396                "aliases": [], "snapshots": [i], "family": m["family"], "generation": m["gen"],397                "description": f"Convenience alias; GET /v1/models/{m['alias']} resolves to {i}. Aliases exist only for pre-4.6 models.",398                "status": ["DOCUMENTED"] + (["LIVE_VERIFIED"] if ga.get("status") == 200 else []),399                "release_date": m["released"], "context_window": m["ctx"], "max_output": m["max_out"], "pricing": "same as " + i,400                "endpoints": ["POST /v1/messages", "GET /v1/models/{model_id}"],401                "last_verified": LAST_VERIFIED,402                "verification": {"method": "live_api", "verified_at": verified_ts, "result": "success" if ga.get("status") == 200 else "n/a",403                                 "http_status": ga.get("status"), "request_note": f"GET /v1/models/{m['alias']} -> id {ga.get('resolved_id')}"},404                "sources": src("about-claude/models/model-ids-and-versions", f"models/{m['slug']}/overview"),405            })406407    for m in LEGACY:408        i = m["id"]409        gm = get_model.get(i, {})410        status = ["DOCUMENTED", "RETIRED"]411        if gm.get("status") == 404:412            status.append("LIVE_VERIFIED")  # retirement verified: 404 not_found_error413        records.append({414            "provider": "anthropic", "id": i, "display_name": m["display"], "kind": "snapshot", "aliases": [m["alias"]] if m.get("alias") else [], "snapshots": [i],415            "family": m["family"], "generation": m["gen"], "status": status, "lifecycle_docs": "Retired",416            "release_date": m["released"], "knowledge_cutoff": None, "context_window": m["ctx"], "max_output": m["max_out"],417            "modalities": {"input": ["text", "image"] if not i.startswith("claude-2") and not i.startswith("claude-1") and not i.startswith("claude-instant") else ["text"], "output": ["text"]},418            "capabilities": {"note": "retired; capability matrix not maintained in current docs", "computer_use_computer_20250124": i in COMPUTER_20250124,419                             "min_cacheable_tokens": CACHE_MIN.get(i)},420            "tools": [{"type": "computer_20250124", "category": "client", "beta_header": "computer-use-2025-01-24"}] if i in COMPUTER_20250124 else [],421            "endpoints": [], "pricing": pricing_block(m["price"], i),422            "pricing_note": None if m["price"] else "not listed on the current pricing page",423            "availability": {"account": "retired on Claude API (404 not_found_error)" if gm.get("status") == 404 else "retired (not probed)",424                             "cloud_ids": {"bedrock": m.get("bedrock"), "vertex": m.get("vertex")}, "cloud_note": m.get("cloud_note")},425            "deprecation": {"state": "Retired", "deprecated_on": m["deprecated"], "retired_on": m["retired"], "replacement": m["replacement"]},426            "last_verified": LAST_VERIFIED,427            "verification": {"method": "live_api" if gm else "docs_only", "verified_at": verified_ts, "result": "success" if gm.get("status") == 404 else "n/a",428                             "http_status": gm.get("status"), "request_note": f"GET /v1/models/{i} -> {gm.get('status')} {json.dumps(gm.get('body'))[:120]}" if gm else "not probed"},429            "sources": src("about-claude/model-deprecations", "about-claude/pricing", "release-notes/overview"),430        })431432    # ---------------- pricing records ----------------433    prices: list[dict] = []434435    def price(model_or_service, dimension, value, unit, tier="standard", notes=None, source="about-claude/pricing"):436        prices.append({"provider": "anthropic", "model_or_service": model_or_service, "dimension": dimension, "price": value, "currency": "USD",437                       "unit": unit, "tier": tier, "effective_notes": notes, "source": D + source, "retrieved_at": RETRIEVED})438439    for m in MODELS + LEGACY:440        if not m.get("price"):441            continue442        inp, out, w5, w1, rd = m["price"]443        note = None444        if m["id"] in {x["id"] for x in LEGACY}:445            note = "retired on the Claude API; price kept for Bedrock/Google Cloud availability"446        if m["id"] == "claude-sonnet-5":447            note = "introductory $2/$10 made permanent on 2026-08-10 (planned $3/$15 increase cancelled)"448        price(m["id"], "input", inp, "per 1M tokens", notes=note)449        price(m["id"], "output", out, "per 1M tokens", notes=note)450        price(m["id"], "cached_input_write_5m", w5, "per 1M tokens", notes="1.25x base input")451        price(m["id"], "cached_input_write_1h", w1, "per 1M tokens", notes="2x base input")452        price(m["id"], "cached_input_read", rd, "per 1M tokens", notes="0.025x base input" if m["id"] in {"claude-fable-5-1", "claude-mythos-5-1"} else "0.1x base input")453        price(m["id"], "input", round(inp / 2, 4), "per 1M tokens", tier="batch", notes="Message Batches API, 50% off")454        price(m["id"], "output", round(out / 2, 4), "per 1M tokens", tier="batch", notes="Message Batches API, 50% off")455        if m["id"] in FAST_MODE_MODELS:456            price(m["id"], "input", 10, "per 1M tokens", tier="fast", notes="speed: fast (research preview); applies across full context; caching + data residency multipliers stack; not available on Batch or clouds")457            price(m["id"], "output", 50, "per 1M tokens", tier="fast", notes="speed: fast (research preview)")458        if m["id"] in TOOL_SYS_PROMPT_TOKENS:459            a, b = TOOL_SYS_PROMPT_TOKENS[m["id"]]460            price(m["id"], "tool_use_system_prompt_overhead_tokens", a, "input tokens per request", notes="tool_choice auto/none, at least one tool provided")461            price(m["id"], "tool_use_system_prompt_overhead_tokens", b, "input tokens per request", notes="tool_choice any/tool")462463    price("claude-opus-4-7", "fast_mode", None, "per 1M tokens", tier="fast", notes="fast mode removed 2026-07-24: speed: fast returns an error")464    price("claude-opus-4-6", "fast_mode", None, "per 1M tokens", tier="fast", notes="fast mode removed 2026-06-29: speed: fast runs at standard speed and standard price; usage.speed reports standard")465    price("long_context_1M", "input_over_200k_multiplier", 1.0, "multiplier", notes="Claude 4.6+ and Mythos Preview: full 1M context at standard pricing (no long-context premium); caching and batch discounts apply across the window")466    price("inference_geo=us", "all_token_dimensions_multiplier", 1.1, "multiplier", notes="Claude 4.6+ only; global (default) = 1.0; also Foundry US Data Zone Standard; 400 on earlier models", source="manage-claude/data-residency")467    price("bedrock_vertex_regional_endpoints", "regional_or_multiregion_premium", 0.10, "fraction over global endpoint price", notes="Sonnet 4.5 / Haiku 4.5 / Opus 4.5 and later on partner clouds; provider invoices")468    price("claude_platform_on_aws", "claude_consumption_unit", 0.01, "per CCU", notes="100 CCU = $1 of standard list price after discounts; hourly metering to AWS Marketplace")469    price("microsoft_foundry", "claude_consumption_unit", 0.01, "per CCU", notes="Azure Marketplace CCU billing, same conversion")470    price("web_search", "search", 10.0, "per 1,000 searches", notes="plus token costs for search results; errors not billed; each search = 1 use regardless of result count", source="agents-and-tools/tool-use/web-search-tool")471    price("web_fetch", "fetch", 0.0, "per fetch", notes="no extra charge; fetched content billed as input tokens (~2,500 tokens per 10 kB page)", source="agents-and-tools/tool-use/web-fetch-tool")472    price("code_execution", "container_hour", 0.05, "per hour per container", notes="beyond 1,550 free hours/org/month; 5-minute minimum per execution; billed even if tool not called when files are preloaded", source="agents-and-tools/tool-use/code-execution-tool")473    price("code_execution", "free_hours_per_month", 1550, "hours per organization per month", notes="free tier")474    price("code_execution", "with_web_search_or_web_fetch", 0.0, "per request", notes="free when web_search_20260209+/web_fetch_20260209+ is in the request (only token costs)")475    price("managed_agents", "session_runtime", 0.08, "per session-hour", notes="metered to the ms while status=running; replaces container-hour billing; tokens at model rates; no batch discount", source="about-claude/pricing")476    price("bash_20250124", "tool_definition_overhead_tokens", 325, "input tokens per request", notes="Opus 5 / Opus 4.8 / Opus 4.7")477    price("bash_20250124", "tool_definition_overhead_tokens", 244, "input tokens per request", notes="Opus 4.6, Sonnet 4.6 and earlier")478    price("text_editor_20250429", "tool_definition_overhead_tokens", 700, "input tokens per request", notes="Claude 4.x text editor")479    price("computer_toolset_20260801", "toolset_definition_overhead_tokens", 4500, "input tokens per request", notes="~4,520 on Fable 5/Mythos 5/Opus 5/Opus 4.8, ~4,590 on Sonnet 5; disabling zoom saves ~410; screenshots billed as image input")480    price("computer_20251124|computer_20250124", "tool_definition_overhead_tokens", 735, "input tokens per request", notes="plus 466-499 system prompt tokens (measured with computer_20250124)")481    price("browser_toolset_20260801", "toolset_definition_overhead_tokens", 6600, "input tokens per request", notes="~6,610 Fable 5/Mythos 5/Opus 5/Opus 4.8, ~6,670 Sonnet 5; all four optional members +~880")482    price("priority_tier", "capacity_commitment", None, "contract", tier="priority", notes="no longer available for purchase; existing commitments run to contract end; token prices unchanged; burndown: cache read 0.1, 5m write 1.25, 1h write 2.0, inference_geo us 1.1", source="api/service-tiers")483    price("files_api", "storage", None, "n/a", notes="no storage price documented on the pricing page; Files API ~500 RPM; files can expire via expires_in_seconds", source="build-with-claude/files")484    price("token_counting", "request", 0.0, "per request", notes="free; separate RPM limits (Start 5,000 / Build 10,000 / Scale 20,000)", source="build-with-claude/token-counting")485    price("claude_code", "n/a", None, "n/a", notes="not priced on the platform pricing page (consumer/Team plans; API usage at model rates)")486    return records, prices487488489# --------------------------------------------------------------------------------------------490def yn(v) -> str:491    if v is True:492        return "yes"493    if v is False:494        return "no"495    if v is None or v == "unknown":496        return "?"497    return str(v)498499500def write_doc(records: list[dict], prices: list[dict]) -> None:501    cur = [r for r in records if r.get("kind") == "snapshot" and "RETIRED" not in r["status"]]502    aliases = [r for r in records if r.get("kind") == "alias"]503    legacy = [r for r in records if "RETIRED" in r["status"]]504    L: list[str] = []505    L.append("# Anthropic models catalogue\n")506    L.append("**Status:** `DOCUMENTED` for every id below; `LIVE_VERIFIED` for the 11 ids returned by `GET /v1/models` on 2026-09-18 (each also probed with `GET /v1/models/{id}`; 5 of them with a minimal `POST /v1/messages`), `ACCOUNT_RESTRICTED` for the invite-only Mythos ids (404 with our key), `RETIRED` (+ 404 verified) for retired ids.  ")507    L.append("**Sources:** models/overview, models/*/overview, about-claude/pricing, about-claude/model-deprecations, about-claude/models/model-ids-and-versions, api/models/list, build-with-claude/{thinking,effort,fast-mode,context-windows,prompt-caching,batch-processing,structured-outputs,vision}, agents-and-tools/tool-use/* (all under https://platform.claude.com/docs/en/), plus `sources/anthropic/models-api-raw.json` and `tmp-live/anthropic-models/summary.json`.  ")508    L.append(f"**Last verified:** {LAST_VERIFIED} · Machine-readable twin: `generated/fragments/models/anthropic-models.json` (built by `scripts/gen_anthropic_models_fragment.py`).\n")509    L.append("## 1. Current lineup (documented, active or deprecated)\n")510    L.append("| id | Display | Family/gen | Status | Released (docs) | created_at (live) | Cutoff reliable / training | Context | Max out | Thinking | Effort default / levels | Retirement |")511    L.append("|---|---|---|---|---|---|---|---|---|---|---|---|")512    for r in cur:513        c = r["capabilities"]514        L.append(f"| `{r['id']}` | {r['display_name']} | {r['family']} {r['generation']} | {', '.join(r['status'])} | {r['release_date']} | {(r.get('created_at_live') or '—')[:10]} | {r['knowledge_cutoff']['reliable'] or '?'} / {r['knowledge_cutoff']['training_data'] or '?'} | {r['context_window']:,} | {r['max_output']:,}{' (300k batch beta)' if r['max_output_batch_beta'] else ''} | {r['thinking']} | {c['effort_default'] or '—'} / {','.join(c['effort_levels']) or '—'} | {r['deprecation']['retirement']} |")515    L.append("\n**Aliases (pre-4.6 only):** " + "; ".join(f"`{a['id']}` → `{a['resolves_to']}` (GET /v1/models → {a['verification']['http_status']})" for a in aliases) + ". Dateless 4.6+ ids are pinned snapshots, not aliases.\n")516    L.append("## 2. Live vs docs discrepancies (2026-09-18)\n")517    for r in cur:518        for d in r["discrepancies_live_vs_docs"]:519            L.append(f"- `{r['id']}`: {d}")520    L.append("- `claude-mythos-5-1`, `claude-mythos-5`, `claude-mythos-preview`: documented (invite-only) but `GET /v1/models/{id}` → 404 `not_found_error` with our key and absent from `GET /v1/models` (does not mean non-existent).")521    L.append("- `GET /v1/models` returns undocumented-but-referenced fields `max_input_tokens`, `max_tokens`, `capabilities{batch,citations,code_execution,context_management{clear_tool_uses_20250919,clear_thinking_20251015,compact_20260112},effort{low..max},image_input,pdf_input,structured_outputs,thinking{types{enabled,adaptive}}}` (added 2026-03-18). The reference example shows `max_input_tokens: 0`; live values are real numbers.")522    L.append("- Messages `usage` (live) contains `cache_creation{ephemeral_5m_input_tokens,ephemeral_1h_input_tokens}`, `output_tokens_details.thinking_tokens` (absent on Sonnet 4.6), `service_tier: \"standard\"`, `inference_geo: \"global\"`; top level `container: null`, `stop_details: null`. No `speed` field unless fast mode is requested.\n")523    L.append("## 3. Model × Capability matrix\n")524    caps = ["vision", "high_resolution_vision_2576px", "pdf_input", "thinking_extended_manual_budget", "thinking_adaptive", "thinking_always_on", "interleaved_thinking", "effort_parameter",525            "fast_mode_speed_fast", "structured_outputs_json", "fine_grained_tool_streaming", "prompt_caching_1h_ttl", "min_cacheable_tokens", "batch_api", "batch_extended_output_300k_beta",526            "citations", "code_execution", "programmatic_tool_calling", "computer_use", "browser_use", "tool_search", "advisor_tool", "compaction_server_side", "context_1m_default_no_beta",527            "inference_geo_data_residency", "mid_conversation_system_messages", "task_budgets_beta", "tool_choice_any_or_tool", "priority_tier", "zero_data_retention_eligible", "latency_class"]528    L.append("| Capability | " + " | ".join(r["display_name"].replace("Claude ", "") for r in cur) + " |")529    L.append("|---|" + "---|" * len(cur))530    for k in caps:531        L.append(f"| {k} | " + " | ".join(yn(r["capabilities"].get(k)) for r in cur) + " |")532    L.append("\n`code_execution` row = live `capabilities.code_execution.supported`; docs list Haiku 4.5 as supported (without programmatic tool calling / REPL persistence).  \n`sampling params`: temperature/top_p/top_k non-default → 400 on Claude 4.7+ and Mythos Preview; incompatible with thinking on older models.\n")533    L.append("## 4. Model × Tool-version matrix (exact `type` strings)\n")534    all_tools = []535    for r in cur:536        for t in r["tools"]:537            if t["type"] not in all_tools:538                all_tools.append(t["type"])539    L.append("| tool `type` | beta header | " + " | ".join(r["display_name"].replace("Claude ", "") for r in cur) + " |")540    L.append("|---|---|" + "---|" * len(cur))541    for tt in all_tools:542        beta = next((t["beta_header"] for r in cur for t in r["tools"] if t["type"] == tt), None)543        L.append(f"| `{tt}` | {beta or '—'} | " + " | ".join("yes" if any(t["type"] == tt for t in r["tools"]) else "no" for r in cur) + " |")544    L.append("\nLegacy tool versions: `computer_20250124` (beta `computer-use-2025-01-24`) for Sonnet 4.5, Haiku 4.5, Opus 4.1, Sonnet 4, Opus 4; `computer_20241022`/`bash_20241022`/`text_editor_20241022` (beta `computer-use-2024-10-22`) only for Sonnet 3.5 (Oct 2024, retired); `text_editor_20250124` for Sonnet 3.7 (retired); `text_editor_20250429` Claude 4.x (superseded by `text_editor_20250728`); `code_execution_20250522` Python-only (legacy); `mcp-client-2025-04-04` deprecated. Undated aliases `tool_search_tool_regex` / `tool_search_tool_bm25` resolve to the latest dated version. Advisor tool: executor→advisor pairs are constrained (advisor must be at least as capable; see advisor-tool page).\n")545    L.append("## 5. Model × Endpoint matrix\n")546    L.append("| Endpoint | Applies to |")547    L.append("|---|---|")548    L.append("| `POST /v1/messages` | all active models (all 11 live ids verified 200 with `GET /v1/models/{id}`; 5 verified with a real message) |")549    L.append("| `POST /v1/messages/count_tokens` | all active models (verified on opus-5, sonnet-5, fable-5-1, opus-4-8, sonnet-4-6) |")550    L.append("| `POST /v1/messages/batches` | all active models; `output-300k-2026-03-24` on Opus 5/4.8/4.7/4.6, Sonnet 5/4.6 |")551    L.append("| `GET /v1/models`, `GET /v1/models/{id}` | lists 11 ids; aliases resolve; retired → 404 `not_found_error`; invite-only Mythos → 404 |")552    L.append("| `/v1/files` (GA 2026-08-19) | model-agnostic; files referenced from Messages / code execution |")553    L.append("| `/v1/skills` (GA 2026-08-19) | any model supporting code execution |")554    L.append("| Managed Agents `/v1/agents`, `/v1/sessions`, `/v1/environments` (beta `managed-agents-2026-04-01`) | model set documented per feature (Dreams: Fable 5, Sonnet 5, Opus 5) |")555    L.append("| `POST /v1/complete` (Text Completions) | legacy; removed from Python SDK v1.0 (2026-08-20) |\n")556    L.append("## 6. Model × Cloud matrix\n")557    L.append("| id | Amazon Bedrock | Google Cloud (Vertex) | Microsoft Foundry | Claude Platform on AWS |")558    L.append("|---|---|---|---|---|")559    for r in cur:560        ci = r["availability"]["cloud_ids"]561        L.append(f"| `{r['id']}` | {', '.join('`%s`' % b for b in ci['bedrock']) if ci['bedrock'] else '—'} | {'`%s`' % ci['vertex'] if ci['vertex'] else '—'} | {'`%s`' % ci['foundry'] if ci['foundry'] else '—'} | {'`%s`' % ci['claude_platform_on_aws'] if ci['claude_platform_on_aws'] else '—'} |")562    L.append("\nBedrock: Opus 4.7+ and Haiku 4.5 use the Messages-API endpoint ids (`anthropic.claude-…`, no `-v1:0`); Opus 4.6 is the last `-v1` id (`anthropic.claude-opus-4-6-v1`); dated models use `anthropic.claude-{name}-{YYYYMMDD}-v1:0` via InvokeModel. Vertex: dated models use `@YYYYMMDD`. Foundry and Claude Platform on AWS use the Claude API id (alias form where one exists). Partner clouds set their own retirement dates and +10% regional-endpoint pricing.\n")563    L.append("## 7. Retired models (deprecations page + release notes)\n")564    L.append("| id | Display | Deprecated | Retired | Replacement | Our probe | Cloud note |")565    L.append("|---|---|---|---|---|---|---|")566    for r in legacy:567        L.append(f"| `{r['id']}` | {r['display_name']} | {r['deprecation']['deprecated_on']} | {r['deprecation']['retired_on']} | `{r['deprecation']['replacement']}` | {r['verification']['http_status'] or '—'} | {r['availability'].get('cloud_note') or ''} |")568    L.append("\n## 8. Pricing snapshot (USD per MTok; full detail in docs/anthropic/pricing.md)\n")569    L.append("| id | input | output | 5m write | 1h write | cache read | batch in/out | fast in/out |")570    L.append("|---|---|---|---|---|---|---|---|")571    for r in cur:572        p = r["pricing"]573        if not p:574            L.append(f"| `{r['id']}` | not listed | | | | | | |")575            continue576        fm = p.get("fast_mode")577        L.append(f"| `{r['id']}` | {p['input']} | {p['output']} | {p['cache_write_5m']} | {p['cache_write_1h']} | {p['cache_read']} | {p['batch_input']} / {p['batch_output']} | {(str(fm['input']) + ' / ' + str(fm['output'])) if fm else '—'} |")578    L.append("\n## 9. Notes\n")579    L.append("- Model IDs: 4.6+ ids are dateless pinned snapshots (`claude-{name}-{major}[-{minor}]`); pre-4.6 ids carry `-YYYYMMDD` and have convenience aliases. Weights never change under an id; serving infrastructure may.")580    L.append("- Tokenizer: Claude 4.7+ and Mythos Preview use a new tokenizer (~30% more tokens for the same text); 1M tokens ≈ 555k words on it vs ≈ 750k words before.")581    L.append("- Thinking: adaptive is the only mode on Fable/Mythos 5.x (cannot be disabled; no budgets; no prefill); Opus 5 / Sonnet 5 have thinking on by default and reject `type: enabled`; Opus 4.6 / Sonnet 4.6 accept `enabled` but it is deprecated; 4.5 models are manual-budget only.")582    L.append("- Fast mode: only Opus 5 and Opus 4.8 (`speed: \"fast\"` + `fast-mode-2026-02-01`); Opus 4.7 → error since 2026-07-24; Opus 4.6 → silently standard since 2026-06-29.")583    L.append("- Rate-limit buckets: Fable 5.x shared; Mythos 5.x shared; Opus 5 own; Opus 4.8/4.7/4.6/4.5 shared; Sonnet 5 own; Sonnet 4.6/4.5 shared; Haiku 4.5 own.")584    L.append("- Data retention: Fable 5/5.1 and Mythos 5/5.1 require 30-day retention (no ZDR unless authorized).")585    L.append("- Watermarking: text from Fable 5.1 / Mythos 5.1 carries Anthropic's text watermark; code-execution media files carry C2PA credentials via the Files API.")586    OUT_DOC.parent.mkdir(parents=True, exist_ok=True)587    OUT_DOC.write_text("\n".join(L) + "\n")588589590def main() -> None:591    records, prices = build()592    OUT_MODELS.parent.mkdir(parents=True, exist_ok=True)593    OUT_PRICING.parent.mkdir(parents=True, exist_ok=True)594    OUT_MODELS.write_text(json.dumps({"record_type": "model", "provider": "anthropic", "generated_at": LAST_VERIFIED, "generator": "scripts/gen_anthropic_models_fragment.py",595                                      "count": len(records), "records": records}, indent=2, ensure_ascii=False) + "\n")596    OUT_PRICING.write_text(json.dumps({"record_type": "price", "provider": "anthropic", "generated_at": LAST_VERIFIED, "generator": "scripts/gen_anthropic_models_fragment.py",597                                       "count": len(prices), "records": prices}, indent=2, ensure_ascii=False) + "\n")598    write_doc(records, prices)599    print(f"models={len(records)} prices={len(prices)} -> {OUT_MODELS.relative_to(ROOT)}, {OUT_PRICING.relative_to(ROOT)}, {OUT_DOC.relative_to(ROOT)}")600601602if __name__ == "__main__":603    main()604