Skip to content

fix(provider): exclude cached tokens from Gemini input usage accounting - #9880

Merged
Soulter merged 2 commits into
AstrBotDevs:masterfrom
xiaoyuyu6420:fix/gemini-usage-cached-tokens
Sep 1, 2026
Merged

fix(provider): exclude cached tokens from Gemini input usage accounting#9880
Soulter merged 2 commits into
AstrBotDevs:masterfrom
xiaoyuyu6420:fix/gemini-usage-cached-tokens

Conversation

@xiaoyuyu6420

@xiaoyuyu6420 xiaoyuyu6420 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

ProviderGoogleGenAI._extract_usage builds TokenUsage by passing Gemini's prompt_token_count straight into input_other while also setting input_cached to cached_content_token_count.

Per the Gemini API docs, prompt_token_count already includes the tokens served from cache. So TokenUsage.input (input_other + input_cached) double-counts cached tokens, inflating context-occupancy stats (e.g. current_context_tokens in the tool-loop runner).

The OpenAI provider handles this correctly (input_other = prompt_tokens - cached, see openai_source.py), so the two providers are inconsistent.

Changes

  • astrbot/core/provider/sources/gemini_source.py: subtract cached_content_token_count from prompt_token_count when building input_other, matching the OpenAI provider's accounting.
  • tests/test_gemini_source.py: add two unit tests covering usage with and without cached tokens.

Verification

$ uv run pytest tests/test_gemini_source.py tests/unit/test_provider_stats.py -q
10 passed

Summary by Sourcery

Prevent cached Gemini prompt tokens from inflating reported input usage.

Bug Fixes:

  • Correct Gemini input token usage accounting so cached prompt tokens are not double-counted.

Tests:

  • Add coverage for Gemini usage accounting with and without cached tokens.

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/core/provider/sources/gemini_source.py Outdated
Address Sourcery review: the method receives GenerateContentResponseUsageMetadata,
not a candidate, so correct the docstring's opening line.
@Soulter
Soulter merged commit 5ad9536 into AstrBotDevs:master Sep 1, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants