From 3156109c71fc6bc04b1587829a65b2210eaa8414 Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 4 Jan 2026 12:21:49 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(management):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=A4=A7=E5=B0=8F/=E5=BC=BA=E5=88=B6=E5=89=8D?= =?UTF-8?q?=E7=BC=80/=E8=B7=AF=E7=94=B1=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/handlers/management/config_basic.go | 66 +++++++++++++++++++ internal/api/server.go | 12 ++++ 2 files changed, 78 insertions(+) diff --git a/internal/api/handlers/management/config_basic.go b/internal/api/handlers/management/config_basic.go index ae292982..2d3cd1fb 100644 --- a/internal/api/handlers/management/config_basic.go +++ b/internal/api/handlers/management/config_basic.go @@ -202,6 +202,26 @@ func (h *Handler) PutLoggingToFile(c *gin.Context) { h.updateBoolField(c, func(v bool) { h.cfg.LoggingToFile = v }) } +// LogsMaxTotalSizeMB +func (h *Handler) GetLogsMaxTotalSizeMB(c *gin.Context) { + c.JSON(200, gin.H{"logs-max-total-size-mb": h.cfg.LogsMaxTotalSizeMB}) +} +func (h *Handler) PutLogsMaxTotalSizeMB(c *gin.Context) { + var body struct { + Value *int `json:"value"` + } + if errBindJSON := c.ShouldBindJSON(&body); errBindJSON != nil || body.Value == nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"}) + return + } + value := *body.Value + if value < 0 { + value = 0 + } + h.cfg.LogsMaxTotalSizeMB = value + h.persist(c) +} + // Request log func (h *Handler) GetRequestLog(c *gin.Context) { c.JSON(200, gin.H{"request-log": h.cfg.RequestLog}) } func (h *Handler) PutRequestLog(c *gin.Context) { @@ -232,6 +252,52 @@ func (h *Handler) PutMaxRetryInterval(c *gin.Context) { h.updateIntField(c, func(v int) { h.cfg.MaxRetryInterval = v }) } +// ForceModelPrefix +func (h *Handler) GetForceModelPrefix(c *gin.Context) { + c.JSON(200, gin.H{"force-model-prefix": h.cfg.ForceModelPrefix}) +} +func (h *Handler) PutForceModelPrefix(c *gin.Context) { + h.updateBoolField(c, func(v bool) { h.cfg.ForceModelPrefix = v }) +} + +func normalizeRoutingStrategy(strategy string) (string, bool) { + normalized := strings.ToLower(strings.TrimSpace(strategy)) + switch normalized { + case "", "round-robin", "roundrobin", "rr": + return "round-robin", true + case "fill-first", "fillfirst", "ff": + return "fill-first", true + default: + return "", false + } +} + +// RoutingStrategy +func (h *Handler) GetRoutingStrategy(c *gin.Context) { + strategy, ok := normalizeRoutingStrategy(h.cfg.Routing.Strategy) + if !ok { + c.JSON(200, gin.H{"strategy": strings.TrimSpace(h.cfg.Routing.Strategy)}) + return + } + c.JSON(200, gin.H{"strategy": strategy}) +} +func (h *Handler) PutRoutingStrategy(c *gin.Context) { + var body struct { + Value *string `json:"value"` + } + if errBindJSON := c.ShouldBindJSON(&body); errBindJSON != nil || body.Value == nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"}) + return + } + normalized, ok := normalizeRoutingStrategy(*body.Value) + if !ok { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid strategy"}) + return + } + h.cfg.Routing.Strategy = normalized + h.persist(c) +} + // Proxy URL func (h *Handler) GetProxyURL(c *gin.Context) { c.JSON(200, gin.H{"proxy-url": h.cfg.ProxyURL}) } func (h *Handler) PutProxyURL(c *gin.Context) { diff --git a/internal/api/server.go b/internal/api/server.go index ceafd6b5..3119bbb9 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -492,6 +492,10 @@ func (s *Server) registerManagementRoutes() { mgmt.PUT("/logging-to-file", s.mgmt.PutLoggingToFile) mgmt.PATCH("/logging-to-file", s.mgmt.PutLoggingToFile) + mgmt.GET("/logs-max-total-size-mb", s.mgmt.GetLogsMaxTotalSizeMB) + mgmt.PUT("/logs-max-total-size-mb", s.mgmt.PutLogsMaxTotalSizeMB) + mgmt.PATCH("/logs-max-total-size-mb", s.mgmt.PutLogsMaxTotalSizeMB) + mgmt.GET("/usage-statistics-enabled", s.mgmt.GetUsageStatisticsEnabled) mgmt.PUT("/usage-statistics-enabled", s.mgmt.PutUsageStatisticsEnabled) mgmt.PATCH("/usage-statistics-enabled", s.mgmt.PutUsageStatisticsEnabled) @@ -564,6 +568,14 @@ func (s *Server) registerManagementRoutes() { mgmt.PUT("/max-retry-interval", s.mgmt.PutMaxRetryInterval) mgmt.PATCH("/max-retry-interval", s.mgmt.PutMaxRetryInterval) + mgmt.GET("/force-model-prefix", s.mgmt.GetForceModelPrefix) + mgmt.PUT("/force-model-prefix", s.mgmt.PutForceModelPrefix) + mgmt.PATCH("/force-model-prefix", s.mgmt.PutForceModelPrefix) + + mgmt.GET("/routing/strategy", s.mgmt.GetRoutingStrategy) + mgmt.PUT("/routing/strategy", s.mgmt.PutRoutingStrategy) + mgmt.PATCH("/routing/strategy", s.mgmt.PutRoutingStrategy) + mgmt.GET("/claude-api-key", s.mgmt.GetClaudeKeys) mgmt.PUT("/claude-api-key", s.mgmt.PutClaudeKeys) mgmt.PATCH("/claude-api-key", s.mgmt.PatchClaudeKey) From f0e73efda2b22727430ce46b0ae9f67229bd835f Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 4 Jan 2026 17:32:00 +0800 Subject: [PATCH 2/3] feat(management): add vertex api key and oauth model mappings endpoints --- .../api/handlers/management/config_lists.go | 308 ++++++++++++++++++ internal/api/server.go | 10 + 2 files changed, 318 insertions(+) diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index e3636fd8..fb0a7655 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -487,6 +487,137 @@ func (h *Handler) DeleteOpenAICompat(c *gin.Context) { c.JSON(400, gin.H{"error": "missing name or index"}) } +// vertex-api-key: []VertexCompatKey +func (h *Handler) GetVertexCompatKeys(c *gin.Context) { + c.JSON(200, gin.H{"vertex-api-key": h.cfg.VertexCompatAPIKey}) +} +func (h *Handler) PutVertexCompatKeys(c *gin.Context) { + data, err := c.GetRawData() + if err != nil { + c.JSON(400, gin.H{"error": "failed to read body"}) + return + } + var arr []config.VertexCompatKey + if err = json.Unmarshal(data, &arr); err != nil { + var obj struct { + Items []config.VertexCompatKey `json:"items"` + } + if err2 := json.Unmarshal(data, &obj); err2 != nil || len(obj.Items) == 0 { + c.JSON(400, gin.H{"error": "invalid body"}) + return + } + arr = obj.Items + } + for i := range arr { + normalizeVertexCompatKey(&arr[i]) + } + h.cfg.VertexCompatAPIKey = arr + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) +} +func (h *Handler) PatchVertexCompatKey(c *gin.Context) { + type vertexCompatPatch struct { + APIKey *string `json:"api-key"` + Prefix *string `json:"prefix"` + BaseURL *string `json:"base-url"` + ProxyURL *string `json:"proxy-url"` + Headers *map[string]string `json:"headers"` + Models *[]config.VertexCompatModel `json:"models"` + } + var body struct { + Index *int `json:"index"` + Match *string `json:"match"` + Value *vertexCompatPatch `json:"value"` + } + if errBindJSON := c.ShouldBindJSON(&body); errBindJSON != nil || body.Value == nil { + c.JSON(400, gin.H{"error": "invalid body"}) + return + } + targetIndex := -1 + if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.VertexCompatAPIKey) { + targetIndex = *body.Index + } + if targetIndex == -1 && body.Match != nil { + match := strings.TrimSpace(*body.Match) + if match != "" { + for i := range h.cfg.VertexCompatAPIKey { + if h.cfg.VertexCompatAPIKey[i].APIKey == match { + targetIndex = i + break + } + } + } + } + if targetIndex == -1 { + c.JSON(404, gin.H{"error": "item not found"}) + return + } + + entry := h.cfg.VertexCompatAPIKey[targetIndex] + if body.Value.APIKey != nil { + trimmed := strings.TrimSpace(*body.Value.APIKey) + if trimmed == "" { + h.cfg.VertexCompatAPIKey = append(h.cfg.VertexCompatAPIKey[:targetIndex], h.cfg.VertexCompatAPIKey[targetIndex+1:]...) + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) + return + } + entry.APIKey = trimmed + } + if body.Value.Prefix != nil { + entry.Prefix = strings.TrimSpace(*body.Value.Prefix) + } + if body.Value.BaseURL != nil { + trimmed := strings.TrimSpace(*body.Value.BaseURL) + if trimmed == "" { + h.cfg.VertexCompatAPIKey = append(h.cfg.VertexCompatAPIKey[:targetIndex], h.cfg.VertexCompatAPIKey[targetIndex+1:]...) + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) + return + } + entry.BaseURL = trimmed + } + if body.Value.ProxyURL != nil { + entry.ProxyURL = strings.TrimSpace(*body.Value.ProxyURL) + } + if body.Value.Headers != nil { + entry.Headers = config.NormalizeHeaders(*body.Value.Headers) + } + if body.Value.Models != nil { + entry.Models = append([]config.VertexCompatModel(nil), (*body.Value.Models)...) + } + normalizeVertexCompatKey(&entry) + h.cfg.VertexCompatAPIKey[targetIndex] = entry + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) +} + +func (h *Handler) DeleteVertexCompatKey(c *gin.Context) { + if val := strings.TrimSpace(c.Query("api-key")); val != "" { + out := make([]config.VertexCompatKey, 0, len(h.cfg.VertexCompatAPIKey)) + for _, v := range h.cfg.VertexCompatAPIKey { + if v.APIKey != val { + out = append(out, v) + } + } + h.cfg.VertexCompatAPIKey = out + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) + return + } + if idxStr := c.Query("index"); idxStr != "" { + var idx int + _, errScan := fmt.Sscanf(idxStr, "%d", &idx) + if errScan == nil && idx >= 0 && idx < len(h.cfg.VertexCompatAPIKey) { + h.cfg.VertexCompatAPIKey = append(h.cfg.VertexCompatAPIKey[:idx], h.cfg.VertexCompatAPIKey[idx+1:]...) + h.cfg.SanitizeVertexCompatKeys() + h.persist(c) + return + } + } + c.JSON(400, gin.H{"error": "missing api-key or index"}) +} + // oauth-excluded-models: map[string][]string func (h *Handler) GetOAuthExcludedModels(c *gin.Context) { c.JSON(200, gin.H{"oauth-excluded-models": config.NormalizeOAuthExcludedModels(h.cfg.OAuthExcludedModels)}) @@ -572,6 +703,102 @@ func (h *Handler) DeleteOAuthExcludedModels(c *gin.Context) { h.persist(c) } +// oauth-model-mappings: map[string][]ModelNameMapping +func (h *Handler) GetOAuthModelMappings(c *gin.Context) { + c.JSON(200, gin.H{"oauth-model-mappings": normalizeOAuthModelMappings(h.cfg.OAuthModelMappings)}) +} + +func (h *Handler) PutOAuthModelMappings(c *gin.Context) { + data, err := c.GetRawData() + if err != nil { + c.JSON(400, gin.H{"error": "failed to read body"}) + return + } + var entries map[string][]config.ModelNameMapping + if err = json.Unmarshal(data, &entries); err != nil { + var wrapper struct { + Items map[string][]config.ModelNameMapping `json:"items"` + } + if err2 := json.Unmarshal(data, &wrapper); err2 != nil { + c.JSON(400, gin.H{"error": "invalid body"}) + return + } + entries = wrapper.Items + } + h.cfg.OAuthModelMappings = normalizeOAuthModelMappings(entries) + h.persist(c) +} + +func (h *Handler) PatchOAuthModelMappings(c *gin.Context) { + var body struct { + Provider *string `json:"provider"` + Channel *string `json:"channel"` + Mappings []config.ModelNameMapping `json:"mappings"` + } + if errBindJSON := c.ShouldBindJSON(&body); errBindJSON != nil { + c.JSON(400, gin.H{"error": "invalid body"}) + return + } + channelRaw := "" + if body.Channel != nil { + channelRaw = *body.Channel + } else if body.Provider != nil { + channelRaw = *body.Provider + } + channel := strings.ToLower(strings.TrimSpace(channelRaw)) + if channel == "" { + c.JSON(400, gin.H{"error": "invalid channel"}) + return + } + + normalized := normalizeOAuthModelMappingsList(body.Mappings) + if len(normalized) == 0 { + if h.cfg.OAuthModelMappings == nil { + c.JSON(404, gin.H{"error": "channel not found"}) + return + } + if _, ok := h.cfg.OAuthModelMappings[channel]; !ok { + c.JSON(404, gin.H{"error": "channel not found"}) + return + } + delete(h.cfg.OAuthModelMappings, channel) + if len(h.cfg.OAuthModelMappings) == 0 { + h.cfg.OAuthModelMappings = nil + } + h.persist(c) + return + } + if h.cfg.OAuthModelMappings == nil { + h.cfg.OAuthModelMappings = make(map[string][]config.ModelNameMapping) + } + h.cfg.OAuthModelMappings[channel] = normalized + h.persist(c) +} + +func (h *Handler) DeleteOAuthModelMappings(c *gin.Context) { + channel := strings.ToLower(strings.TrimSpace(c.Query("channel"))) + if channel == "" { + channel = strings.ToLower(strings.TrimSpace(c.Query("provider"))) + } + if channel == "" { + c.JSON(400, gin.H{"error": "missing channel"}) + return + } + if h.cfg.OAuthModelMappings == nil { + c.JSON(404, gin.H{"error": "channel not found"}) + return + } + if _, ok := h.cfg.OAuthModelMappings[channel]; !ok { + c.JSON(404, gin.H{"error": "channel not found"}) + return + } + delete(h.cfg.OAuthModelMappings, channel) + if len(h.cfg.OAuthModelMappings) == 0 { + h.cfg.OAuthModelMappings = nil + } + h.persist(c) +} + // codex-api-key: []CodexKey func (h *Handler) GetCodexKeys(c *gin.Context) { c.JSON(200, gin.H{"codex-api-key": h.cfg.CodexKey}) @@ -789,6 +1016,87 @@ func normalizeCodexKey(entry *config.CodexKey) { entry.Models = normalized } +func normalizeVertexCompatKey(entry *config.VertexCompatKey) { + if entry == nil { + return + } + entry.APIKey = strings.TrimSpace(entry.APIKey) + entry.Prefix = strings.TrimSpace(entry.Prefix) + entry.BaseURL = strings.TrimSpace(entry.BaseURL) + entry.ProxyURL = strings.TrimSpace(entry.ProxyURL) + entry.Headers = config.NormalizeHeaders(entry.Headers) + if len(entry.Models) == 0 { + return + } + normalized := make([]config.VertexCompatModel, 0, len(entry.Models)) + for i := range entry.Models { + model := entry.Models[i] + model.Name = strings.TrimSpace(model.Name) + model.Alias = strings.TrimSpace(model.Alias) + if model.Name == "" || model.Alias == "" { + continue + } + normalized = append(normalized, model) + } + entry.Models = normalized +} + +func normalizeOAuthModelMappingsList(entries []config.ModelNameMapping) []config.ModelNameMapping { + if len(entries) == 0 { + return nil + } + seenName := make(map[string]struct{}, len(entries)) + seenAlias := make(map[string]struct{}, len(entries)) + clean := make([]config.ModelNameMapping, 0, len(entries)) + for _, mapping := range entries { + name := strings.TrimSpace(mapping.Name) + alias := strings.TrimSpace(mapping.Alias) + if name == "" || alias == "" { + continue + } + if strings.EqualFold(name, alias) { + continue + } + nameKey := strings.ToLower(name) + aliasKey := strings.ToLower(alias) + if _, ok := seenName[nameKey]; ok { + continue + } + if _, ok := seenAlias[aliasKey]; ok { + continue + } + seenName[nameKey] = struct{}{} + seenAlias[aliasKey] = struct{}{} + clean = append(clean, config.ModelNameMapping{Name: name, Alias: alias, Fork: mapping.Fork}) + } + if len(clean) == 0 { + return nil + } + return clean +} + +func normalizeOAuthModelMappings(entries map[string][]config.ModelNameMapping) map[string][]config.ModelNameMapping { + if len(entries) == 0 { + return nil + } + out := make(map[string][]config.ModelNameMapping, len(entries)) + for rawChannel, mappings := range entries { + channel := strings.ToLower(strings.TrimSpace(rawChannel)) + if channel == "" { + continue + } + normalized := normalizeOAuthModelMappingsList(mappings) + if len(normalized) == 0 { + continue + } + out[channel] = normalized + } + if len(out) == 0 { + return nil + } + return out +} + // GetAmpCode returns the complete ampcode configuration. func (h *Handler) GetAmpCode(c *gin.Context) { if h == nil || h.cfg == nil { diff --git a/internal/api/server.go b/internal/api/server.go index 3119bbb9..05bb2fee 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -591,11 +591,21 @@ func (s *Server) registerManagementRoutes() { mgmt.PATCH("/openai-compatibility", s.mgmt.PatchOpenAICompat) mgmt.DELETE("/openai-compatibility", s.mgmt.DeleteOpenAICompat) + mgmt.GET("/vertex-api-key", s.mgmt.GetVertexCompatKeys) + mgmt.PUT("/vertex-api-key", s.mgmt.PutVertexCompatKeys) + mgmt.PATCH("/vertex-api-key", s.mgmt.PatchVertexCompatKey) + mgmt.DELETE("/vertex-api-key", s.mgmt.DeleteVertexCompatKey) + mgmt.GET("/oauth-excluded-models", s.mgmt.GetOAuthExcludedModels) mgmt.PUT("/oauth-excluded-models", s.mgmt.PutOAuthExcludedModels) mgmt.PATCH("/oauth-excluded-models", s.mgmt.PatchOAuthExcludedModels) mgmt.DELETE("/oauth-excluded-models", s.mgmt.DeleteOAuthExcludedModels) + mgmt.GET("/oauth-model-mappings", s.mgmt.GetOAuthModelMappings) + mgmt.PUT("/oauth-model-mappings", s.mgmt.PutOAuthModelMappings) + mgmt.PATCH("/oauth-model-mappings", s.mgmt.PatchOAuthModelMappings) + mgmt.DELETE("/oauth-model-mappings", s.mgmt.DeleteOAuthModelMappings) + mgmt.GET("/auth-files", s.mgmt.ListAuthFiles) mgmt.GET("/auth-files/models", s.mgmt.GetAuthFileModels) mgmt.GET("/auth-files/download", s.mgmt.DownloadAuthFile) From cd22c849e2d54d81ebc2f62659b98fd490f321db Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 4 Jan 2026 17:57:34 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(management):=20=E6=9B=B4=E6=96=B0OAuth?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=98=A0=E5=B0=84=E7=9A=84=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BB=A5=E5=A2=9E=E5=BC=BA=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/handlers/management/config_lists.go | 61 +++++-------------- 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index fb0a7655..edb7a677 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -705,7 +705,7 @@ func (h *Handler) DeleteOAuthExcludedModels(c *gin.Context) { // oauth-model-mappings: map[string][]ModelNameMapping func (h *Handler) GetOAuthModelMappings(c *gin.Context) { - c.JSON(200, gin.H{"oauth-model-mappings": normalizeOAuthModelMappings(h.cfg.OAuthModelMappings)}) + c.JSON(200, gin.H{"oauth-model-mappings": sanitizedOAuthModelMappings(h.cfg.OAuthModelMappings)}) } func (h *Handler) PutOAuthModelMappings(c *gin.Context) { @@ -725,7 +725,7 @@ func (h *Handler) PutOAuthModelMappings(c *gin.Context) { } entries = wrapper.Items } - h.cfg.OAuthModelMappings = normalizeOAuthModelMappings(entries) + h.cfg.OAuthModelMappings = sanitizedOAuthModelMappings(entries) h.persist(c) } @@ -751,7 +751,8 @@ func (h *Handler) PatchOAuthModelMappings(c *gin.Context) { return } - normalized := normalizeOAuthModelMappingsList(body.Mappings) + normalizedMap := sanitizedOAuthModelMappings(map[string][]config.ModelNameMapping{channel: body.Mappings}) + normalized := normalizedMap[channel] if len(normalized) == 0 { if h.cfg.OAuthModelMappings == nil { c.JSON(404, gin.H{"error": "channel not found"}) @@ -1041,60 +1042,26 @@ func normalizeVertexCompatKey(entry *config.VertexCompatKey) { entry.Models = normalized } -func normalizeOAuthModelMappingsList(entries []config.ModelNameMapping) []config.ModelNameMapping { +func sanitizedOAuthModelMappings(entries map[string][]config.ModelNameMapping) map[string][]config.ModelNameMapping { if len(entries) == 0 { return nil } - seenName := make(map[string]struct{}, len(entries)) - seenAlias := make(map[string]struct{}, len(entries)) - clean := make([]config.ModelNameMapping, 0, len(entries)) - for _, mapping := range entries { - name := strings.TrimSpace(mapping.Name) - alias := strings.TrimSpace(mapping.Alias) - if name == "" || alias == "" { + copied := make(map[string][]config.ModelNameMapping, len(entries)) + for channel, mappings := range entries { + if len(mappings) == 0 { continue } - if strings.EqualFold(name, alias) { - continue - } - nameKey := strings.ToLower(name) - aliasKey := strings.ToLower(alias) - if _, ok := seenName[nameKey]; ok { - continue - } - if _, ok := seenAlias[aliasKey]; ok { - continue - } - seenName[nameKey] = struct{}{} - seenAlias[aliasKey] = struct{}{} - clean = append(clean, config.ModelNameMapping{Name: name, Alias: alias, Fork: mapping.Fork}) + copied[channel] = append([]config.ModelNameMapping(nil), mappings...) } - if len(clean) == 0 { + if len(copied) == 0 { return nil } - return clean -} - -func normalizeOAuthModelMappings(entries map[string][]config.ModelNameMapping) map[string][]config.ModelNameMapping { - if len(entries) == 0 { + cfg := config.Config{OAuthModelMappings: copied} + cfg.SanitizeOAuthModelMappings() + if len(cfg.OAuthModelMappings) == 0 { return nil } - out := make(map[string][]config.ModelNameMapping, len(entries)) - for rawChannel, mappings := range entries { - channel := strings.ToLower(strings.TrimSpace(rawChannel)) - if channel == "" { - continue - } - normalized := normalizeOAuthModelMappingsList(mappings) - if len(normalized) == 0 { - continue - } - out[channel] = normalized - } - if len(out) == 0 { - return nil - } - return out + return cfg.OAuthModelMappings } // GetAmpCode returns the complete ampcode configuration.