Skip to content

fix: gemini 原生格式流模式中断请求未计费 - #1272

Merged
Calcium-Ion merged 4 commits into
alphafrom
gemini-stream-completion-count-fix
Jun 20, 2025
Merged

fix: gemini 原生格式流模式中断请求未计费#1272
Calcium-Ion merged 4 commits into
alphafrom
gemini-stream-completion-count-fix

Conversation

@creamlike1024

@creamlike1024 creamlike1024 commented Jun 20, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Bug Fixes
    • Improved accuracy of token usage reporting for Gemini streaming responses by locally counting completion tokens when not provided.
    • Ensured that total token usage is correctly updated after streaming completes.
  • Chores
    • Simplified token counting by removing error handling from token count functions and related calls.
    • Cleaned up code by discarding unused error returns in multiple token counting and usage calculation steps.

@coderabbitai

coderabbitai Bot commented Jun 20, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The Gemini text generation stream handler was updated to accumulate all streamed text parts into a buffer and recalculate usage tokens if completion tokens are zero. Additionally, multiple token counting functions and calls across various relay channels and services were simplified by removing error returns and related error handling, assuming token counting never fails. Some error checks were removed along with error assignments, resulting in more consistent omission of error handling.

Changes

File(s) Change Summary
relay/channel/gemini/relay-gemini-native.go Modified Gemini stream handler to accumulate response text and recalculate usage tokens if completion tokens are zero.
relay/channel/claude/relay-claude.go, relay/channel/cloudflare/relay_cloudflare.go, relay/channel/cohere/relay-cohere.go, relay/channel/coze/relay-coze.go, relay/channel/dify/relay-dify.go, relay/channel/openai/relay-openai.go, relay/channel/openai/relay_responses.go, relay/channel/palm/adaptor.go, relay/channel/palm/relay-palm.go, relay/channel/tencent/adaptor.go, relay/channel/xai/text.go Removed ignored error returns from token counting and usage functions, assigning only token counts without error variables; removed some error checks accordingly.
relay/audio_handler.go Removed error assignment from token counting call and eliminated error check, assuming no error returned.
relay/embedding_handler.go, relay/gemini_handler.go, relay/relay-text.go, relay/rerank_handler.go, relay/responses_handler.go Simplified token counting calls by removing error returns and error handling; updated function signatures accordingly.
service/token_counter.go Changed token counting functions to return only token counts without errors; removed all related error handling.
service/usage_helpr.go Modified usage calculation function to remove error return and error handling from token counting calls.

Poem

In Gemini’s stream, tokens we tally,
Counting each word in a diligent rally.
If the numbers are missing, we fill in the sum,
Ensuring the totals will never be glum.
With paws on the counter, this rabbit’s delight—
All tokens accounted, the future is bright! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0708452 and 7afd3f9.

📒 Files selected for processing (5)
  • relay/audio_handler.go (1 hunks)
  • relay/channel/claude/relay-claude.go (3 hunks)
  • relay/channel/coze/relay-coze.go (3 hunks)
  • relay/channel/dify/relay-dify.go (1 hunks)
  • relay/responses_handler.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • relay/audio_handler.go
  • relay/responses_handler.go
  • relay/channel/dify/relay-dify.go
  • relay/channel/coze/relay-coze.go
  • relay/channel/claude/relay-claude.go
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai 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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
relay/channel/gemini/relay-gemini-native.go (1)

95-100: Verify token counting accuracy in streaming context.

The token counting logic looks correct for addressing the billing issue. However, consider these potential optimizations:

  1. Performance: Token counting on every streaming chunk adds latency. Consider batching or optimizing if this becomes a bottleneck.

  2. Error handling: Currently errors are only logged. Consider tracking if token counting consistently fails to alert on potential under-billing.

For improved error visibility, consider tracking token counting failures:

+var tokenCountingErrors int
 // 本地统计completion tokens
 textTokens, err := service.CountTextToken(part.Text, info.UpstreamModelName)
 if err != nil {
     common.LogError(c, "error counting text token: "+err.Error())
+    tokenCountingErrors++
 }
 localCompletionTokens += textTokens
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5e80af and a56d9ea.

📒 Files selected for processing (1)
  • relay/channel/gemini/relay-gemini-native.go (3 hunks)
🔇 Additional comments (2)
relay/channel/gemini/relay-gemini-native.go (2)

78-79: LGTM: Clean variable initialization.

The local completion tokens counter is properly initialized and clearly named.


134-138: Review the interaction with image token counting logic.

The fallback logic correctly uses local token counts when the API doesn't provide completion tokens. However, verify the interaction with the existing image counting logic (lines 128-132).

Current flow:

  1. Image logic sets CompletionTokens = imageCount * 258 if CompletionTokens == 0
  2. Text fallback only triggers if CompletionTokens is still 0

Potential issue: For responses containing both images and text, only image tokens may be counted, potentially under-billing text content.

#!/bin/bash
# Description: Search for test cases or examples that handle both images and text in Gemini responses
# Expected: Find test cases that verify mixed content billing

# Search for test files related to Gemini functionality
fd -e go -t f | grep -i test | xargs rg -l "gemini.*image\|image.*gemini" -A 5 -B 5

# Search for mixed content handling patterns
rg -A 10 -B 5 "imageCount.*CompletionTokens\|CompletionTokens.*imageCount"

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🔭 Outside diff range comments (3)
relay/gemini_handler.go (1)

109-114: Remove dead code - error handling for function that no longer returns error.

The getGeminiInputTokens function no longer returns an error, making the error handling code on lines 110-112 unreachable dead code.

Apply this diff to remove the dead code:

 	} else {
 		promptTokens := getGeminiInputTokens(req, relayInfo)
-		if err != nil {
-			return service.OpenAIErrorWrapperLocal(err, "count_input_tokens_error", http.StatusBadRequest)
-		}
 		c.Set("prompt_tokens", promptTokens)
 	}
service/token_counter.go (2)

174-178: Remove inconsistent error handling for function that no longer returns error.

The CountTokenInput function no longer returns an error, making this error handling code unreachable and inconsistent with the refactoring.

Apply this diff to remove the inconsistent error handling:

 		toolTokens := CountTokenInput(countStr, request.Model)
-		if err != nil {
-			return 0, err
-		}
 		tkm += 8

197-202: Remove inconsistent error handling for function that no longer returns error.

The CountTokenInput function no longer returns an error, making this error handling code unreachable and inconsistent with the refactoring.

Apply this diff to remove the inconsistent error handling:

 		systemTokens := CountTokenInput(request.System, model)
-		if err != nil {
-			return 0, err
-		}
 		tkm += systemTokens
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a56d9ea and a9e5d99.

📒 Files selected for processing (20)
  • relay/audio_handler.go (1 hunks)
  • relay/channel/claude/relay-claude.go (3 hunks)
  • relay/channel/cloudflare/relay_cloudflare.go (3 hunks)
  • relay/channel/cohere/relay-cohere.go (1 hunks)
  • relay/channel/coze/relay-coze.go (1 hunks)
  • relay/channel/dify/relay-dify.go (1 hunks)
  • relay/channel/gemini/relay-gemini-native.go (4 hunks)
  • relay/channel/openai/relay-openai.go (6 hunks)
  • relay/channel/openai/relay_responses.go (1 hunks)
  • relay/channel/palm/adaptor.go (1 hunks)
  • relay/channel/palm/relay-palm.go (1 hunks)
  • relay/channel/tencent/adaptor.go (1 hunks)
  • relay/channel/xai/text.go (1 hunks)
  • relay/embedding_handler.go (1 hunks)
  • relay/gemini_handler.go (3 hunks)
  • relay/relay-text.go (1 hunks)
  • relay/rerank_handler.go (1 hunks)
  • relay/responses_handler.go (2 hunks)
  • service/token_counter.go (9 hunks)
  • service/usage_helpr.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • relay/channel/openai/relay-openai.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • relay/channel/gemini/relay-gemini-native.go
🔇 Additional comments (23)
relay/channel/palm/relay-palm.go (1)

158-158: ```shell
#!/bin/bash

Description: Inspect implementations of getTokenEncoder and getTokenNum to verify they never fail/panic

ast-grep --pattern 'func getTokenEncoder($) $ { $$$ }'
ast-grep --pattern 'func getTokenNum($, $) $_ { $$$ }'


</details>
<details>
<summary>relay/channel/xai/text.go (1)</summary>

`71-71`: **Verify that usage calculation never fails before removing error handling.**

The error return from `service.ResponseText2Usage` has been removed. This function is used as a fallback when stream usage information is not available, making error handling particularly important.



Please verify that ResponseText2Usage can never fail:

```shell
#!/bin/bash
# Description: Check the implementation of ResponseText2Usage to verify it handles all edge cases
# Expected: Function should gracefully handle all inputs without returning errors

ast-grep --pattern 'func ResponseText2Usage($_, $_, $_) $_ {
  $$$
}'
relay/channel/openai/relay_responses.go (1)

113-113: Verify error handling removal is safe for fallback token counting.

The error return from service.CountTextToken has been removed in a fallback scenario (when usage.CompletionTokens == 0). Since this is used when primary token counting fails, robust error handling is especially important here.

relay/rerank_handler.go (1)

17-21: Verify that unconditional token accumulation is safe.

The error handling logic has been significantly changed. Previously, document tokens were only added to the total if token counting succeeded. Now, all tokens are added unconditionally, which could lead to incorrect billing if token counting fails but returns a default value.

The logic change is more significant than just removing error returns. Please verify that CountTokenInput never fails and always returns accurate counts:

#!/bin/bash
# Description: Check CountTokenInput implementation and verify it handles all edge cases safely
# Expected: Function should never fail and always return accurate token counts

ast-grep --pattern 'func CountTokenInput($_, $_) $_ {
  $$$
}'
relay/embedding_handler.go (1)

18-18: LGTM: Clean removal of error handling from token counting.

The change correctly removes error assignment from service.CountTokenInput call, aligning with the broader refactor to simplify token counting operations.

relay/channel/cohere/relay-cohere.go (1)

165-165: LGTM: Consistent removal of error handling from usage calculation.

The change correctly removes error assignment from service.ResponseText2Usage call, maintaining consistency with the broader refactor to simplify usage calculation operations.

relay/channel/dify/relay-dify.go (1)

253-253: LGTM: Proper simplification of token counting call.

The change correctly removes error assignment from service.CountTextToken call, consistent with the refactor to streamline token counting operations.

relay/channel/claude/relay-claude.go (1)

552-552: LGTM: Consistent usage calculation simplification.

Both calls to service.ResponseText2Usage correctly remove error handling, aligning with the broader refactor to streamline usage calculation operations.

Also applies to: 561-561

relay/channel/palm/adaptor.go (1)

77-77: I wasn’t able to locate the definition or usages with the previous AST query—let’s fall back to a grep search:

#!/bin/bash
# Locate the definition of ResponseText2Usage
rg -n 'func ResponseText2Usage' 

# Find all call sites of ResponseText2Usage
rg -n 'ResponseText2Usage'
relay/channel/tencent/adaptor.go (1)

101-101: LGTM! Consistent with service function refactor.

This change matches the pattern seen in other relay channels where error handling is removed from service.ResponseText2Usage calls.

relay/channel/cloudflare/relay_cloudflare.go (3)

74-74: LGTM! Service function signature simplified.

The removal of error handling from service.ResponseText2Usage is consistent with the broader refactor.


111-111: LGTM! Consistent error handling removal.

This change aligns with the pattern of removing error returns from token counting service functions.


153-153: Let’s locate the CountTextToken definition and all its usages to confirm its signature change:

#!/bin/bash
# Find where CountTextToken is defined
rg -n "func CountTextToken" .

# Find all calls to CountTextToken (qualified or not)
rg -n "CountTextToken" .
relay/relay-text.go (3)

254-254: LGTM! CountTokenInput simplified for completions mode.

The removal of error handling from service.CountTokenInput is consistent with the service layer simplification.


256-256: LGTM! CountTokenInput simplified for moderations mode.

Consistent with the pattern of removing error returns from token counting functions.


258-258: LGTM! CountTokenInput simplified for embeddings mode.

This completes the consistent pattern of removing error handling from service.CountTokenInput across all relevant relay modes.

relay/responses_handler.go (1)

43-47: LGTM! Function signature simplified correctly.

The getInputTokens function has been correctly updated to remove error returns, consistent with the service.CountTokenInput simplification.

service/usage_helpr.go (1)

19-26: LGTM! Clean signature simplification.

The function signature change and implementation are consistent with the broader refactoring to remove error returns from token counting functions. The logic remains correct.

relay/gemini_handler.go (1)

62-77: LGTM! Function signature change implemented correctly.

The function signature change from returning (int, error) to int is properly implemented, and the call to service.CountTokenInput is updated accordingly.

service/token_counter.go (4)

400-418: LGTM! Function signature simplified correctly.

The removal of error return from CountTokenInput is implemented correctly. The function logic remains intact and the recursive call maintains consistency.


437-443: LGTM! Function signature simplified correctly.

The removal of error return from CountTTSToken is implemented correctly with proper return statements.


474-480: LGTM! Function signature simplified correctly.

The removal of error return from CountTextToken is implemented correctly with proper early return and final return statements.


400-480: I want to inspect how errors from the tokenizer are handled. Let’s locate getTokenNum and getTokenEncoder in service/token_counter.go:

#!/bin/bash
# Find definition of getTokenNum to see if it propagates errors
rg -A5 -B5 "func getTokenNum" --type go

# Find definition of getTokenEncoder to see if it can ever return nil or error
rg -A5 -B5 "func getTokenEncoder" --type go

Comment thread relay/channel/coze/relay-coze.go Outdated
Comment thread relay/audio_handler.go Outdated
Comment thread relay/channel/claude/relay-claude.go Outdated
Comment thread relay/responses_handler.go Outdated
@Calcium-Ion
Calcium-Ion merged commit 2c6d446 into alpha Jun 20, 2025
@creamlike1024
creamlike1024 deleted the gemini-stream-completion-count-fix branch June 21, 2025 03:17
@coderabbitai coderabbitai Bot mentioned this pull request Jun 25, 2025
x22x22 pushed a commit to x22x22/new-api that referenced this pull request Apr 24, 2026
…mpletion-count-fix

fix: gemini 原生格式流模式中断请求未计费
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