Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7a8226e
fix(model_prices): registry audit 2026-09-02, add claude-mythos-5-1 a…
mateo-berri Sep 2, 2026
9c5b20a
fix(model_prices): add Nebius, watsonx and Volcengine models and corr…
mateo-berri Sep 2, 2026
71823af
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 2, 2026
60ffde6
fix(model_prices): drop unpriced Volcengine Seed 2.1 entries, they wo…
mateo-berri Sep 2, 2026
211f5d2
test(savings): update gpt-5.5 priority baseline to the published 2.5x…
mateo-berri Sep 2, 2026
671559e
fix(model_prices): set watsonx max_tokens equal to max_output_tokens …
mateo-berri Sep 2, 2026
e148868
fix(model_prices): set watsonx max_output_tokens from IBM's documente…
mateo-berri Sep 2, 2026
1a39275
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 3, 2026
55a5f14
fix(model_prices): add azure_ai Codestral-2501 and FW-Nemotron-Lightn…
mateo-berri Sep 3, 2026
f26407a
feat(registry): add azure_ai/MAI-Thinking-1 from Azure Retail Prices …
mateo-berri Sep 3, 2026
840173e
feat(registry): add azure_ai/mistral-ocr-4-0 page and annotation pric…
mateo-berri Sep 3, 2026
2c63095
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 3, 2026
32a3a65
fix(model_prices): add Lyria 3.5, Perplexity Agent API and OpenRouter…
mateo-berri Sep 3, 2026
2c4eb69
fix(model_prices): absorb Baseten GLM-5.3 and OpenRouter live prices,…
mateo-berri Sep 3, 2026
080e364
fix(registry): carry Anthropic thinking/sampling flags on new Perplex…
mateo-berri Sep 3, 2026
eae7b80
fix(registry): point Bedrock Qwen3 Coder 480B source at the us-west-2…
mateo-berri Sep 3, 2026
5a3a2f3
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 3, 2026
8d82f28
test(ocr): register the azure ocr4 mixed-rate cost test in the parity…
mateo-berri Sep 3, 2026
00bdfe7
fix(registry): mark gemini-3.5-live-translate-preview as realtime wit…
mateo-berri Sep 3, 2026
f292667
fix(registry): mark gpt-daybreak-*-latest as responses mode to match …
mateo-berri Sep 3, 2026
08bfdad
chore: merge litellm_internal_staging into litellm_registry_audit_202…
mateo-berri Sep 4, 2026
af4340d
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 4, 2026
8e83d6d
fix(model_prices): add Databricks Sep-2026 catalog, Azure gpt-realtim…
mateo-berri Sep 4, 2026
c707f2f
fix(model_prices): databricks gpt-5-3-codex is served via the Respons…
mateo-berri Sep 4, 2026
0c29f51
fix(registry): drop gpt-image-2 text output price, add openrouter min…
mateo-berri Sep 4, 2026
93abc3a
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 4, 2026
7b96a11
feat(registry): add OpenRouter catalog gaps, Fireworks DeepSeek V4 Fl…
mateo-berri Sep 4, 2026
1f0611a
fix(registry): drop Together MiniMax M2.7 and revert Qwen2.5 7B Turbo…
mateo-berri Sep 4, 2026
f5157a6
test: allow 128k and 256k tiered cache fields in registry schema test
mateo-berri Sep 4, 2026
50d6b26
fix(registry): mark baseten GLM-5.3 as vision-capable per Baseten vis…
mateo-berri Sep 4, 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
4,914 changes: 4,669 additions & 245 deletions litellm/model_prices_and_context_window_backup.json

Large diffs are not rendered by default.

4,914 changes: 4,669 additions & 245 deletions model_prices_and_context_window.json

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions model_prices_and_context_window.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
"minimum": 0,
"description": "USD per token written to the provider's prompt cache."
},
"cache_creation_input_token_cost_above_128k_tokens": {
"type": "number",
"minimum": 0,
"description": "Rate applied once the prompt exceeds the token threshold in the field name."
},
"cache_creation_input_token_cost_above_1hr": {
"type": "number",
"minimum": 0,
Expand All @@ -94,6 +99,11 @@
"minimum": 0,
"description": "Rate applied once the prompt exceeds the token threshold in the field name."
},
"cache_creation_input_token_cost_above_256k_tokens": {
"type": "number",
"minimum": 0,
"description": "Rate applied once the prompt exceeds the token threshold in the field name."
},
"cache_creation_input_token_cost_above_272k_tokens": {
"type": "number",
"minimum": 0,
Expand Down Expand Up @@ -128,6 +138,11 @@
"minimum": 0,
"description": "USD per prompt token served from the provider's prompt cache."
},
"cache_read_input_token_cost_above_128k_tokens": {
"type": "number",
"minimum": 0,
"description": "Rate applied once the prompt exceeds the token threshold in the field name."
},
"cache_read_input_token_cost_above_200k_tokens": {
"type": "number",
"minimum": 0,
Expand All @@ -138,6 +153,11 @@
"minimum": 0,
"description": "Priority service-tier rate for the same-named base field."
},
"cache_read_input_token_cost_above_256k_tokens": {
"type": "number",
"minimum": 0,
"description": "Rate applied once the prompt exceeds the token threshold in the field name."
},
"cache_read_input_token_cost_above_272k_tokens": {
"type": "number",
"minimum": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4630,6 +4630,28 @@ def test_generic_cost_per_token_grok_46_long_context(_local_model_cost_map):
assert completion_cost == pytest.approx(1_000 * 1.2e-05)


@pytest.mark.parametrize(
("model", "provider", "image_token_rate"),
[
("gpt-realtime-2.1", "openai", 5e-06),
("gpt-realtime-2.1-mini", "openai", 8e-07),
("azure/gpt-realtime-2.1", "azure", 5e-06),
("azure/gpt-realtime-2.1-mini", "azure", 8e-07),
],
)
def test_realtime_image_tokens_priced_per_token(model, provider, image_token_rate, _local_model_cost_map):
"""Realtime image input is billed per 1M image tokens, not per image."""
usage = Usage(
prompt_tokens=1_100,
completion_tokens=0,
total_tokens=1_100,
prompt_tokens_details=PromptTokensDetailsWrapper(text_tokens=100, image_tokens=1_000),
)
prompt_cost, _ = generic_cost_per_token(model=model, usage=usage, custom_llm_provider=provider)
text_rate = litellm.model_cost[model]["input_cost_per_token"]
assert prompt_cost == pytest.approx(100 * text_rate + 1_000 * image_token_rate)


@pytest.mark.parametrize(
("response_quality", "requested_quality", "expected_cost"),
[
Expand Down
52 changes: 51 additions & 1 deletion tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import pytest


from litellm.litellm_core_utils.fallback_generalizations import (
get_fallback_generalization_rules,
match_capability_generalizations,
Expand Down Expand Up @@ -248,6 +247,57 @@ def test_azure_ai_claude_1m_context_entries(cost_map: dict):
assert cost_map[model]["max_input_tokens"] == 200000, model


# OpenRouter headline rates from GET https://openrouter.ai/api/v1/models.
# These were the catalog values that disagreed with that API (and, for the
# two spotlight models, the public model pages that their source fields cite).
_OPENROUTER_LIVE_COSTS = {
"openrouter/qwen/qwen3.5-plus-02-15": (2.6e-07, 1.56e-06, None),
"openrouter/openai/gpt-oss-120b": (3.7e-08, 1.7e-07, None),
"openrouter/qwen/qwen3-coder-plus": (6.5e-07, 3.25e-06, None),
"openrouter/qwen/qwen3.5-flash-02-23": (6.5e-08, 2.6e-07, None),
"openrouter/qwen/qwen3.5-27b": (1.95e-07, 1.56e-06, None),
"openrouter/gryphe/mythomax-l2-13b": (6e-08, 6e-08, None),
"openrouter/mancer/weaver": (4e-07, 7.5e-07, None),
"openrouter/xiaomi/mimo-v2.5-pro": (4.35e-07, 8.7e-07, 3.6e-09),
"openrouter/moonshotai/kimi-k2.5": (4.5e-07, 2.25e-06, 7e-08),
"openrouter/z-ai/glm-5": (6e-07, 1.92e-06, None),
}

_OPENROUTER_STALE_COSTS = {
"openrouter/qwen/qwen3.5-plus-02-15": (4e-07, 2.4e-06),
"openrouter/openai/gpt-oss-120b": (1.8e-07, 8e-07),
"openrouter/gryphe/mythomax-l2-13b": (1.875e-06, 1.875e-06),
}


@pytest.mark.parametrize(
"cost_map",
[_load_root_cost_map(), GetModelCostMap.load_local_model_cost_map()],
ids=["root", "bundled_backup"],
)
def test_openrouter_catalog_costs_match_live_headline_rates(cost_map: dict):
"""openrouter/* spend tracking reads these catalog fields. The values must
stay aligned with OpenRouter's published headline rate, not the stale
figures that over/under-counted by up to 30x. Both maps are checked so
the root file and bundled backup cannot drift apart."""
control = cost_map["openrouter/anthropic/claude-opus-5"]
assert control["input_cost_per_token"] == 5e-06
assert control["output_cost_per_token"] == 2.5e-05
assert control["cache_read_input_token_cost"] == 5e-07

for model, (inp, out, cache) in _OPENROUTER_LIVE_COSTS.items():
entry = cost_map[model]
assert entry["input_cost_per_token"] == inp, model
assert entry["output_cost_per_token"] == out, model
if cache is not None:
assert entry["cache_read_input_token_cost"] == cache, model

for model, (stale_in, stale_out) in _OPENROUTER_STALE_COSTS.items():
entry = cost_map[model]
assert entry["input_cost_per_token"] != stale_in, model
assert entry["output_cost_per_token"] != stale_out, model


def test_get_model_cost_map_stamps_loaded_at(monkeypatch):
"""The load time feeds each pod's reload-due decision; a load that does not stamp it
would make manual reload requests race the proxy's startup"""
Expand Down
29 changes: 29 additions & 0 deletions tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,32 @@ def test_service_tier_flex_lower_cost(self):

assert flex_prompt < standard_prompt
assert flex_completion < standard_completion


def test_codestral_2501_model_info_and_cost(local_model_cost_map):
model_info = get_model_info(model="Codestral-2501", custom_llm_provider="azure_ai")
usage = Usage(prompt_tokens=1_000_000, completion_tokens=1_000_000, total_tokens=2_000_000)

prompt_cost, completion_cost = cost_per_token(model="Codestral-2501", usage=usage)

assert model_info["mode"] == "chat"
assert model_info["max_input_tokens"] == 256000
assert model_info["max_output_tokens"] == 4096
assert prompt_cost == pytest.approx(0.3)
assert completion_cost == pytest.approx(0.9)


def test_mai_thinking_1_model_info_and_cost(local_model_cost_map):
model_info = get_model_info(model="MAI-Thinking-1", custom_llm_provider="azure_ai")
usage = Usage(prompt_tokens=1_000_000, completion_tokens=1_000_000, total_tokens=2_000_000)

prompt_cost, completion_cost = cost_per_token(model="MAI-Thinking-1", usage=usage)

assert model_info["mode"] == "chat"
assert model_info["max_input_tokens"] == 256000
assert model_info["max_output_tokens"] == 64000
assert model_info["cache_read_input_token_cost"] == pytest.approx(2e-07)
assert model_info["supports_reasoning"] is True
assert model_info["supports_function_calling"] is True
assert prompt_cost == pytest.approx(2.0)
assert completion_cost == pytest.approx(8.0)
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def test_azure_ai_fw_model_info(use_local_model_cost_map, model_key, expected):
("FW-MiniMax-M2.5", 0.33, 1.32),
("FW-Inkling", 1.0, 4.05),
("FW-Nemotron-3-Ultra-NVFP4", 0.6, 2.4),
("FW-Nemotron-Lightning-3.5-30B-A3B", 0.06, 0.22),
],
)
def test_azure_ai_fw_cost_per_token(
Expand All @@ -196,6 +197,30 @@ def test_azure_ai_fw_cost_per_token(
assert completion_cost == pytest.approx(expected_completion)


def test_azure_ai_fw_nemotron_lightning_model_info(use_local_model_cost_map):
model_info = use_local_model_cost_map.get_model_info(model="azure_ai/FW-Nemotron-Lightning-3.5-30B-A3B")

assert model_info["litellm_provider"] == "azure_ai"
assert model_info["mode"] == "chat"
assert model_info["input_cost_per_token"] == pytest.approx(6e-08)
assert model_info["output_cost_per_token"] == pytest.approx(2.2e-07)
assert model_info["cache_read_input_token_cost"] == pytest.approx(1e-08)
assert model_info["max_input_tokens"] == 262144
assert model_info["supports_function_calling"] is True
assert model_info["supports_reasoning"] is True
assert model_info["supports_tool_choice"] is True
assert model_info["supports_prompt_caching"] is True
assert model_info["supports_vision"] is False


def test_azure_ai_fw_nemotron_lightning_supports_tool_choice(use_local_model_cost_map):
from litellm.llms.azure_ai.chat.transformation import AzureAIStudioConfig

supported_params = AzureAIStudioConfig().get_supported_openai_params("FW-Nemotron-Lightning-3.5-30B-A3B")

assert "tool_choice" in supported_params


def test_azure_ai_fw_kimi_k26_case_insensitive_lookup(use_local_model_cost_map):
upper = use_local_model_cost_map.get_model_info(model="azure_ai/FW-Kimi-K2.6")
lower = use_local_model_cost_map.get_model_info(model="azure_ai/fw-kimi-k2.6")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"databricks/databricks-claude-opus-5",
"databricks/databricks-claude-sonnet-5",
"databricks/databricks-claude-fable-5",
"databricks/databricks-claude-fable-5-1",
"databricks/databricks-gpt-5-6-sol",
"databricks/databricks-gpt-5-6-terra",
"databricks/databricks-gpt-5-6-luna",
)

DOLLARS_PER_DBU: Final = Decimal("0.070")
Expand All @@ -28,6 +32,7 @@
"cache_read_input_token_cost",
)
PUBLISHED_DBU_PER_MILLION: Final = {
"databricks/databricks-claude-fable-5-1": ("142.858", "714.286", "178.572", "3.572"),
"databricks/databricks-claude-fable-5": ("142.858", "714.286", "178.572", "14.286"),
"databricks/databricks-claude-opus-5": ("71.429", "357.143", "89.286", "7.143"),
"databricks/databricks-claude-opus-4-8": ("71.429", "357.143", "89.286", "7.143"),
Expand All @@ -52,9 +57,17 @@
"databricks/databricks-gpt-5-2": ("25.000", "200.000", "25.000", "2.500"),
"databricks/databricks-gpt-5-2-codex": ("25.000", "200.000", "25.000", "2.500"),
"databricks/databricks-gpt-5-3-codex": ("25.000", "200.000", "25.000", "2.500"),
"databricks/databricks-gpt-5-6-sol": ("57.143", "285.714", "71.429", "5.714"),
"databricks/databricks-gpt-5-6-terra": ("35.714", "214.286", "44.643", "3.571"),
"databricks/databricks-gpt-5-6-luna": ("14.286", "85.714", "17.857", "1.429"),
"databricks/databricks-gpt-5-5": ("71.429", "428.571", "71.429", "7.143"),
"databricks/databricks-gpt-5-5-pro": ("428.571", "2571.429", "428.571", "428.571"),
"databricks/databricks-gpt-5-4": ("35.714", "214.286", "35.714", "3.571"),
"databricks/databricks-gpt-5-4-mini": ("10.714", "64.286", "10.714", "1.071"),
"databricks/databricks-gpt-5-4-nano": ("2.857", "17.857", "2.857", "0.286"),
"databricks/databricks-gemini-3-6-flash": ("26.786", "133.929", "26.786", "2.679"),
"databricks/databricks-gemini-3-5-flash": ("26.786", "160.714", "26.786", "2.679"),
"databricks/databricks-gemini-3-5-flash-lite": ("5.357", "44.643", "5.357", "0.536"),
"databricks/databricks-gemini-3-1-pro": ("35.714", "214.286", "35.714", "3.571"),
"databricks/databricks-gemini-3-pro": ("35.714", "214.286", "35.714", "3.571"),
"databricks/databricks-gemini-3-flash": ("8.929", "53.571", "8.929", "0.893"),
Expand All @@ -65,6 +78,13 @@
"databricks/databricks-deepseek-v4-flash-0731": ("2.000", "4.000", "2.000", "0.400"),
"databricks/databricks-deepseek-v4-pro-0813": ("18.857", "56.571", "18.857", "1.886"),
"databricks/databricks-glm-5-2": ("20.000", "62.857", "20.000", "3.714"),
"databricks/databricks-glm-5-3": ("20.000", "62.857", "20.000", "3.714"),
"databricks/databricks-glm-5-3-flash": ("2.143", "7.143", "2.143", "0.429"),
"databricks/databricks-inkling": ("14.286", "57.857", "14.286", "2.429"),
"databricks/databricks-grok-4-6": ("35.714", "107.143", "35.714", "8.929"),
"databricks/databricks-qwen35-122b-a10b": ("3.143", "31.429", "3.143", "3.143"),
"databricks/databricks-qwen3-next-80b-a3b-instruct": ("2.143", "17.143", "2.143", "2.143"),
"databricks/databricks-qwen3-embedding-0-6b": ("0.286", "0", "0.286", "0.286"),
}
PROMOTIONAL_DISCOUNT: Final = 0.80
PROMOTION_EXPIRES: Final = "2027-01-31"
Expand All @@ -73,6 +93,10 @@
"databricks/databricks-gemini-2-5-flash",
)
ENTRIES_STORING_LIST_RATE_DESPITE_PROMOTION: Final = (
"databricks/databricks-gemini-3-6-flash",
"databricks/databricks-gemini-3-5-flash",
"databricks/databricks-gemini-3-5-flash-lite",
"databricks/databricks-grok-4-6",
"databricks/databricks-gemini-3-1-pro",
"databricks/databricks-gemini-3-pro",
"databricks/databricks-gemini-3-flash",
Expand Down
14 changes: 14 additions & 0 deletions tests/test_litellm/llms/mistral/ocr/test_mistral_ocr_cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from litellm.llms.base_llm.ocr.transformation import OCRPage, OCRResponse, OCRUsageInfo

OCR4_COST_PER_PAGE = 0.004
OCR4_ANNOTATION_COST_PER_PAGE = 0.005

REPO_ROOT = Path(__file__).parents[5]
MAIN_COST_MAP = REPO_ROOT / "model_prices_and_context_window.json"
Expand Down Expand Up @@ -133,3 +134,16 @@ def test_azure_doc_ai_annotation_pages_fall_back_to_ocr_rate(local_model_cost_ma
call_type="ocr",
)
assert cost == pytest.approx(AZURE_DOC_AI_COST_PER_PAGE)


def test_azure_ocr4_bills_ocr_and_annotation_pages_at_their_own_rates(local_model_cost_map) -> None:
info = litellm.get_model_info(model="azure_ai/mistral-ocr-4-0", custom_llm_provider="azure_ai")
assert info["ocr_cost_per_page"] == OCR4_COST_PER_PAGE
assert info["annotation_cost_per_page"] == OCR4_ANNOTATION_COST_PER_PAGE
cost = completion_cost(
completion_response=_annotated_ocr_response("mistral-ocr-4-0", 2, 3),
model="azure_ai/mistral-ocr-4-0",
custom_llm_provider="azure_ai",
call_type="ocr",
)
assert cost == pytest.approx(2 * OCR4_COST_PER_PAGE + 3 * OCR4_ANNOTATION_COST_PER_PAGE)
5 changes: 3 additions & 2 deletions tests/test_litellm/proxy/spend_tracking/test_savings.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ def test_the_served_arm_is_read_from_the_record_not_repriced():
@pytest.mark.parametrize(
"basis, expected_multiplier",
[
pytest.param({"service_tier": "priority"}, 2.0, id="priority tier doubles the baseline"),
pytest.param({"service_tier": "priority"}, 2.5, id="priority tier uplifts the baseline"),
pytest.param({"data_residency": "eu"}, 1.1, id="eu residency uplifts the baseline"),
pytest.param({}, 1.0, id="no basis recorded prices at standard"),
pytest.param(None, 1.0, id="row predating the field prices at standard"),
Expand All @@ -872,7 +872,8 @@ def test_the_baseline_is_priced_on_the_basis_the_request_was_billed_at(basis, ex
"""
gpt = litellm.get_model_info("gpt-5.5", "openai")
haiku = litellm.get_model_info("claude-haiku-4-5", "anthropic")
assert gpt.get("input_cost_per_token_priority") == 2 * gpt["input_cost_per_token"]
assert gpt.get("input_cost_per_token_priority") == pytest.approx(2.5 * gpt["input_cost_per_token"])
assert gpt.get("output_cost_per_token_priority") == pytest.approx(2.5 * gpt["output_cost_per_token"])
assert gpt.get("regional_processing_uplift_multiplier_eu") == 1.1
assert haiku.get("input_cost_per_token_priority") is None, "served model must not move with the basis"
assert haiku.get("regional_processing_uplift_multiplier_eu") is None
Expand Down
Loading
Loading