From 57c76584a6c15a26b3f6a54ca0ed5e7aa77ea97d Mon Sep 17 00:00:00 2001 From: Long Li Date: Sun, 30 Aug 2026 11:40:39 +0900 Subject: [PATCH] fix(codex): advertise priority tier for fast models --- .../service/openai_codex_models_service.go | 29 +++++++++- .../openai_codex_models_service_test.go | 56 ++++++++++++++++++- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/backend/internal/service/openai_codex_models_service.go b/backend/internal/service/openai_codex_models_service.go index b55549e753..3f164af38a 100644 --- a/backend/internal/service/openai_codex_models_service.go +++ b/backend/internal/service/openai_codex_models_service.go @@ -309,6 +309,12 @@ type configuredCodexTruncationPolicy struct { Limit int64 `json:"limit"` } +type configuredCodexServiceTier struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} + type configuredCodexModelMessages struct { InstructionsTemplate string `json:"instructions_template"` InstructionsVariables any `json:"instructions_variables"` @@ -336,7 +342,7 @@ type configuredCodexModelDescriptor struct { SupportedInAPI bool `json:"supported_in_api"` Priority int `json:"priority"` AdditionalSpeedTiers []string `json:"additional_speed_tiers"` - ServiceTiers []any `json:"service_tiers"` + ServiceTiers []configuredCodexServiceTier `json:"service_tiers"` DefaultServiceTier any `json:"default_service_tier"` AvailabilityNUX any `json:"availability_nux"` Upgrade any `json:"upgrade"` @@ -392,7 +398,7 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr SupportedInAPI: true, Priority: configuredCodexModelPriority, AdditionalSpeedTiers: []string{}, - ServiceTiers: []any{}, + ServiceTiers: []configuredCodexServiceTier{}, ModelMessages: configuredCodexModelMessages{InstructionsTemplate: openai.CodexBaseInstructionsForModel(modelID)}, SupportsReasoningSummaryParameter: true, DefaultReasoningSummary: "auto", @@ -448,6 +454,15 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr descriptor.DisplayName = openaiCodexDisplayName(modelID) descriptor.Description = "OpenAI GPT coding model routed through Sub2API." descriptor.SupportsParallelToolCalls = true + if configuredCodexSupportsPriorityServiceTier(modelID) { + descriptor.ServiceTiers = []configuredCodexServiceTier{ + { + ID: "priority", + Name: "Fast", + Description: "Priority processing for lower latency.", + }, + } + } if isOpenAICodexReasoningGPTModel(modelID) { defaultReasoningLevel := "medium" if getNormalizedCodexModel(modelID) == "gpt-5.6-sol" { @@ -471,6 +486,16 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr return descriptor } +func configuredCodexSupportsPriorityServiceTier(modelID string) bool { + normalized := canonicalizeOpenAIModelAliasSpelling(modelID) + for _, family := range []string{"gpt-5.4", "gpt-5.5", "gpt-5.6"} { + if normalized == family || strings.HasPrefix(normalized, family+"-") { + return true + } + } + return false +} + func configuredCodexGrokReasoningLevels(modelID string) []configuredCodexReasoningLevel { levels := []configuredCodexReasoningLevel{ {Effort: "low", Description: "Fast responses with lighter reasoning"}, diff --git a/backend/internal/service/openai_codex_models_service_test.go b/backend/internal/service/openai_codex_models_service_test.go index 77a65cfb38..5c98825a52 100644 --- a/backend/internal/service/openai_codex_models_service_test.go +++ b/backend/internal/service/openai_codex_models_service_test.go @@ -181,7 +181,7 @@ func requireCompleteConfiguredCodexModel(t *testing.T, model map[string]any, slu require.Equal(t, true, model["supported_in_api"]) require.NotNil(t, model["priority"]) require.Equal(t, []any{}, model["additional_speed_tiers"]) - require.Equal(t, []any{}, model["service_tiers"]) + require.IsType(t, []any{}, model["service_tiers"]) require.Contains(t, model, "default_service_tier") require.Contains(t, model, "availability_nux") require.Contains(t, model, "upgrade") @@ -403,6 +403,51 @@ func TestBuildCodexModelsManifestKeepsKnownReasoningChoices(t *testing.T) { require.NotEqual(t, "none", firstLevel["effort"]) } +// Scenario: 支持 Fast 的 GPT 型号在目录中声明 priority service tier。 +func TestBuildCodexModelsManifestAdvertisesPriorityServiceTierForFastGPTModels(t *testing.T) { + t.Parallel() + + body, err := BuildCodexModelsManifest([]string{ + "gpt-5.4-mini", + "gpt-5.5", + "gpt-5.6-sol", + }) + require.NoError(t, err) + models := decodeCodexManifestModels(t, body) + require.Len(t, models, 3) + + for _, model := range models { + require.Equal(t, []any{ + map[string]any{ + "id": "priority", + "name": "Fast", + "description": "Priority processing for lower latency.", + }, + }, model["service_tiers"]) + require.Nil(t, model["default_service_tier"]) + } +} + +// Scenario: 未明确支持 Fast 的型号不推测 service tier。 +func TestBuildCodexModelsManifestLeavesServiceTiersEmptyForOtherModels(t *testing.T) { + t.Parallel() + + body, err := BuildCodexModelsManifest([]string{ + "gpt-4o", + "claude-opus-4-6", + "deepseek-v4-pro", + "company-coding-model", + }) + require.NoError(t, err) + models := decodeCodexManifestModels(t, body) + require.Len(t, models, 4) + + for _, model := range models { + require.Equal(t, []any{}, model["service_tiers"]) + require.Nil(t, model["default_service_tier"]) + } +} + // Scenario: 专用图片生成模型不进入 Codex 主模型目录。 func TestBuildCodexModelsManifestOmitsDedicatedImageModels(t *testing.T) { t.Parallel() @@ -1745,7 +1790,7 @@ func TestCompleteAPIKeyCodexModelsManifestForClientPreservesProviderMetadata(t * svc := &OpenAIGatewayService{} manifest := &CodexModelsManifest{ - Body: []byte(`{"models":[{"slug":"grok-4.6","description":"Provider supplied","model_messages":{"auto_review":{"enabled":true}},"truncation_policy":{"mode":"tokens"},"unknown":{"kept":true}}],"metadata":{"source":"upstream"}}`), + Body: []byte(`{"models":[{"slug":"grok-4.6","description":"Provider supplied","service_tiers":[{"id":"provider-priority","name":"Provider Fast","description":"Provider supplied tier."}],"model_messages":{"auto_review":{"enabled":true}},"truncation_policy":{"mode":"tokens"},"unknown":{"kept":true}}],"metadata":{"source":"upstream"}}`), } account := newCodexModelsAPIKeyTestAccount("https://upstream.example/v1") @@ -1755,6 +1800,13 @@ func TestCompleteAPIKeyCodexModelsManifestForClientPreservesProviderMetadata(t * requireCompleteConfiguredCodexModel(t, models[0], "grok-4.6") require.Equal(t, "Provider supplied", models[0]["description"]) require.Equal(t, map[string]any{"kept": true}, models[0]["unknown"]) + require.Equal(t, []any{ + map[string]any{ + "id": "provider-priority", + "name": "Provider Fast", + "description": "Provider supplied tier.", + }, + }, models[0]["service_tiers"]) require.Equal(t, []any{"text"}, models[0]["input_modalities"]) require.Equal(t, []string{"low", "medium", "high", "xhigh"}, effortsFromManifestModel(t, models[0])) modelMessages, ok := models[0]["model_messages"].(map[string]any)