Skip to content

fix: prompt calculation - #1606

Merged
Calcium-Ion merged 1 commit into
QuantumNous:alphafrom
funnycups:patch-1
Aug 22, 2025
Merged

fix: prompt calculation#1606
Calcium-Ion merged 1 commit into
QuantumNous:alphafrom
funnycups:patch-1

Conversation

@funnycups

@funnycups funnycups commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

closes #1602
User will correctly get estimated prompt usage when upstream returns either zero or nothing.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected token usage calculation for responses, ensuring accurate prompt/completion/total token counts.
    • Fixed edge cases where token totals could be zero by aggregating tokens across choices.
    • Improved pricing accuracy by reliably tracking prompt tokens.
  • Improvements

    • Reduced unnecessary response reformatting; original provider responses are preserved unless updates are needed.
    • More consistent pass-through behavior for upstream payloads.
    • Enhanced reliability of usage metrics displayed to users.

User will correctly get estimated prompt usage when upstream returns either zero or nothing.
@coderabbitai

coderabbitai Bot commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds prompt token storage to relayInfo during request handling and revises OpenAI response handling to recompute token usage when PromptTokens is zero. Introduces a usageModified flag and conditions formatting to run only when forced or when usage was updated, otherwise forwarding the upstream payload unchanged.

Changes

Cohort / File(s) Summary of Changes
Relay info token tracking
controller/relay.go
Stores computed prompt token count via relayInfo.SetPromptTokens(tokens) before price calculation. No changes to public APIs or error handling.
OpenAI usage reconciliation & conditional formatting
relay/channel/openai/relay-openai.go
Recomputes CompletionTokens when PromptTokens is zero; aggregates tokens across choices if needed; updates usage and sets usageModified. Applies OpenAI formatting only if forceFormat or usageModified; otherwise forwards original response.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Relay
  participant Tokenizer
  participant PriceHelper

  Client->>Relay: Request
  Relay->>Tokenizer: Compute prompt tokens
  Tokenizer-->>Relay: tokens
  Relay->>Relay: relayInfo.SetPromptTokens(tokens)
  Relay->>PriceHelper: Calculate price(tokens, ...)
  PriceHelper-->>Relay: price
  Relay-->>Client: Response
Loading
sequenceDiagram
  participant Upstream as OpenAI Upstream
  participant Handler as OpenAI Handler
  participant Client

  Upstream-->>Handler: Response (body, usage)
  alt PromptTokens == 0
    Handler->>Handler: Initialize completionTokens
    opt completionTokens == 0
      Handler->>Handler: Sum tokens across choices
    end
    Handler->>Handler: Update usage and set usageModified = true
  end
  alt forceFormat || usageModified
    Handler->>Handler: Reformat simpleResponse and marshal
    Handler-->>Client: Formatted response with updated usage
  else
    Handler-->>Client: Forward original payload unchanged
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A twitch of whiskers, tokens tallied neat,
I hop through relays with rhythmic feet.
If prompts were zero, I count anew—
Then only format when it’s due.
Carrots logged, prices set just right,
Packet by packet, I bound through the night. 🥕✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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 (3)
relay/channel/openai/relay-openai.go (3)

200-215: Avoid overwriting entire Usage; update in place to preserve details

When recomputing usage you replace the whole dto.Usage, which can clobber upstream fields like PromptTokensDetails, CompletionTokenDetails, Input/OutputTokens, Cost, etc. Prefer updating only the specific fields you fix (PromptTokens, CompletionTokens, TotalTokens).

Apply this diff within the current block:

-usageModified := false
+usageModified := false
 if simpleResponse.Usage.PromptTokens == 0 {
   completionTokens := simpleResponse.Usage.CompletionTokens
   if completionTokens == 0 {
     for _, choice := range simpleResponse.Choices {
       ctkm := service.CountTextToken(choice.Message.StringContent()+choice.Message.ReasoningContent+choice.Message.Reasoning, info.UpstreamModelName)
       completionTokens += ctkm
     }
   }
-  simpleResponse.Usage = dto.Usage{
-    PromptTokens:     info.PromptTokens,
-    CompletionTokens: completionTokens,
-    TotalTokens:      info.PromptTokens + completionTokens,
-  }
+  // Preserve any existing usage details from upstream; only fill the missing core fields.
+  simpleResponse.Usage.PromptTokens = info.PromptTokens
+  simpleResponse.Usage.CompletionTokens = completionTokens
+  simpleResponse.Usage.TotalTokens = simpleResponse.Usage.PromptTokens + simpleResponse.Usage.CompletionTokens
   usageModified = true
 }

203-208: Consider counting tool_calls to reduce underestimation

When upstream omits usage entirely, completions that are mostly tool_call arguments may be undercounted. Optionally include ToolCalls JSON in the token estimate.

Here’s a minimal, low-risk addition:

-    for _, choice := range simpleResponse.Choices {
-      ctkm := service.CountTextToken(choice.Message.StringContent()+choice.Message.ReasoningContent+choice.Message.Reasoning, info.UpstreamModelName)
-      completionTokens += ctkm
-    }
+    for _, choice := range simpleResponse.Choices {
+      ctkm := service.CountTextToken(
+        choice.Message.StringContent()+choice.Message.ReasoningContent+choice.Message.Reasoning,
+        info.UpstreamModelName,
+      )
+      completionTokens += ctkm
+      if len(choice.Message.ToolCalls) > 0 {
+        completionTokens += service.CountTextToken(string(choice.Message.ToolCalls), info.UpstreamModelName)
+      }
+    }

219-227: Formatting gate is correct; add a clarifying comment to the break

The “break” exits the switch, preserving the original upstream response body when no formatting/usage updates are needed. Consider a short inline comment to avoid misreads.

-    if forceFormat || usageModified {
+    if forceFormat || usageModified {
       responseBody, err = common.Marshal(simpleResponse)
       if err != nil {
         return nil, types.NewError(err, types.ErrorCodeBadResponseBody)
       }
     } else {
-      break
+      // Keep upstream payload as-is; responseBody remains the original bytes
+      break
     }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a1cab15 and e3473e3.

📒 Files selected for processing (2)
  • controller/relay.go (1 hunks)
  • relay/channel/openai/relay-openai.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-21T03:37:41.726Z
Learnt from: 9Ninety
PR: QuantumNous/new-api#1273
File: relay/channel/gemini/relay-gemini.go:97-116
Timestamp: 2025-06-21T03:37:41.726Z
Learning: In relay/channel/gemini/relay-gemini.go, the thinking budget calculation logic (including the MaxOutputTokens multiplication) was introduced in PR #1247. PR #1273 focused specifically on decoupling the thoughts summary feature from thinking budget settings and did not modify the existing thinking budget behavior.

Applied to files:

  • controller/relay.go
🧬 Code Graph Analysis (1)
relay/channel/openai/relay-openai.go (4)
dto/openai_response.go (1)
  • Usage (217-230)
service/token_counter.go (1)
  • CountTextToken (641-647)
dto/openai_request.go (2)
  • Message (247-258)
  • Reasoning (844-847)
types/relay_format.go (2)
  • RelayFormat (3-3)
  • RelayFormatOpenAI (6-6)
🔇 Additional comments (1)
controller/relay.go (1)

131-132: Confirmed PromptTokens propagation across codebase

I ran the suggested grep and confirmed that relayInfo.SetPromptTokens(tokens) (controller/relay.go:131) fires before any downstream reference to PromptTokens. All key handlers—usage helpers, quota logic, Convert services, and individual channel adapters—now derive their prompt-token counts from relayInfo.PromptTokens, ensuring no zero/missing values slip through. No further changes are needed.

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