Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions litellm/model_prices_and_context_window_backup.json
Original file line number Diff line number Diff line change
Expand Up @@ -43373,6 +43373,7 @@
]
},
"xai/grok-2": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43385,6 +43386,7 @@
"supports_web_search": true
},
"xai/grok-2-1212": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43397,6 +43399,7 @@
"supports_web_search": true
},
"xai/grok-2-latest": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43409,6 +43412,7 @@
"supports_web_search": true
},
"xai/grok-2-vision": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 2e-06,
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -43438,6 +43442,7 @@
"supports_web_search": true
},
"xai/grok-2-vision-latest": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 2e-06,
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -43836,7 +43841,7 @@
"max_input_tokens": 1000000,
"max_output_tokens": 1000000,
"max_tokens": 1000000,
"mode": "chat",
"mode": "responses",
"output_cost_per_token": 2.5e-06,
"source": "https://docs.x.ai/docs/models",
"supports_function_calling": true,
Expand Down Expand Up @@ -44018,6 +44023,7 @@
"supports_web_search": true
},
"xai/grok-beta": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 5e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand Down Expand Up @@ -44094,6 +44100,7 @@
"deprecation_date": "2026-05-15"
},
"xai/grok-vision-beta": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 5e-06,
"input_cost_per_token": 5e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -50703,7 +50710,7 @@
"max_input_tokens": 1000000,
"max_output_tokens": 1000000,
"max_tokens": 1000000,
"mode": "chat",
"mode": "responses",
"output_cost_per_token": 2.5e-06,
"source": "https://docs.x.ai/docs/models",
"supports_function_calling": true,
Expand Down
11 changes: 9 additions & 2 deletions model_prices_and_context_window.json
Original file line number Diff line number Diff line change
Expand Up @@ -43373,6 +43373,7 @@
]
},
"xai/grok-2": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43385,6 +43386,7 @@
"supports_web_search": true
},
"xai/grok-2-1212": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43397,6 +43399,7 @@
"supports_web_search": true
},
"xai/grok-2-latest": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand All @@ -43409,6 +43412,7 @@
"supports_web_search": true
},
"xai/grok-2-vision": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 2e-06,
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -43438,6 +43442,7 @@
"supports_web_search": true
},
"xai/grok-2-vision-latest": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 2e-06,
"input_cost_per_token": 2e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -43836,7 +43841,7 @@
"max_input_tokens": 1000000,
"max_output_tokens": 1000000,
"max_tokens": 1000000,
"mode": "chat",
"mode": "responses",
"output_cost_per_token": 2.5e-06,
"source": "https://docs.x.ai/docs/models",
"supports_function_calling": true,
Expand Down Expand Up @@ -44018,6 +44023,7 @@
"supports_web_search": true
},
"xai/grok-beta": {
"deprecation_date": "2026-02-28",
"input_cost_per_token": 5e-06,
"litellm_provider": "xai",
"max_input_tokens": 131072,
Expand Down Expand Up @@ -44094,6 +44100,7 @@
"deprecation_date": "2026-05-15"
},
"xai/grok-vision-beta": {
"deprecation_date": "2026-02-28",
"input_cost_per_image": 5e-06,
"input_cost_per_token": 5e-06,
"litellm_provider": "xai",
Expand Down Expand Up @@ -50703,7 +50710,7 @@
"max_input_tokens": 1000000,
"max_output_tokens": 1000000,
"max_tokens": 1000000,
"mode": "chat",
"mode": "responses",
"output_cost_per_token": 2.5e-06,
"source": "https://docs.x.ai/docs/models",
"supports_function_calling": true,
Expand Down
85 changes: 85 additions & 0 deletions tests/test_litellm/test_xai_grok_model_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Avoid redundant test state

This bug fix creates a separate test module whose autouse fixture replaces litellm.model_cost, although every assertion reads the JSON files directly. This bypasses the mapped-test convention and adds global-state and cache-isolation risk without contributing to the regression coverage

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

from pathlib import Path

# https://github.com/BerriAI/litellm/issues/38179
# grok-2-era slugs: xAI deprecated them effective 2026-02-28 (same batch date as
# the pre-existing xai/grok-2-vision-1212 annotation) and they now hard-fail with
# "Model not found — retired by xAI".
RETIRED_GROK2_MODELS = [
"xai/grok-2",
"xai/grok-2-1212",
"xai/grok-2-latest",
"xai/grok-2-vision",
"xai/grok-2-vision-latest",
"xai/grok-beta",
"xai/grok-vision-beta",
]
GROK2_DEPRECATION_DATE = "2026-02-28"

# Per https://docs.x.ai/developers/model-capabilities/text/multi-agent (Limitations):
# "The multi-agent model does not work with the OpenAI Chat Completions API."
RESPONSES_ONLY_MODELS = [
"xai/grok-4.20-multi-agent-0309",
"xai/grok-4.20-multi-agent-beta-0309",
]

# Slugs still served by xAI (https://docs.x.ai/developers/models) that must not
# be marked deprecated.
ACTIVE_GROK_MODELS = [
"xai/grok-4.5",
"xai/grok-4.6",
"xai/grok-4.20-0309-reasoning",
]


def _load_model_cost(path: Path) -> dict:
with open(path) as f:
return json.load(f)


def test_retired_grok2_models_are_annotated():
json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
model_cost = _load_model_cost(json_path)

for model in RETIRED_GROK2_MODELS:
info = model_cost.get(model)
assert info is not None, f"{model} not found in model_prices_and_context_window.json"
assert (
info.get("deprecation_date") == GROK2_DEPRECATION_DATE
), f"{model} should carry deprecation_date {GROK2_DEPRECATION_DATE}"


def test_grok_4_20_multi_agent_is_responses_only():
json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
model_cost = _load_model_cost(json_path)

for model in RESPONSES_ONLY_MODELS:
info = model_cost.get(model)
assert info is not None, f"{model} not found in model_prices_and_context_window.json"
assert (
info.get("mode") == "responses"
), f"{model} only works with the xAI Responses API, not Chat Completions"


def test_active_grok_models_are_not_marked_deprecated():
json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
model_cost = _load_model_cost(json_path)

for model in ACTIVE_GROK_MODELS:
info = model_cost.get(model)
assert info is not None, f"{model} not found in model_prices_and_context_window.json"
assert (
"deprecation_date" not in info
), f"{model} is still served by xAI and must not be marked deprecated"


def test_xai_grok_metadata_backup_matches_main():
repo_root = Path(__file__).parents[2]
main_path = repo_root / "model_prices_and_context_window.json"
backup_path = repo_root / "litellm" / "model_prices_and_context_window_backup.json"

main_cost = _load_model_cost(main_path)
backup_cost = _load_model_cost(backup_path)

for model in RETIRED_GROK2_MODELS + RESPONSES_ONLY_MODELS:
assert backup_cost.get(model) == main_cost.get(model), f"{model} differs between main and backup"