This commit is contained in:
kejunleng 2026-08-27 16:00:17 -05:00 committed by GitHub
commit acf1bdc4b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 126 additions and 0 deletions

View file

@ -16966,6 +16966,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-coder": {
"cache_read_input_token_cost": 1.4e-08,
"input_cost_per_token": 1.4e-07,
"input_cost_per_token_cache_hit": 1.4e-08,
"litellm_provider": "deepseek",
@ -16980,6 +16981,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-r1": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5.5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "deepseek",
@ -17035,6 +17037,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-v3.2": {
"cache_read_input_token_cost": 2.8e-08,
"input_cost_per_token": 2.8e-07,
"input_cost_per_token_cache_hit": 2.8e-08,
"litellm_provider": "deepseek",
@ -34446,6 +34449,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-chat-v3.1": {
"cache_read_input_token_cost": 2e-08,
"input_cost_per_token": 2e-07,
"input_cost_per_token_cache_hit": 2e-08,
"litellm_provider": "openrouter",
@ -34461,6 +34465,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-v3.2": {
"cache_read_input_token_cost": 2.8e-08,
"input_cost_per_token": 2.8e-07,
"input_cost_per_token_cache_hit": 2.8e-08,
"litellm_provider": "openrouter",
@ -34476,6 +34481,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-v3.2-exp": {
"cache_read_input_token_cost": 2e-08,
"input_cost_per_token": 2e-07,
"input_cost_per_token_cache_hit": 2e-08,
"litellm_provider": "openrouter",
@ -34491,6 +34497,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-r1": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5.5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "openrouter",
@ -34506,6 +34513,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-r1-0528": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "openrouter",

View file

@ -16966,6 +16966,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-coder": {
"cache_read_input_token_cost": 1.4e-08,
"input_cost_per_token": 1.4e-07,
"input_cost_per_token_cache_hit": 1.4e-08,
"litellm_provider": "deepseek",
@ -16980,6 +16981,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-r1": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5.5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "deepseek",
@ -17035,6 +17037,7 @@
"supports_tool_choice": true
},
"deepseek/deepseek-v3.2": {
"cache_read_input_token_cost": 2.8e-08,
"input_cost_per_token": 2.8e-07,
"input_cost_per_token_cache_hit": 2.8e-08,
"litellm_provider": "deepseek",
@ -34446,6 +34449,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-chat-v3.1": {
"cache_read_input_token_cost": 2e-08,
"input_cost_per_token": 2e-07,
"input_cost_per_token_cache_hit": 2e-08,
"litellm_provider": "openrouter",
@ -34461,6 +34465,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-v3.2": {
"cache_read_input_token_cost": 2.8e-08,
"input_cost_per_token": 2.8e-07,
"input_cost_per_token_cache_hit": 2.8e-08,
"litellm_provider": "openrouter",
@ -34476,6 +34481,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-v3.2-exp": {
"cache_read_input_token_cost": 2e-08,
"input_cost_per_token": 2e-07,
"input_cost_per_token_cache_hit": 2e-08,
"litellm_provider": "openrouter",
@ -34491,6 +34497,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-r1": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5.5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "openrouter",
@ -34506,6 +34513,7 @@
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-r1-0528": {
"cache_read_input_token_cost": 1.4e-07,
"input_cost_per_token": 5e-07,
"input_cost_per_token_cache_hit": 1.4e-07,
"litellm_provider": "openrouter",

View file

@ -0,0 +1,110 @@
"""
Regression test for DeepSeek and OpenRouter/DeepSeek models that historically
only declared ``input_cost_per_token_cache_hit`` in the pricing JSON.
The cost calculator (``litellm.litellm_core_utils.llm_cost_calc.utils``) only
reads ``cache_read_input_token_cost`` for prompt-cache-hit pricing. When a
model defines ``input_cost_per_token_cache_hit`` without the canonical key,
cache-hit tokens are billed at $0 because the cache-read cost resolves to
``None`` -> ``0.0``.
This test ensures the two keys are kept in sync for the affected models and
that a representative model actually bills cache-hit tokens at the cache rate.
"""
import os
import sys
import pytest
sys.path.insert(0, os.path.abspath("../.."))
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import generic_cost_per_token
from litellm.types.utils import PromptTokensDetailsWrapper, Usage
# Models that previously only carried ``input_cost_per_token_cache_hit``.
# Each must also carry ``cache_read_input_token_cost`` with an identical value.
DEEPSEEK_CACHE_HIT_MODELS = [
"deepseek/deepseek-coder",
"deepseek/deepseek-r1",
"deepseek/deepseek-v3.2",
"openrouter/deepseek/deepseek-chat-v3.1",
"openrouter/deepseek/deepseek-v3.2",
"openrouter/deepseek/deepseek-v3.2-exp",
"openrouter/deepseek/deepseek-r1",
"openrouter/deepseek/deepseek-r1-0528",
]
@pytest.fixture(autouse=True)
def _use_local_model_cost_map(monkeypatch):
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
litellm.get_model_info.cache_clear()
yield
litellm.get_model_info.cache_clear()
@pytest.mark.parametrize("model", DEEPSEEK_CACHE_HIT_MODELS)
def test_cache_read_input_token_cost_present(model):
"""``cache_read_input_token_cost`` must be present and equal to the legacy
``input_cost_per_token_cache_hit`` field."""
info = litellm.model_cost[model]
legacy = info.get("input_cost_per_token_cache_hit")
canonical = info.get("cache_read_input_token_cost")
assert legacy is not None, f"{model} is missing input_cost_per_token_cache_hit"
assert canonical is not None, (
f"{model} is missing cache_read_input_token_cost; cache-hit tokens would "
f"be billed at $0 because the cost calculator only reads the canonical key."
)
assert canonical == legacy, (
f"{model} cache_read_input_token_cost ({canonical}) must match input_cost_per_token_cache_hit ({legacy})"
)
def test_deepseek_r1_cache_hit_billed_at_cache_rate():
"""End-to-end check that a cache hit on deepseek/deepseek-r1 is billed at
the cache-read rate instead of the regular input rate."""
model = "deepseek/deepseek-r1"
info = litellm.model_cost[model]
prompt_tokens = 1000
cached_tokens = 800
text_tokens = prompt_tokens - cached_tokens
completion_tokens = 100
usage = Usage(
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
total_tokens=prompt_tokens + completion_tokens,
prompt_tokens_details=PromptTokensDetailsWrapper(
cached_tokens=cached_tokens,
text_tokens=text_tokens,
),
)
input_cost, output_cost = generic_cost_per_token(
model=model,
usage=usage,
custom_llm_provider="deepseek",
)
expected_input_cost = (
info["input_cost_per_token"] * text_tokens + info["cache_read_input_token_cost"] * cached_tokens
)
expected_output_cost = info["output_cost_per_token"] * completion_tokens
assert abs(input_cost - expected_input_cost) < 1e-12, (
f"input cost mismatch: got {input_cost}, expected {expected_input_cost}"
)
assert abs(output_cost - expected_output_cost) < 1e-12, (
f"output cost mismatch: got {output_cost}, expected {expected_output_cost}"
)
# Sanity check: regression scenario (cache_read_input_token_cost missing)
# would have billed cached tokens at the full input rate.
naive_full_input_cost = info["input_cost_per_token"] * prompt_tokens
assert input_cost < naive_full_input_cost, (
"cache-hit tokens were not discounted; cache_read_input_token_cost is likely missing from this model entry."
)