mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(cost): add missing cache_read_input_token_cost for DeepSeek models
Several DeepSeek and OpenRouter/DeepSeek models had cache pricing data under input_cost_per_token_cache_hit but not under the key the cost calculator actually uses (cache_read_input_token_cost). This caused cost_breakdown to omit cache_read_cost for these models. Also adds cache_creation_input_token_cost=0.0 for all DeepSeek provider models since DeepSeek auto-caches with no creation cost. Closes #31594 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
70eb4e5d00
commit
8d3c273d55
4 changed files with 144 additions and 0 deletions
|
|
@ -11255,6 +11255,7 @@
|
|||
]
|
||||
},
|
||||
"deepseek-chat": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"litellm_provider": "deepseek",
|
||||
|
|
@ -11276,6 +11277,7 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek-reasoner": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"litellm_provider": "deepseek",
|
||||
|
|
@ -13806,6 +13808,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-coder": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -13820,6 +13824,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-r1": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -13835,6 +13841,7 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-reasoner": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"input_cost_per_token_cache_hit": 2.8e-08,
|
||||
|
|
@ -13875,6 +13882,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-v3.2": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -28324,6 +28333,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",
|
||||
|
|
@ -28339,6 +28349,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",
|
||||
|
|
@ -28354,6 +28365,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",
|
||||
|
|
@ -28369,6 +28381,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",
|
||||
|
|
@ -28384,6 +28397,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",
|
||||
|
|
|
|||
|
|
@ -11255,6 +11255,7 @@
|
|||
]
|
||||
},
|
||||
"deepseek-chat": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"litellm_provider": "deepseek",
|
||||
|
|
@ -11276,6 +11277,7 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek-reasoner": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"litellm_provider": "deepseek",
|
||||
|
|
@ -13806,6 +13808,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-coder": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -13820,6 +13824,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-r1": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -13835,6 +13841,7 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-reasoner": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
"input_cost_per_token": 2.8e-07,
|
||||
"input_cost_per_token_cache_hit": 2.8e-08,
|
||||
|
|
@ -13875,6 +13882,8 @@
|
|||
"supports_tool_choice": true
|
||||
},
|
||||
"deepseek/deepseek-v3.2": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"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",
|
||||
|
|
@ -28485,6 +28494,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",
|
||||
|
|
@ -28500,6 +28510,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",
|
||||
|
|
@ -28515,6 +28526,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",
|
||||
|
|
@ -28530,6 +28542,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",
|
||||
|
|
@ -28545,6 +28558,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",
|
||||
|
|
|
|||
|
|
@ -2038,3 +2038,43 @@ def test_token_type_cost_breakdown_applies_regional_uplift():
|
|||
text_input_cost = 600 * model_info["input_cost_per_token"] * uplift
|
||||
assert text_output_cost + eu.reasoning_cost == pytest.approx(completion_cost)
|
||||
assert text_input_cost + eu.cache_read_cost == pytest.approx(prompt_cost)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"model,custom_llm_provider,expected_cache_read_rate",
|
||||
[
|
||||
("deepseek-chat", "deepseek", 2.8e-08),
|
||||
("deepseek/deepseek-r1", "deepseek", 1.4e-07),
|
||||
("deepseek/deepseek-v3.2", "deepseek", 2.8e-08),
|
||||
("deepseek/deepseek-coder", "deepseek", 1.4e-08),
|
||||
],
|
||||
)
|
||||
def test_deepseek_cache_read_cost_in_breakdown(
|
||||
model, custom_llm_provider, expected_cache_read_rate
|
||||
):
|
||||
"""
|
||||
DeepSeek models report cached tokens via prompt_cache_hit_tokens. The
|
||||
cost breakdown must produce a non-zero cache_read_cost matching the
|
||||
model's cache_read_input_token_cost rate.
|
||||
|
||||
Regression for https://github.com/BerriAI/litellm/issues/31594
|
||||
"""
|
||||
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
|
||||
litellm.model_cost = litellm.get_model_cost_map(url="")
|
||||
|
||||
cache_hit_tokens = 64
|
||||
usage = Usage(
|
||||
prompt_tokens=100,
|
||||
completion_tokens=50,
|
||||
total_tokens=150,
|
||||
prompt_cache_hit_tokens=cache_hit_tokens,
|
||||
prompt_cache_miss_tokens=36,
|
||||
)
|
||||
|
||||
breakdown = get_token_type_cost_breakdown(
|
||||
model=model, custom_llm_provider=custom_llm_provider, usage=usage
|
||||
)
|
||||
|
||||
assert breakdown.cache_read_cost == pytest.approx(
|
||||
cache_hit_tokens * expected_cache_read_rate
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3176,3 +3176,79 @@ def test_completion_cost_logs_reasoning_and_cache_breakdown():
|
|||
assert logging_obj.cost_breakdown is not None
|
||||
assert logging_obj.cost_breakdown["reasoning_cost"] == pytest.approx(3114 * 2.5e-06)
|
||||
assert logging_obj.cost_breakdown["cache_read_cost"] == pytest.approx(100 * 3e-08)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"model,custom_llm_provider,cache_read_rate",
|
||||
[
|
||||
("deepseek-chat", "deepseek", 2.8e-08),
|
||||
("deepseek/deepseek-r1", "deepseek", 1.4e-07),
|
||||
("deepseek/deepseek-v3.2", "deepseek", 2.8e-08),
|
||||
("deepseek/deepseek-coder", "deepseek", 1.4e-08),
|
||||
],
|
||||
)
|
||||
def test_deepseek_cost_breakdown_includes_cache_read_cost(
|
||||
model, custom_llm_provider, cache_read_rate
|
||||
):
|
||||
"""
|
||||
DeepSeek reports cached tokens via prompt_cache_hit_tokens. The cost
|
||||
breakdown must surface cache_read_cost so spend logs show where cache
|
||||
savings come from, not just a lower total.
|
||||
|
||||
Regression for https://github.com/BerriAI/litellm/issues/31594
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging
|
||||
from litellm.types.utils import Choices, Message
|
||||
|
||||
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
|
||||
litellm.model_cost = litellm.get_model_cost_map(url="")
|
||||
|
||||
cache_hit_tokens = 64
|
||||
|
||||
logging_obj = Logging(
|
||||
model=model,
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=False,
|
||||
call_type="completion",
|
||||
start_time=datetime.now(),
|
||||
litellm_call_id="deepseek-cache-breakdown",
|
||||
function_id="f",
|
||||
)
|
||||
|
||||
response = ModelResponse(
|
||||
id="x",
|
||||
created=1,
|
||||
model=model,
|
||||
object="chat.completion",
|
||||
choices=[
|
||||
Choices(
|
||||
index=0,
|
||||
message=Message(role="assistant", content="hi"),
|
||||
finish_reason="stop",
|
||||
)
|
||||
],
|
||||
usage=Usage(
|
||||
prompt_tokens=100,
|
||||
completion_tokens=50,
|
||||
total_tokens=150,
|
||||
prompt_cache_hit_tokens=cache_hit_tokens,
|
||||
prompt_cache_miss_tokens=36,
|
||||
),
|
||||
)
|
||||
|
||||
litellm.completion_cost(
|
||||
completion_response=response,
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
litellm_logging_obj=logging_obj,
|
||||
)
|
||||
|
||||
assert logging_obj.cost_breakdown is not None
|
||||
assert "cache_read_cost" in logging_obj.cost_breakdown, (
|
||||
f"cache_read_cost missing from cost_breakdown for {model}"
|
||||
)
|
||||
assert logging_obj.cost_breakdown["cache_read_cost"] == pytest.approx(
|
||||
cache_hit_tokens * cache_read_rate
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue