-
-
Notifications
You must be signed in to change notification settings - Fork 11k
fix(responses_adapter): read cache_read_tokens from input_tokens_details on Responses usage (#28354) #28380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(responses_adapter): read cache_read_tokens from input_tokens_details on Responses usage (#28354) #28380
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -257,17 +257,33 @@ def _process_event(self, event: Any) -> None: # noqa: PLR0915 | |
| stop_reason = "max_tokens" | ||
| usage = getattr(response_obj, "usage", None) | ||
| if usage is not None: | ||
| input_tokens = getattr(usage, "input_tokens", 0) or 0 | ||
| output_tokens = getattr(usage, "output_tokens", 0) or 0 | ||
| cache_creation_tokens = getattr(usage, "input_tokens_details", None) # type: ignore[assignment] | ||
| cache_read_tokens = getattr(usage, "output_tokens_details", None) # type: ignore[assignment] | ||
| # Prefer direct cache fields if present | ||
| input_tokens = int(getattr(usage, "input_tokens", 0) or 0) | ||
| output_tokens = int(getattr(usage, "output_tokens", 0) or 0) | ||
| # OpenAI Responses API exposes cached prompt tokens at | ||
| # ``usage.input_tokens_details.cached_tokens`` (parallel to | ||
| # ``usage.prompt_tokens_details.cached_tokens`` on Chat Completions). | ||
| itd = getattr(usage, "input_tokens_details", None) | ||
| if itd is not None: | ||
| if isinstance(itd, dict): | ||
| cache_read_tokens = int(itd.get("cached_tokens", 0) or 0) | ||
| else: | ||
| cache_read_tokens = int( | ||
| getattr(itd, "cached_tokens", 0) or 0 | ||
| ) | ||
| # Fall back to Anthropic-style direct fields if the upstream | ||
| # provider already speaks Anthropic usage. | ||
| if not cache_read_tokens: | ||
| cache_read_tokens = int( | ||
| getattr(usage, "cache_read_input_tokens", 0) or 0 | ||
| ) | ||
| cache_creation_tokens = int( | ||
| getattr(usage, "cache_creation_input_tokens", 0) or 0 | ||
| ) | ||
| cache_read_tokens = int( | ||
| getattr(usage, "cache_read_input_tokens", 0) or 0 | ||
| ) | ||
| # Anthropic semantics: ``input_tokens`` is the *uncached* prompt | ||
| # count. OpenAI reports total prompt tokens (cached + uncached), | ||
| # so subtract to keep client-side accounting consistent. | ||
| if cache_read_tokens and input_tokens >= cache_read_tokens: | ||
| input_tokens = input_tokens - cache_read_tokens | ||
|
Comment on lines
259
to
+286
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The existing test file for this module only exercises Rule Used: What: Ensure that any PR claiming to fix an issue ... (source) |
||
|
|
||
| # Check if tool_use was in the output to override stop_reason | ||
| if response_obj is not None: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
input_tokensfor Anthropic-native upstreamsWhen
input_tokens_detailsis absent (Anthropic providers don't include it) andcache_read_input_tokens > 0, the fallback correctly captures cached tokens — but the unconditional subtraction that follows then reduces aninput_tokensvalue that is already the uncached count (Anthropic semantics). For a request with 3 000 uncached tokens and 500 cached, the emittedinput_tokensbecomes 2 500 instead of 3 000.The subtraction is only valid for the OpenAI Responses path (where
input_tokensis the total prompt count). It should be skipped whenever the value came from thecache_read_input_tokensfallback.