From e44128c68c241c46afbabcfc906cdc12ff3e4381 Mon Sep 17 00:00:00 2001 From: drexkooo Date: Tue, 17 Mar 2026 02:25:53 +0100 Subject: [PATCH] fix: map OpenRouter prompt_tokens_details to Anthropic cache fields in Usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When routing Anthropic models through OpenRouter, cache token counts arrive in the final streaming chunk as prompt_tokens_details.cached_tokens and prompt_tokens_details.cache_write_tokens (OpenAI format). The Usage model was not populating its Anthropic-style private fields (_cache_read_input_tokens, _cache_creation_input_tokens) from these values, so cache tokens always showed as 0 in cost calculations, logging, and downstream clients. Changes: - Usage.__init__: after Anthropic and DeepSeek mappings, add an OpenRouter/ OpenAI-format mapping block that reads prompt_tokens_details.cached_tokens → _cache_read_input_tokens and prompt_tokens_details.cache_write_tokens → _cache_creation_input_tokens when the Anthropic native params are absent. - PromptTokensDetailsWrapper: add explicit cache_write_tokens field (Optional[int]) to properly type the OpenRouter extension field that carries cache write counts. This fixes the Anthropic experimental pass-through adapter when using OpenRouter as the backend: the streaming adapter already has the merge logic in streaming_iterator.py, but it had no data to merge because _cache_*_input_tokens was never populated from prompt_tokens_details. Made-with: Cursor --- litellm/types/utils.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 38425c7ac4a..763480c1f28 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -1467,6 +1467,9 @@ class PromptTokensDetailsWrapper( image_count: Optional[int] = None """Number of images sent to the model. Used for Vertex AI multimodal embeddings.""" + cache_write_tokens: Optional[int] = None + """Tokens written to the prompt cache in this request (OpenRouter extension for Anthropic models).""" + video_length_seconds: Optional[float] = None """Length of videos sent to the model. Used for Vertex AI multimodal embeddings.""" @@ -1654,6 +1657,24 @@ class Usage(SafeAttributeModel, CompletionUsage): ): self._cache_read_input_tokens = params["prompt_cache_hit_tokens"] + ## OPENROUTER / OPENAI FORMAT MAPPING ## + # OpenRouter (and other OpenAI-compatible providers) return cache token counts + # in prompt_tokens_details rather than as top-level Anthropic fields. + # Populate the private Anthropic-style fields from prompt_tokens_details when + # the explicit Anthropic params (cache_creation_input_tokens, + # cache_read_input_tokens) were not provided, so that downstream consumers + # (cost calculators, streaming adapters, logging hooks) see the correct values. + _ptd = getattr(self, "prompt_tokens_details", None) + if _ptd is not None: + if not self._cache_read_input_tokens: + _cached = getattr(_ptd, "cached_tokens", 0) or 0 + if _cached > 0: + self._cache_read_input_tokens = _cached + if not self._cache_creation_input_tokens: + _writes = getattr(_ptd, "cache_write_tokens", 0) or 0 + if _writes > 0: + self._cache_creation_input_tokens = _writes + for k, v in params.items(): setattr(self, k, v)