diff --git a/backend/internal/service/openai_codex_models_service.go b/backend/internal/service/openai_codex_models_service.go index d4f0f48e6e..a0253a4241 100644 --- a/backend/internal/service/openai_codex_models_service.go +++ b/backend/internal/service/openai_codex_models_service.go @@ -1074,7 +1074,7 @@ func groupCodexModelSupportsImageInput( return false } } - if platform != PlatformOpenAI && platform != PlatformGrok { + if platform != PlatformOpenAI && platform != PlatformGrok && platform != PlatformDeepseek { return false } @@ -1213,7 +1213,7 @@ func accountCodexModelSupportsImageInput(account *Account, upstreamModel string) return false } switch account.Platform { - case PlatformOpenAI: + case PlatformOpenAI, PlatformDeepseek: if metadata, ok := account.GetUpstreamModelMetadata(upstreamModel); ok { if modalities := normalizeCodexInputModalities(metadata.InputModalities); len(modalities) > 0 { // Official GPT-6 Astra metadata briefly shipped with a stale @@ -1226,7 +1226,10 @@ func accountCodexModelSupportsImageInput(account *Account, upstreamModel string) return stringSliceContains(modalities, "image") } } - if !isOpenAICodexImageInputModel(upstreamModel) { + if strings.EqualFold(strings.TrimSpace(upstreamModel), "deepseek-v4-flash-vision-exp") { + return account.Type == AccountTypeAPIKey + } + if account.Platform != PlatformOpenAI || !isOpenAICodexImageInputModel(upstreamModel) { return false } if account.IsOpenAIOAuth() { diff --git a/backend/internal/service/openai_codex_models_service_test.go b/backend/internal/service/openai_codex_models_service_test.go index d16de67d7e..087b3b8dac 100644 --- a/backend/internal/service/openai_codex_models_service_test.go +++ b/backend/internal/service/openai_codex_models_service_test.go @@ -785,6 +785,81 @@ func TestBuildCodexModelsManifestForGroupUsesProviderImageCapabilities(t *testin } } +func TestBuildCodexModelsManifestForGroupUsesDeepSeekVisionCapabilities(t *testing.T) { + t.Parallel() + + const visionModel = "deepseek-v4-flash-vision-exp" + newAccount := func(id int64, platform, model string, modalities []string) Account { + account := Account{ + ID: id, Platform: platform, Type: AccountTypeAPIKey, + Credentials: map[string]any{"model_mapping": map[string]any{"vision-alias": model}}, + } + if modalities != nil { + account.SetUpstreamModelMetadataSnapshot(UpstreamModelMetadataSnapshot{Models: map[string]UpstreamModelMetadata{ + model: {ID: model, InputModalities: modalities}, + }}) + } + return account + } + + tests := []struct { + name string + platform string + accounts []Account + modalities []any + }{ + { + name: "native DeepSeek", platform: PlatformDeepseek, + accounts: []Account{newAccount(1, PlatformDeepseek, visionModel, nil)}, + modalities: []any{"text", "image"}, + }, + { + name: "OpenAI-compatible DeepSeek", platform: PlatformOpenAI, + accounts: []Account{newAccount(1, PlatformOpenAI, visionModel, nil)}, + modalities: []any{"text", "image"}, + }, + { + name: "Composite DeepSeek alias", platform: PlatformComposite, + accounts: []Account{newAccount(1, PlatformDeepseek, visionModel, nil)}, + modalities: []any{"text", "image"}, + }, + { + name: "text-only DeepSeek Flash", platform: PlatformDeepseek, + accounts: []Account{newAccount(1, PlatformDeepseek, "deepseek-v4-flash", nil)}, + modalities: []any{"text"}, + }, + { + name: "explicit text-only metadata", platform: PlatformDeepseek, + accounts: []Account{newAccount(1, PlatformDeepseek, visionModel, []string{"text"})}, + modalities: []any{"text"}, + }, + { + name: "mixed vision and text-only alias", platform: PlatformDeepseek, + accounts: []Account{ + newAccount(1, PlatformDeepseek, visionModel, nil), + newAccount(2, PlatformDeepseek, "deepseek-v4-flash", nil), + }, + modalities: []any{"text"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + const groupID int64 = 790 + svc := &GatewayService{accountRepo: codexModelsVisibilityAccountRepo{byGroup: map[int64][]Account{ + groupID: tt.accounts, + }}} + body, err := svc.BuildCodexModelsManifestForGroup(context.Background(), + &Group{ID: groupID, Platform: tt.platform}, "", []string{"vision-alias"}) + require.NoError(t, err) + models := decodeCodexManifestModels(t, body) + require.Len(t, models, 1) + require.Equal(t, tt.modalities, models[0]["input_modalities"]) + }) + } +} + func TestBuildCodexModelsManifestForGroupPrefersSyncedOpenAIImageCapabilities(t *testing.T) { t.Parallel()