diff --git a/backend/internal/pkg/openai/constants.go b/backend/internal/pkg/openai/constants.go index abe73e75b3..5c7cad3674 100644 --- a/backend/internal/pkg/openai/constants.go +++ b/backend/internal/pkg/openai/constants.go @@ -35,8 +35,6 @@ var DefaultModels = []Model{ {ID: "gpt-image-2", Object: "model", Created: 1738368000, OwnedBy: "openai", Type: "model", DisplayName: "GPT Image 2"}, {ID: "gpt-image-2.5-flare", Object: "model", Created: 1788825600, OwnedBy: "openai", Type: "model", DisplayName: "GPT Image 2.5 Flare"}, {ID: "gpt-image-2.5-sunburst", Object: "model", Created: 1788825600, OwnedBy: "openai", Type: "model", DisplayName: "GPT Image 2.5 Sunburst"}, - {ID: "gpt-image-2.5-flare-2026-09-08", Object: "model", Created: 1788825600, OwnedBy: "openai", Type: "model", DisplayName: "GPT Image 2.5 Flare (2026-09-08)"}, - {ID: "gpt-image-2.5-sunburst-2026-09-08", Object: "model", Created: 1788825600, OwnedBy: "openai", Type: "model", DisplayName: "GPT Image 2.5 Sunburst (2026-09-08)"}, } // DefaultModelIDs returns the default model ID list diff --git a/backend/internal/service/pricing_service.go b/backend/internal/service/pricing_service.go index 8ecc0eaa5b..a585487191 100644 --- a/backend/internal/service/pricing_service.go +++ b/backend/internal/service/pricing_service.go @@ -37,12 +37,16 @@ var ( // cache_creation_input_token_cost_above_1hr_above_200k_tokens 等)。 // 组 1 为基础价字段名主干,组 2 为 1h 缓存时长段(可为空),组 3 为服务档后缀(可为空)。 cacheTierPricePattern = regexp.MustCompile(`^(cache_(?:creation|read)_input_token_cost)(_above_1hr)?_above_\d+k_tokens((?:_[a-z]+)?)$`) - // 官方 Image 2.5 价格独立兜底,避免远程目录滞后时命中旧图片模型费率。 - openAIImage25FallbackPricing = &LiteLLMModelPricing{ - InputCostPerToken: 5e-6, CacheReadInputTokenCost: 1.25e-6, - InputCostPerImageToken: 8e-6, CacheReadInputImageTokenCost: 2e-6, - OutputCostPerImageToken: 30e-6, - LiteLLMProvider: "openai", Mode: "image_generation", SupportsPromptCaching: true, + // Official GPT Image 2.5 token rates (2026-09-08): + // https://developers.openai.com/api/docs/pricing#image-generation-models + openAIGPTImage25FallbackPricing = &LiteLLMModelPricing{ + InputCostPerToken: 5e-06, + CacheReadInputTokenCost: 1.25e-06, + InputCostPerImageToken: 8e-06, + OutputCostPerImageToken: 3e-05, + LiteLLMProvider: "openai", + Mode: "image_generation", + SupportsPromptCaching: true, } openAIGPT54FallbackPricing = &LiteLLMModelPricing{ InputCostPerToken: 2.5e-06, // $2.5 per MTok @@ -148,10 +152,9 @@ type LiteLLMModelPricing struct { LiteLLMProvider string `json:"litellm_provider"` Mode string `json:"mode"` SupportsPromptCaching bool `json:"supports_prompt_caching"` - OutputCostPerImage float64 `json:"output_cost_per_image"` // 图片生成模型每张图片价格 - OutputCostPerImageToken float64 `json:"output_cost_per_image_token"` // 图片输出 token 价格 - InputCostPerImageToken float64 `json:"input_cost_per_image_token"` // 图片输入 token 价格(如 gpt-image-2 图片编辑) - CacheReadInputImageTokenCost float64 `json:"cache_read_input_image_token_cost"` // 图片缓存输入价格 + OutputCostPerImage float64 `json:"output_cost_per_image"` // 图片生成模型每张图片价格 + OutputCostPerImageToken float64 `json:"output_cost_per_image_token"` // 图片输出 token 价格 + InputCostPerImageToken float64 `json:"input_cost_per_image_token"` // 图片输入 token 价格(如 gpt-image-2 图片编辑) // TokenPricingAbsent 表示源数据中 input/output token 价格均缺失(仅有图片价)。 // 此类条目只可用于图片计费,token 计费必须回退到 fallback 或 fail-closed, @@ -186,7 +189,6 @@ type LiteLLMRawEntry struct { OutputCostPerImage *float64 `json:"output_cost_per_image"` OutputCostPerImageToken *float64 `json:"output_cost_per_image_token"` InputCostPerImageToken *float64 `json:"input_cost_per_image_token"` - CacheReadInputImageTokenCost *float64 `json:"cache_read_input_image_token_cost"` } // PricingService 动态价格服务 @@ -655,9 +657,6 @@ func (s *PricingService) parsePricingData(body []byte) (map[string]*LiteLLMModel if entry.InputCostPerImageToken != nil { pricing.InputCostPerImageToken = *entry.InputCostPerImageToken } - if entry.CacheReadInputImageTokenCost != nil { - pricing.CacheReadInputImageTokenCost = *entry.CacheReadInputImageTokenCost - } hasExplicitLongContext := entry.LongContextInputTokenThreshold != nil || entry.LongContextInputCostMultiplier != nil || @@ -1440,7 +1439,7 @@ func (s *PricingService) matchByModelFamily(model string) *LiteLLMModelPricing { // 2. gpt-5.2-codex -> gpt-5.2(去掉后缀如 -codex, -mini, -max 等) // 3. gpt-5.2-20251222 -> gpt-5.2(去掉日期版本号) // 4. gpt-5.3-codex -> gpt-5.2-codex -// 5. gpt-5.4* / gpt-5.6* -> 业务静态兜底价 +// 5. gpt-5.4* -> 业务静态兜底价 // 6. 最终回退到 DefaultTestModel (gpt-5.1-codex) func (s *PricingService) matchOpenAIModel(model string) *LiteLLMModelPricing { if strings.HasPrefix(model, "gpt-5.3-codex-spark") { @@ -1518,11 +1517,14 @@ func (s *PricingService) matchOpenAIModel(model string) *LiteLLMModelPricing { return openAIGPT54FallbackPricing } - if isOpenAIImageGenerationModel(model) { - switch model { - case "gpt-image-2.5-flare", "gpt-image-2.5-sunburst", "gpt-image-2.5-flare-2026-09-08", "gpt-image-2.5-sunburst-2026-09-08": - return openAIImage25FallbackPricing + // Remote price mirrors can lag new releases. Never bill GPT Image 2.5 + // using the older image model's rates when its entry is absent. + for _, imageModel := range []string{"gpt-image-2.5-flare", "gpt-image-2.5-sunburst"} { + if model == imageModel || model == imageModel+"-2026-09-08" { + return openAIGPTImage25FallbackPricing } + } + if isOpenAIImageGenerationModel(model) { for _, candidate := range []string{"gpt-image-2", "gpt-image-1.5", "gpt-image-1"} { if pricing, ok := s.pricingData[candidate]; ok { logger.LegacyPrintf("service.pricing", "[Pricing] OpenAI image fallback matched %s -> %s", model, candidate) diff --git a/backend/resources/model-pricing/model_prices_and_context_window.json b/backend/resources/model-pricing/model_prices_and_context_window.json index 5e658196e6..579d18db86 100644 --- a/backend/resources/model-pricing/model_prices_and_context_window.json +++ b/backend/resources/model-pricing/model_prices_and_context_window.json @@ -6348,68 +6348,68 @@ "supports_vision": true, "supports_web_search": true }, - "gpt-image-2.5-flare": { - "cache_read_input_image_token_cost": 0.000002, - "cache_read_input_token_cost": 0.00000125, - "input_cost_per_image_token": 0.000008, - "input_cost_per_token": 0.000005, - "litellm_provider": "openai", - "mode": "image_generation", - "output_cost_per_image_token": 0.00003, - "output_cost_per_token": 0, - "supported_endpoints": [ - "/v1/images/generations", - "/v1/images/edits" - ], - "supports_vision": true, - "supports_prompt_caching": true - }, - "gpt-image-2.5-flare-2026-09-08": { - "cache_read_input_image_token_cost": 0.000002, - "cache_read_input_token_cost": 0.00000125, - "input_cost_per_image_token": 0.000008, - "input_cost_per_token": 0.000005, - "litellm_provider": "openai", - "mode": "image_generation", - "output_cost_per_image_token": 0.00003, - "output_cost_per_token": 0, - "supported_endpoints": [ - "/v1/images/generations", - "/v1/images/edits" - ], - "supports_vision": true, - "supports_prompt_caching": true - }, - "gpt-image-2.5-sunburst": { - "cache_read_input_image_token_cost": 0.000002, - "cache_read_input_token_cost": 0.00000125, - "input_cost_per_image_token": 0.000008, - "input_cost_per_token": 0.000005, - "litellm_provider": "openai", - "mode": "image_generation", - "output_cost_per_image_token": 0.00003, - "output_cost_per_token": 0, - "supported_endpoints": [ - "/v1/images/generations", - "/v1/images/edits" - ], - "supports_vision": true, - "supports_prompt_caching": true - }, - "gpt-image-2.5-sunburst-2026-09-08": { - "cache_read_input_image_token_cost": 0.000002, - "cache_read_input_token_cost": 0.00000125, - "input_cost_per_image_token": 0.000008, - "input_cost_per_token": 0.000005, - "litellm_provider": "openai", - "mode": "image_generation", - "output_cost_per_image_token": 0.00003, - "output_cost_per_token": 0, - "supported_endpoints": [ - "/v1/images/generations", - "/v1/images/edits" - ], - "supports_vision": true, - "supports_prompt_caching": true - } + "gpt-image-2.5-flare": { + "cache_read_input_image_token_cost": 2e-06, + "cache_read_input_token_cost": 1.25e-06, + "input_cost_per_image_token": 8e-06, + "input_cost_per_token": 5e-06, + "litellm_provider": "openai", + "mode": "image_generation", + "output_cost_per_image_token": 3e-05, + "output_cost_per_token": 0, + "supported_endpoints": [ + "/v1/images/generations", + "/v1/images/edits" + ], + "supports_vision": true, + "supports_prompt_caching": true + }, + "gpt-image-2.5-flare-2026-09-08": { + "cache_read_input_image_token_cost": 2e-06, + "cache_read_input_token_cost": 1.25e-06, + "input_cost_per_image_token": 8e-06, + "input_cost_per_token": 5e-06, + "litellm_provider": "openai", + "mode": "image_generation", + "output_cost_per_image_token": 3e-05, + "output_cost_per_token": 0, + "supported_endpoints": [ + "/v1/images/generations", + "/v1/images/edits" + ], + "supports_vision": true, + "supports_prompt_caching": true + }, + "gpt-image-2.5-sunburst": { + "cache_read_input_image_token_cost": 2e-06, + "cache_read_input_token_cost": 1.25e-06, + "input_cost_per_image_token": 8e-06, + "input_cost_per_token": 5e-06, + "litellm_provider": "openai", + "mode": "image_generation", + "output_cost_per_image_token": 3e-05, + "output_cost_per_token": 0, + "supported_endpoints": [ + "/v1/images/generations", + "/v1/images/edits" + ], + "supports_vision": true, + "supports_prompt_caching": true + }, + "gpt-image-2.5-sunburst-2026-09-08": { + "cache_read_input_image_token_cost": 2e-06, + "cache_read_input_token_cost": 1.25e-06, + "input_cost_per_image_token": 8e-06, + "input_cost_per_token": 5e-06, + "litellm_provider": "openai", + "mode": "image_generation", + "output_cost_per_image_token": 3e-05, + "output_cost_per_token": 0, + "supported_endpoints": [ + "/v1/images/generations", + "/v1/images/edits" + ], + "supports_vision": true, + "supports_prompt_caching": true + } } diff --git a/frontend/src/composables/useModelWhitelist.ts b/frontend/src/composables/useModelWhitelist.ts index c9a205f7bf..feed1c37f4 100644 --- a/frontend/src/composables/useModelWhitelist.ts +++ b/frontend/src/composables/useModelWhitelist.ts @@ -4,8 +4,6 @@ // OpenAI const openaiModels = [ - // GPT-5.6 系列 - 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna', // GPT-5.2 系列 'gpt-5.2', 'gpt-5.2-2025-12-11', 'gpt-5.2-chat-latest', 'gpt-5.2-pro', 'gpt-5.2-pro-2025-12-11', @@ -21,9 +19,7 @@ const openaiModels = [ 'gpt-5.3-codex-spark', 'codex-auto-review', 'gpt-4o-audio-preview', 'gpt-4o-realtime-preview', // GPT Image 系列 - 'gpt-image-1', 'gpt-image-1.5', 'gpt-image-2', - 'gpt-image-2.5-flare', 'gpt-image-2.5-sunburst', - 'gpt-image-2.5-flare-2026-09-08', 'gpt-image-2.5-sunburst-2026-09-08' + 'gpt-image-1', 'gpt-image-1.5', 'gpt-image-2', 'gpt-image-2.5-flare', 'gpt-image-2.5-sunburst' ] // Anthropic Claude