Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
ab160fb
fix(model_prices): sync gpt-5.6-sol bedrock rates, add gpt-5.6-cyber,…
devin-ai-integration[bot] Aug 25, 2026
310591f
test(model_prices): pin claude 3 1h cache write rates to 2x base input
devin-ai-integration[bot] Aug 25, 2026
2b0c4c6
Merge litellm_internal_staging into registry audit branch
devin-ai-integration[bot] Aug 25, 2026
fb15851
fix(model_prices): verified Novita, DeepInfra, W&B, Gemini cache-read…
devin-ai-integration[bot] Aug 25, 2026
9300018
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
devin-ai-integration[bot] Aug 26, 2026
d266326
fix(model_prices): azure gpt-4.1-nano retirement date, together depre…
devin-ai-integration[bot] Aug 26, 2026
d3ede97
fix(model_prices): keep novita gpt-oss-120b vision flag per provider …
devin-ai-integration[bot] Aug 26, 2026
07c9812
fix(model_prices): carry anthropic behavior flags on deepinfra claude…
devin-ai-integration[bot] Aug 26, 2026
0cc407a
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
devin-ai-integration[bot] Aug 26, 2026
4456a44
fix(model_prices): azure gpt-5.6 cache writes, mistral missing models…
devin-ai-integration[bot] Aug 26, 2026
7c7af51
merge: litellm_internal_staging into rolling registry branch
devin-ai-integration[bot] Aug 26, 2026
4b3e82b
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
devin-ai-integration[bot] Aug 27, 2026
5d26ae0
fix(model_prices): absorb Databricks/Z.AI and xAI registry PRs, add T…
devin-ai-integration[bot] Aug 27, 2026
a0689f0
fix(model_prices): cap ministral-3-3b at Mistral API's 131072 and mir…
mateo-berri Aug 27, 2026
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
2,830 changes: 2,556 additions & 274 deletions litellm/model_prices_and_context_window_backup.json

Large diffs are not rendered by default.

2,830 changes: 2,556 additions & 274 deletions model_prices_and_context_window.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ class GptProfile(NamedTuple):
GPT_5_6_PROFILES = [
GptProfile(
model_id="us.openai.gpt-5.6-sol",
input_cost=5.5e-06, input_cost_above_272k=1.1e-05,
cache_write=6.875e-06, cache_write_above_272k=1.375e-05,
cache_read=5.5e-07, cache_read_above_272k=1.1e-06,
output_cost=3.3e-05, output_cost_above_272k=4.95e-05,
input_cost=4.4e-06, input_cost_above_272k=8.8e-06,
cache_write=5.5e-06, cache_write_above_272k=1.1e-05,
cache_read=4.4e-07, cache_read_above_272k=8.8e-07,
output_cost=2.2e-05, output_cost_above_272k=3.3e-05,
),
GptProfile(
model_id="global.openai.gpt-5.6-sol",
input_cost=5e-06, input_cost_above_272k=1e-05,
cache_write=6.25e-06, cache_write_above_272k=1.25e-05,
cache_read=5e-07, cache_read_above_272k=1e-06,
output_cost=3e-05, output_cost_above_272k=4.5e-05,
input_cost=4e-06, input_cost_above_272k=8e-06,
cache_write=5e-06, cache_write_above_272k=1e-05,
cache_read=4e-07, cache_read_above_272k=8e-07,
output_cost=2e-05, output_cost_above_272k=3e-05,
),
GptProfile(
model_id="us.openai.gpt-5.6-terra",
Expand Down Expand Up @@ -221,7 +221,7 @@ def test_bedrock_gpt_5_6_above_272k_tier_applies_to_cost(local_model_cost_map):
custom_llm_provider="bedrock",
)

assert cost == pytest.approx((300000 * 1.1e-05) + (1000 * 4.95e-05), rel=1e-9)
assert cost == pytest.approx((300000 * 8.8e-06) + (1000 * 3.3e-05), rel=1e-9)


def test_bedrock_gpt_5_6_bills_cache_read_tokens(local_model_cost_map):
Expand All @@ -241,10 +241,10 @@ def test_bedrock_gpt_5_6_bills_cache_read_tokens(local_model_cost_map):
custom_llm_provider="bedrock",
)

expected = (2 * 5.5e-06) + (15609 * 5.5e-07) + (5 * 3.3e-05)
expected = (2 * 4.4e-06) + (15609 * 4.4e-07) + (5 * 2.2e-05)
assert cost == pytest.approx(expected, rel=1e-9)
# Without cache_read_input_token_cost the cached prefix bills at zero.
assert cost > (15611 * 5.5e-06) * 0.1
assert cost > (15611 * 4.4e-06) * 0.1


def test_bedrock_gpt_5_6_bills_cache_write_tokens(local_model_cost_map):
Expand All @@ -263,7 +263,7 @@ def test_bedrock_gpt_5_6_bills_cache_write_tokens(local_model_cost_map):
custom_llm_provider="bedrock",
)

expected = (2 * 5.5e-06) + (15609 * 6.875e-06) + (5 * 3.3e-05)
expected = (2 * 4.4e-06) + (15609 * 5.5e-06) + (5 * 2.2e-05)
assert cost == pytest.approx(expected, rel=1e-9)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1683,10 +1683,19 @@ def test_gpt_5_4_pricing_and_mode(self, local_cost_map):
assert info["cache_read_input_token_cost"] == pytest.approx(2.75e-07)
assert info["max_input_tokens"] == 1050000

def test_gpt_5_6_cyber_pricing_and_mode(self, local_cost_map):
info = litellm.get_model_info("bedrock_mantle/openai.gpt-5.6-cyber")
assert info["mode"] == "responses"
assert info["input_cost_per_token"] == pytest.approx(1.375e-05)
assert info["cache_creation_input_token_cost"] == pytest.approx(1.71875e-05)
assert info["cache_read_input_token_cost"] == pytest.approx(1.375e-06)
assert info["output_cost_per_token"] == pytest.approx(8.25e-05)
assert info["max_input_tokens"] == 272000

@pytest.mark.parametrize(
"model, input_cost, cache_creation_cost, cache_read_cost, output_cost",
[
("openai.gpt-5.6-sol", 5.5e-06, 6.875e-06, 5.5e-07, 3.3e-05),
("openai.gpt-5.6-sol", 4.4e-06, 5.5e-06, 4.4e-07, 2.2e-05),
("openai.gpt-5.6-terra", 2.2e-06, 2.75e-06, 2.2e-07, 1.32e-05),
("openai.gpt-5.6-luna", 2.2e-07, 2.75e-07, 2.2e-08, 1.32e-06),
],
Expand All @@ -1709,7 +1718,7 @@ def test_gpt_5_6_pricing_and_mode(
@pytest.mark.parametrize(
"model, input_cost, output_cost",
[
("openai.gpt-5.6-sol", 5.5e-06, 3.3e-05),
("openai.gpt-5.6-sol", 4.4e-06, 2.2e-05),
("openai.gpt-5.6-terra", 2.2e-06, 1.32e-05),
("openai.gpt-5.6-luna", 2.2e-07, 1.32e-06),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"databricks/databricks-gemini-3-1-flash-lite": ("4.464", "26.786", "4.464", "0.446"),
"databricks/databricks-gemini-2-5-pro": ("22.321", "178.571", "22.321", "2.232"),
"databricks/databricks-gemini-2-5-flash": ("5.357", "44.643", "5.357", "0.536"),
"databricks/databricks-kimi-k3": ("42.857", "214.286", "42.857", "4.286"),
"databricks/databricks-glm-5-2": ("20.000", "62.857", "20.000", "3.714"),
}
PROMOTIONAL_DISCOUNT: Final = 0.80
PROMOTION_EXPIRES: Final = "2027-01-31"
Expand Down
75 changes: 75 additions & 0 deletions tests/test_litellm/llms/xai/test_xai_model_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
Registry regression tests for xAI entries in the model cost map.
"""

from __future__ import annotations

import json
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).parents[4]
PRICES_PATH = REPO_ROOT / "model_prices_and_context_window.json"
BACKUP_PRICES_PATH = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json"

# Retired by xAI and no longer served: requests to these slugs 404 rather than
# redirecting, and they are absent from https://docs.x.ai/docs/models
RETIRED_MODELS = (
"xai/grok-2",
"xai/grok-2-1212",
"xai/grok-2-latest",
"xai/grok-2-vision",
"xai/grok-2-vision-1212",
"xai/grok-2-vision-latest",
"xai/grok-beta",
"xai/grok-vision-beta",
)

# https://docs.x.ai/developers/model-capabilities/text/multi-agent
# "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",
)

MAP_PATHS = (PRICES_PATH, BACKUP_PRICES_PATH)


@pytest.fixture(scope="module", params=[p.name for p in MAP_PATHS])
def cost_map(request: pytest.FixtureRequest) -> dict:
path = next(p for p in MAP_PATHS if p.name == request.param)
return json.loads(path.read_text(encoding="utf-8"))


@pytest.mark.parametrize("model", RETIRED_MODELS)
def test_retired_xai_models_are_not_advertised(cost_map: dict, model: str):
assert model not in cost_map


@pytest.mark.parametrize("model", RESPONSES_ONLY_MODELS)
def test_multi_agent_models_are_responses_only(cost_map: dict, model: str):
entry = cost_map[model]
assert entry["supported_endpoints"] == ["/v1/responses"]
assert entry["mode"] == "responses"
assert "/v1/chat/completions" not in entry["supported_endpoints"]


def test_surviving_xai_chat_models_still_serve_chat_completions(cost_map: dict):
"""Guard against the removal above over-reaching into live models."""
chat_models = [
key
for key, value in cost_map.items()
if isinstance(value, dict) and value.get("litellm_provider") == "xai" and value.get("mode") == "chat"
]
assert "xai/grok-4.3" in chat_models
assert "xai/grok-4.6" in chat_models
assert not any(key.startswith("xai/grok-2") for key in chat_models)


def test_both_cost_maps_agree_on_xai_entries():
prices = json.loads(PRICES_PATH.read_text(encoding="utf-8"))
backup = json.loads(BACKUP_PRICES_PATH.read_text(encoding="utf-8"))
xai_keys = {k for k, v in prices.items() if isinstance(v, dict) and v.get("litellm_provider") == "xai"}
assert xai_keys
assert {k: prices[k] for k in xai_keys} == {k: backup[k] for k in xai_keys}
53 changes: 53 additions & 0 deletions tests/test_litellm/test_anthropic_sonnet_1hr_cache_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,56 @@ def test_anthropic_sonnet_1hr_cache_write_pricing(
), f"{model_key}: long-context 1hr/5min ratio is {ratio_lc}, expected 1.6"
else:
assert "cache_creation_input_token_cost_above_1hr_above_200k_tokens" not in info


CLAUDE_3_EXPECTED = [
("claude-3-haiku-20240307", 5e-07),
("claude-3-opus-20240229", 3e-05),
]


@pytest.mark.parametrize("model_key, expected_1hr", CLAUDE_3_EXPECTED)
def test_claude_3_1hr_cache_write_pricing(model_data, model_key, expected_1hr):
"""Haiku 3 and Opus 3 both carried Sonnet's 6e-06 1hr rate, overbilling Haiku 3
1-hour cache writes 12x and underbilling Opus 3 5x."""
info = model_data[model_key]

assert info["cache_creation_input_token_cost_above_1hr"] == expected_1hr


@pytest.mark.parametrize("model_key, expected_1hr", CLAUDE_3_EXPECTED)
def test_backup_matches_main_for_claude_3_1hr_cache_write(model_key, expected_1hr):
json_path = os.path.join(
os.path.dirname(__file__),
"../../litellm/model_prices_and_context_window_backup.json",
)
with open(json_path) as f:
backup = json.load(f)

assert (
backup[model_key]["cache_creation_input_token_cost_above_1hr"] == expected_1hr
)


def test_first_party_anthropic_1hr_cache_writes_are_2x_base_input(model_data):
"""Anthropic charges 1-hour cache writes at 2x base input for every first-party
model, so any entry that drifts off that multiple is a copy-paste error."""
offenders = tuple(
(
model_key,
info["input_cost_per_token"],
info["cache_creation_input_token_cost_above_1hr"],
)
for model_key, info in model_data.items()
if isinstance(info, dict)
and info.get("litellm_provider") == "anthropic"
and info.get("input_cost_per_token")
and info.get("cache_creation_input_token_cost_above_1hr")
and abs(
info["cache_creation_input_token_cost_above_1hr"]
- 2 * info["input_cost_per_token"]
)
> 1e-12
)

assert offenders == (), f"1hr cache write is not 2x base input for: {offenders}"
86 changes: 86 additions & 0 deletions tests/test_litellm/test_fireworks_serverless_model_costs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Validate the Fireworks AI Serverless entry added for #37274 exists in
`model_prices_and_context_window.json` and that the bare Fireworks model ID
resolves through `get_model_info`.

Pricing as published at https://docs.fireworks.ai/serverless/pricing
(USD per 1M tokens, uncached input / cached input / output):

accounts/fireworks/models/deepseek-v4-pro-0813 -> $1.32 / $0.044 / $3.96
"""

import json
import os

import pytest

import litellm
from litellm.utils import get_model_info


@pytest.fixture(scope="module", autouse=True)
def _local_model_cost_map():
"""
Point litellm at the bundled cost map for the duration of this module
only. ``mp.undo()`` restores both the environment variable and
``litellm.model_cost`` so nothing leaks into later tests.
"""
mp = pytest.MonkeyPatch()
mp.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
mp.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
get_model_info.cache_clear()
yield
mp.undo()
get_model_info.cache_clear()


NEW_ENTRIES = {
"fireworks_ai/accounts/fireworks/models/deepseek-v4-pro-0813": {
"input_cost_per_token": 1.32e-06,
"cache_read_input_token_cost": 4.4e-08,
"output_cost_per_token": 3.96e-06,
"max_input_tokens": 1048576,
"max_output_tokens": 131072,
},
}


@pytest.fixture(scope="module")
def model_data():
json_path = os.path.join(
os.path.dirname(__file__), "../../model_prices_and_context_window.json"
)
with open(json_path) as f:
return json.load(f)


def test_fireworks_serverless_entries_exist(model_data):
"""The new prefixed entry carries the pricing and metadata from #37274."""
for key, expected in NEW_ENTRIES.items():
assert key in model_data, f"{key} is missing from model_prices_and_context_window.json"
entry = model_data[key]
for field, value in expected.items():
assert entry[field] == pytest.approx(value), f"{key}.{field}"
assert entry["litellm_provider"] == "fireworks_ai"
assert entry["mode"] == "chat"
assert entry["supports_function_calling"] is True
assert entry["supports_vision"] is False


def test_bare_fireworks_ids_resolve_through_prefixed_entries():
"""Bare IDs from #37274 resolve via the provider-prefix lookup path."""
for bare_id, prefixed_key in [
(
"accounts/fireworks/models/deepseek-v4-pro-0813",
"fireworks_ai/accounts/fireworks/models/deepseek-v4-pro-0813",
),
]:
info = get_model_info(model=bare_id, custom_llm_provider="fireworks_ai")
expected = NEW_ENTRIES[prefixed_key]
assert info.get("key") == prefixed_key
assert info["litellm_provider"] == "fireworks_ai"
assert info["input_cost_per_token"] == pytest.approx(expected["input_cost_per_token"])
assert info["cache_read_input_token_cost"] == pytest.approx(expected["cache_read_input_token_cost"])
assert info["output_cost_per_token"] == pytest.approx(expected["output_cost_per_token"])
assert info["max_input_tokens"] == expected["max_input_tokens"]
assert info["max_output_tokens"] == expected["max_output_tokens"]
12 changes: 6 additions & 6 deletions tests/test_litellm/test_together_ai_model_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
SERVERLESS_CHAT_MODELS: Final = (
"together_ai/moonshotai/Kimi-K3",
"together_ai/zai-org/GLM-5.2",
"together_ai/deepseek-ai/DeepSeek-V4-Pro",
"together_ai/deepseek-ai/DeepSeek-V4-Pro-0813",
"together_ai/deepseek-ai/DeepSeek-V4-Flash-0731",
"together_ai/moonshotai/Kimi-K2.7-Code",
"together_ai/MiniMaxAI/MiniMax-M3",
"together_ai/thinkingmachines/Inkling",
"together_ai/thinkingmachines/Inkling-Small",
Expand All @@ -27,20 +25,22 @@
"together_ai/Qwen/Qwen3.7-Plus",
"together_ai/Qwen/Qwen3.6-Plus",
"together_ai/Qwen/Qwen3.5-9B",
"together_ai/nvidia/nemotron-3-ultra-550b-a55b",
"together_ai/meta-models/Muse-Glimmer-30B",
"together_ai/google/gemma-4-31B-it",
"together_ai/pearl-ai/gemma-4-31b-it",
"together_ai/google/gemma-3n-E4B-it",
"together_ai/arize-ai/qwen-2-1.5b-instruct",
"together_ai/Prism-ML/Ternary-Bonsai-27B",
"together_ai/meta-llama/Llama-Guard-4-12B",
"together_ai/openai/gpt-oss-120b",
"together_ai/openai/gpt-oss-20b",
"together_ai/meta-llama/Llama-3.3-70B-Instruct-Turbo",
)

DEPRECATED_MODELS: Final = {
"together_ai/nvidia/nemotron-3-ultra-550b-a55b": "2026-08-27",
"together_ai/pearl-ai/gemma-4-31b-it": "2026-08-27",
"together_ai/deepseek-ai/DeepSeek-V4-Pro": "2026-08-27",
"together_ai/moonshotai/Kimi-K2.7-Code": "2026-08-27",
"together_ai/google/gemma-3n-E4B-it": "2026-08-25",
"together_ai/meta-llama/Llama-Guard-4-12B": "2026-08-25",
"together_ai/Qwen/Qwen3-235B-A22B-Instruct-2507-tput": "2026-07-10",
"together_ai/Qwen/Qwen3.5-397B-A17B": "2026-06-29",
"together_ai/Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": "2026-06-04",
Expand Down
Loading