-
Notifications
You must be signed in to change notification settings - Fork 11.1k
fix: openai responses api 未统计图像生成调用计费 #1813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -276,6 +276,13 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage | |||||||||||||||||||||||||||||||||||||||||||||||
| fileSearchTool.CallCount, dFileSearchQuota.String()) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| var dImageGenerationCallQuota decimal.Decimal | ||||||||||||||||||||||||||||||||||||||||||||||||
| var imageGenerationCallPrice float64 | ||||||||||||||||||||||||||||||||||||||||||||||||
| if ctx.GetBool("image_generation_call") { | ||||||||||||||||||||||||||||||||||||||||||||||||
| imageGenerationCallPrice = operation_setting.GetGPTImage1PriceOnceCall(ctx.GetString("image_generation_call_quality"), ctx.GetString("image_generation_call_size")) | ||||||||||||||||||||||||||||||||||||||||||||||||
| dImageGenerationCallQuota = decimal.NewFromFloat(imageGenerationCallPrice).Mul(dGroupRatio).Mul(dQuotaPerUnit) | ||||||||||||||||||||||||||||||||||||||||||||||||
| extraContent += fmt.Sprintf("Image Generation Call 花费 %s", dImageGenerationCallQuota.String()) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+279
to
286
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Under-billing when multiple image-generation calls occur. You bill as a single call; if N calls happen, only 1× price is charged. Apply this minimal diff (count defaulting to 1 for backward compatibility): var dImageGenerationCallQuota decimal.Decimal
var imageGenerationCallPrice float64
if ctx.GetBool("image_generation_call") {
- imageGenerationCallPrice = operation_setting.GetGPTImage1PriceOnceCall(ctx.GetString("image_generation_call_quality"), ctx.GetString("image_generation_call_size"))
- dImageGenerationCallQuota = decimal.NewFromFloat(imageGenerationCallPrice).Mul(dGroupRatio).Mul(dQuotaPerUnit)
- extraContent += fmt.Sprintf("Image Generation Call 花费 %s", dImageGenerationCallQuota.String())
+ imageGenerationCallPrice = operation_setting.GetGPTImage1PriceOnceCall(
+ ctx.GetString("image_generation_call_quality"),
+ ctx.GetString("image_generation_call_size"),
+ )
+ callCount := ctx.GetInt("image_generation_call_count")
+ if callCount <= 0 {
+ callCount = 1
+ }
+ dImageGenerationCallQuota = decimal.NewFromFloat(imageGenerationCallPrice).
+ Mul(decimal.NewFromInt(int64(callCount))).
+ Mul(dGroupRatio).Mul(dQuotaPerUnit)
+ extraContent += fmt.Sprintf("Image Generation Call %d 次,花费 %s", callCount, dImageGenerationCallQuota.String())
}Note: Best is summing per-call prices when qualities/sizes differ; see suggestions in openai/relay_responses.go. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| var quotaCalculateDecimal decimal.Decimal | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -331,6 +338,8 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage | |||||||||||||||||||||||||||||||||||||||||||||||
| quotaCalculateDecimal = quotaCalculateDecimal.Add(dFileSearchQuota) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // 添加 audio input 独立计费 | ||||||||||||||||||||||||||||||||||||||||||||||||
| quotaCalculateDecimal = quotaCalculateDecimal.Add(audioInputQuota) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // 添加 image generation call 计费 | ||||||||||||||||||||||||||||||||||||||||||||||||
| quotaCalculateDecimal = quotaCalculateDecimal.Add(dImageGenerationCallQuota) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| quota := int(quotaCalculateDecimal.Round(0).IntPart()) | ||||||||||||||||||||||||||||||||||||||||||||||||
| totalTokens := promptTokens + completionTokens | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -429,6 +438,10 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage | |||||||||||||||||||||||||||||||||||||||||||||||
| other["audio_input_token_count"] = audioTokens | ||||||||||||||||||||||||||||||||||||||||||||||||
| other["audio_input_price"] = audioInputPrice | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if !dImageGenerationCallQuota.IsZero() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| other["image_generation_call"] = true | ||||||||||||||||||||||||||||||||||||||||||||||||
| other["image_generation_call_price"] = imageGenerationCallPrice | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| model.RecordConsumeLog(ctx, relayInfo.UserId, model.RecordConsumeLogParams{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| ChannelId: relayInfo.ChannelId, | ||||||||||||||||||||||||||||||||||||||||||||||||
| PromptTokens: promptTokens, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,18 @@ const ( | |
| FileSearchPrice = 2.5 | ||
| ) | ||
|
|
||
| const ( | ||
| GPTImage1Low1024x1024 = 0.011 | ||
| GPTImage1Low1024x1536 = 0.016 | ||
| GPTImage1Low1536x1024 = 0.016 | ||
| GPTImage1Medium1024x1024 = 0.042 | ||
| GPTImage1Medium1024x1536 = 0.063 | ||
| GPTImage1Medium1536x1024 = 0.063 | ||
| GPTImage1High1024x1024 = 0.167 | ||
| GPTImage1High1024x1536 = 0.25 | ||
| GPTImage1High1536x1024 = 0.25 | ||
| ) | ||
|
|
||
| const ( | ||
| // Gemini Audio Input Price | ||
| Gemini25FlashPreviewInputAudioPrice = 1.00 | ||
|
|
@@ -65,3 +77,31 @@ func GetGeminiInputAudioPricePerMillionTokens(modelName string) float64 { | |
| } | ||
| return 0 | ||
| } | ||
|
|
||
| func GetGPTImage1PriceOnceCall(quality string, size string) float64 { | ||
| prices := map[string]map[string]float64{ | ||
| "low": { | ||
| "1024x1024": GPTImage1Low1024x1024, | ||
| "1024x1536": GPTImage1Low1024x1536, | ||
| "1536x1024": GPTImage1Low1536x1024, | ||
| }, | ||
| "medium": { | ||
| "1024x1024": GPTImage1Medium1024x1024, | ||
| "1024x1536": GPTImage1Medium1024x1536, | ||
| "1536x1024": GPTImage1Medium1536x1024, | ||
| }, | ||
| "high": { | ||
| "1024x1024": GPTImage1High1024x1024, | ||
| "1024x1536": GPTImage1High1024x1536, | ||
| "1536x1024": GPTImage1High1536x1024, | ||
| }, | ||
| } | ||
|
|
||
| if qualityMap, exists := prices[quality]; exists { | ||
| if price, exists := qualityMap[size]; exists { | ||
| return price | ||
| } | ||
| } | ||
|
|
||
| return GPTImage1High1024x1024 | ||
| } | ||
|
Comment on lines
+81
to
+107
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normalize inputs and avoid per-call map allocation.
Apply these diffs: +var gptImage1Prices = map[string]map[string]float64{
+ "low": {
+ "1024x1024": GPTImage1Low1024x1024,
+ "1024x1536": GPTImage1Low1024x1536,
+ "1536x1024": GPTImage1Low1536x1024,
+ },
+ "medium": {
+ "1024x1024": GPTImage1Medium1024x1024,
+ "1024x1536": GPTImage1Medium1024x1536,
+ "1536x1024": GPTImage1Medium1536x1024,
+ },
+ "high": {
+ "1024x1024": GPTImage1High1024x1024,
+ "1024x1536": GPTImage1High1024x1536,
+ "1536x1024": GPTImage1High1536x1024,
+ },
+}
+
func GetGPTImage1PriceOnceCall(quality string, size string) float64 {
- prices := map[string]map[string]float64{
- "low": { ... },
- "medium": { ... },
- "high": { ... },
- }
-
- if qualityMap, exists := prices[quality]; exists {
- if price, exists := qualityMap[size]; exists {
+ q := strings.ToLower(strings.TrimSpace(quality))
+ s := strings.ToLower(strings.TrimSpace(size))
+ if qualityMap, exists := gptImage1Prices[q]; exists {
+ if price, exists := qualityMap[s]; exists {
return price
}
}
return GPTImage1High1024x1024
}Optionally, support common synonyms ("standard"→"medium", "hd"→"high"). I can add a small alias map if desired. 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only detects presence; no support for multiple image-generation calls.
If a single Responses object includes multiple image-generation calls, billing will undercount. Provide a count API.
Apply this diff to expose a count helper:
func (o *OpenAIResponsesResponse) HasImageGenerationCall() bool { @@ } +// CountImageGenerationCalls returns how many image-generation calls are present. +func (o *OpenAIResponsesResponse) CountImageGenerationCalls() int { + if len(o.Output) == 0 { + return 0 + } + count := 0 + for _, output := range o.Output { + if output.Type == ResponsesOutputTypeImageGenerationCall { + count++ + } + } + return count +}📝 Committable suggestion
🤖 Prompt for AI Agents