feat(openai): 支持 gpt-5.3-codex-spark 并统一 gpt-5.3 到 codex 计费

This commit is contained in:
yangjianbo
2026-02-13 09:28:07 +08:00
parent abf5de69fb
commit 3734abed4c
8 changed files with 131 additions and 59 deletions

View File

@@ -15,8 +15,8 @@ type Model struct {
// DefaultModels OpenAI models list // DefaultModels OpenAI models list
var DefaultModels = []Model{ var DefaultModels = []Model{
{ID: "gpt-5.3", Object: "model", Created: 1735689600, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.3"},
{ID: "gpt-5.3-codex", Object: "model", Created: 1735689600, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.3 Codex"}, {ID: "gpt-5.3-codex", Object: "model", Created: 1735689600, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.3 Codex"},
{ID: "gpt-5.3-codex-spark", Object: "model", Created: 1735689600, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.3 Codex Spark"},
{ID: "gpt-5.2", Object: "model", Created: 1733875200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.2"}, {ID: "gpt-5.2", Object: "model", Created: 1733875200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.2"},
{ID: "gpt-5.2-codex", Object: "model", Created: 1733011200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.2 Codex"}, {ID: "gpt-5.2-codex", Object: "model", Created: 1733011200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.2 Codex"},
{ID: "gpt-5.1-codex-max", Object: "model", Created: 1730419200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.1 Codex Max"}, {ID: "gpt-5.1-codex-max", Object: "model", Created: 1730419200, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.1 Codex Max"},

View File

@@ -9,54 +9,59 @@ import (
var codexCLIInstructions string var codexCLIInstructions string
var codexModelMap = map[string]string{ var codexModelMap = map[string]string{
"gpt-5.3": "gpt-5.3", "gpt-5.3": "gpt-5.3-codex",
"gpt-5.3-none": "gpt-5.3", "gpt-5.3-none": "gpt-5.3-codex",
"gpt-5.3-low": "gpt-5.3", "gpt-5.3-low": "gpt-5.3-codex",
"gpt-5.3-medium": "gpt-5.3", "gpt-5.3-medium": "gpt-5.3-codex",
"gpt-5.3-high": "gpt-5.3", "gpt-5.3-high": "gpt-5.3-codex",
"gpt-5.3-xhigh": "gpt-5.3", "gpt-5.3-xhigh": "gpt-5.3-codex",
"gpt-5.3-codex": "gpt-5.3-codex", "gpt-5.3-codex": "gpt-5.3-codex",
"gpt-5.3-codex-low": "gpt-5.3-codex", "gpt-5.3-codex-spark": "gpt-5.3-codex",
"gpt-5.3-codex-medium": "gpt-5.3-codex", "gpt-5.3-codex-spark-low": "gpt-5.3-codex",
"gpt-5.3-codex-high": "gpt-5.3-codex", "gpt-5.3-codex-spark-medium": "gpt-5.3-codex",
"gpt-5.3-codex-xhigh": "gpt-5.3-codex", "gpt-5.3-codex-spark-high": "gpt-5.3-codex",
"gpt-5.1-codex": "gpt-5.1-codex", "gpt-5.3-codex-spark-xhigh": "gpt-5.3-codex",
"gpt-5.1-codex-low": "gpt-5.1-codex", "gpt-5.3-codex-low": "gpt-5.3-codex",
"gpt-5.1-codex-medium": "gpt-5.1-codex", "gpt-5.3-codex-medium": "gpt-5.3-codex",
"gpt-5.1-codex-high": "gpt-5.1-codex", "gpt-5.3-codex-high": "gpt-5.3-codex",
"gpt-5.1-codex-max": "gpt-5.1-codex-max", "gpt-5.3-codex-xhigh": "gpt-5.3-codex",
"gpt-5.1-codex-max-low": "gpt-5.1-codex-max", "gpt-5.1-codex": "gpt-5.1-codex",
"gpt-5.1-codex-max-medium": "gpt-5.1-codex-max", "gpt-5.1-codex-low": "gpt-5.1-codex",
"gpt-5.1-codex-max-high": "gpt-5.1-codex-max", "gpt-5.1-codex-medium": "gpt-5.1-codex",
"gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max", "gpt-5.1-codex-high": "gpt-5.1-codex",
"gpt-5.2": "gpt-5.2", "gpt-5.1-codex-max": "gpt-5.1-codex-max",
"gpt-5.2-none": "gpt-5.2", "gpt-5.1-codex-max-low": "gpt-5.1-codex-max",
"gpt-5.2-low": "gpt-5.2", "gpt-5.1-codex-max-medium": "gpt-5.1-codex-max",
"gpt-5.2-medium": "gpt-5.2", "gpt-5.1-codex-max-high": "gpt-5.1-codex-max",
"gpt-5.2-high": "gpt-5.2", "gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max",
"gpt-5.2-xhigh": "gpt-5.2", "gpt-5.2": "gpt-5.2",
"gpt-5.2-codex": "gpt-5.2-codex", "gpt-5.2-none": "gpt-5.2",
"gpt-5.2-codex-low": "gpt-5.2-codex", "gpt-5.2-low": "gpt-5.2",
"gpt-5.2-codex-medium": "gpt-5.2-codex", "gpt-5.2-medium": "gpt-5.2",
"gpt-5.2-codex-high": "gpt-5.2-codex", "gpt-5.2-high": "gpt-5.2",
"gpt-5.2-codex-xhigh": "gpt-5.2-codex", "gpt-5.2-xhigh": "gpt-5.2",
"gpt-5.1-codex-mini": "gpt-5.1-codex-mini", "gpt-5.2-codex": "gpt-5.2-codex",
"gpt-5.1-codex-mini-medium": "gpt-5.1-codex-mini", "gpt-5.2-codex-low": "gpt-5.2-codex",
"gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini", "gpt-5.2-codex-medium": "gpt-5.2-codex",
"gpt-5.1": "gpt-5.1", "gpt-5.2-codex-high": "gpt-5.2-codex",
"gpt-5.1-none": "gpt-5.1", "gpt-5.2-codex-xhigh": "gpt-5.2-codex",
"gpt-5.1-low": "gpt-5.1", "gpt-5.1-codex-mini": "gpt-5.1-codex-mini",
"gpt-5.1-medium": "gpt-5.1", "gpt-5.1-codex-mini-medium": "gpt-5.1-codex-mini",
"gpt-5.1-high": "gpt-5.1", "gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini",
"gpt-5.1-chat-latest": "gpt-5.1", "gpt-5.1": "gpt-5.1",
"gpt-5-codex": "gpt-5.1-codex", "gpt-5.1-none": "gpt-5.1",
"codex-mini-latest": "gpt-5.1-codex-mini", "gpt-5.1-low": "gpt-5.1",
"gpt-5-codex-mini": "gpt-5.1-codex-mini", "gpt-5.1-medium": "gpt-5.1",
"gpt-5-codex-mini-medium": "gpt-5.1-codex-mini", "gpt-5.1-high": "gpt-5.1",
"gpt-5-codex-mini-high": "gpt-5.1-codex-mini", "gpt-5.1-chat-latest": "gpt-5.1",
"gpt-5": "gpt-5.1", "gpt-5-codex": "gpt-5.1-codex",
"gpt-5-mini": "gpt-5.1", "codex-mini-latest": "gpt-5.1-codex-mini",
"gpt-5-nano": "gpt-5.1", "gpt-5-codex-mini": "gpt-5.1-codex-mini",
"gpt-5-codex-mini-medium": "gpt-5.1-codex-mini",
"gpt-5-codex-mini-high": "gpt-5.1-codex-mini",
"gpt-5": "gpt-5.1",
"gpt-5-mini": "gpt-5.1",
"gpt-5-nano": "gpt-5.1",
} }
type codexTransformResult struct { type codexTransformResult struct {
@@ -153,7 +158,7 @@ func normalizeCodexModel(model string) string {
return "gpt-5.3-codex" return "gpt-5.3-codex"
} }
if strings.Contains(normalized, "gpt-5.3") || strings.Contains(normalized, "gpt 5.3") { if strings.Contains(normalized, "gpt-5.3") || strings.Contains(normalized, "gpt 5.3") {
return "gpt-5.3" return "gpt-5.3-codex"
} }
if strings.Contains(normalized, "gpt-5.1-codex-max") || strings.Contains(normalized, "gpt 5.1 codex max") { if strings.Contains(normalized, "gpt-5.1-codex-max") || strings.Contains(normalized, "gpt 5.1 codex max") {
return "gpt-5.1-codex-max" return "gpt-5.1-codex-max"

View File

@@ -167,10 +167,13 @@ func TestApplyCodexOAuthTransform_EmptyInput(t *testing.T) {
func TestNormalizeCodexModel_Gpt53(t *testing.T) { func TestNormalizeCodexModel_Gpt53(t *testing.T) {
cases := map[string]string{ cases := map[string]string{
"gpt-5.3": "gpt-5.3", "gpt-5.3": "gpt-5.3-codex",
"gpt-5.3-codex": "gpt-5.3-codex", "gpt-5.3-codex": "gpt-5.3-codex",
"gpt-5.3-codex-xhigh": "gpt-5.3-codex", "gpt-5.3-codex-xhigh": "gpt-5.3-codex",
"gpt 5.3 codex": "gpt-5.3-codex", "gpt-5.3-codex-spark": "gpt-5.3-codex",
"gpt-5.3-codex-spark-high": "gpt-5.3-codex",
"gpt-5.3-codex-spark-xhigh": "gpt-5.3-codex",
"gpt 5.3 codex": "gpt-5.3-codex",
} }
for input, expected := range cases { for input, expected := range cases {

View File

@@ -650,11 +650,20 @@ func (s *PricingService) matchByModelFamily(model string) *LiteLLMModelPricing {
// matchOpenAIModel OpenAI 模型回退匹配策略 // matchOpenAIModel OpenAI 模型回退匹配策略
// 回退顺序: // 回退顺序:
// 1. gpt-5.2-codex -> gpt-5.2去掉后缀如 -codex, -mini, -max 等 // 1. gpt-5.3-codex-spark* -> gpt-5.1-codex按业务要求固定计费
// 2. gpt-5.2-20251222 -> gpt-5.2(去掉日期版本号 // 2. gpt-5.2-codex -> gpt-5.2(去掉后缀如 -codex, -mini, -max 等
// 3. gpt-5.3-codex -> gpt-5.2-codex // 3. gpt-5.2-20251222 -> gpt-5.2(去掉日期版本号)
// 4. 最终回退到 DefaultTestModel (gpt-5.1-codex) // 4. gpt-5.3-codex -> gpt-5.2-codex
// 5. 最终回退到 DefaultTestModel (gpt-5.1-codex)
func (s *PricingService) matchOpenAIModel(model string) *LiteLLMModelPricing { func (s *PricingService) matchOpenAIModel(model string) *LiteLLMModelPricing {
if strings.HasPrefix(model, "gpt-5.3-codex-spark") {
if pricing, ok := s.pricingData["gpt-5.1-codex"]; ok {
logger.LegacyPrintf("service.pricing", "[Pricing][SparkBilling] %s -> %s billing", model, "gpt-5.1-codex")
logger.LegacyPrintf("service.pricing", "[Pricing] OpenAI fallback matched %s -> %s", model, "gpt-5.1-codex")
return pricing
}
}
// 尝试的回退变体 // 尝试的回退变体
variants := s.generateOpenAIModelVariants(model, openAIModelDatePattern) variants := s.generateOpenAIModelVariants(model, openAIModelDatePattern)

View File

@@ -0,0 +1,35 @@
package service
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestGetModelPricing_Gpt53CodexSparkUsesGpt51CodexPricing(t *testing.T) {
sparkPricing := &LiteLLMModelPricing{InputCostPerToken: 1}
gpt53Pricing := &LiteLLMModelPricing{InputCostPerToken: 9}
svc := &PricingService{
pricingData: map[string]*LiteLLMModelPricing{
"gpt-5.1-codex": sparkPricing,
"gpt-5.3": gpt53Pricing,
},
}
got := svc.GetModelPricing("gpt-5.3-codex-spark")
require.Same(t, sparkPricing, got)
}
func TestGetModelPricing_Gpt53CodexFallbackStillUsesGpt52Codex(t *testing.T) {
gpt52CodexPricing := &LiteLLMModelPricing{InputCostPerToken: 2}
svc := &PricingService{
pricingData: map[string]*LiteLLMModelPricing{
"gpt-5.2-codex": gpt52CodexPricing,
},
}
got := svc.GetModelPricing("gpt-5.3-codex")
require.Same(t, gpt52CodexPricing, got)
}

View File

@@ -716,6 +716,7 @@ const allModels = [
{ value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' }, { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' },
{ value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' }, { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' },
{ value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }, { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' },
{ value: 'gpt-5.3-codex-spark', label: 'GPT-5.3 Codex Spark' },
{ value: 'gpt-5.2-2025-12-11', label: 'GPT-5.2' }, { value: 'gpt-5.2-2025-12-11', label: 'GPT-5.2' },
{ value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' }, { value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
{ value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' }, { value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
@@ -760,6 +761,12 @@ const presetMappings = [
to: 'claude-sonnet-4-5-20250929', to: 'claude-sonnet-4-5-20250929',
color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400'
}, },
{
label: 'GPT-5.3 Codex Spark',
from: 'gpt-5.3-codex-spark',
to: 'gpt-5.3-codex-spark',
color: 'bg-teal-100 text-teal-700 hover:bg-teal-200 dark:bg-teal-900/30 dark:text-teal-400'
},
{ {
label: 'GPT-5.2', label: 'GPT-5.2',
from: 'gpt-5.2-2025-12-11', from: 'gpt-5.2-2025-12-11',

View File

@@ -534,6 +534,18 @@ function generateOpenCodeConfig(platform: string, baseUrl: string, apiKey: strin
} }
} }
const openaiModels = { const openaiModels = {
'gpt-5.3-codex-spark': {
name: 'GPT-5.3 Codex Spark',
options: {
store: false
},
variants: {
low: {},
medium: {},
high: {},
xhigh: {}
}
},
'gpt-5.2-codex': { 'gpt-5.2-codex': {
name: 'GPT-5.2 Codex', name: 'GPT-5.2 Codex',
options: { options: {

View File

@@ -15,7 +15,7 @@ const openaiModels = [
'o4-mini', 'o4-mini',
// GPT-5 系列(同步后端定价文件) // GPT-5 系列(同步后端定价文件)
'gpt-5', 'gpt-5-2025-08-07', 'gpt-5-chat', 'gpt-5-chat-latest', 'gpt-5', 'gpt-5-2025-08-07', 'gpt-5-chat', 'gpt-5-chat-latest',
'gpt-5-codex', 'gpt-5-pro', 'gpt-5-pro-2025-10-06', 'gpt-5-codex', 'gpt-5.3-codex-spark', 'gpt-5-pro', 'gpt-5-pro-2025-10-06',
'gpt-5-mini', 'gpt-5-mini-2025-08-07', 'gpt-5-mini', 'gpt-5-mini-2025-08-07',
'gpt-5-nano', 'gpt-5-nano-2025-08-07', 'gpt-5-nano', 'gpt-5-nano-2025-08-07',
// GPT-5.1 系列 // GPT-5.1 系列
@@ -264,6 +264,7 @@ const openaiPresetMappings = [
{ label: 'o1', from: 'o1', to: 'o1', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' }, { label: 'o1', from: 'o1', to: 'o1', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
{ label: 'o3', from: 'o3', to: 'o3', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' }, { label: 'o3', from: 'o3', to: 'o3', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' },
{ label: 'GPT-5', from: 'gpt-5', to: 'gpt-5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }, { label: 'GPT-5', from: 'gpt-5', to: 'gpt-5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' },
{ label: 'GPT-5.3 Codex Spark', from: 'gpt-5.3-codex-spark', to: 'gpt-5.3-codex-spark', color: 'bg-teal-100 text-teal-700 hover:bg-teal-200 dark:bg-teal-900/30 dark:text-teal-400' },
{ label: 'GPT-5.1', from: 'gpt-5.1', to: 'gpt-5.1', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-400' }, { label: 'GPT-5.1', from: 'gpt-5.1', to: 'gpt-5.1', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-400' },
{ label: 'GPT-5.2', from: 'gpt-5.2', to: 'gpt-5.2', color: 'bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400' }, { label: 'GPT-5.2', from: 'gpt-5.2', to: 'gpt-5.2', color: 'bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400' },
{ label: 'GPT-5.1 Codex', from: 'gpt-5.1-codex', to: 'gpt-5.1-codex', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' } { label: 'GPT-5.1 Codex', from: 'gpt-5.1-codex', to: 'gpt-5.1-codex', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' }