From 07da781336820b3d1df93d2a3c40dae48863ced8 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 31 Oct 2025 09:15:14 +0800 Subject: [PATCH] feat(registry): add client model support check for executor filtering - Introduced `ClientSupportsModel` function to `ModelRegistry` for verifying client support for specific models. - Integrated model support validation into executor candidate filtering logic. - Updated CLIProxy registry interface to include the new support check method. --- internal/registry/model_registry.go | 25 +++++++++++++++++++++++++ sdk/cliproxy/auth/manager.go | 5 +++++ sdk/cliproxy/model_registry.go | 1 + 3 files changed, 31 insertions(+) diff --git a/internal/registry/model_registry.go b/internal/registry/model_registry.go index 46ada713..f5af06f4 100644 --- a/internal/registry/model_registry.go +++ b/internal/registry/model_registry.go @@ -523,6 +523,31 @@ func (r *ModelRegistry) ResumeClientModel(clientID, modelID string) { log.Debugf("Resumed client %s for model %s", clientID, modelID) } +// ClientSupportsModel reports whether the client registered support for modelID. +func (r *ModelRegistry) ClientSupportsModel(clientID, modelID string) bool { + clientID = strings.TrimSpace(clientID) + modelID = strings.TrimSpace(modelID) + if clientID == "" || modelID == "" { + return false + } + + r.mutex.RLock() + defer r.mutex.RUnlock() + + models, exists := r.clientModels[clientID] + if !exists || len(models) == 0 { + return false + } + + for _, id := range models { + if strings.EqualFold(strings.TrimSpace(id), modelID) { + return true + } + } + + return false +} + // GetAvailableModels returns all models that have at least one available client // Parameters: // - handlerType: The handler type to filter models for (e.g., "openai", "claude", "gemini") diff --git a/sdk/cliproxy/auth/manager.go b/sdk/cliproxy/auth/manager.go index 1e3f4197..fe41cfe3 100644 --- a/sdk/cliproxy/auth/manager.go +++ b/sdk/cliproxy/auth/manager.go @@ -841,6 +841,8 @@ func (m *Manager) pickNext(ctx context.Context, provider, model string, opts cli return nil, nil, &Error{Code: "executor_not_found", Message: "executor not registered"} } candidates := make([]*Auth, 0, len(m.auths)) + modelKey := strings.TrimSpace(model) + registryRef := registry.GetGlobalRegistry() for _, candidate := range m.auths { if candidate.Provider != provider || candidate.Disabled { continue @@ -848,6 +850,9 @@ func (m *Manager) pickNext(ctx context.Context, provider, model string, opts cli if _, used := tried[candidate.ID]; used { continue } + if modelKey != "" && registryRef != nil && !registryRef.ClientSupportsModel(candidate.ID, modelKey) { + continue + } candidates = append(candidates, candidate) } if len(candidates) == 0 { diff --git a/sdk/cliproxy/model_registry.go b/sdk/cliproxy/model_registry.go index 63703189..44ef8d7d 100644 --- a/sdk/cliproxy/model_registry.go +++ b/sdk/cliproxy/model_registry.go @@ -11,6 +11,7 @@ type ModelRegistry interface { UnregisterClient(clientID string) SetModelQuotaExceeded(clientID, modelID string) ClearModelQuotaExceeded(clientID, modelID string) + ClientSupportsModel(clientID, modelID string) bool GetAvailableModels(handlerType string) []map[string]any }