fix 2522 - #3035
Conversation
# Conflicts: # dto/gemini.go # relay/channel/gemini/relay-gemini.go # setting/ratio_setting/exposed_cache.go # setting/ratio_setting/model_ratio.go # web/src/hooks/usage-logs/useUsageLogsData.jsx
WalkthroughThe PR adds image output ratio support for pricing calculations, introducing new configuration handling, token tracking structures, and pricing logic across backend controllers, relay handlers, settings, and frontend UI components. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
relay/channel/gemini/relay-gemini.go (1)
1360-1362: Magic number 1400 for image token estimation.The hardcoded value
1400tokens per image should be documented or extracted to a named constant for clarity and maintainability.📝 Suggested improvement
+// geminiImageTokenEstimate is the estimated token count per generated image +// when the API doesn't return explicit token counts +const geminiImageTokenEstimate = 1400 + func geminiStreamHandler(...) { ... if imageCount != 0 && usage.CompletionTokens == 0 { - usage.CompletionTokens = imageCount * 1400 + usage.CompletionTokens = imageCount * geminiImageTokenEstimate }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@relay/channel/gemini/relay-gemini.go` around lines 1360 - 1362, Replace the magic number 1400 used to estimate tokens per image with a named constant and document it; specifically, define a descriptive constant (e.g., EstimatedImageTokensPerImage or IMAGE_TOKEN_ESTIMATE) near the top of the package and use it in the conditional that sets usage.CompletionTokens when imageCount != 0 and usage.CompletionTokens == 0, and add a short comment explaining the basis for the estimate or reference to where it came from.types/price_data.go (1)
41-43:ToSetting()doesn't include the newImageOutputRatiofield.The
ToSetting()method is used for debug logging (e.g., inrelay/helper/price.go:139) but doesn't include the newly addedImageOutputRatiofield, which could make debugging image output pricing issues harder.📝 Suggested fix
func (p *PriceData) ToSetting() string { - return fmt.Sprintf("ModelPrice: %f, ModelRatio: %f, CompletionRatio: %f, CacheRatio: %f, GroupRatio: %f, UsePrice: %t, CacheCreationRatio: %f, CacheCreation5mRatio: %f, CacheCreation1hRatio: %f, QuotaToPreConsume: %d, ImageRatio: %f, AudioRatio: %f, AudioCompletionRatio: %f", p.ModelPrice, p.ModelRatio, p.CompletionRatio, p.CacheRatio, p.GroupRatioInfo.GroupRatio, p.UsePrice, p.CacheCreationRatio, p.CacheCreation5mRatio, p.CacheCreation1hRatio, p.QuotaToPreConsume, p.ImageRatio, p.AudioRatio, p.AudioCompletionRatio) + return fmt.Sprintf("ModelPrice: %f, ModelRatio: %f, CompletionRatio: %f, CacheRatio: %f, GroupRatio: %f, UsePrice: %t, CacheCreationRatio: %f, CacheCreation5mRatio: %f, CacheCreation1hRatio: %f, QuotaToPreConsume: %d, ImageRatio: %f, ImageOutputRatio: %f, AudioRatio: %f, AudioCompletionRatio: %f", p.ModelPrice, p.ModelRatio, p.CompletionRatio, p.CacheRatio, p.GroupRatioInfo.GroupRatio, p.UsePrice, p.CacheCreationRatio, p.CacheCreation5mRatio, p.CacheCreation1hRatio, p.QuotaToPreConsume, p.ImageRatio, p.ImageOutputRatio, p.AudioRatio, p.AudioCompletionRatio) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@types/price_data.go` around lines 41 - 43, The ToSetting method on PriceData is missing the new ImageOutputRatio field; update PriceData.ToSetting() (the function named ToSetting) to include ImageOutputRatio in the fmt.Sprintf format string and add p.ImageOutputRatio to the argument list so the debug string prints the image output pricing ratio alongside the other fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@controller/option.go`:
- Around line 154-162: The controller is mutating runtime ratio state by calling
ratio_setting.UpdateImageOutputRatioByJSONString(option.Value.(string)) before
persistence; instead, change this flow so the controller only validates the
incoming ratio JSON (e.g., parse/validate the payload or call a non-mutating
validator) and do not call the mutating UpdateImageOutputRatioByJSONString here;
let model.UpdateOption (and its updateOptionMap path) be the single place that
applies the runtime mutation after the DB update succeeds. Concretely: remove
the direct mutation call from the controller, replace it with a validation step
(or a new ratio_setting.ValidateImageOutputRatioJSON function) to ensure the
value is well-formed, and ensure model.UpdateOption/updateOptionMap performs the
actual ratio_setting.UpdateImageOutputRatioByJSONString call after successful
persistence.
In `@setting/ratio_setting/model_ratio.go`:
- Around line 647-656: UpdateImageOutputRatioByJSONString currently clears
imageOutputRatioMap before unmarshaling, which wipes config on invalid JSON;
instead, unmarshal into a local temp map (e.g., tmp := map[string]float64{}),
keep imageOutputRatioMap untouched while decoding, and only acquire the
imageOutputRatioMapMutex to replace imageOutputRatioMap and call
InvalidateExposedDataCache when unmarshaling succeeds (preserve the lock usage
around the swap). Reference: UpdateImageOutputRatioByJSONString,
imageOutputRatioMap, imageOutputRatioMapMutex, InvalidateExposedDataCache.
---
Nitpick comments:
In `@relay/channel/gemini/relay-gemini.go`:
- Around line 1360-1362: Replace the magic number 1400 used to estimate tokens
per image with a named constant and document it; specifically, define a
descriptive constant (e.g., EstimatedImageTokensPerImage or
IMAGE_TOKEN_ESTIMATE) near the top of the package and use it in the conditional
that sets usage.CompletionTokens when imageCount != 0 and usage.CompletionTokens
== 0, and add a short comment explaining the basis for the estimate or reference
to where it came from.
In `@types/price_data.go`:
- Around line 41-43: The ToSetting method on PriceData is missing the new
ImageOutputRatio field; update PriceData.ToSetting() (the function named
ToSetting) to include ImageOutputRatio in the fmt.Sprintf format string and add
p.ImageOutputRatio to the argument list so the debug string prints the image
output pricing ratio alongside the other fields.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
controller/option.godto/gemini.godto/openai_response.gomodel/option.gomodel/user_oauth_binding.gorelay/channel/gemini/relay-gemini.gorelay/channel/openai/relay-openai.gorelay/compatible_handler.gorelay/helper/price.goservice/task_billing_test.gosetting/ratio_setting/exposed_cache.gosetting/ratio_setting/model_ratio.gotypes/price_data.goweb/src/components/settings/RatioSetting.jsxweb/src/helpers/render.jsxweb/src/hooks/usage-logs/useUsageLogsData.jsxweb/src/pages/Setting/Ratio/ModelRatioSettings.jsx
| case "ImageOutputRatio": | ||
| err = ratio_setting.UpdateImageOutputRatioByJSONString(option.Value.(string)) | ||
| if err != nil { | ||
| c.JSON(http.StatusOK, gin.H{ | ||
| "success": false, | ||
| "message": "图片输出倍率设置失败: " + err.Error(), | ||
| }) | ||
| return | ||
| } |
There was a problem hiding this comment.
Don’t mutate runtime ratio state during controller validation.
Line 155 applies the update before model.UpdateOption persists it. If DB/update fails later, memory state can diverge from stored state.
🔧 Suggested direction
case "ImageOutputRatio":
- err = ratio_setting.UpdateImageOutputRatioByJSONString(option.Value.(string))
+ err = ratio_setting.CheckImageOutputRatioByJSONString(option.Value.(string))
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "图片输出倍率设置失败: " + err.Error(),
})
return
}Then keep the actual mutation only in model.UpdateOption -> updateOptionMap.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@controller/option.go` around lines 154 - 162, The controller is mutating
runtime ratio state by calling
ratio_setting.UpdateImageOutputRatioByJSONString(option.Value.(string)) before
persistence; instead, change this flow so the controller only validates the
incoming ratio JSON (e.g., parse/validate the payload or call a non-mutating
validator) and do not call the mutating UpdateImageOutputRatioByJSONString here;
let model.UpdateOption (and its updateOptionMap path) be the single place that
applies the runtime mutation after the DB update succeeds. Concretely: remove
the direct mutation call from the controller, replace it with a validation step
(or a new ratio_setting.ValidateImageOutputRatioJSON function) to ensure the
value is well-formed, and ensure model.UpdateOption/updateOptionMap performs the
actual ratio_setting.UpdateImageOutputRatioByJSONString call after successful
persistence.
| func UpdateImageOutputRatioByJSONString(jsonStr string) error { | ||
| imageOutputRatioMapMutex.Lock() | ||
| defer imageOutputRatioMapMutex.Unlock() | ||
| imageOutputRatioMap = make(map[string]float64) | ||
| err := common.Unmarshal([]byte(jsonStr), &imageOutputRatioMap) | ||
| if err == nil { | ||
| InvalidateExposedDataCache() | ||
| } | ||
| return err | ||
| } |
There was a problem hiding this comment.
Avoid clearing existing ratios on invalid JSON updates.
Line 650 resets imageOutputRatioMap before decode. If Line 651 fails, runtime config is silently wiped.
🔧 Proposed fix
func UpdateImageOutputRatioByJSONString(jsonStr string) error {
- imageOutputRatioMapMutex.Lock()
- defer imageOutputRatioMapMutex.Unlock()
- imageOutputRatioMap = make(map[string]float64)
- err := common.Unmarshal([]byte(jsonStr), &imageOutputRatioMap)
- if err == nil {
- InvalidateExposedDataCache()
- }
- return err
+ next := make(map[string]float64)
+ if err := common.Unmarshal([]byte(jsonStr), &next); err != nil {
+ return err
+ }
+ imageOutputRatioMapMutex.Lock()
+ defer imageOutputRatioMapMutex.Unlock()
+ imageOutputRatioMap = next
+ InvalidateExposedDataCache()
+ return nil
}📝 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.
| func UpdateImageOutputRatioByJSONString(jsonStr string) error { | |
| imageOutputRatioMapMutex.Lock() | |
| defer imageOutputRatioMapMutex.Unlock() | |
| imageOutputRatioMap = make(map[string]float64) | |
| err := common.Unmarshal([]byte(jsonStr), &imageOutputRatioMap) | |
| if err == nil { | |
| InvalidateExposedDataCache() | |
| } | |
| return err | |
| } | |
| func UpdateImageOutputRatioByJSONString(jsonStr string) error { | |
| next := make(map[string]float64) | |
| if err := common.Unmarshal([]byte(jsonStr), &next); err != nil { | |
| return err | |
| } | |
| imageOutputRatioMapMutex.Lock() | |
| defer imageOutputRatioMapMutex.Unlock() | |
| imageOutputRatioMap = next | |
| InvalidateExposedDataCache() | |
| return nil | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@setting/ratio_setting/model_ratio.go` around lines 647 - 656,
UpdateImageOutputRatioByJSONString currently clears imageOutputRatioMap before
unmarshaling, which wipes config on invalid JSON; instead, unmarshal into a
local temp map (e.g., tmp := map[string]float64{}), keep imageOutputRatioMap
untouched while decoding, and only acquire the imageOutputRatioMapMutex to
replace imageOutputRatioMap and call InvalidateExposedDataCache when
unmarshaling succeeds (preserve the lock usage around the swap). Reference:
UpdateImageOutputRatioByJSONString, imageOutputRatioMap,
imageOutputRatioMapMutex, InvalidateExposedDataCache.
Summary by CodeRabbit
New Features
Improvements