Skip to content

feat: gemini-2.5-flash-image-preview 文本和图片输出计费 - #1677

Merged
seefs001 merged 1 commit into
alphafrom
gemini-2.5-flash-image-preview-billing
Sep 7, 2025
Merged

feat: gemini-2.5-flash-image-preview 文本和图片输出计费#1677
seefs001 merged 1 commit into
alphafrom
gemini-2.5-flash-image-preview-billing

Conversation

@creamlike1024

@creamlike1024 creamlike1024 commented Aug 27, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added support for Gemini 2.5 Flash Image Preview with image-output token accounting and billing.
    • Pricing UI now separates image input and image output, displaying image-output costs in breakdowns.
    • Usage logs now include image-output token count and price.
  • Refactor

    • Breaking: Updated API token-usage detail representation (renamed and extended to include per-candidate details).
  • Chores

    • Updated default ratios and pricing for the new model.
    • Recognized the new model in imagine-capable lists.

@coderabbitai

coderabbitai Bot commented Aug 27, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Introduces image-output token accounting and billing for Gemini 2.5 flash image preview models across relay, billing, pricing, and UI. Renames a DTO for token details and adds candidate token details. Adjusts token counting logic, extends model support and ratios, adds operation pricing, and updates frontend rendering and logs.

Changes

Cohort / File(s) Change summary
DTO rename and usage metadata
dto/gemini.go
Renames GeminiPromptTokensDetails to GeminiModalityTokenCount. Updates GeminiUsageMetadata.PromptTokensDetails type and adds CandidatesTokensDetails []GeminiModalityTokenCount. Minor import reorder.
Gemini relay token adjustments
relay/channel/gemini/relay-gemini-native.go
For gemini-2.5-flash-image-preview*, adjusts completion/total tokens to exclude image-output tokens; non-stream counts images from parts, stream uses CandidatesTokensDetails. Stores gemini_image_tokens in context.
Billing integration for image output
relay/compatible_handler.go
Adds image-output quota computation using gemini_image_tokens and price lookup for gemini-2.5-flash-image-preview*. Includes logging of image output token count and price.
Request token counting logic
service/token_counter.go
For gemini-2.5-flash-image-preview*, switches from fixed 256 image tokens to getImageToken(...); retains 256 tokens for other Gemini image cases.
Model support list
setting/model_setting/gemini.go
Adds gemini-2.5-flash-image-preview to supported imagine models.
Operation pricing API
setting/operation_setting/tools.go
Adds exported constant Gemini25FlashImagePreviewImageOutputPrice = 30.00 and function GetGeminiImageOutputPricePerMillionTokens.
Ratio and completion maps
setting/ratio_setting/model_ratio.go
Adds ratios for gemini-2.5-flash-image-preview to default model/completion maps; initializes with these defaults.
Frontend pricing renderer API
web/src/helpers/render.jsx
Updates renderModelPrice signature to add imageInputTokens, imageOutputTokens, imageOutputPrice. Splits input vs output image pricing and displays new “图片输出价格”. Adjusts effective token computations and extraServices text.
Usage logs integration
web/src/hooks/usage-logs/useUsageLogsData.jsx
Passes image_output_token_count and image_output_price to renderModelPrice for logs formatting.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Relay as Relay (Gemini handlers)
  participant Billing as CompatibleHandler
  participant Ops as operation_setting
  participant Ratios as ratio_setting
  participant DTO as dto/gemini

  Client->>Relay: Request (model: gemini-2.5-flash-image-preview*)
  Relay->>DTO: Parse usage metadata (PromptTokensDetails, CandidatesTokensDetails)
  alt Streaming
    Relay->>Relay: Sum IMAGE in CandidatesTokensDetails
  else Non-stream
    Relay->>Relay: Scan candidate parts for image inline data
  end
  Relay->>Relay: Subtract image-output tokens from completion/total
  Relay->>Billing: Context gemini_image_tokens
  Billing->>Ops: GetGeminiImageOutputPricePerMillionTokens(model)
  Ops-->>Billing: price or 0
  Billing->>Ratios: Get group/model ratios
  Billing->>Billing: Compute image-output quota (decimal)
  Billing-->>Client: Response + logs (image_output_token_count, image_output_price)
Loading
sequenceDiagram
  autonumber
  participant Counter as service/token_counter
  participant Store as Model settings
  participant Relay as Relay

  Counter->>Store: Check model startsWith gemini-2.5-flash-image-preview
  alt preview model
    Counter->>Counter: getImageToken(file, model, isStream)
  else other Gemini image
    Counter->>Counter: +256 image tokens
  end
  Counter-->>Relay: Input token count
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Suggested reviewers

  • xyfacai

Poem

Hop, hop—pixels gleam in view,
I nibble tokens, count what’s due.
Text goes left, and images right—
New math for flashes, crisp and light.
Prices lined, logs neatly true,
Carrot-approved: a shiny cue. 🥕✨

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
  • Commit unit tests in branch gemini-2.5-flash-image-preview-billing

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/hooks/usage-logs/useUsageLogsData.jsx (1)

426-450: Align renderModelPrice call arguments to the updated signature

The helper’s signature now declares:

export function renderModelPrice(
  …,
  image = false,
  imageRatio = 1.0,
  imageInputTokens = 0,           ← new slot
  webSearch = false,
  …,
  imageOutputTokens = 0,
  imageOutputPrice = 0,
)

But the current invocation still passes the legacy other?.image_output as the 12th argument—so it’s being interpreted as imageInputTokens, shifting every subsequent argument out of place. To fix:

• Remove the deprecated other?.image_output argument.
• Immediately after other?.image_ratio, insert other?.image_input_token_count || 0 so that imageInputTokens is populated correctly.
• Leave the existing other?.image_output_token_count and other?.image_output_price at the end (they already map to slots 22 and 23).

No other call sites were found.

Locations to update:

  • File: web/src/hooks/usage-logs/useUsageLogsData.jsx
  • Lines: around 426–450 (the renderModelPrice( invocation)

Suggested diff:

@@ web/src/hooks/usage-logs/useUsageLogsData.jsx:426
-            other?.image_ratio || 0,
-            other?.image_output || 0,
+            other?.image_ratio || 0,
+            other?.image_input_token_count || 0,
             other?.web_search || false,
             other?.web_search_call_count || 0,
             other?.web_search_price || 0,
             // …rest unchanged…

This change ensures that:

  • imageInputTokens is correctly passed from other.image_input_token_count
  • imageOutputTokens and imageOutputPrice still map to the final two parameters
  • The deprecated image_output argument is dropped, preventing misaligned math
🧹 Nitpick comments (6)
setting/model_setting/gemini.go (1)

29-30: Addition looks correct; consider future-proofing with prefix match

Exact-name inclusion is fine. If Google ships date-suffixed variants (e.g., gemini-2.5-flash-image-preview-YYYY-MM-DD), an exact equality check in IsGeminiModelSupportImagine will miss them. You could either add a small prefix check in IsGeminiModelSupportImagine or list additional variants here when they arrive.

setting/operation_setting/tools.go (1)

73-79: Price resolver is correct; small robustness nit

Prefix check is correct for future date-suffixed variants. To be defensive against accidental casing, consider normalizing once.

Apply this minimal diff:

-func GetGeminiImageOutputPricePerMillionTokens(modelName string) float64 {
-	if strings.HasPrefix(modelName, "gemini-2.5-flash-image-preview") {
+func GetGeminiImageOutputPricePerMillionTokens(modelName string) float64 {
+	name := strings.ToLower(modelName)
+	if strings.HasPrefix(name, "gemini-2.5-flash-image-preview") {
 		return Gemini25FlashImagePreviewImageOutputPrice
 	}
 	return 0
}
service/token_counter.go (1)

307-315: Gemini image counting exception is correct; minor hardening recommended

Logic correctly falls back to precise image token calc for gemini-2.5-flash-image-preview while keeping 256 for other Gemini formats. To avoid edge-case casing issues and magic numbers, consider:

  • Normalize model once for prefix check.
  • Lift 256 into a named const for readability.
-		case types.FileTypeImage:
-			if info.RelayFormat == types.RelayFormatGemini && !strings.HasPrefix(model, "gemini-2.5-flash-image-preview") {
-				tkm += 256
+		case types.FileTypeImage:
+			constFixedGeminiImg := 256
+			modelLower := strings.ToLower(model)
+			if info.RelayFormat == types.RelayFormatGemini && !strings.HasPrefix(modelLower, "gemini-2.5-flash-image-preview") {
+				tkm += constFixedGeminiImg
 			} else {
 				token, err := getImageToken(file, model, info.IsStream)
 				if err != nil {
 					return 0, fmt.Errorf("error counting image token: %v", err)
 				}
 				tkm += token
 			}

If model remapping is used (upstream_model_name in logs), please confirm we want to key this decision off the original request model (ContextKeyOriginalModel) rather than the mapped upstream model. This impacts whether certain remapped Gemini calls take the 256 shortcut or the precise path.

relay/channel/gemini/relay-gemini-native.go (1)

49-64: Consider using CandidatesTokensDetails for image output accounting

The current implementation counts image outputs by scanning content parts, but there's commented-out code that uses CandidatesTokensDetails (lines 65-74). Since the DTO now supports CandidatesTokensDetails with modality information, consider using the more reliable API-provided token counts instead of hardcoding 1290 tokens per image.

-	if strings.HasPrefix(info.UpstreamModelName, "gemini-2.5-flash-image-preview") {
-		imageOutputCounts := 0
-		for _, candidate := range geminiResponse.Candidates {
-			for _, part := range candidate.Content.Parts {
-				if part.InlineData != nil && strings.HasPrefix(part.InlineData.MimeType, "image/") {
-					imageOutputCounts++
-				}
-			}
-		}
-		if imageOutputCounts != 0 {
-			usage.CompletionTokens = usage.CompletionTokens - imageOutputCounts*1290
-			usage.TotalTokens = usage.TotalTokens - imageOutputCounts*1290
-			c.Set("gemini_image_tokens", imageOutputCounts*1290)
-		}
-	}
-
-	// if strings.HasPrefix(info.UpstreamModelName, "gemini-2.5-flash-image-preview") {
-	// 	for _, detail := range geminiResponse.UsageMetadata.CandidatesTokensDetails {
-	// 		if detail.Modality == "IMAGE" {
-	// 			usage.CompletionTokens = usage.CompletionTokens - detail.TokenCount
-	// 			usage.TotalTokens = usage.TotalTokens - detail.TokenCount
-	// 			c.Set("gemini_image_tokens", detail.TokenCount)
-	// 		}
-	// 	}
-	// }
+	if strings.HasPrefix(info.UpstreamModelName, "gemini-2.5-flash-image-preview") {
+		for _, detail := range geminiResponse.UsageMetadata.CandidatesTokensDetails {
+			if detail.Modality == "IMAGE" {
+				usage.CompletionTokens = usage.CompletionTokens - detail.TokenCount
+				usage.TotalTokens = usage.TotalTokens - detail.TokenCount
+				c.Set("gemini_image_tokens", detail.TokenCount)
+			}
+		}
+	}
dto/gemini.go (1)

9-11: Minor import formatting adjustment

Moving the github.com/gin-gonic/gin import to a new line is fine, but consider grouping all external imports together for better consistency.

web/src/helpers/render.jsx (1)

1083-1094: Consider better parameter organization

The renderModelPrice function now has 16 parameters, which makes it difficult to use and maintain. Consider using an options object pattern to group related parameters.

 export function renderModelPrice(
   inputTokens,
   completionTokens,
-  modelRatio,
-  modelPrice = -1,
-  completionRatio,
-  groupRatio,
-  user_group_ratio,
-  cacheTokens = 0,
-  cacheRatio = 1.0,
-  image = false,
-  imageRatio = 1.0,
-  imageInputTokens = 0,
-  webSearch = false,
-  webSearchCallCount = 0,
-  webSearchPrice = 0,
-  fileSearch = false,
-  fileSearchCallCount = 0,
-  fileSearchPrice = 0,
-  audioInputSeperatePrice = false,
-  audioInputTokens = 0,
-  audioInputPrice = 0,
-  imageOutputTokens = 0,
-  imageOutputPrice = 0,
+  options = {}
 ) {
+  const {
+    modelRatio,
+    modelPrice = -1,
+    completionRatio,
+    groupRatio,
+    user_group_ratio,
+    cacheTokens = 0,
+    cacheRatio = 1.0,
+    image = false,
+    imageRatio = 1.0,
+    imageInputTokens = 0,
+    webSearch = false,
+    webSearchCallCount = 0,
+    webSearchPrice = 0,
+    fileSearch = false,
+    fileSearchCallCount = 0,
+    fileSearchPrice = 0,
+    audioInputSeperatePrice = false,
+    audioInputTokens = 0,
+    audioInputPrice = 0,
+    imageOutputTokens = 0,
+    imageOutputPrice = 0,
+  } = options;
📜 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 c5a1cbe and e732c58.

📒 Files selected for processing (9)
  • dto/gemini.go (2 hunks)
  • relay/channel/gemini/relay-gemini-native.go (2 hunks)
  • relay/compatible_handler.go (2 hunks)
  • service/token_counter.go (1 hunks)
  • setting/model_setting/gemini.go (1 hunks)
  • setting/operation_setting/tools.go (2 hunks)
  • setting/ratio_setting/model_ratio.go (2 hunks)
  • web/src/helpers/render.jsx (7 hunks)
  • web/src/hooks/usage-logs/useUsageLogsData.jsx (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
📚 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:

  • service/token_counter.go
  • relay/channel/gemini/relay-gemini-native.go
  • setting/operation_setting/tools.go
  • relay/compatible_handler.go
📚 Learning: 2025-08-05T17:14:17.246Z
Learnt from: neotf
PR: QuantumNous/new-api#1511
File: setting/ratio_setting/model_ratio.go:118-123
Timestamp: 2025-08-05T17:14:17.246Z
Learning: Claude models handle "-thinking" variants differently from Gemini models. For Claude models, only the base model (without "-thinking") gets an entry in defaultModelRatio map. The "-thinking" variants rely on the Claude relay handler stripping the suffix using strings.TrimSuffix(textRequest.Model, "-thinking") before looking up the ratio, so they automatically use the base model's ratio.

Applied to files:

  • setting/ratio_setting/model_ratio.go
🧬 Code graph analysis (2)
service/token_counter.go (1)
types/relay_format.go (2)
  • RelayFormat (3-3)
  • RelayFormatGemini (8-8)
relay/compatible_handler.go (1)
setting/operation_setting/tools.go (1)
  • GetGeminiImageOutputPricePerMillionTokens (73-78)
🔇 Additional comments (8)
setting/ratio_setting/model_ratio.go (2)

181-182: Model ratio entry aligns with comment and existing flash pricing

0.15 matches the annotated $0.30/1M tokens baseline and keeps parity with gemini-2.5-flash. No functional concerns.


297-302: Completion ratio value is consistent with hardcoded fallback

8.3333333333 matches the existing 2.5/0.3 logic in getHardcodedCompletionModelRatio for flash. Explicit mapping here removes reliance on fallback; good for clarity.

setting/operation_setting/tools.go (1)

27-30: Adds image-output price constant: OK

Naming and visibility are consistent with existing pricing constants.

relay/channel/gemini/relay-gemini-native.go (1)

166-174: LGTM! Consistent implementation with non-streaming handler

The streaming implementation correctly uses CandidatesTokensDetails to account for image tokens, which is more reliable than counting image parts.

dto/gemini.go (1)

271-283: LGTM! Well-structured type renaming and field additions

The renaming from GeminiPromptTokensDetails to GeminiModalityTokenCount is more descriptive and accurately represents the shared structure for both prompt and candidate token details. The addition of CandidatesTokensDetails field enables proper tracking of output tokens by modality.

relay/compatible_handler.go (2)

427-430: LGTM! Image output billing integrated correctly

The image output token count and price are properly added to the logging context when applicable. This ensures accurate billing tracking for image generation.


317-325: No additional validation required for gemini_image_tokens

The call to ctx.GetInt("gemini_image_tokens") safely returns 0 when the key is unset (per the Gin Context behavior), so:

  • If the native Gemini handler set a positive token count, that value will be used.
  • If it wasn’t set (e.g. for non-image models or in the compatible handler), dImageOutputTokens becomes 0, yielding a zero quota without error.

Because defaulting to zero tokens has no runtime side-effects, and the native handler always populates this key for supported image requests, no further checks are needed here.

web/src/helpers/render.jsx (1)

1197-1205: LGTM! Image output pricing display implemented correctly

The UI properly displays image output pricing when both tokens and price are present, maintaining consistency with other pricing displays.

Comment on lines +1122 to 1125
if (image && imageInputTokens > 0) {
effectiveInputTokens =
inputTokens - imageOutputTokens + imageOutputTokens * imageRatio;
inputTokens - imageInputTokens + imageInputTokens * imageRatio;
}

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.

⚠️ Potential issue

Fix logical error in image input token handling

The condition checks imageInputTokens > 0 but the gating uses image && imageInputTokens > 0. This could lead to incorrect calculations if image is false but imageInputTokens is provided.

-    if (image && imageInputTokens > 0) {
+    if (imageInputTokens > 0) {
       effectiveInputTokens =
         inputTokens - imageInputTokens + imageInputTokens * imageRatio;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (image && imageInputTokens > 0) {
effectiveInputTokens =
inputTokens - imageOutputTokens + imageOutputTokens * imageRatio;
inputTokens - imageInputTokens + imageInputTokens * imageRatio;
}
if (imageInputTokens > 0) {
effectiveInputTokens =
inputTokens - imageInputTokens + imageInputTokens * imageRatio;
}
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1122-1125, the conditional currently
gates the image token adjustment with "image && imageInputTokens > 0" which
skips the calculation when an explicit imageInputTokens value exists but image
is false; change the condition to rely on the presence/value of imageInputTokens
instead (e.g. check that imageInputTokens is not null/undefined and > 0) and
then apply the effectiveInputTokens calculation so the adjustment runs whenever
imageInputTokens is provided and positive.

@seefs001
seefs001 merged commit b7527eb into alpha Sep 7, 2025
3 checks passed
@hoopan007

Copy link
Copy Markdown

Hello, can this feature be merged into the main branch?

@hoopan007

Copy link
Copy Markdown

我在设置了gemini-2.5-flash-image-preview,使用Google Gemini API,可以正常绘画,但没有正确计算绘画token和费用。请问是需要额外的设置吗?
Snipaste_2025-09-12_14-12-43

This was referenced Sep 29, 2025
@Calcium-Ion
Calcium-Ion deleted the gemini-2.5-flash-image-preview-billing branch October 11, 2025 03:06
@coderabbitai coderabbitai Bot mentioned this pull request Feb 27, 2026
x22x22 pushed a commit to x22x22/new-api that referenced this pull request Apr 24, 2026
…-image-preview-billing

feat: gemini-2.5-flash-image-preview 文本和图片输出计费
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.

3 participants