fix(provider): exclude cached tokens from Gemini input usage accounting - #9880
Merged
Soulter merged 2 commits intoSep 1, 2026
Merged
Conversation
Gemini's prompt_token_count already includes tokens served from cache. _extract_usage was passing it straight to TokenUsage.input_other while also setting input_cached to cached_content_token_count, so usage.input double counted cached tokens and inflated context-occupancy stats. Subtract cached tokens from input_other, matching the OpenAI provider's accounting.
Contributor
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/provider/sources/gemini_source.py" line_range="454" />
<code_context>
self, usage_metadata: types.GenerateContentResponseUsageMetadata
) -> TokenUsage:
- """Extract usage from candidate"""
+ """Extract usage from candidate.
+
+ `prompt_token_count` includes tokens served from cache, so subtract
+ `cached_content_token_count` to avoid double-counting cached input
+ (matching the OpenAI provider's TokenUsage accounting).
+ """
+ prompt_tokens = usage_metadata.prompt_token_count or 0
</code_context>
<issue_to_address>
**nitpick:** The expanded docstring still says the method extracts usage from a candidate, but `_extract_usage` accepts and reads `GenerateContentResponseUsageMetadata`; callers pass `result.usage_metadata` or `chunk.usage_metadata`. The description therefore misstates the method's input and can mislead maintainers about its contract.
**Suggested fix:** Change the first line to `Extract usage from response metadata.`
```suggestion
"""Extract usage from response metadata.
```
</issue_to_address>Sourcery assessment
Needs a human reviewer. This changes the token counts used for Gemini usage and potentially cost or billing calculations; if the API's cached count is not a subset of the prompt count, it could record incorrect or negative non-cached usage. Reverting restores the old calculation, but any already-recorded usage or charges would need to be recomputed or corrected.
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Address Sourcery review: the method receives GenerateContentResponseUsageMetadata, not a candidate, so correct the docstring's opening line.
Soulter
approved these changes
Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ProviderGoogleGenAI._extract_usagebuildsTokenUsageby passing Gemini'sprompt_token_countstraight intoinput_otherwhile also settinginput_cachedtocached_content_token_count.Per the Gemini API docs,
prompt_token_countalready includes the tokens served from cache. SoTokenUsage.input(input_other + input_cached) double-counts cached tokens, inflating context-occupancy stats (e.g.current_context_tokensin the tool-loop runner).The OpenAI provider handles this correctly (
input_other = prompt_tokens - cached, seeopenai_source.py), so the two providers are inconsistent.Changes
astrbot/core/provider/sources/gemini_source.py: subtractcached_content_token_countfromprompt_token_countwhen buildinginput_other, matching the OpenAI provider's accounting.tests/test_gemini_source.py: add two unit tests covering usage with and without cached tokens.Verification
Summary by Sourcery
Prevent cached Gemini prompt tokens from inflating reported input usage.
Bug Fixes:
Tests: