From 38573050aa23bc7a3c704b100aa601daecf1dc61 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sun, 26 Apr 2026 21:49:36 +0800 Subject: [PATCH] feat(config): add support for disabling OpenAI compatibility providers - Introduced a `Disabled` flag to OpenAI compatibility configurations. - Updated routing, auth selection, and API handling logic to respect the `Disabled` state. - Extended relevant APIs, YAML configurations, and data structures to include the `Disabled` field. - Adjusted all relevant loops and filters to skip disabled providers. Closes: #3060 #3059 #2977 --- config.example.yaml | 1 + internal/api/handlers/management/api_tools.go | 3 +++ internal/api/handlers/management/config_auth_index.go | 2 ++ internal/api/handlers/management/config_lists.go | 4 ++++ internal/api/server.go | 3 +++ internal/config/config.go | 3 +++ internal/runtime/executor/openai_compat_executor.go | 3 +++ internal/util/provider.go | 6 ++++++ internal/watcher/clients.go | 3 +++ internal/watcher/diff/openai_compat.go | 3 +++ internal/watcher/synthesizer/config.go | 3 +++ sdk/cliproxy/auth/conductor.go | 3 +++ sdk/cliproxy/service.go | 3 +++ 13 files changed, 40 insertions(+) diff --git a/config.example.yaml b/config.example.yaml index 13042b78d..22696069f 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -229,6 +229,7 @@ nonstream-keepalive-interval: 0 # OpenAI compatibility providers # openai-compatibility: # - name: "openrouter" # The name of the provider; it will be used in the user agent and other places. +# disabled: false # optional: set to true to disable this provider without removing it # prefix: "test" # optional: require calls like "test/kimi-k2" to target this provider's credentials # base-url: "https://openrouter.ai/api/v1" # The base URL of the provider. # headers: diff --git a/internal/api/handlers/management/api_tools.go b/internal/api/handlers/management/api_tools.go index cb4805e9e..51b08cea4 100644 --- a/internal/api/handlers/management/api_tools.go +++ b/internal/api/handlers/management/api_tools.go @@ -766,6 +766,9 @@ func resolveOpenAICompatAPIKeyProxyURL(cfg *config.Config, auth *coreauth.Auth, for i := range cfg.OpenAICompatibility { compat := &cfg.OpenAICompatibility[i] + if compat.Disabled { + continue + } for _, candidate := range candidates { if candidate != "" && strings.EqualFold(strings.TrimSpace(candidate), compat.Name) { for j := range compat.APIKeyEntries { diff --git a/internal/api/handlers/management/config_auth_index.go b/internal/api/handlers/management/config_auth_index.go index ed0b3ec42..7b0151255 100644 --- a/internal/api/handlers/management/config_auth_index.go +++ b/internal/api/handlers/management/config_auth_index.go @@ -36,6 +36,7 @@ type openAICompatibilityAPIKeyWithAuthIndex struct { type openAICompatibilityWithAuthIndex struct { Name string `json:"name"` Priority int `json:"priority,omitempty"` + Disabled bool `json:"disabled"` Prefix string `json:"prefix,omitempty"` BaseURL string `json:"base-url"` APIKeyEntries []openAICompatibilityAPIKeyWithAuthIndex `json:"api-key-entries,omitempty"` @@ -215,6 +216,7 @@ func (h *Handler) openAICompatibilityWithAuthIndex() []openAICompatibilityWithAu response := openAICompatibilityWithAuthIndex{ Name: entry.Name, Priority: entry.Priority, + Disabled: entry.Disabled, Prefix: entry.Prefix, BaseURL: entry.BaseURL, Models: entry.Models, diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index ee3a4714b..e487627a0 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -464,6 +464,7 @@ func (h *Handler) PatchOpenAICompat(c *gin.Context) { type openAICompatPatch struct { Name *string `json:"name"` Prefix *string `json:"prefix"` + Disabled *bool `json:"disabled"` BaseURL *string `json:"base-url"` APIKeyEntries *[]config.OpenAICompatibilityAPIKey `json:"api-key-entries"` Models *[]config.OpenAICompatibilityModel `json:"models"` @@ -506,6 +507,9 @@ func (h *Handler) PatchOpenAICompat(c *gin.Context) { if body.Value.Prefix != nil { entry.Prefix = strings.TrimSpace(*body.Value.Prefix) } + if body.Value.Disabled != nil { + entry.Disabled = *body.Value.Disabled + } if body.Value.BaseURL != nil { trimmed := strings.TrimSpace(*body.Value.BaseURL) if trimmed == "" { diff --git a/internal/api/server.go b/internal/api/server.go index e70883b02..f817ac309 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -1100,6 +1100,9 @@ func (s *Server) UpdateClients(cfg *config.Config) { openAICompatCount := 0 for i := range cfg.OpenAICompatibility { entry := cfg.OpenAICompatibility[i] + if entry.Disabled { + continue + } openAICompatCount += len(entry.APIKeyEntries) } diff --git a/internal/config/config.go b/internal/config/config.go index 1ebbb460c..9817a8a71 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -519,6 +519,9 @@ type OpenAICompatibility struct { // Higher values are preferred; defaults to 0. Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` + // Disabled prevents this provider from being used for routing. + Disabled bool `yaml:"disabled,omitempty" json:"disabled,omitempty"` + // Prefix optionally namespaces model aliases for this provider (e.g., "teamA/kimi-k2"). Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"` diff --git a/internal/runtime/executor/openai_compat_executor.go b/internal/runtime/executor/openai_compat_executor.go index 7f202055a..d5739a637 100644 --- a/internal/runtime/executor/openai_compat_executor.go +++ b/internal/runtime/executor/openai_compat_executor.go @@ -378,6 +378,9 @@ func (e *OpenAICompatExecutor) resolveCompatConfig(auth *cliproxyauth.Auth) *con } for i := range e.cfg.OpenAICompatibility { compat := &e.cfg.OpenAICompatibility[i] + if compat.Disabled { + continue + } for _, candidate := range candidates { if candidate != "" && strings.EqualFold(strings.TrimSpace(candidate), compat.Name) { return compat diff --git a/internal/util/provider.go b/internal/util/provider.go index ce0ed1a39..beee9add9 100644 --- a/internal/util/provider.go +++ b/internal/util/provider.go @@ -98,6 +98,9 @@ func IsOpenAICompatibilityAlias(modelName string, cfg *config.Config) bool { } for _, compat := range cfg.OpenAICompatibility { + if compat.Disabled { + continue + } for _, model := range compat.Models { if model.Alias == modelName { return true @@ -123,6 +126,9 @@ func GetOpenAICompatibilityConfig(alias string, cfg *config.Config) (*config.Ope } for _, compat := range cfg.OpenAICompatibility { + if compat.Disabled { + continue + } for _, model := range compat.Models { if model.Alias == alias { return &compat, &model diff --git a/internal/watcher/clients.go b/internal/watcher/clients.go index 7746f4ad3..fb0d7865b 100644 --- a/internal/watcher/clients.go +++ b/internal/watcher/clients.go @@ -357,6 +357,9 @@ func BuildAPIKeyClients(cfg *config.Config) (int, int, int, int, int) { } if len(cfg.OpenAICompatibility) > 0 { for _, compatConfig := range cfg.OpenAICompatibility { + if compatConfig.Disabled { + continue + } openAICompatCount += len(compatConfig.APIKeyEntries) } } diff --git a/internal/watcher/diff/openai_compat.go b/internal/watcher/diff/openai_compat.go index 6b01aed29..541b35b3d 100644 --- a/internal/watcher/diff/openai_compat.go +++ b/internal/watcher/diff/openai_compat.go @@ -66,6 +66,9 @@ func describeOpenAICompatibilityUpdate(oldEntry, newEntry config.OpenAICompatibi oldModelCount := countOpenAIModels(oldEntry.Models) newModelCount := countOpenAIModels(newEntry.Models) details := make([]string, 0, 3) + if oldEntry.Disabled != newEntry.Disabled { + details = append(details, fmt.Sprintf("disabled %t -> %t", oldEntry.Disabled, newEntry.Disabled)) + } if oldKeyCount != newKeyCount { details = append(details, fmt.Sprintf("api-keys %d -> %d", oldKeyCount, newKeyCount)) } diff --git a/internal/watcher/synthesizer/config.go b/internal/watcher/synthesizer/config.go index 52ae9a480..8026b02fa 100644 --- a/internal/watcher/synthesizer/config.go +++ b/internal/watcher/synthesizer/config.go @@ -194,6 +194,9 @@ func (s *ConfigSynthesizer) synthesizeOpenAICompat(ctx *SynthesisContext) []*cor out := make([]*coreauth.Auth, 0) for i := range cfg.OpenAICompatibility { compat := &cfg.OpenAICompatibility[i] + if compat.Disabled { + continue + } prefix := strings.TrimSpace(compat.Prefix) providerName := strings.ToLower(strings.TrimSpace(compat.Name)) if providerName == "" { diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index 2091f669a..6571518d3 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -1799,6 +1799,9 @@ func resolveOpenAICompatConfig(cfg *internalconfig.Config, providerKey, compatNa } for i := range cfg.OpenAICompatibility { compat := &cfg.OpenAICompatibility[i] + if compat.Disabled { + continue + } for _, candidate := range candidates { if candidate != "" && strings.EqualFold(strings.TrimSpace(candidate), compat.Name) { return compat diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index c5458b488..d9613150e 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -969,6 +969,9 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) { } for i := range s.cfg.OpenAICompatibility { compat := &s.cfg.OpenAICompatibility[i] + if compat.Disabled { + continue + } if strings.EqualFold(compat.Name, compatName) { isCompatAuth = true // Convert compatibility models to registry models