fix(routing): fallback to static model definitions and heuristics in GetProviderName - #5441
fix(routing): fallback to static model definitions and heuristics in GetProviderName#5441Visio-Vanitas wants to merge 4 commits into
Conversation
…GetProviderName - Add static model definition lookup fallback via registry.LookupStaticModelInfo - Add model name heuristics fallback for standard model prefixes - Add comprehensive unit tests in internal/util/provider_test.go Co-Authored-By: Claude Code <noreply@anthropic.com>
|
This pull request targeted The base branch has been automatically changed to |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd06ca1a7b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for _, p := range staticModelProviders(modelName) { | ||
| appendProvider(p) |
There was a problem hiding this comment.
Make inferred routes eligible for auth selection
When this fallback is reached because no client has registered the model—the cold-start and catalog-lag scenarios this change targets—the inferred providers only bypass the handler's model_not_found check. Execution still rejects every credential: scheduledAuthMeta.supportsModel returns false for an empty registry-backed model set (sdk/cliproxy/auth/scheduler.go:834-843), while the legacy path likewise requires ClientSupportsModel (sdk/cliproxy/auth/conductor_selection.go:824-836). Consequently these requests still cannot execute and now fail later as auth unavailable; the fallback must also establish compatible auth-selection eligibility rather than changing routing alone.
Useful? React with 👍 / 👎.
| case ownedBy == "claude" || typeStr == "claude" || ownedBy == "anthropic": | ||
| return []string{"claude"} |
There was a problem hiding this comment.
Derive static providers from catalog membership
LookupStaticModelInfo returns the first matching metadata record rather than every catalog section containing the ID (internal/registry/model_definitions.go:343-357), so ownership cannot identify all capable providers. For example, claude-sonnet-4-6 appears in both the Claude and Antigravity sections (internal/registry/models/models.json:48 and :3363), but this branch returns only claude; whenever the dynamic registry is unavailable, an Antigravity-only installation therefore omits its valid provider. Determine providers from section membership instead of the first record's OwnedBy/Type.
Useful? React with 👍 / 👎.
…lish auth selection eligibility - Derive static providers across all catalog sections via LookupStaticModelProviders - Allow conductor and scheduler auth selection for statically known models - Add unit tests for multi-section lookup and auth fallback eligibility Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Addressed both review suggestions in commit 73cefc4:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 73cefc4373
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // 2. Name heuristics fallback | ||
| for _, p := range heuristicModelProviders(modelName) { | ||
| appendProvider(p) | ||
| } |
There was a problem hiding this comment.
Make heuristic-only routes selectable
When the registry and static catalog both miss a newly named model, this branch returns inferred providers, but auth selection still rejects every credential because both newly added eligibility paths only call LookupStaticModelProviders. For example, GetProviderName("deepseek-chat") returns codex/claude, while scheduledAuthMeta.supportsModel and authSupportsRouteModel return false for that ID, so the heuristic changes the initial 400 into a later auth_not_found rather than enabling execution. Fresh evidence in this revision is that the added auth-selection fallback covers static membership only; heuristic providers need corresponding eligibility handling.
Useful? React with 👍 / 👎.
| if m.auth != nil { | ||
| providerKey := strings.ToLower(strings.TrimSpace(executorKeyFromAuth(m.auth))) | ||
| if providerKey != "" { | ||
| for _, p := range registry.LookupStaticModelProviders(modelKey) { | ||
| if strings.EqualFold(p, providerKey) { |
There was a problem hiding this comment.
Preserve per-auth model restrictions before static fallback
When an auth has a non-empty registry snapshot that omits a static model—because models narrows the credential's catalog, excluded-models filtered it out, or a Codex plan lacks it—the miss now falls through and returns true solely from provider-wide static membership. This makes the scheduler select credentials for models explicitly absent from their registered capabilities, defeating exclusions and plan restrictions and causing requests to be sent to an unsupported upstream; the static fallback should apply only when no authoritative per-auth model snapshot exists. The legacy path in authSupportsRouteModel has the same fallthrough.
Useful? React with 👍 / 👎.
| {"claude", data.Claude}, | ||
| {"gemini", data.Gemini}, | ||
| {"vertex", data.Vertex}, |
There was a problem hiding this comment.
Include the Gemini Interactions channel in static ownership
When dynamic registrations are unavailable and the installation has only gemini-interactions credentials, every Gemini model is routed to gemini, vertex, or aistudio, even though GetStaticModelDefinitionsByChannel("gemini-interactions") and registerModelsForAuth explicitly use the same Gemini catalog for that provider. Because this table also drives the new auth eligibility fallback, the interactions credential cannot be selected in exactly the cold-registry scenario this change targets; add gemini-interactions as another owner of data.Gemini.
Useful? React with 👍 / 👎.
…rName with strict auth eligibility - Derive static providers across all catalog sections via LookupStaticModelProviders (including gemini-interactions) - Allow cold-start/unindexed credentials to be selected for static and heuristic models via InferredModelProviders - Preserve per-auth model exclusions and whitelist restrictions when credentials have registered model sets - Trigger scheduler shard rebuild on dynamic OAuth model alias updates - Add 512-character length guard for incoming model names - Add comprehensive unit tests covering edge cases and race conditions Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Addressed all feedback and completed edge-case hardening in commit 83d8898:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83d8898f5e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } | ||
| if len(m.supportedModelSet) == 0 { | ||
| return false | ||
| if len(m.supportedModelSet) > 0 { |
There was a problem hiding this comment.
Distinguish fully excluded auths from unindexed auths
When exclusions remove every model—most notably when the management API disables a config API key by adding excluded-models: ["*"]—model registration deliberately unregisters the client, so supportedModelSet is empty here. This branch then mistakes that authoritative empty catalog for cold-start state and makes the credential eligible for any inferred provider model, effectively re-enabling the disabled key; authSupportsRouteModel mirrors the same behavior. Fresh evidence in this revision is that only non-empty snapshots are now protected, leaving the all-models-excluded case bypassable; preserve an explicit registered/excluded state rather than using slice length to identify unindexed auths.
Useful? React with 👍 / 👎.
| lower := strings.ToLower(strings.TrimSpace(modelName)) | ||
| switch { | ||
| case strings.Contains(lower, "gemini"): | ||
| return []string{"gemini", "antigravity", "vertex", "aistudio"} |
There was a problem hiding this comment.
Include interactions in the Gemini heuristic fallback
When a newly named Gemini model is absent from both the dynamic registry and bundled catalog, this heuristic is the only routing source, but it omits gemini-interactions even though that channel uses the same Gemini catalog and executor family. An installation with only interactions credentials therefore rejects the request or reports no auth while the other Gemini channels are considered. The static-table omission raised earlier is fixed, but fresh evidence here is that the equivalent heuristic-only path still excludes the channel.
Useful? React with 👍 / 👎.
Co-Authored-By: Claude Code <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62e1e58321
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| func heuristicModelProviders(modelName string) []string { | ||
| lower := strings.ToLower(strings.TrimSpace(modelName)) | ||
| switch { | ||
| case strings.Contains(lower, "gemini"): |
There was a problem hiding this comment.
Restrict the Gemini heuristic to recognized prefixes
When an unknown model merely contains gemini anywhere in its name (for example, not-gemini-model), this fallback classifies it as supported by the Google providers; in an unindexed/cold-start state, the matching auth fallback can then select a credential and send the invalid model upstream, while otherwise the request incorrectly changes from model_not_found to an auth-selection failure. The documented heuristic is for Gemini-family prefixes, so this should use a prefix or another explicitly recognized model-name form rather than an unrestricted substring match.
Useful? React with 👍 / 👎.
Problem
When
GetProviderName(modelName)is called to resolve AI service providers for an incoming request, it currently only queries dynamic model registrations viaregistry.GetGlobalRegistry().GetModelProviders(modelName).If the global registry is during background refresh, or when requests arrive for static models (e.g.
gemini-3.7-flash-high, newly released models, or cold-start scenarios where clients haven't registered models dynamically),GetProviderNamereturns an empty slice[]. This immediately causeshandlers_routing.goto reject the request with:{"error":{"message":"unknown provider for model <model_name>","type":"invalid_request_error","code":"model_not_found","param":"model"}}Note: the function doc comment already mentions "When the model has not been registered yet, it falls back to legacy string heuristics to infer potential providers", but the actual fallback implementation was missing.
Solution
registry.LookupStaticModelInfo(modelName)to match known providers (gemini,antigravity,claude,codex,xai,kimi).gemini*,claude*,gpt-*,o1-*,o3-*,o4-*,grok*,kimi*,deepseek*).internal/util/provider_test.gocovering dynamic, static, heuristic, and edge cases.Verification
go test -v ./internal/util/...passes all test cases.go test ./...andgo build ./cmd/servercompile cleanly with zero errors.gemini-3.7-flash-highand other static models are properly resolved without 400 errors.🤖 Generated with Claude Code
Co-Authored-By: Claude Code noreply@anthropic.com