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%
51.4 KB · 467 lines python
Raw Blame History
1#!/usr/bin/env python32"""Build the xAI model catalogue, pricing and rate-limit fragments.34Inputs : sources/xai/models-api-raw.json, language-models-raw.json, image-generation-models-raw.json5         (sanitized live GET results, see scripts/discover_xai_models.py), tmp-live/xai-models/*.json (optional,6         observed headers / usage shapes) and documented facts hard-coded below (docs.x.ai retrieved 2026-09-19).7Outputs: generated/fragments/models/xai-models.json8         generated/fragments/pricing/xai-pricing.json9         generated/fragments/rate-limits/xai-rate-limits.json1011Price units: the live catalogue returns prices in "USD ticks" = 1e-10 USD per token (documented as "USD cents per12100 million tokens" which is the same quantity: 12500 -> $1.25 / 1M tokens). image_price / price_per_image are in the13same ticks per image (200000000 -> $0.02). cost_in_usd_ticks in usage objects uses the same 1e10 ticks per USD.14"""15from __future__ import annotations1617import datetime as dt18import json19from pathlib import Path2021ROOT = Path(__file__).resolve().parents[1]22SRC = ROOT / "sources" / "xai"23OUT_MODELS = ROOT / "generated" / "fragments" / "models" / "xai-models.json"24OUT_PRICES = ROOT / "generated" / "fragments" / "pricing" / "xai-pricing.json"25OUT_RATES = ROOT / "generated" / "fragments" / "rate-limits" / "xai-rate-limits.json"2627TODAY = "2026-09-18"28RETRIEVED = "2026-09-19"29DOCS = "https://docs.x.ai/developers"30TICKS_PER_USD = 10_000_000_000313233def src(path: str, note: str | None = None) -> dict:34    d = {"url": f"{DOCS}/{path}" if not path.startswith("http") else path, "retrieved_at": RETRIEVED}35    if note:36        d["note"] = note37    return d383940def per_million(ticks: int | None) -> float | None:41    return None if ticks is None else round(ticks / TICKS_PER_USD * 1_000_000, 4)424344def usd(ticks: int | None) -> float | None:45    return None if ticks is None else round(ticks / TICKS_PER_USD, 6)464748def ts(unix: int | None) -> str | None:49    return None if unix is None else dt.datetime.fromtimestamp(unix, dt.UTC).strftime("%Y-%m-%d")505152live_models = {m["id"]: m for m in json.loads((SRC / "models-api-raw.json").read_text())["data"]}53live_lang = {m["id"]: m for m in json.loads((SRC / "language-models-raw.json").read_text())["models"]}54live_img = {m["id"]: m for m in json.loads((SRC / "image-generation-models-raw.json").read_text())["models"]}55live_video_ids = [i for i, m in live_models.items() if "video" in i]5657# ----------------------------------------------------------------------------------------------------------------58# Documented facts (model detail pages https://docs.x.ai/developers/models/<id>, models, pricing, rate-limits,59# release-notes, reasoning, multi-agent, migration guides). Observed values come from tmp-live/xai-models/*.json.60# ----------------------------------------------------------------------------------------------------------------61TEXT_TIER0 = {"grok-4.6": (150, 50_000_000), "grok-4.5": (150, 50_000_000), "grok-4.3": (37, 10_000_000),62              "grok-4.20-0309-reasoning": (37, 10_000_000), "grok-4.20-0309-non-reasoning": (37, 10_000_000),63              "grok-build-0.1": (37, 10_000_000), "grok-4.20-multi-agent-0309": (9, 2_500_000)}64TIER_RPS = {  # documented RPS T0..T465    "grok-4.6": [150, 172, 208, 312, 500], "grok-4.5": [150, 172, 208, 312, 500],66    "grok-4.3": [37, 50, 75, 125, 208], "grok-4.20-0309-reasoning": [37, 50, 75, 125, 208],67    "grok-4.20-0309-non-reasoning": [37, 50, 75, 125, 208], "grok-build-0.1": [37, 50, 75, 125, 208],68    "grok-4.20-multi-agent-0309": [9, 12, 18, 31, 56],69    "grok-imagine-image": [6, 12, 25, 50, 100], "grok-imagine-image-quality": [6, 12, 25, 50, 100],70    "grok-imagine-image-2.0": [6, 12, 25, 50, 100],71    "grok-imagine-video": [10, 20, 39, 79, 158], "grok-imagine-video-1.5": [10, 20, 39, 79, 158],72}73TIER_TPM = {74    "grok-4.6": ["50M", "53M", "60M", "74M", "100M"], "grok-4.5": ["50M", "53M", "60M", "74M", "100M"],75    "grok-4.3": ["10M", "15M", "25M", "45M", "85M"], "grok-4.20-0309-reasoning": ["10M", "15M", "25M", "45M", "85M"],76    "grok-4.20-0309-non-reasoning": ["10M", "15M", "25M", "45M", "85M"], "grok-build-0.1": ["10M", "15M", "25M", "45M", "85M"],77    "grok-4.20-multi-agent-0309": ["2.5M", "3.7M", "6.2M", "11M", "21M"],78}79OBSERVED_HEADERS = {  # 2026-09-19, our key (Tier 0 spend), POST /v1/chat/completions unless noted80    "grok-4.6": {"x-ratelimit-limit-requests": "7200", "x-ratelimit-limit-tokens": "50000000"},81    "grok-4.5": {"x-ratelimit-limit-requests": "7200", "x-ratelimit-limit-tokens": "50000000"},82    "grok-4.3": {"x-ratelimit-limit-requests": "1800", "x-ratelimit-limit-tokens": "10000000"},83    "grok-4.20-0309-non-reasoning": {"x-ratelimit-limit-requests": "1800", "x-ratelimit-limit-tokens": "10000000"},84    "grok-build-0.1": {"x-ratelimit-limit-requests": "1800", "x-ratelimit-limit-tokens": "10000000"},85}86OBSERVED_USAGE = {  # minimal "Reply with OK." chat completions, max_tokens 8 (2026-09-19)87    "grok-4.6": {"prompt_tokens": 640, "completion_tokens": 1, "reasoning_tokens": 91, "cached_tokens": 512, "total_tokens": 732, "cost_in_usd_ticks": 10640000, "reasoning_content_returned": True},88    "grok-4.5": {"prompt_tokens": 498, "completion_tokens": 1, "reasoning_tokens": 55, "cached_tokens": 384, "total_tokens": 554, "cost_in_usd_ticks": 6792000, "reasoning_content_returned": True},89    "grok-4.20-0309-non-reasoning": {"prompt_tokens": 188, "completion_tokens": 2, "reasoning_tokens": 0, "cached_tokens": 128, "total_tokens": 190, "cost_in_usd_ticks": 1056000, "reasoning_content_returned": False},90    "grok-4.3": {"prompt_tokens": 188, "completion_tokens": 2, "reasoning_tokens": 0, "cached_tokens": 128, "total_tokens": 190, "cost_in_usd_ticks": 1056000, "reasoning_content_returned": False, "request_note": "reasoning_effort=none accepted"},91    "grok-build-0.1": {"prompt_tokens": 190, "completion_tokens": 1, "reasoning_tokens": 162, "cached_tokens": 128, "total_tokens": 353, "cost_in_usd_ticks": 4136000, "reasoning_content_returned": True},92    "grok-4.20-multi-agent-0309": {"endpoint": "POST /v1/responses (chat/completions -> 400 'Multi Agent requests are not allowed on chat completions')", "input_tokens": 2655, "cached_tokens": 2560, "output_tokens": 1645, "reasoning_tokens": 1618, "total_tokens": 4300, "num_server_side_tools_used": 0, "cost_in_usd_ticks": 47432500, "reasoning_effort_reported": "medium", "context_details": {"input_tokens": 647, "output_tokens": 388}, "output_text": "OK\\n\\n\\n\\\\confidence{80}"},93}9495TEXT_DOCS = {96    "grok-4.6": dict(display="Grok 4.6", family="Grok 4.x", generation="4.6", release="2026-08 (release notes, August 2026)",97                     cutoff="2026-02-01", batch=False, batch_discount=None, reasoning=True, efforts=["low", "medium", "high", "xhigh"],98                     effort_default="high", effort_param=True, regions=["us-east-1", "us-west-2", "us-central-1"],99                     us_regional=True, statuses=["DOCUMENTED", "LIVE_VERIFIED"],100                     desc="SpaceXAI's frontier model for coding, agentic tasks, and knowledge work. No text output limit. Recommended default for code and chat.",101                     notes=["xhigh reasoning effort is available on grok-4.6 and later", "reasoning summaries exposed (reasoning_content deltas when streaming)",102                            "logprobs/top_logprobs silently ignored (grok-4.20 and newer)", "only model on https://us.api.x.ai/v1 (1.1x token prices)"]),103    "grok-4.5": dict(display="Grok 4.5", family="Grok 4.x", generation="4.5", release="2026-07 (release notes, July 2026; EU console availability announced same month)",104                     cutoff=None, batch=False, batch_discount=None, reasoning=True, efforts=["low", "medium", "high", "xhigh"],105                     effort_default="high", effort_param=True, regions=["us-east-1", "us-west-2"], us_regional=False,106                     statuses=["DOCUMENTED", "LIVE_VERIFIED"],107                     desc="Intelligent coding model for agentic software, engineering and workflow tasks (trained in Memphis with new science/engineering/math datasets).",108                     notes=["reasoning page: supports low/medium/high only and 'xhigh' is treated as 'high'; the model page lists xhigh as supported - discrepancy recorded",109                            "aliases grok-4.5-latest and grok-build-latest (live)"]),110    "grok-4.3": dict(display="Grok 4.3", family="Grok 4.x", generation="4.3", release="2026-04 (created 2026-04-17 live; migration guide names it the May-15 redirect target)",111                     cutoff=None, batch=True, batch_discount=0.20, reasoning=True, efforts=["none", "low", "medium", "high", "xhigh"],112                     effort_default="low", effort_param=True, regions=["us-east-1", "eu-west-1", "us-west-2"], us_regional=False,113                     statuses=["DOCUMENTED", "LIVE_VERIFIED"],114                     desc="Fast, reliable model with strong tool calling and instruction following; 1M context; 4-5 reasoning effort levels incl. none. Redirect target of the models retired on 2026-05-15.",115                     notes=["reasoning_effort='none' accepted live (0 reasoning tokens)", "only model returned by GET https://eu-west-1.api.x.ai/v1/models (LIVE_DISCOVERED, undocumented base URL)",116                            "cheapest current text model; used for this atlas's probes"]),117    "grok-4.20-0309-reasoning": dict(display="Grok 4.20 (reasoning)", family="Grok 4.20", generation="4.20", release="2026-03-09 (created; release notes March 2026)",118                     cutoff=None, batch=True, batch_discount=0.20, reasoning=True, efforts=None, effort_default=None, effort_param=False,119                     regions=["us-east-1", "us-west-2"], us_regional=False, statuses=["DOCUMENTED", "LIVE_VERIFIED"],120                     desc="High-performance model with industry-leading speed and agentic tool calling; lowest hallucination rate claim; strict prompt adherence.",121                     notes=["reasoning_effort rejected live: 400 invalid-argument 'Model grok-4.20-0309-reasoning does not support parameter reasoningEffort'",122                            "15 aliases incl. grok-4.20, grok-4.20-beta, grok-4.20-reasoning-gv2"]),123    "grok-4.20-0309-non-reasoning": dict(display="Grok 4.20 (non-reasoning)", family="Grok 4.20", generation="4.20", release="2026-03-09 (created; release notes March 2026)",124                     cutoff=None, batch=True, batch_discount=0.20, reasoning=False, efforts=None, effort_default=None, effort_param=False,125                     regions=["us-east-1", "us-west-2"], us_regional=False, statuses=["DOCUMENTED", "LIVE_VERIFIED"],126                     desc="Non-reasoning variant of Grok 4.20 (no reasoning tokens).",127                     notes=["reasoning_effort rejected live (400 invalid-argument)", "usage.completion_tokens_details.reasoning_tokens = 0 live"]),128    "grok-4.20-multi-agent-0309": dict(display="Grok 4.20 Multi-Agent (beta)", family="Grok 4.20", generation="4.20", release="2026-03 (release notes March 2026)",129                     cutoff=None, batch=True, batch_discount=0.20, reasoning=True, efforts=["low", "medium", "high", "xhigh"], effort_default="medium (observed in response.reasoning.effort)",130                     effort_param="reasoning.effort controls agent count (low/medium = 4 agents, high/xhigh = 16), not reasoning depth",131                     regions=["us-east-1", "us-west-2"], us_regional=False, statuses=["DOCUMENTED", "BETA", "LIVE_VERIFIED"],132                     desc="Multiple agents collaborate in parallel to perform deep research tasks. Responses API only.",133                     notes=["POST /v1/chat/completions -> 400 'Multi Agent requests are not allowed on chat completions' (live)",134                            "output text ended with a '\\\\confidence{NN}' marker in both live probes", "usage.context_details{input_tokens,output_tokens} present (undocumented)"]),135    "grok-build-0.1": dict(display="Grok Build 0.1", family="Build", generation="0.1", release="2026-05 (release notes May 2026, 'early access'); created 2026-04-16 live",136                     cutoff=None, batch=False, batch_discount=None, reasoning=True, efforts=None, effort_default=None, effort_param=False,137                     regions=["us-east-1", "us-west-2"], us_regional=False, statuses=["DOCUMENTED", "PREVIEW", "LIVE_VERIFIED"],138                     desc="Coding model trained for agentic coding workflows; default model of the Grok Build CLI. Redirect target of grok-code-fast-1 (now an alias).",139                     notes=["reasoning_effort rejected live (400 invalid-argument)", "aliases grok-code-fast-1, grok-code-fast, grok-code-fast-1-0825 resolve to it (GET /v1/models/grok-code-fast-1 returns id grok-build-0.1)"]),140}141142SERVER_TOOLS = ["web_search", "x_search", "code_execution", "code_interpreter", "image_generation", "attachment_search",143                "collections_search", "file_search", "view_image", "view_x_video", "mcp"]144TEXT_ENDPOINTS = ["POST /v1/chat/completions", "POST /v1/responses", "POST /v1/responses/compact", "WS wss://api.x.ai/v1/responses",145                  "POST /v1/messages (Anthropic-compatible)", "GET /v1/chat/deferred-completion/{request_id}", "POST /v1/tokenize-text",146                  "Batch API (/v1/batches, gRPC BatchMgmt)"]147148149def text_model(mid: str) -> dict:150    lm, m, d = live_lang[mid], live_models[mid], TEXT_DOCS[mid]151    thr = lm["long_context_threshold"]152    pricing = {153        "unit": "USD per 1M tokens",154        "standard": {"input": per_million(lm["prompt_text_token_price"]), "cached_input": per_million(lm["cached_prompt_text_token_price"]),155                     "image_input": per_million(lm["prompt_image_token_price"]), "output": per_million(lm["completion_text_token_price"]),156                     "applies": f"prompt < {thr:,} tokens"},157        "long_context": {"input": per_million(lm["prompt_text_token_price_long_context"]), "cached_input": per_million(lm["cached_prompt_text_token_price_long_context"]),158                         "output": per_million(lm["completion_text_token_price_long_context"]),159                         "applies": f"prompt >= {thr:,} tokens: the higher rate is billed for ALL tokens of the request"},160        "batch_discount": d["batch_discount"], "priority_multiplier": 2.0 if mid != "grok-4.20-multi-agent-0309" else "2.0 (Chat Completions/Responses; not documented per model)",161        "us_regional_multiplier": 1.1 if d["us_regional"] else None,162        "search_price_live": lm.get("search_price"),163        "live_raw_ticks": {k: lm[k] for k in lm if k.endswith("_price") or k.endswith("_long_context") or k == "long_context_threshold"},164    }165    caps = {166        "text_input": True, "image_input": "image" in lm["input_modalities"], "text_output": True,167        "reasoning": d["reasoning"], "reasoning_always_on": d["reasoning"] and mid not in ("grok-4.3",),168        "reasoning_can_be_disabled": mid == "grok-4.3", "reasoning_effort_parameter": d["effort_param"],169        "reasoning_effort_levels": d["efforts"], "reasoning_effort_default": d["effort_default"],170        "encrypted_reasoning_content": "some models (Responses include: reasoning.encrypted_content)",171        "function_calling": True, "parallel_tool_calls": True, "structured_outputs": True, "streaming": True,172        "logprobs": False if mid.startswith("grok-4.") and mid != "grok-4.0" else "unknown",173        "prompt_caching_automatic": True, "prompt_cache_key_or_x_grok_conv_id": True,174        "batch_api": d["batch"], "deferred_completions": mid != "grok-4.20-multi-agent-0309", "priority_processing": mid != "grok-4.20-multi-agent-0309",175        "context_compaction": True, "websocket_responses": True, "files_attachments": True, "collections_search": True,176        "server_side_tools": True if mid == "grok-4.6" else "unknown (docs only list tools on the grok-4.6 page; tools overview examples use grok-4.6)",177        "web_search": True if mid == "grok-4.6" else "unknown", "x_search": True if mid == "grok-4.6" else "unknown", "code_execution": True if mid == "grok-4.6" else "unknown",178        "remote_mcp": "unknown", "image_generation_tool": "unknown", "fine_tuning": False, "embeddings": False, "audio_input": False,179        "voice_realtime": False, "video_output": False, "image_output": False, "anthropic_messages_compat": True,180    }181    rl = {182        "documented": {"tier_rps_T0_T4": TIER_RPS[mid], "tier_tpm_T0_T4": TIER_TPM[mid], "unit": "RPS = requests per second (derived from RPM/60); TPM counts prompt+completion+reasoning+cached tokens"},183        "observed_headers_2026_09_19": OBSERVED_HEADERS.get(mid),184    }185    endpoints = TEXT_ENDPOINTS if mid != "grok-4.20-multi-agent-0309" else ["POST /v1/responses", "Batch API"]186    return {187        "provider": "xai", "id": mid, "display_name": d["display"], "kind": "model", "aliases": lm["aliases"], "snapshots": [mid],188        "family": d["family"], "generation": d["generation"], "description": d["desc"], "status": d["statuses"],189        "release_date": d["release"], "created_at_live": ts(m["created"]), "fingerprint_live": lm.get("fingerprint"), "version_live": lm.get("version"),190        "knowledge_cutoff": d["cutoff"], "context_window": m["context_length"], "context_window_note": f"long-context pricing above {thr:,} prompt tokens",191        "max_output": None, "max_output_note": "no documented text output limit (grok-4.6 page: 'No text output limit'); other models: not documented",192        "modalities": {"input": lm["input_modalities"], "output": lm["output_modalities"]},193        "capabilities": caps, "endpoints": endpoints, "tools": ["function"] + SERVER_TOOLS, "pricing": pricing, "rate_limits": rl,194        "beta_headers": [], "restrictions": d["notes"],195        "availability": {"account": "available to our team (Tier 0)", "regions": d["regions"], "base_urls": ["https://api.x.ai/v1"] + (["https://us.api.x.ai/v1"] if d["us_regional"] else []) + (["https://eu-west-1.api.x.ai/v1 (LIVE_DISCOVERED)"] if mid == "grok-4.3" else []),196                         "gateways": ["Vercel AI Gateway", "OpenRouter", "Cloudflare", "Google Cloud Vertex AI (partner model)", "Microsoft Foundry"] if mid == "grok-4.6" else "unknown"},197        "live_usage_shape": OBSERVED_USAGE.get(mid), "last_verified": TODAY,198        "verification": {"method": "live_api", "verified_at": TODAY, "result": "success", "http_status": 200,199                         "request_note": "GET /v1/models + GET /v1/language-models (2026-09-19) and one minimal POST (see live_usage_shape)"},200        "sources": [src("models"), src(f"models/{mid}"), src("pricing"), src("rate-limits"), src("release-notes"), src("model-capabilities/text/reasoning"),201                    src("rest-api-reference/inference/models"), src("https://api.x.ai/v1/language-models", "live 2026-09-19")],202    }203204205IMG_DOCS = {206    "grok-imagine-image": dict(display="Grok Imagine Image (1.0)", statuses=["DOCUMENTED", "LIVE_VERIFIED"], batch="supported in us-east-1, us-west-2", regions=["us-east-1", "us-west-2", "us-saltlake-2"],207                               notes=["not affected by the Nov 2 retirement of grok-imagine-image-quality"], release="2026-01 (release notes January 2026: 'next-gen image generation'); created 2026-01-28 live"),208    "grok-imagine-image-2.0": dict(display="Grok Imagine Image 2.0", statuses=["DOCUMENTED", "LIVE_VERIFIED"], batch="supported", regions=["us-east-1", "us-west-2"],209                                   notes=["quality: low | medium | auto (default since Aug 2026; auto = low for generation, medium for editing; billed at the quality served)", "resolution: 1k (default) | 1.5k | 2k", "up to 5 source images for editing; aspect ratios incl. 21:9 and 5:2", "recommended image model"],210                                   release="2026-08 (release notes August 2026); created 2026-08-08 live"),211    "grok-imagine-image-quality": dict(display="Grok Imagine Image Quality", statuses=["DOCUMENTED", "DEPRECATED", "LIVE_VERIFIED"], batch="not supported", regions=["us-east-1", "us-west-2"],212                                       notes=["RETIRES 2026-11-02: requests then served by grok-imagine-image-2.0 with quality=low ($0.01/image cheaper); slug keeps resolving", "grok-imagine-image-pro already redirects here (since 2026-05-15)"],213                                       release="2026-04-03 (alias grok-imagine-image-quality-20260403); created 2026-04-03 live"),214}215216217def image_model(mid: str) -> dict:218    im, m, d = live_img[mid], live_models[mid], IMG_DOCS[mid]219    pricing = {"unit": "USD per image", "per_image_default": usd(im["image_price"]),220               "matrix": [{"quality": p["quality"], "resolution": p["resolution"], "price": usd(p["price_per_image"])} for p in im.get("pricing", [])] or None,221               "batch_discount": 0.0, "note": "Batch API accepted (where supported) but billed at standard rates; also billable via the image_generation server-side tool"}222    return {223        "provider": "xai", "id": mid, "display_name": d["display"], "kind": "model", "aliases": im["aliases"], "snapshots": [mid], "family": "Imagine", "generation": "image",224        "description": "Text/image -> image generation and editing model (Imagine API).", "status": d["statuses"], "release_date": d["release"], "created_at_live": ts(m["created"]),225        "fingerprint_live": im.get("fingerprint"), "knowledge_cutoff": None, "context_window": m.get("context_length"), "context_window_note": f"max_prompt_length {im['max_prompt_length']} (live)", "max_output": None,226        "modalities": {"input": im["input_modalities"], "output": im["output_modalities"]},227        "capabilities": {"image_generation": True, "image_editing": True, "multi_image_editing": True, "batch_api": d["batch"], "quality_parameter": mid == "grok-imagine-image-2.0",228                         "resolution_parameter": mid == "grok-imagine-image-2.0", "files_api_inputs_outputs": True, "streaming": False, "function_calling": False, "reasoning": False},229        "endpoints": ["POST /v1/images/generations", "POST /v1/images/edits", "Batch API", "gRPC xai_api.Image/GenerateImage"], "tools": [],230        "pricing": pricing, "rate_limits": {"documented": {"tier_rps_T0_T4": TIER_RPS[mid], "tpm": None, "note": "Imagine limits are not spend-tiered like text; contact sales@x.ai for increases"}},231        "beta_headers": [], "restrictions": d["notes"], "availability": {"account": "available", "regions": d["regions"], "base_urls": ["https://api.x.ai/v1 (not served on us.api.x.ai)"]},232        "deprecation": {"retirement": "2026-11-02", "replacement": "grok-imagine-image-2.0 (quality=low)"} if mid == "grok-imagine-image-quality" else None,233        "last_verified": TODAY, "verification": {"method": "live_api", "verified_at": TODAY, "result": "success", "http_status": 200, "request_note": "GET /v1/models + GET /v1/image-generation-models; no image generated (cost)"},234        "sources": [src("models"), src(f"models/{mid}"), src("pricing"), src("rate-limits"), src("release-notes"), src("migration/imagine-image-quality-nov-2"), src("model-capabilities/images/generation")],235    }236237238VIDEO_DOCS = {239    "grok-imagine-video": dict(display="Grok Imagine Video (1.0)", price=0.05, batch="supported in us-east-1, us-west-2", regions=["us-east-1", "us-west-2", "us-saltlake-2"], release="2026-01 (release notes January 2026); created 2026-01-28 live",240                               notes=["input modalities live: text, image, video (video editing/extension)"]),241    "grok-imagine-video-1.5": dict(display="Grok Imagine Video 1.5", price=0.08, batch="supported", regions=["us-east-1", "us-west-2"], release="2026-05-30 (alias date; created 2026-05-27 live; July 2026 release notes for T2V/I2V/reference-to-video)",242                                   notes=["text-to-video runs as text-to-image then image-to-video under the hood", "native 1080p for T2V and I2V; reference-to-video with optional preset voices",243                                          "live input_modalities include 'audio' (reference audio) while the model page lists text, image only"]),244}245246247def video_model(mid: str) -> dict:248    m, d = live_models[mid], VIDEO_DOCS[mid]249    live_v = json.loads(Path(ROOT / "tmp-live" / "xai-models" / "catalogue-probe.json").read_text())["GET /v1/video-generation-models"]["body"]["models"] if (ROOT / "tmp-live" / "xai-models" / "catalogue-probe.json").exists() else []250    lv = next((x for x in live_v if x["id"] == mid), {})251    return {252        "provider": "xai", "id": mid, "display_name": d["display"], "kind": "model", "aliases": m["aliases"], "snapshots": [mid], "family": "Imagine", "generation": "video",253        "description": "Video generation / image-to-video / editing / extension model (Imagine API, asynchronous requests).", "status": ["DOCUMENTED", "LIVE_VERIFIED"], "release_date": d["release"], "created_at_live": ts(m["created"]),254        "fingerprint_live": lv.get("fingerprint"), "knowledge_cutoff": None, "context_window": None, "max_output": None,255        "modalities": {"input": lv.get("input_modalities", ["text", "image"]), "output": lv.get("output_modalities", ["video"])},256        "capabilities": {"text_to_video": True, "image_to_video": True, "video_editing": mid == "grok-imagine-video", "video_extension": True, "reference_to_video": mid == "grok-imagine-video-1.5",257                         "batch_api": d["batch"], "files_api_inputs_outputs": True, "async_polling": True, "streaming": False, "reasoning": False},258        "endpoints": ["POST /v1/videos/generations", "POST /v1/videos/edits", "POST /v1/videos/extensions", "GET /v1/videos/{request_id}", "Batch API", "gRPC xai_api.Video/{GenerateVideo,ExtendVideo,GetDeferredVideo}"], "tools": [],259        "pricing": {"unit": "USD per second of generated video", "per_second": d["price"], "batch_discount": 0.0, "note": "video URLs in batch results expire after 1 hour"},260        "rate_limits": {"documented": {"tier_rps_T0_T4": TIER_RPS[mid], "tpm": None}}, "beta_headers": [], "restrictions": d["notes"],261        "availability": {"account": "available", "regions": d["regions"], "base_urls": ["https://api.x.ai/v1 (not served on us.api.x.ai)"]},262        "last_verified": TODAY, "verification": {"method": "live_api", "verified_at": TODAY, "result": "success", "http_status": 200, "request_note": "GET /v1/models + GET /v1/video-generation-models(/{id}); no video generated (cost)"},263        "sources": [src("models"), src(f"models/{mid}"), src("pricing"), src("rate-limits"), src("release-notes"), src("model-capabilities/video/generation")],264    }265266267def voice_and_other() -> list[dict]:268    common_v = {"provider": "xai", "family": "Voice", "kind": "model", "snapshots": [], "knowledge_cutoff": None, "context_window": None, "max_output": None, "beta_headers": [], "last_verified": TODAY}269    return [270        {**common_v, "id": "grok-voice-think-fast-2.0", "display_name": "Grok Voice Think Fast 2.0", "aliases": ["grok-voice-latest (routes here since 2026-08-05)"], "generation": "2.0",271         "description": "Speech-to-speech realtime voice model (WebSocket wss://api.x.ai/v1/realtime, OpenAI Realtime-compatible event protocol, SIP, ephemeral client secrets).",272         "status": ["DOCUMENTED"], "release_date": "2026-07 (release notes July 2026)", "modalities": {"input": ["text", "audio"], "output": ["text", "audio"]},273         "capabilities": {"speech_to_speech": True, "function_calling": True, "web_search": True, "x_search": True, "collections_search": True, "remote_mcp": True, "custom_voices": True, "sip": True, "max_session_minutes": 120, "reasoning": "think-fast (built-in)"},274         "endpoints": ["WS wss://api.x.ai/v1/realtime?model=grok-voice-latest", "POST /v1/realtime/client_secrets", "/v1/realtime/calls/* (SIP)"], "tools": ["function", "web_search", "x_search", "collections_search", "mcp"],275         "pricing": {"audio_per_minute": 0.08, "audio_per_hour": 4.80, "text_input_per_message": 0.004, "unit": "USD", "note": "audio sent or received billed per minute; each conversation.item.create text item billed $0.004 except function_call_output and audio items"},276         "rate_limits": {"documented": {"concurrent_sessions_T0_T4": [10, 20, 50, 100, 200], "rps": None}}, "restrictions": ["not in GET /v1/models (GET /v1/models/grok-voice-think-fast-2.0 -> 404 not-found live)", "not served on us.api.x.ai"],277         "availability": {"regions": ["us-east-1 (speech-to-speech page)", "model page: us-east-1, eu-west-1, us-saltlake-2"], "account": "unknown (not exercised)"},278         "verification": {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": 404, "request_note": "GET /v1/models/grok-voice-think-fast-2.0 -> 404 (voice models are not in the models catalogue); realtime session not opened by this agent"},279         "sources": [src("models"), src("models/speech-to-speech"), src("models/grok-voice-think-fast-2.0"), src("pricing"), src("rate-limits"), src("release-notes"), src("model-capabilities/audio/speech-to-speech")]},280        {**common_v, "id": "grok-voice-think-fast-1.0", "display_name": "Grok Voice Think Fast 1.0", "aliases": [], "generation": "1.0", "description": "Previous speech-to-speech model (April 2026); grok-voice-latest moved to 2.0 on 2026-08-05.",281         "status": ["DOCUMENTED", "LEGACY"], "release_date": "2026-04 (release notes April 2026)", "modalities": {"input": ["text", "audio"], "output": ["text", "audio"]}, "capabilities": {"speech_to_speech": True},282         "endpoints": ["WS wss://api.x.ai/v1/realtime"], "tools": [], "pricing": "not listed on the pricing page (only grok-voice-think-fast-2.0 is priced)", "rate_limits": None, "restrictions": ["no retirement date documented"],283         "availability": {"account": "unknown"}, "verification": {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": "release notes only"},284         "sources": [src("release-notes")]},285        {**common_v, "id": "grok-voice-transcribe-2.0", "display_name": "Grok Voice Transcribe 2.0", "aliases": [], "generation": "2.0", "description": "Speech-to-text model (REST POST /v1/stt and streaming wss://api.x.ai/v1/stt).",286         "status": ["DOCUMENTED"], "release_date": "2026-09 (release notes September 2026)", "modalities": {"input": ["audio"], "output": ["text"]},287         "capabilities": {"speech_to_text": True, "streaming": True, "languages": "25 (STT GA note)", "smart_turn": True, "vad_threshold": True, "keyterm_prompting": True, "formats": ["WAV", "MP3", "WebM", "OGG", "M4A"]},288         "endpoints": ["POST /v1/stt", "WS wss://api.x.ai/v1/stt"], "tools": [], "pricing": {"rest_per_hour": 0.10, "streaming_per_hour": 0.20, "unit": "USD per hour of audio"},289         "rate_limits": {"documented": {"rps_T0_T4": [10, 10, 20, 30, 40], "concurrent_sessions_T0_T4": [100, 200, 200, 300, 500]}},290         "restrictions": ["DEFAULT DISCREPANCY: release notes say the default is grok-voice-transcribe-1.0; the Speech to Text model page says the default is grok-voice-transcribe-2.0"],291         "availability": {"regions": ["us-east-1"]}, "verification": {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": "voice agent covers /v1/stt"},292         "sources": [src("models/speech-to-text"), src("release-notes"), src("pricing"), src("rate-limits"), src("model-capabilities/audio/speech-to-text")]},293        {**common_v, "id": "grok-voice-transcribe-1.0", "display_name": "Grok Voice Transcribe 1.0", "aliases": [], "generation": "1.0", "description": "First speech-to-text model (STT GA April 2026).",294         "status": ["DOCUMENTED"], "release_date": "2026-04 (STT GA)", "modalities": {"input": ["audio"], "output": ["text"]}, "capabilities": {"speech_to_text": True, "streaming": True},295         "endpoints": ["POST /v1/stt", "WS wss://api.x.ai/v1/stt"], "tools": [], "pricing": {"rest_per_hour": 0.10, "streaming_per_hour": 0.20, "unit": "USD per hour of audio"}, "rate_limits": "same table as Speech to Text",296         "restrictions": [], "availability": {"regions": ["us-east-1"]}, "verification": {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": None},297         "sources": [src("models/speech-to-text"), src("release-notes")]},298        {**common_v, "id": "text-to-speech", "display_name": "Grok Text to Speech (service, no model id documented)", "kind": "service", "aliases": [], "generation": None,299         "description": "Text-to-speech API (POST /v1/tts, streaming wss://api.x.ai/v1/tts, voice catalogue GET /v1/tts/voices, custom voices). Docs do not expose a model id; requests select a voice.",300         "status": ["DOCUMENTED"], "release_date": "2026-03 (release notes March 2026)", "modalities": {"input": ["text"], "output": ["audio"]},301         "capabilities": {"text_to_speech": True, "streaming": True, "custom_voices": True, "formats": ["MP3", "WAV", "PCM", "mu-law", "A-law"]},302         "endpoints": ["POST /v1/tts", "WS wss://api.x.ai/v1/tts", "GET /v1/tts/voices", "GET /v1/tts/voices/{voice}"], "tools": [], "pricing": {"per_1M_characters": 15.00, "unit": "USD per 1M input characters"},303         "rate_limits": {"documented": {"rps_T0_T4": [50, 50, 100, 250, 500], "concurrent_sessions_T0_T4": [100, 200, 200, 300, 500]}}, "restrictions": [], "availability": {"regions": ["us-east-1"]},304         "verification": {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": "voice agent covers /v1/tts"},305         "sources": [src("models/text-to-speech"), src("pricing"), src("rate-limits"), src("model-capabilities/audio/text-to-speech")]},306        {"provider": "xai", "id": "grok-embedding-small", "display_name": "Grok Embedding Small", "kind": "model", "aliases": [], "snapshots": [], "family": "Embeddings", "generation": None,307         "description": "Embedding model referenced as Collections index_configuration.model_name in the Collections API reference. /v1/embedding-models exists (OpenAPI + live) and returns an empty list for our team.",308         "status": ["DOCUMENTED", "ACCOUNT_RESTRICTED"], "release_date": None, "knowledge_cutoff": None, "context_window": None, "max_output": None, "modalities": {"input": ["text"], "output": ["embedding"]},309         "capabilities": {"embeddings": True, "collections_indexing": True}, "endpoints": ["POST /v1/embeddings (OpenAPI)", "GET /v1/embedding-models(/{id})", "gRPC xai_api.Embedder/Embed"], "tools": [],310         "pricing": "not on the pricing page; EmbeddingModel schema carries prompt_text_token_price / prompt_image_token_price", "rate_limits": "rate-limit tiers apply to embedding models (rate-limits page); no table published",311         "beta_headers": [], "restrictions": ["GET /v1/embedding-models -> {\"models\": []} and GET /v1/embedding-models/grok-embedding-small -> 404 not-found ('does not exist or your team ... does not have access') with our key"],312         "availability": {"account": "not available to our team"}, "last_verified": TODAY,313         "verification": {"method": "live_api", "verified_at": TODAY, "result": "restricted", "http_status": 404, "request_note": "GET /v1/embedding-models (200, empty) + GET /v1/embedding-models/grok-embedding-small (404)"},314         "sources": [src("rest-api-reference/collections/collection"), src("rest-api-reference/inference/models"), src("rate-limits")]},315    ]316317318def retired_and_legacy() -> list[dict]:319    def rec(mid, display, family, status, retirement, replacement, note, probe, sources, aliases=None, kind="retired_redirect"):320        return {"provider": "xai", "id": mid, "display_name": display, "kind": kind, "aliases": aliases or [], "snapshots": [], "family": family, "status": status, "release_date": None,321                "knowledge_cutoff": None, "context_window": None, "max_output": None, "modalities": {"input": [], "output": []}, "capabilities": {}, "endpoints": [], "tools": [], "pricing": None, "rate_limits": None, "beta_headers": [],322                "deprecation": {"retirement": retirement, "replacement": replacement, "note": note}, "restrictions": [], "availability": None, "last_verified": TODAY,323                "verification": probe, "sources": sources}324    may15 = src("migration/may-15-retirement")325    ok_redirect = lambda target, path: {"method": "live_api", "verified_at": TODAY, "result": "success", "http_status": 200, "request_note": f"GET {path} -> 200 with id '{target}' (redirect confirmed live 2026-09-19)"}326    not_probed = {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": "not probed"}327    out = [328        rec("grok-3", "Grok 3", "Grok 3", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort none)", "billed at grok-4.3 rates after redirect", ok_redirect("grok-4.3", "/v1/models/grok-3"), [may15, src("release-notes")]),329        rec("grok-4-0709", "Grok 4 (0709)", "Grok 4", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort low)", "Grok 4 launched July 2025", ok_redirect("grok-4.3", "/v1/language-models/grok-4-0709"), [may15, src("release-notes")]),330        rec("grok-4-fast-reasoning", "Grok 4 Fast (reasoning)", "Grok 4", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort low)", "slug keeps resolving", ok_redirect("grok-4.3", "/v1/models/grok-4-fast"), [may15], aliases=["grok-4-fast (probed)"]),331        rec("grok-4-fast-non-reasoning", "Grok 4 Fast (non-reasoning)", "Grok 4", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort none)", "slug keeps resolving", not_probed, [may15]),332        rec("grok-4-1-fast-reasoning", "Grok 4.1 Fast (reasoning)", "Grok 4.1", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort low)", "launched Nov 2025 (Enterprise API)", not_probed, [may15, src("release-notes")]),333        rec("grok-4-1-fast-non-reasoning", "Grok 4.1 Fast (non-reasoning)", "Grok 4.1", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-4.3 (reasoning_effort none)", None, not_probed, [may15]),334        rec("grok-code-fast-1", "Grok Code Fast 1", "Build", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-build-0.1", "now listed as an alias of grok-build-0.1 in the live catalogue (with grok-code-fast, grok-code-fast-1-0825)", ok_redirect("grok-build-0.1", "/v1/models/grok-code-fast-1"), [may15, src("models/grok-build-0.1")], kind="alias"),335        rec("grok-imagine-image-pro", "Grok Imagine Image Pro", "Imagine", ["DOCUMENTED", "RETIRED"], "2026-05-15T12:00 PT", "grok-imagine-image-quality (then grok-imagine-image-2.0 quality=low from 2026-11-02)", "alias of grok-imagine-image-quality in the live catalogue", {"method": "live_api", "verified_at": TODAY, "result": "success", "http_status": 200, "request_note": "appears in aliases of grok-imagine-image-quality (GET /v1/models)"}, [may15, src("migration/imagine-image-quality-nov-2")], kind="alias"),336    ]337    legacy = {"method": "docs_only", "verified_at": TODAY, "result": "not_tested", "http_status": None, "request_note": "mentioned only in release notes / legacy reference examples; absent from current models & pricing pages"}338    for mid, disp, fam, note in [339        ("grok-2-image-1212", "Grok 2 Image", "Grok 2", "image generation model of March 2025; GET /v1/models/grok-2-image -> 404 not-found (2026-09-19)"),340        ("grok-2-vision-1212", "Grok 2 Vision", "Grok 2", "released December 2024"), ("grok-2-1212", "Grok 2", "Grok 2", "released December 2024"),341        ("grok-3-mini", "Grok 3 Mini", "Grok 3", "grok-3-mini / -fast / -latest / -beta appear in legacy examples only"),342        ("grok-beta", "Grok beta", "Grok 1/beta", "public beta November 2024"), ("grok-vision-beta", "Grok Vision beta", "Grok 1/beta", "public beta November 2024"),343        ("grok-4-latest", "grok-4 / grok-4-latest", "Grok 4", "alias family of Grok 4 (July 2025); grok-4-0709 retired 2026-05-15"),344    ]:345        v = dict(legacy)346        if mid == "grok-2-image-1212":347            v = {"method": "live_api", "verified_at": TODAY, "result": "failure", "http_status": 404, "request_note": "GET /v1/models/grok-2-image -> 404 {code: not-found}; POST /v1/chat/completions model=grok-2-image -> 404"}348        out.append(rec(mid, disp, fam, ["LEGACY", "UNVERIFIED"], "not documented", None, note, v, [src("release-notes")], kind="legacy"))349    return out350351352def prices(models: list[dict]) -> list[dict]:353    P = []354355    def add(mos, dim, price, unit, tier="standard", note=None, source="pricing"):356        P.append({"provider": "xai", "model_or_service": mos, "dimension": dim, "price": price, "currency": "USD", "unit": unit, "tier": tier,357                  "effective_notes": note, "source": f"{DOCS}/{source}", "retrieved_at": RETRIEVED})358359    for mid in TEXT_DOCS:360        lm = live_lang[mid]361        thr = lm["long_context_threshold"]362        std = {"input": lm["prompt_text_token_price"], "cached_input": lm["cached_prompt_text_token_price"], "image_input": lm["prompt_image_token_price"], "output": lm["completion_text_token_price"]}363        lc = {"input": lm["prompt_text_token_price_long_context"], "cached_input": lm["cached_prompt_text_token_price_long_context"], "output": lm["completion_text_token_price_long_context"]}364        for dim, t in std.items():365            add(mid, dim, per_million(t), "per 1M tokens", "standard", f"prompt < {thr:,} tokens; reasoning tokens billed as output" + (" (image tokens priced like text)" if dim == "image_input" else ""))366        for dim, t in lc.items():367            add(mid, dim, per_million(t), "per 1M tokens", "long_context", f"prompt >= {thr:,} tokens -> higher rate for ALL tokens of the request")368        disc = TEXT_DOCS[mid]["batch_discount"]369        if TEXT_DOCS[mid]["batch"]:370            for dim, t in std.items():371                if dim == "image_input":372                    continue373                add(mid, dim, round(per_million(t) * (1 - (disc or 0)), 4), "per 1M tokens", "batch", f"Batch API {int((disc or 0)*100)}% off standard (< {thr:,} prompt tokens); applies to input, output, cached and reasoning tokens")374        else:375            add(mid, "batch", None, "per 1M tokens", "batch", "Batch API not supported for this model (model page)")376        if mid != "grok-4.20-multi-agent-0309":377            for dim, t in std.items():378                if dim == "image_input":379                    continue380                add(mid, dim, round(per_million(t) * 2, 4), "per 1M tokens", "priority", "service_tier=priority -> 2x standard on all token types (caching discount applied before multiplier); billed only when response service_tier == 'priority'", "advanced-api-usage/priority-processing")381        if TEXT_DOCS[mid]["us_regional"]:382            for dim, t in std.items():383                add(f"{mid} @ https://us.api.x.ai/v1", dim, round(per_million(t) * 1.1, 4), "per 1M tokens", "us_regional", "US regional endpoint = 1.1x global token rates (live catalogue on us.api.x.ai shows 22000/5500/66000 ticks); long-context rates also x1.1", "advanced-api-usage/regions")384    for mid in IMG_DOCS:385        im = live_img[mid]386        add(mid, "image_output", usd(im["image_price"]), "per image", "standard", "default tier (image_price); Batch API accepted where supported but no discount; also via image_generation tool")387        for p in im.get("pricing", []):388            add(mid, "image_output", usd(p["price_per_image"]), "per image", f"quality={p['quality']} resolution={p['resolution']}", "quality auto (default) bills at the quality actually served (low for generation, medium for editing)")389    for mid in VIDEO_DOCS:390        add(mid, "video_output", VIDEO_DOCS[mid]["price"], "per second", "standard", "billed per second of generated video; batch at standard rates")391    add("grok-voice-think-fast-2.0 (speech-to-speech)", "audio_input_output", 0.08, "per minute", "standard", "$4.80 / hour of audio sent or received")392    add("grok-voice-think-fast-2.0 (speech-to-speech)", "text_input_message", 0.004, "per message", "standard", "per conversation.item.create text item (function_call_output and audio items excluded)")393    add("speech-to-text (grok-voice-transcribe-*)", "audio_input", 0.10, "per hour", "rest", "POST /v1/stt")394    add("speech-to-text (grok-voice-transcribe-*)", "audio_input", 0.20, "per hour", "streaming", "wss://api.x.ai/v1/stt")395    add("text-to-speech", "text_input", 15.00, "per 1M characters", "standard", "POST /v1/tts and streaming")396    for tool, price, note in [("web_search", 5.0, "includes image search; view_image results token-billed only"), ("x_search", 5.0, "UNTIL 2026-09-21 12:00 PT: per 1k calls. FROM 2026-09-21: $5 per 1k posts fetched + $10 per 1k user profiles fetched (every post returned incl. parent/quoted counts)"),397                              ("code_execution / code_interpreter", 5.0, "Python sandbox; code_interpreter alias not supported in gRPC/xai-sdk"), ("attachment_search", 10.0, "search files attached to messages"),398                              ("collections_search / file_search", 2.5, "RAG over collections; file_search alias not supported in gRPC/xai-sdk")]:399        add(f"tool:{tool}", "per_call", price, "per 1K calls", "standard", f"only successful server-side tool executions are billed; {note}")400    add("tool:x_search (posts)", "per_post_fetched", 5.0, "per 1K posts", "from 2026-09-21", "replaces per-call billing on 2026-09-21 12:00 PT")401    add("tool:x_search (profiles)", "per_profile_fetched", 10.0, "per 1K profiles", "from 2026-09-21", "user search results")402    for tool in ("image_generation", "view_image", "view_x_video", "mcp (remote MCP tools)"):403        add(f"tool:{tool}", "per_call", 0.0, "per call", "standard", "no invocation fee: image_generation billed at Imagine per-image rates; view_* and MCP billed through tokens only")404    add("files", "storage_gb_day", 0.025, "per GiB per day", "standard", "file storage")405    add("collections", "storage_gb_day", 0.10, "per GiB per day", "standard", "collection storage")406    add("files", "download_gb", 0.20, "per GiB", "standard", "downloads")407    add("collections", "download_gb", 0.20, "per GiB", "standard", "downloads")408    add("usage-guideline-violation (Responses API)", "per_request_fee", 0.05, "per request", "standard", "charged when a violation is caught before generation; generations that violate are still charged")409    add("management-api (https://management-api.x.ai)", "per_call", 0.0, "per call", "standard", "no pricing documented; requires a Management key (not available to this atlas -> ACCOUNT_RESTRICTED)", "management-api-guide")410    add("GET /v1/models, /v1/language-models, /v1/api-key, /v1/me, POST /v1/tokenize-text", "per_call", 0.0, "per call", "standard", "no usage returned; free (not documented explicitly, observed: no cost_in_usd_ticks)", "rest-api-reference/inference/models")411    return P412413414def rate_limits() -> dict:415    return {416        "record_type": "rate_limits", "provider": "xai", "generated_at": TODAY, "status": ["DOCUMENTED", "LIVE_DISCOVERED"],417        "sources": [src("rate-limits"), src("management-api-guide"), src("debugging"), src("pricing"), src("advanced-api-usage/batch-api")] + [src(f"models/{m}") for m in TIER_RPS],418        "documented": {419            "principles": [420                "Every team has per-model limits on two dimensions: requests per second (RPS) and tokens per minute (TPM). RPS is derived from the per-minute request budget (RPM/60): a full minute of requests cannot be spent in one second.",421                "Limits scale with the team's tier, determined by cumulative spend on the xAI API since 2026-01-01 (prepaid purchases or fulfilled invoices). Tiers unlock automatically and never downgrade.",422                "Tiers apply to text, embedding and voice models. Imagine (image/video) limits are RPS-only and are raised via sales@x.ai.",423                "TPM counts prompt tokens (text, image, audio), completion tokens, reasoning tokens and cached prompt tokens (cached tokens still count although billed less).",424                "Exceeding any limit -> HTTP 429 Too Many Requests (gRPC RESOURCE_EXHAUSTED). Use exponential backoff.",425                "Batch API requests do not count towards rate limits.",426                "Per-API-key caps (qps, qpm, tpm) can additionally be set via the Management API (POST/PUT api-keys); the tpm limiter engages when strictly exceeded and does not abort in-flight requests.",427                "Team-level personalized limits are shown on the console Models page (https://console.x.ai/team/default/models).",428            ],429            "tiers": {"Tier 0": {"spend_threshold_usd": 0, "note": "default"}, "Tier 1": {"spend_threshold_usd": 50}, "Tier 2": {"spend_threshold_usd": 250}, "Tier 3": {"spend_threshold_usd": 1000}, "Tier 4": {"spend_threshold_usd": 5000}, "Enterprise": {"note": "on request (sales@x.ai)"}},430            "per_model": {m: {"rps_T0_T4": TIER_RPS[m], "tpm_T0_T4": TIER_TPM.get(m)} for m in TIER_RPS},431            "voice_and_audio": {"grok-voice-think-fast-2.0": {"rps": None, "concurrent_sessions_T0_T4": [10, 20, 50, 100, 200], "max_session_minutes": 120},432                                "text-to-speech": {"rps_T0_T4": [50, 50, 100, 250, 500], "concurrent_sessions_T0_T4": [100, 200, 200, 300, 500]},433                                "speech-to-text": {"rps_T0_T4": [10, 10, 20, 30, 40], "concurrent_sessions_T0_T4": [100, 200, 200, 300, 500], "note": "concurrent sessions apply to streaming"}},434            "increasing_limits": ["spend more (automatic)", "request an increase in the console Models page", "sales@x.ai for enterprise capacity"],435            "http_429_semantics": "429 on inference endpoints when RPS or TPM is exceeded; no documented Retry-After header (none observed in this run since no 429 was triggered)",436        },437        "observed_for_this_key": {438            "note": "Headers returned to OUR key on 2026-09-19 (Tier 0 team, spend < $50). Account-specific; not documentation.",439            "headers_seen": ["x-ratelimit-limit-requests", "x-ratelimit-remaining-requests", "x-ratelimit-limit-tokens", "x-ratelimit-remaining-tokens", "x-request-id", "Server-Timing (cfEdge/cfOrigin)", "CF-RAY (Cloudflare edge, e.g. ...-YUL)"],440            "headers_not_seen": ["x-ratelimit-reset-*", "retry-after (no 429 triggered)"],441            "per_model": OBSERVED_HEADERS,442            "tokenize_text": {"x-ratelimit-limit-requests": "1800", "x-ratelimit-limit-tokens": "absent"},443            "list_endpoints": "GET /v1/models, /v1/language-models, /v1/api-key, /v1/me return no x-ratelimit-* headers",444            "interpretation": "x-ratelimit-limit-tokens matches the documented Tier 0 TPM (50M for grok-4.6/4.5, 10M for grok-4.3/4.20/build). x-ratelimit-limit-requests looks like a per-MINUTE request budget (7200 -> 120 RPS; 1800 -> 30 RPS), lower than the documented Tier 0 RPS (150 / 37) - the docs say RPS = RPM/60, so the header is probably the RPM budget and our team sits slightly below the published T0 figures. Not verified further.",445            "grok-4.20-multi-agent-0309": "POST /v1/responses returned no x-ratelimit-* headers in our probe",446        },447    }448449450def main() -> None:451    models = [text_model(m) for m in TEXT_DOCS] + [image_model(m) for m in IMG_DOCS] + [video_model(m) for m in VIDEO_DOCS] + voice_and_other() + retired_and_legacy()452    live_ids = sorted(live_models)453    doc = {"record_type": "model", "provider": "xai", "generated_at": TODAY, "generator": "scripts/gen_xai_fragments.py",454           "live_catalogue": {"endpoint": "GET https://api.x.ai/v1/models", "retrieved_at": "2026-09-19", "ids": live_ids, "count": len(live_ids),455                              "price_units": "ticks: 1 USD = 1e10 ticks; token prices are ticks per token (12500 -> $1.25/1M), image_price ticks per image (200000000 -> $0.02)"},456           "count": len(models), "records": models}457    OUT_MODELS.parent.mkdir(parents=True, exist_ok=True)458    OUT_MODELS.write_text(json.dumps(doc, indent=1, ensure_ascii=False) + "\n")459    pr = prices(models)460    OUT_PRICES.write_text(json.dumps({"record_type": "price", "provider": "xai", "generated_at": TODAY, "generator": "scripts/gen_xai_fragments.py", "count": len(pr), "records": pr}, indent=1, ensure_ascii=False) + "\n")461    OUT_RATES.write_text(json.dumps(rate_limits(), indent=1, ensure_ascii=False) + "\n")462    print(f"models={len(models)} prices={len(pr)} -> {OUT_MODELS.relative_to(ROOT)}, {OUT_PRICES.relative_to(ROOT)}, {OUT_RATES.relative_to(ROOT)}")463464465if __name__ == "__main__":466    main()467