diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go index 44c8845e4..b30c6829a 100644 --- a/backend/internal/service/account.go +++ b/backend/internal/service/account.go @@ -1861,18 +1861,30 @@ func (a *Account) openAIEndpointCapabilitySet() (map[string]bool, bool) { result[value] = true } + // 空容器({} / [])与未配置一致:不限制任何能力。 + // 避免 OAuth 账号因 API 直写/导入/历史数据遗留的空对象而被调度器静默排除(#5530)。 + // 注意:非空但全 false / 类型异常的数据仍视为「已配置且不含能力」,保持原行为。 switch capabilities := raw.(type) { case []any: + if len(capabilities) == 0 { + return nil, false + } for _, item := range capabilities { if value, ok := item.(string); ok { add(value) } } case []string: + if len(capabilities) == 0 { + return nil, false + } for _, value := range capabilities { add(value) } case map[string]any: + if len(capabilities) == 0 { + return nil, false + } for key, value := range capabilities { enabled, ok := value.(bool) if ok && enabled { @@ -1880,6 +1892,9 @@ func (a *Account) openAIEndpointCapabilitySet() (map[string]bool, bool) { } } case map[string]bool: + if len(capabilities) == 0 { + return nil, false + } for key, enabled := range capabilities { if enabled { add(key) diff --git a/backend/internal/service/openai_images_test.go b/backend/internal/service/openai_images_test.go index cb2fdf1b5..99afeba6a 100644 --- a/backend/internal/service/openai_images_test.go +++ b/backend/internal/service/openai_images_test.go @@ -593,6 +593,67 @@ func TestAccountSupportsOpenAIEndpointCapability(t *testing.T) { require.True(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityEmbeddings)) }) + t.Run("空 openai_capabilities({})与未配置一致,不排除 OAuth 文本调度", func(t *testing.T) { + account := &Account{ + Platform: PlatformOpenAI, + Type: AccountTypeOAuth, + Credentials: map[string]any{ + "openai_capabilities": map[string]any{}, + }, + } + + require.True(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityChatCompletions)) + require.True(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityResponses)) + }) + + t.Run("空 openai_capabilities([]any)与未配置一致,不排除 OAuth 文本调度", func(t *testing.T) { + account := &Account{ + Platform: PlatformOpenAI, + Type: AccountTypeOAuth, + Credentials: map[string]any{ + "openai_capabilities": []any{}, + }, + } + + require.True(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityChatCompletions)) + }) + + t.Run("空 openai_capabilities([]string)与未配置一致,不排除 OAuth 文本调度", func(t *testing.T) { + account := &Account{ + Platform: PlatformOpenAI, + Type: AccountTypeOAuth, + Credentials: map[string]any{ + "openai_capabilities": []string{}, + }, + } + + require.True(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityChatCompletions)) + }) + + t.Run("非空但全 false 的 map 仍按显式禁用处理,不默认放行", func(t *testing.T) { + account := &Account{ + Platform: PlatformOpenAI, + Type: AccountTypeOAuth, + Credentials: map[string]any{ + "openai_capabilities": map[string]any{"chat_completions": false}, + }, + } + + require.False(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityChatCompletions)) + }) + + t.Run("类型异常(字符串)仍视为已配置但不含能力,不默认放行", func(t *testing.T) { + account := &Account{ + Platform: PlatformOpenAI, + Type: AccountTypeOAuth, + Credentials: map[string]any{ + "openai_capabilities": "chat_completions", + }, + } + + require.False(t, account.SupportsOpenAIEndpointCapability(OpenAIEndpointCapabilityChatCompletions)) + }) + t.Run("未知能力不应默认放行", func(t *testing.T) { account := &Account{ Platform: PlatformOpenAI,