From 5fd4a8b97439000d62af32192c983b2949c882b5 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Mon, 13 Oct 2025 09:02:32 +0800 Subject: [PATCH] feat(config): Remove OpenAI providers with empty BaseURL --- .../api/handlers/management/config_lists.go | 37 ++++++++++++++++++- internal/config/config.go | 25 +++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index 22bff1b3..b49940b8 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -227,7 +227,14 @@ func (h *Handler) PutOpenAICompat(c *gin.Context) { for i := range arr { normalizeOpenAICompatibilityEntry(&arr[i]) } - h.cfg.OpenAICompatibility = arr + // Filter out providers with empty base-url -> remove provider entirely + filtered := make([]config.OpenAICompatibility, 0, len(arr)) + for i := range arr { + if strings.TrimSpace(arr[i].BaseURL) != "" { + filtered = append(filtered, arr[i]) + } + } + h.cfg.OpenAICompatibility = filtered h.persist(c) } func (h *Handler) PatchOpenAICompat(c *gin.Context) { @@ -241,6 +248,32 @@ func (h *Handler) PatchOpenAICompat(c *gin.Context) { return } normalizeOpenAICompatibilityEntry(body.Value) + // If base-url becomes empty, delete the provider instead of updating + if strings.TrimSpace(body.Value.BaseURL) == "" { + if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.OpenAICompatibility) { + h.cfg.OpenAICompatibility = append(h.cfg.OpenAICompatibility[:*body.Index], h.cfg.OpenAICompatibility[*body.Index+1:]...) + h.persist(c) + return + } + if body.Name != nil { + out := make([]config.OpenAICompatibility, 0, len(h.cfg.OpenAICompatibility)) + removed := false + for i := range h.cfg.OpenAICompatibility { + if !removed && h.cfg.OpenAICompatibility[i].Name == *body.Name { + removed = true + continue + } + out = append(out, h.cfg.OpenAICompatibility[i]) + } + if removed { + h.cfg.OpenAICompatibility = out + h.persist(c) + return + } + } + c.JSON(404, gin.H{"error": "item not found"}) + return + } if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.OpenAICompatibility) { h.cfg.OpenAICompatibility[*body.Index] = *body.Value h.persist(c) @@ -359,6 +392,8 @@ func normalizeOpenAICompatibilityEntry(entry *config.OpenAICompatibility) { if entry == nil { return } + // Trim base-url; empty base-url indicates provider should be removed by sanitization + entry.BaseURL = strings.TrimSpace(entry.BaseURL) existing := make(map[string]struct{}, len(entry.APIKeyEntries)) for i := range entry.APIKeyEntries { trimmed := strings.TrimSpace(entry.APIKeyEntries[i].APIKey) diff --git a/internal/config/config.go b/internal/config/config.go index 3de596a7..e144c263 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "os" + "strings" "syscall" "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" @@ -207,10 +208,34 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) { // Sync request authentication providers with inline API keys for backwards compatibility. syncInlineAccessProvider(&cfg) + // Sanitize OpenAI compatibility providers: drop entries without base-url + sanitizeOpenAICompatibility(&cfg) + // Return the populated configuration struct. return &cfg, nil } +// sanitizeOpenAICompatibility removes OpenAI-compatibility provider entries that are +// not actionable, specifically those missing a BaseURL. It trims whitespace before +// evaluation and preserves the relative order of remaining entries. +func sanitizeOpenAICompatibility(cfg *Config) { + if cfg == nil || len(cfg.OpenAICompatibility) == 0 { + return + } + out := make([]OpenAICompatibility, 0, len(cfg.OpenAICompatibility)) + for i := range cfg.OpenAICompatibility { + e := cfg.OpenAICompatibility[i] + e.Name = strings.TrimSpace(e.Name) + e.BaseURL = strings.TrimSpace(e.BaseURL) + if e.BaseURL == "" { + // Skip providers with no base-url; treated as removed + continue + } + out = append(out, e) + } + cfg.OpenAICompatibility = out +} + func syncInlineAccessProvider(cfg *Config) { if cfg == nil { return