-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
fix(cliproxy): keep original model ids routable with aliases #4125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 5 commits
8b42bc3
adc389d
28f8f0a
a2a2cb2
e9df43e
cc21639
5afc7ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1951,7 +1951,7 @@ func (s *Service) registerModelsForAuthWithCache(ctx context.Context, a *coreaut | |
| models = buildGeminiConfigModels(entry) | ||
| } | ||
| if authKind == "apikey" { | ||
| excluded = entry.ExcludedModels | ||
| excluded = expandExcludedModelsForConfigAliases(entry.ExcludedModels, entry.Models) | ||
| } | ||
| } | ||
| models = applyExcludedModels(models, excluded) | ||
|
|
@@ -1962,7 +1962,7 @@ func (s *Service) registerModelsForAuthWithCache(ctx context.Context, a *coreaut | |
| models = buildGeminiConfigModels(entry) | ||
| } | ||
| if authKind == "apikey" { | ||
| excluded = entry.ExcludedModels | ||
| excluded = expandExcludedModelsForConfigAliases(entry.ExcludedModels, entry.Models) | ||
| } | ||
| } | ||
| models = applyExcludedModels(models, excluded) | ||
|
|
@@ -1974,7 +1974,7 @@ func (s *Service) registerModelsForAuthWithCache(ctx context.Context, a *coreaut | |
| models = buildVertexCompatConfigModels(entry) | ||
| } | ||
| if authKind == "apikey" { | ||
| excluded = entry.ExcludedModels | ||
| excluded = expandExcludedModelsForConfigAliases(entry.ExcludedModels, entry.Models) | ||
| } | ||
| } | ||
| models = applyExcludedModels(models, excluded) | ||
|
|
@@ -1992,7 +1992,7 @@ func (s *Service) registerModelsForAuthWithCache(ctx context.Context, a *coreaut | |
| models = buildClaudeConfigModels(entry) | ||
| } | ||
| if authKind == "apikey" { | ||
| excluded = entry.ExcludedModels | ||
| excluded = expandExcludedModelsForConfigAliases(entry.ExcludedModels, entry.Models) | ||
| } | ||
| } | ||
| models = applyExcludedModels(models, excluded) | ||
|
|
@@ -2018,7 +2018,7 @@ func (s *Service) registerModelsForAuthWithCache(ctx context.Context, a *coreaut | |
| models = buildCodexConfigModels(entry) | ||
| } | ||
| if authKind == "apikey" { | ||
| excluded = entry.ExcludedModels | ||
| excluded = expandExcludedModelsForConfigAliases(entry.ExcludedModels, entry.Models) | ||
| } | ||
| } | ||
| models = applyExcludedModels(models, excluded) | ||
|
|
@@ -2383,6 +2383,58 @@ func applyExcludedModels(models []*ModelInfo, excluded []string) []*ModelInfo { | |
| return filtered | ||
| } | ||
|
|
||
| func expandExcludedModelsForConfigAliases[T modelEntry](excluded []string, models []T) []string { | ||
| if len(excluded) == 0 || len(models) == 0 { | ||
| return excluded | ||
| } | ||
| patterns := make([]string, 0, len(excluded)) | ||
| seen := make(map[string]struct{}, len(excluded)+len(models)*2) | ||
| out := make([]string, 0, len(excluded)+len(models)*2) | ||
| add := func(model string) { | ||
| model = strings.ToLower(strings.TrimSpace(model)) | ||
| if model == "" { | ||
| return | ||
| } | ||
| if _, exists := seen[model]; exists { | ||
| return | ||
| } | ||
| seen[model] = struct{}{} | ||
| out = append(out, model) | ||
| } | ||
| for _, item := range excluded { | ||
| trimmed := strings.ToLower(strings.TrimSpace(item)) | ||
| if trimmed == "" { | ||
| continue | ||
| } | ||
| patterns = append(patterns, trimmed) | ||
| add(trimmed) | ||
| } | ||
| if len(patterns) == 0 { | ||
| return excluded | ||
| } | ||
| for i := range models { | ||
| name := strings.TrimSpace(models[i].GetName()) | ||
| alias := strings.TrimSpace(models[i].GetAlias()) | ||
| if name == "" || alias == "" || name == alias { | ||
| continue | ||
| } | ||
| nameKey := strings.ToLower(name) | ||
| aliasKey := strings.ToLower(alias) | ||
| matched := false | ||
| for _, pattern := range patterns { | ||
| if matchWildcard(pattern, nameKey) || matchWildcard(pattern, aliasKey) { | ||
| matched = true | ||
| break | ||
| } | ||
| } | ||
| if matched { | ||
| add(name) | ||
| add(alias) | ||
| } | ||
| } | ||
| return out | ||
| } | ||
|
|
||
| func applyModelPrefixes(models []*ModelInfo, prefix string, forceModelPrefix bool) []*ModelInfo { | ||
| trimmedPrefix := strings.TrimSpace(prefix) | ||
| if trimmedPrefix == "" || len(models) == 0 { | ||
|
|
@@ -2479,12 +2531,15 @@ func buildOpenAICompatibilityConfigModels(compat *config.OpenAICompatibility) [] | |
| return nil | ||
| } | ||
| now := time.Now().Unix() | ||
| models := make([]*ModelInfo, 0, len(compat.Models)) | ||
| models := make([]*ModelInfo, 0, len(compat.Models)*2) | ||
| seen := make(map[string]struct{}, len(compat.Models)*2) | ||
| for i := range compat.Models { | ||
| model := compat.Models[i] | ||
| modelID := strings.TrimSpace(model.Alias) | ||
| alias := strings.TrimSpace(model.Alias) | ||
| name := strings.TrimSpace(model.Name) | ||
| modelID := alias | ||
| if modelID == "" { | ||
| modelID = strings.TrimSpace(model.Name) | ||
| modelID = name | ||
| } | ||
| if modelID == "" { | ||
| continue | ||
|
|
@@ -2497,7 +2552,7 @@ func buildOpenAICompatibilityConfigModels(compat *config.OpenAICompatibility) [] | |
| if thinking == nil && !model.Image { | ||
| thinking = ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}} | ||
| } | ||
| models = append(models, &ModelInfo{ | ||
| info := &ModelInfo{ | ||
| ID: modelID, | ||
| Object: "model", | ||
| Created: now, | ||
|
|
@@ -2506,18 +2561,72 @@ func buildOpenAICompatibilityConfigModels(compat *config.OpenAICompatibility) [] | |
| DisplayName: modelID, | ||
| UserDefined: false, | ||
| Thinking: thinking, | ||
| }) | ||
| } | ||
| models = appendModelInfoIfUnique(models, info, seen) | ||
| if alias != "" && name != "" && alias != name { | ||
| models = appendModelInfoIfUnique(models, configOriginalModelInfo(name, compat.Name, modelType, now, false, thinking), seen) | ||
| } | ||
| } | ||
| return models | ||
| } | ||
|
|
||
| func appendModelInfoIfUnique(models []*ModelInfo, info *ModelInfo, seen map[string]struct{}) []*ModelInfo { | ||
| if info == nil { | ||
| return models | ||
| } | ||
| modelID := strings.TrimSpace(info.ID) | ||
| if modelID == "" { | ||
| return models | ||
| } | ||
| key := modelID | ||
| if _, exists := seen[key]; exists { | ||
| return models | ||
| } | ||
| seen[key] = struct{}{} | ||
| info.ID = modelID | ||
| return append(models, info) | ||
| } | ||
|
|
||
| func configOriginalModelInfo(name, ownedBy, modelType string, created int64, userDefined bool, thinking *registry.ThinkingSupport) *ModelInfo { | ||
| name = strings.TrimSpace(name) | ||
| if name == "" { | ||
| return nil | ||
| } | ||
| if info := registry.LookupStaticModelInfo(name); info != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh issue in the static-metadata fix: when an OpenAI-compatible entry aliases a static native model (for example Useful? React with 👍 / 👎. |
||
| info.ID = name | ||
| if info.Object == "" { | ||
| info.Object = "model" | ||
| } | ||
| if info.Created == 0 { | ||
| info.Created = created | ||
| } | ||
| info.OwnedBy = ownedBy | ||
| info.Type = modelType | ||
| if info.DisplayName == "" { | ||
| info.DisplayName = name | ||
| } | ||
| info.UserDefined = false | ||
| return info | ||
| } | ||
| return &ModelInfo{ | ||
| ID: name, | ||
| Object: "model", | ||
| Created: created, | ||
| OwnedBy: ownedBy, | ||
| Type: modelType, | ||
| DisplayName: name, | ||
| UserDefined: userDefined, | ||
| Thinking: thinking, | ||
| } | ||
| } | ||
|
|
||
| func buildConfigModels[T modelEntry](models []T, ownedBy, modelType string) []*ModelInfo { | ||
| if len(models) == 0 { | ||
| return nil | ||
| } | ||
| now := time.Now().Unix() | ||
| out := make([]*ModelInfo, 0, len(models)) | ||
| seen := make(map[string]struct{}, len(models)) | ||
| out := make([]*ModelInfo, 0, len(models)*2) | ||
| seen := make(map[string]struct{}, len(models)*2) | ||
| for i := range models { | ||
| model := models[i] | ||
| name := strings.TrimSpace(model.GetName()) | ||
|
|
@@ -2528,11 +2637,6 @@ func buildConfigModels[T modelEntry](models []T, ownedBy, modelType string) []*M | |
| if alias == "" { | ||
| continue | ||
| } | ||
| key := strings.ToLower(alias) | ||
| if _, exists := seen[key]; exists { | ||
| continue | ||
| } | ||
| seen[key] = struct{}{} | ||
| display := name | ||
| if display == "" { | ||
| display = alias | ||
|
|
@@ -2551,7 +2655,10 @@ func buildConfigModels[T modelEntry](models []T, ownedBy, modelType string) []*M | |
| info.Thinking = upstream.Thinking | ||
| } | ||
| } | ||
| out = append(out, info) | ||
| out = appendModelInfoIfUnique(out, info, seen) | ||
| if name != "" && alias != name { | ||
| out = appendModelInfoIfUnique(out, configOriginalModelInfo(name, ownedBy, modelType, now, true, info.Thinking), seen) | ||
| } | ||
| } | ||
| return out | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a config contains two aliases that differ only by case, this helper now registers both spellings as separate model IDs because the de-dupe key is the exact ID. The execution path still resolves API-key aliases case-insensitively and keeps the first mapping in
compileAPIKeyModelAliasForModels(OpenAI-compatible pooling also matches aliases withEqualFold), so a request for the second advertised alias selects this credential but is sent to the first alias's upstream model. Please keep folded de-dupe for configured aliases and apply case-sensitive uniqueness only when adding the extra original upstream ID.Useful? React with 👍 / 👎.