fix(billing): channel pricing override for image generation + display image_output_tokens#2930
Open
touwaeriol wants to merge 2 commits into
Open
fix(billing): channel pricing override for image generation + display image_output_tokens#2930touwaeriol wants to merge 2 commits into
touwaeriol wants to merge 2 commits into
Conversation
When image generation models are billed by token (channel pricing mode=token), the usage pages previously showed only image count format instead of detailed token breakdown. This fixes the display to properly show image output tokens separately from text output tokens.
When channel pricing is configured, image output token price now correctly overrides all other sources (LiteLLM, group config). Add ImageOutputPriceExplicit flag to distinguish intentional zero price from missing price, preventing fallback to output price. Affects Gemini, OpenAI inline image gen, and Images API paths.
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.
背景 / Background
按 token 计费的图片生成模型(如 gemini-3.x image-preview)存在两个相互配套的问题:
ImageOutputPricePerToken未真正覆盖默认值——若渠道显式配置为 0,会回退到 LiteLLM 或 group config 的价格;同时区间定价 fallback 到 BasePricing 时图片价格也漏覆盖。image_output_tokens/image_output_cost字段,前端无法显示 token 拆分。Image generation models billed per token (e.g. gemini-3.x image-preview) had two related issues:
ImageOutputPricePerTokenwas not fully overriding the default sources — explicit zero on the channel would fall back to LiteLLM/group config; interval fallback to BasePricing also missed the image price.image_output_tokens/image_output_costfields needed for the breakdown.目的 / Purpose
让渠道定价完全主导按 token 计费的图片生成计费,并在用量页面正确展示 token / 费用拆分。
Make channel pricing the single source of truth for token-billed image generation, and properly display the token/cost breakdown on usage pages.
改动内容 / Changes
后端 / Backend
ImageOutputPriceExplicit标志(billing_service.go):区分"渠道显式配置图片输出价"和"未配置缺失值"。true时即使价格为 0 也不回退到 outputPriceGetModelPricingWithChannel:渠道存在时,未配置的ImageOutputPrice归零并设Explicit=true(不再回退 LiteLLM)computeTokenBreakdown:用Explicit标志决定是否回退 outputPricemodel_pricing_resolver.go:applyTokenOverridesflat / interval 两条路径都设 explicit + 归零;intervalToModelPricing也接收 channel pricing 设 explicitcalculateRecordUsageCost/ OpenAIcalculateOpenAIRecordUsageCost:渠道定价为 token 模式时图片走 token 路径而非按图计费buildRecordUsageLog+ OpenAIRecordUsage:图片请求走 token 计费时RateMultiplier用普通 multiplier 而非 image multiplierUsageLogDTO 添加image_output_tokens和image_output_cost(数据库/service 早有,DTO 漏映射)ImageOutputPriceExplicitflag (billing_service.go): Distinguish "channel explicitly set image output price" from "missing value". Whentrue, a zero price does NOT fall back to outputPriceGetModelPricingWithChannel: With a channel present, missingImageOutputPriceis zeroed and markedExplicit=true(no more LiteLLM fallback)computeTokenBreakdown: Uses theExplicitflag to decide whether to fall back to outputPricemodel_pricing_resolver.go: Both flat and interval paths inapplyTokenOverridesset explicit + zero;intervalToModelPricingalso receives channel pricing and sets explicitcalculateRecordUsageCost/ OpenAIcalculateOpenAIRecordUsageCost: When channel pricing is token-mode, image requests go through the token path instead of per-image billingbuildRecordUsageLog+ OpenAIRecordUsage: When a token-billed image request is recorded,RateMultiplieruses the regular multiplier rather than the image multiplierimage_output_tokensandimage_output_costtoUsageLogDTO (already existed in DB/service, missing in DTO mapping)前端 / Frontend
isImageUsage()现在检查billing_mode !== 'token',token 计费的图片请求走 token 展示路径而非图片格式isImageUsage、hasImageOutputTokens、textOutputTokens抽到@/utils/billingMode.ts和@/utils/imageUsage.ts,Admin 和 User 页面共用imageOutput*翻译统一到usage.*命名空间isImageUsage()now checksbilling_mode !== 'token', routing token-billed image requests to the token display pathisImageUsage,hasImageOutputTokens,textOutputTokensto shared utils used by both Admin and User pagesimageOutput*translation keys underusage.*namespace测试 / Tests
后端新增 6 个单测覆盖:
TestGetModelPricingWithChannel_NilImageOutputPriceZerosAndMarksExplicitTestComputeTokenBreakdown_ExplicitZeroImagePrice_NoFallbackTestComputeTokenBreakdown_NonExplicitZeroImagePrice_FallsBackToOutputTestApplyTokenOverrides_FlatSetsImageOutputPriceExplicitTestApplyTokenOverrides_FlatWithImageOutputPriceSetsExplicitTestApplyTokenOverrides_IntervalSetsImageOutputPriceExplicit全部 PASS。
Six new backend unit tests covering all four code paths (flat / interval / non-explicit fallback / explicit zero). All passing.
截图 / Screenshot
Token 列展示(按量计费的图片生成):
Token 明细 Tooltip:
费用 Tooltip:
备注 / Notes
This supersedes #2774 by bundling the backend billing fix with the frontend display changes; the prior PR's frontend-only scope depended on backend changes that hadn't landed.