From 0fd2abbc3bb026ba4fb124d71051a13cd4ccb0d0 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 2 Dec 2025 09:12:53 +0800 Subject: [PATCH] **refactor(cliproxy, config): remove vertex-compat flow, streamline Vertex API key handling** - Removed `vertex-compat` executor and related configuration. - Consolidated Vertex compatibility checks into `vertex` handling with `apikey`-based model resolution. - Streamlined model generation logic for Vertex API key entries. --- config.example.yaml | 13 +++ internal/config/vertex_compat.go | 2 +- .../executor/gemini_vertex_executor.go | 17 --- internal/watcher/watcher.go | 13 ++- sdk/cliproxy/service.go | 106 +++++++++++++----- 5 files changed, 97 insertions(+), 54 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index 0685a83a..3086ca78 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -145,6 +145,19 @@ ws-auth: false # - name: "moonshotai/kimi-k2:free" # The actual model name. # alias: "kimi-k2" # The alias used in the API. +# Vertex API keys (Vertex-compatible endpoints, use API key + base URL) +#vertex-api-key: +# - api-key: "vk-123..." # x-goog-api-key header +# base-url: "https://example.com/api" # e.g. https://zenmux.ai/api +# proxy-url: "socks5://proxy.example.com:1080" # optional per-key proxy override +# headers: +# X-Custom-Header: "custom-value" +# models: # optional: map aliases to upstream model names +# - name: "gemini-2.0-flash" # upstream model name +# alias: "vertex-flash" # client-visible alias +# - name: "gemini-1.5-pro" +# alias: "vertex-pro" + #payload: # Optional payload configuration # default: # Default rules only set parameters when they are missing in the payload. # - models: diff --git a/internal/config/vertex_compat.go b/internal/config/vertex_compat.go index a8d94ccb..1257dd62 100644 --- a/internal/config/vertex_compat.go +++ b/internal/config/vertex_compat.go @@ -55,7 +55,7 @@ func (cfg *Config) SanitizeVertexCompatKeys() { } entry.BaseURL = strings.TrimSpace(entry.BaseURL) if entry.BaseURL == "" { - // BaseURL is required for vertex-compat keys + // BaseURL is required for Vertex API key entries continue } entry.ProxyURL = strings.TrimSpace(entry.ProxyURL) diff --git a/internal/runtime/executor/gemini_vertex_executor.go b/internal/runtime/executor/gemini_vertex_executor.go index eeb7356e..de4ba072 100644 --- a/internal/runtime/executor/gemini_vertex_executor.go +++ b/internal/runtime/executor/gemini_vertex_executor.go @@ -44,22 +44,6 @@ func NewGeminiVertexExecutor(cfg *config.Config) *GeminiVertexExecutor { // Identifier returns provider key for manager routing. func (e *GeminiVertexExecutor) Identifier() string { return "vertex" } -// GeminiVertexCompatExecutor is a thin wrapper around GeminiVertexExecutor -// that provides the correct identifier for vertex-compat routing. -type GeminiVertexCompatExecutor struct { - *GeminiVertexExecutor -} - -// NewGeminiVertexCompatExecutor constructs the Vertex-compatible executor. -func NewGeminiVertexCompatExecutor(cfg *config.Config) *GeminiVertexCompatExecutor { - return &GeminiVertexCompatExecutor{ - GeminiVertexExecutor: NewGeminiVertexExecutor(cfg), - } -} - -// Identifier returns provider key for manager routing. -func (e *GeminiVertexCompatExecutor) Identifier() string { return "vertex-compat" } - // PrepareRequest is a no-op for Vertex. func (e *GeminiVertexExecutor) PrepareRequest(_ *http.Request, _ *cliproxyauth.Auth) error { return nil @@ -393,7 +377,6 @@ func (e *GeminiVertexExecutor) executeWithServiceAccount(ctx context.Context, au } // executeWithAPIKey handles authentication using API key credentials. -// This method follows the vertex-compat pattern for API key authentication. func (e *GeminiVertexExecutor) executeWithAPIKey(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options, apiKey, baseURL string) (resp cliproxyexecutor.Response, err error) { reporter := newUsageReporter(ctx, e.Identifier(), req.Model, auth) defer reporter.trackFailure(ctx, &err) diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index 2798b724..02abb743 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -986,7 +986,7 @@ func (w *Watcher) reloadClients(rescanAuth bool, affectedOAuthProviders []string w.refreshAuthState() - log.Infof("full client load complete - %d clients (%d auth files + %d Gemini API keys + %d Vertex-compat keys + %d Claude API keys + %d Codex keys + %d OpenAI-compat)", + log.Infof("full client load complete - %d clients (%d auth files + %d Gemini API keys + %d Vertex API keys + %d Claude API keys + %d Codex keys + %d OpenAI-compat)", totalNewClients, authFileCount, geminiAPIKeyCount, @@ -1273,18 +1273,18 @@ func (w *Watcher) SnapshotCoreAuths() []*coreauth.Auth { } } - // Process Vertex compatibility providers + // Process Vertex API key providers (Vertex-compatible endpoints) for i := range cfg.VertexCompatAPIKey { compat := &cfg.VertexCompatAPIKey[i] - providerName := "vertex-compat" + providerName := "vertex" base := strings.TrimSpace(compat.BaseURL) key := strings.TrimSpace(compat.APIKey) proxyURL := strings.TrimSpace(compat.ProxyURL) - idKind := fmt.Sprintf("vertex-compatibility:%s", base) + idKind := fmt.Sprintf("vertex:apikey:%s", base) id, token := idGen.next(idKind, key, base, proxyURL) attrs := map[string]string{ - "source": fmt.Sprintf("config:vertex-compatibility[%s]", token), + "source": fmt.Sprintf("config:vertex-apikey[%s]", token), "base_url": base, "provider_key": providerName, } @@ -1298,13 +1298,14 @@ func (w *Watcher) SnapshotCoreAuths() []*coreauth.Auth { a := &coreauth.Auth{ ID: id, Provider: providerName, - Label: "Vertex Compatibility", + Label: "vertex-apikey", Status: coreauth.StatusActive, ProxyURL: proxyURL, Attributes: attrs, CreatedAt: now, UpdatedAt: now, } + applyAuthExcludedModelsMeta(a, cfg, nil, "apikey") out = append(out, a) } diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index f0b6bf53..8b9a6639 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -362,8 +362,6 @@ func (s *Service) ensureExecutorsForAuth(a *coreauth.Auth) { s.coreManager.RegisterExecutor(executor.NewGeminiExecutor(s.cfg)) case "vertex": s.coreManager.RegisterExecutor(executor.NewGeminiVertexExecutor(s.cfg)) - case "vertex-compat": - s.coreManager.RegisterExecutor(executor.NewGeminiVertexCompatExecutor(s.cfg)) case "gemini-cli": s.coreManager.RegisterExecutor(executor.NewGeminiCLIExecutor(s.cfg)) case "aistudio": @@ -681,36 +679,12 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) { case "vertex": // Vertex AI Gemini supports the same model identifiers as Gemini. models = registry.GetGeminiVertexModels() - models = applyExcludedModels(models, excluded) - case "vertex-compat": - // Handle Vertex AI compatibility providers with custom model definitions - if s.cfg != nil && len(s.cfg.VertexCompatAPIKey) > 0 { - // Create models for all Vertex compatibility providers - allModels := make([]*ModelInfo, 0) - for i := range s.cfg.VertexCompatAPIKey { - compat := &s.cfg.VertexCompatAPIKey[i] - for j := range compat.Models { - m := compat.Models[j] - // Use alias as model ID, fallback to name if alias is empty - modelID := m.Alias - if modelID == "" { - modelID = m.Name - } - if modelID != "" { - allModels = append(allModels, &ModelInfo{ - ID: modelID, - Object: "model", - Created: time.Now().Unix(), - OwnedBy: "vertex-compat", - Type: "vertex-compat", - DisplayName: m.Name, - }) - } - } + if authKind == "apikey" { + if entry := s.resolveConfigVertexCompatKey(a); entry != nil && len(entry.Models) > 0 { + models = buildVertexCompatConfigModels(entry) } - models = allModels } - + models = applyExcludedModels(models, excluded) case "gemini-cli": models = registry.GetGeminiCLIModels() models = applyExcludedModels(models, excluded) @@ -905,6 +879,40 @@ func (s *Service) resolveConfigGeminiKey(auth *coreauth.Auth) *config.GeminiKey return nil } +func (s *Service) resolveConfigVertexCompatKey(auth *coreauth.Auth) *config.VertexCompatKey { + if auth == nil || s.cfg == nil { + return nil + } + var attrKey, attrBase string + if auth.Attributes != nil { + attrKey = strings.TrimSpace(auth.Attributes["api_key"]) + attrBase = strings.TrimSpace(auth.Attributes["base_url"]) + } + for i := range s.cfg.VertexCompatAPIKey { + entry := &s.cfg.VertexCompatAPIKey[i] + cfgKey := strings.TrimSpace(entry.APIKey) + cfgBase := strings.TrimSpace(entry.BaseURL) + if attrKey != "" && strings.EqualFold(cfgKey, attrKey) { + if cfgBase == "" || strings.EqualFold(cfgBase, attrBase) { + return entry + } + continue + } + if attrKey == "" && attrBase != "" && strings.EqualFold(cfgBase, attrBase) { + return entry + } + } + if attrKey != "" { + for i := range s.cfg.VertexCompatAPIKey { + entry := &s.cfg.VertexCompatAPIKey[i] + if strings.EqualFold(strings.TrimSpace(entry.APIKey), attrKey) { + return entry + } + } + } + return nil +} + func (s *Service) resolveConfigCodexKey(auth *coreauth.Auth) *config.CodexKey { if auth == nil || s.cfg == nil { return nil @@ -1023,6 +1031,44 @@ func matchWildcard(pattern, value string) bool { return true } +func buildVertexCompatConfigModels(entry *config.VertexCompatKey) []*ModelInfo { + if entry == nil || len(entry.Models) == 0 { + return nil + } + now := time.Now().Unix() + out := make([]*ModelInfo, 0, len(entry.Models)) + seen := make(map[string]struct{}, len(entry.Models)) + for i := range entry.Models { + model := entry.Models[i] + name := strings.TrimSpace(model.Name) + alias := strings.TrimSpace(model.Alias) + if alias == "" { + alias = name + } + if alias == "" { + continue + } + key := strings.ToLower(alias) + if _, exists := seen[key]; exists { + continue + } + seen[key] = struct{}{} + display := name + if display == "" { + display = alias + } + out = append(out, &ModelInfo{ + ID: alias, + Object: "model", + Created: now, + OwnedBy: "vertex", + Type: "vertex", + DisplayName: display, + }) + } + return out +} + func buildClaudeConfigModels(entry *config.ClaudeKey) []*ModelInfo { if entry == nil || len(entry.Models) == 0 { return nil