Merge pull request #3587 from alfadb/fix/openai-gpt-55-pro-codex-model

fix(openai): 保留 GPT-5.5 Pro Codex 模型名
This commit is contained in:
Wesley Liddick
2026-07-01 10:54:51 +08:00
committed by GitHub
7 changed files with 62 additions and 2 deletions
+5 -2
View File
@@ -276,8 +276,9 @@ func (s *BillingService) initFallbackPricing() {
LongContextInputMultiplier: openAIGPT54LongContextInputMultiplier,
LongContextOutputMultiplier: openAIGPT54LongContextOutputMultiplier,
}
// GPT-5.5 暂无独立定价,回退到 GPT-5.4
// GPT-5.5 / GPT-5.5 Pro 暂无独立定价,回退到 GPT-5.4。
s.fallbackPrices["gpt-5.5"] = s.fallbackPrices["gpt-5.4"]
s.fallbackPrices["gpt-5.5-pro"] = s.fallbackPrices["gpt-5.4"]
s.fallbackPrices["gpt-5.4-mini"] = &ModelPricing{
InputPricePerToken: 7.5e-7,
@@ -666,6 +667,8 @@ func (s *BillingService) getFallbackPricing(model string) *ModelPricing {
// OpenAI(GPT-5 / Codex 族):仅匹配已知型号,避免未知 OpenAI 型号误计价。
if normalized := normalizeKnownOpenAICodexModel(modelLower); normalized != "" {
switch normalized {
case "gpt-5.5-pro":
return s.fallbackPrices["gpt-5.5-pro"]
case "gpt-5.5":
return s.fallbackPrices["gpt-5.5"]
case "gpt-5.4-mini":
@@ -1057,7 +1060,7 @@ func isOpenAIGPT54Model(model string) bool {
// normalizeCodexModel 的默认兜底把非 OpenAI 模型(claude-*、gemini-*、gpt-4o)
// 误识别为 gpt-5.4。
normalized := normalizeKnownOpenAICodexModel(model)
return normalized == "gpt-5.4" || normalized == "gpt-5.5"
return normalized == "gpt-5.4" || normalized == "gpt-5.5" || normalized == "gpt-5.5-pro"
}
// CalculateCostWithConfig 使用配置中的默认倍率计算费用
@@ -263,6 +263,25 @@ func TestCalculateCost_OpenAIGPT54LongContextAppliesWholeSessionMultipliers(t *t
require.InDelta(t, expectedInput+expectedOutput, cost.ActualCost, 1e-10)
}
func TestCalculateCost_OpenAIGPT55ProUsesGPT55PricingPolicy(t *testing.T) {
svc := newTestBillingService()
tokens := UsageTokens{
InputTokens: 300000,
OutputTokens: 4000,
}
cost, err := svc.CalculateCost("gpt-5.5-pro", tokens, 1.0)
require.NoError(t, err)
expectedInput := float64(tokens.InputTokens) * 2.5e-6 * 2.0
expectedOutput := float64(tokens.OutputTokens) * 15e-6 * 1.5
require.InDelta(t, expectedInput, cost.InputCost, 1e-10)
require.InDelta(t, expectedOutput, cost.OutputCost, 1e-10)
require.InDelta(t, expectedInput+expectedOutput, cost.TotalCost, 1e-10)
require.InDelta(t, expectedInput+expectedOutput, cost.ActualCost, 1e-10)
}
// 回归测试 #2293:长上下文计费触发时,cache_read_tokens 也应应用 LongContextInputMultiplier。
// 修复前:CacheReadCost = tokens * 0.25e-6 (漏乘倍率,少计费用)。
// 修复后:CacheReadCost = tokens * 0.25e-6 * LongContextInputMultiplier(=2.0)。
@@ -10,6 +10,7 @@ import (
var codexModelMap = map[string]string{
"gpt-5.5": "gpt-5.5",
"gpt-5.5-pro": "gpt-5.5-pro",
"codex-auto-review": "codex-auto-review",
"gpt-5.4": "gpt-5.4",
"gpt-5.4-mini": "gpt-5.4-mini",
@@ -61,6 +62,7 @@ var codexVersionModelPrefixes = []struct {
{prefix: "gpt-5.3-codex", target: "gpt-5.3-codex"},
{prefix: "gpt-5.4-mini", target: "gpt-5.4-mini"},
{prefix: "gpt-5.4-nano", target: "gpt-5.4-nano"},
{prefix: "gpt-5.5-pro", target: "gpt-5.5-pro"},
{prefix: "gpt-5.5", target: "gpt-5.5"},
{prefix: "gpt-5.4", target: "gpt-5.4"},
{prefix: "gpt-5.2", target: "gpt-5.2"},
@@ -898,6 +898,10 @@ func TestNormalizeCodexModel_Gpt53(t *testing.T) {
"gpt-5.4": "gpt-5.4",
"gpt5.5": "gpt-5.5",
"openai/gpt5.5": "gpt-5.5",
"gpt-5.5-pro": "gpt-5.5-pro",
"gpt5.5-pro": "gpt-5.5-pro",
"openai/gpt5.5-pro": "gpt-5.5-pro",
"gpt-5.5-pro-high": "gpt-5.5-pro",
"codex-auto-review": "codex-auto-review",
"gpt5.4": "gpt-5.4",
"gpt-5.4-high": "gpt-5.4",
@@ -16,6 +16,7 @@ func mustRawJSON(t *testing.T, s string) json.RawMessage {
func TestShouldAutoInjectPromptCacheKeyForCompat(t *testing.T) {
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.5"))
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.5-pro"))
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.4"))
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.4-mini"))
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.2"))
@@ -65,6 +65,8 @@ func normalizeKnownOpenAICodexModel(model string) string {
}
switch {
case strings.Contains(normalized, "gpt-5.5-pro"):
return "gpt-5.5-pro"
case strings.Contains(normalized, "gpt-5.5"):
return "gpt-5.5"
case strings.Contains(normalized, "gpt-5.4-mini"):
@@ -94,6 +94,15 @@ func TestResolveOpenAIForwardModel(t *testing.T) {
defaultMappedModel: "gpt-5.4",
expectedModel: "gpt-5.5",
},
{
name: "preserves gpt-5.5-pro instead of group default",
account: &Account{
Credentials: map[string]any{},
},
requestedModel: "gpt-5.5-pro",
defaultMappedModel: "gpt-5.5",
expectedModel: "gpt-5.5-pro",
},
{
name: "preserves compact-spelled gpt5.5 instead of group default",
account: &Account{
@@ -261,6 +270,12 @@ func TestNormalizeOpenAIModelForUpstream(t *testing.T) {
model: "gpt-5.4-high",
want: "gpt-5.4",
},
{
name: "oauth preserves GPT-5.5 Pro model",
account: &Account{Type: AccountTypeOAuth},
model: "openai/gpt-5.5-pro",
want: "gpt-5.5-pro",
},
{
name: "oauth preserves codex auto review model",
account: &Account{Type: AccountTypeOAuth},
@@ -303,3 +318,17 @@ func TestUsageBillingModelCandidatesPreserveCodexAutoReviewModel(t *testing.T) {
}
}
}
func TestUsageBillingModelCandidatesPreserveGPT55ProModel(t *testing.T) {
candidates := usageBillingModelCandidates("openai/gpt-5.5-pro")
expected := []string{"openai/gpt-5.5-pro", "gpt-5.5-pro"}
if len(candidates) != len(expected) {
t.Fatalf("usageBillingModelCandidates(openai/gpt-5.5-pro) = %#v, want %#v", candidates, expected)
}
for i := range expected {
if candidates[i] != expected[i] {
t.Fatalf("usageBillingModelCandidates(openai/gpt-5.5-pro) = %#v, want %#v", candidates, expected)
}
}
}