fix(types): map nested prompt_tokens_details.cache_creation_input_tokens to cache_write_tokens

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Devin AI 2026-08-18 22:40:27 +00:00
parent d9e9270574
commit 645b87fae1
3 changed files with 74 additions and 1 deletions

View file

@ -1627,8 +1627,17 @@ class PromptTokensDetailsWrapper(
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
nested_cache_creation_input_tokens: Final = (self.model_extra or {}).get("cache_creation_input_tokens")
self.cache_write_tokens = (
self.cache_write_tokens if self.cache_write_tokens is not None else self.cache_creation_tokens
self.cache_write_tokens
if self.cache_write_tokens is not None
else (
self.cache_creation_tokens
if self.cache_creation_tokens is not None
else (
nested_cache_creation_input_tokens if isinstance(nested_cache_creation_input_tokens, int) else None
)
)
)
if self.character_count is None:
del self.character_count

View file

@ -271,6 +271,47 @@ class TestDashscopeCostCalculator:
assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-10)
def test_dashscope_nested_cache_creation_input_tokens_bill_at_cache_write_rate(self):
"""
Regression (LIT-5757): DashScope nests cache_creation_input_tokens inside
prompt_tokens_details; those tokens must bill at the tier's cache-creation
rate instead of being folded into text tokens at the input rate.
"""
self._register_tiered_model(
"dashscope/qwen-nested-cache-write-test",
[
{
"range": [0, 128000],
"input_cost_per_token": 4e-07,
"cache_read_input_token_cost": 1.6e-07,
"cache_creation_input_token_cost": 5e-07,
"output_cost_per_token": 1.6e-06,
}
],
)
usage = Usage(
prompt_tokens=2059,
completion_tokens=201,
total_tokens=2260,
prompt_tokens_details={
"cached_tokens": 0,
"text_tokens": 2059,
"cache_type": "ephemeral",
"cache_creation_input_tokens": 2048,
"cache_creation": {"ephemeral_5m_input_tokens": 2048},
},
completion_tokens_details={"reasoning_tokens": 170},
)
prompt_cost, _ = dashscope_cost_per_token(
model="qwen-nested-cache-write-test", usage=usage
)
assert math.isclose(
prompt_cost, (2048 * 5e-07) + (11 * 4e-07), rel_tol=1e-10
)
def test_dashscope_tiered_cache_creation_falls_back_to_tier_input_rate(self):
"""
Tiers without a cache_creation_input_token_cost bill cache-creation tokens at

View file

@ -75,6 +75,29 @@ def test_usage_dump():
assert new_usage.prompt_tokens_details.web_search_requests == 1
def test_prompt_tokens_details_maps_nested_cache_creation_input_tokens():
"""Regression (LIT-5757): DashScope nests the Anthropic-spelled
cache_creation_input_tokens inside prompt_tokens_details. It must populate
the canonical cache_write_tokens/cache_creation_tokens pair, without
overriding an explicitly provided canonical value."""
from litellm.types.utils import PromptTokensDetailsWrapper
nested = PromptTokensDetailsWrapper(
cached_tokens=0, text_tokens=2059, cache_creation_input_tokens=2048
)
assert nested.cache_write_tokens == 2048
assert nested.cache_creation_tokens == 2048
explicit = PromptTokensDetailsWrapper(
cache_write_tokens=100, cache_creation_input_tokens=2048
)
assert explicit.cache_write_tokens == 100
assert explicit.cache_creation_tokens == 100
non_int = PromptTokensDetailsWrapper(cache_creation_input_tokens=None)
assert not hasattr(non_int, "cache_write_tokens")
def test_usage_server_tool_use_dict_is_coerced_and_round_trips():
from litellm.types.utils import ServerToolUse, Usage