Skip to content

feat(routing): native provider priority with automatic fallback#1220

Closed
wayfind wants to merge 3 commits intorouter-for-me:mainfrom
wayfind:feat/native-provider-priority
Closed

feat(routing): native provider priority with automatic fallback#1220
wayfind wants to merge 3 commits intorouter-for-me:mainfrom
wayfind:feat/native-provider-priority

Conversation

@wayfind
Copy link

@wayfind wayfind commented Jan 25, 2026

Problem

When multiple providers support the same model (e.g., native claude and alias-mapped antigravity both provide claude-haiku-4-5), the system had no preference mechanism. Users expected native providers to be used first, with automatic fallback to alternatives when quota is exceeded.

Solution

Two coordinated changes that work together:

1. Registry: Native provider priority sorting

File: internal/registry/model_registry.go

GetModelProviders() now returns providers sorted by native affinity:

// "claude" provider comes before "antigravity" for "claude-haiku-*" models
iMatch := strings.HasPrefix(modelLower, providers[i].nameLower)

2. Conductor: Preserve provider order in selection

File: sdk/cliproxy/auth/conductor.go

pickNextMixed() now iterates by provider order instead of converting to an unordered set:

// Before: providers → set (order lost)
// After:  iterate by provider priority
for _, providerKey := range normalizedProviders {
    for _, candidate := range m.auths { ... }
}

Why Both Changes Are Needed

Change Without the other
Registry sorting only Order lost in pickNextMixed set conversion
Conductor ordering only No native-first ordering to preserve

Behavior

  • Zero configuration: Works automatically based on model name prefix matching
  • Automatic fallback: When native provider quota exceeded, falls back to alias-mapped provider
  • Backward compatible: No API changes, existing configs work unchanged

Example

oauth-model-alias:
  antigravity:
    - name: "gemini-2.5-flash"
      alias: "claude-3-5-haiku-20241022"

Request for claude-3-5-haiku-20241022:

  1. Try native claude provider first
  2. If unavailable/quota exceeded → fallback to antigravity

When multiple providers support the same model, prefer the provider
whose name matches the model's prefix. This enables zero-config
native-first routing with automatic fallback.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @wayfind, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the model provider selection mechanism to move beyond simple alphabetical sorting. By introducing a rule that prioritizes providers whose names are prefixes of the model ID, the system now makes more intuitive and effective choices, aligning with common model naming conventions and enhancing the reliability of provider interactions, particularly for fallback strategies.

Highlights

  • Provider Prioritization: The model provider selection logic has been updated to prioritize providers whose name matches the prefix of the requested model ID. This ensures that native providers (e.g., 'claude' for 'claude-haiku') are chosen over alias-mapped providers when both are available.
  • Improved Fallback Mechanism: This change facilitates better collaboration between providers, allowing for intelligent fallback scenarios, such as using a native provider with caching when available, and automatically falling back to an alternative provider when quotas are exceeded.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a useful feature to prioritize native providers when multiple providers are available for a model. The implementation is straightforward and correctly modifies the sorting logic to prefer providers whose names are prefixes of the model ID. I have one suggestion for a minor performance optimization to avoid repeated work inside the sort closure. Overall, this is a solid improvement.

Comment on lines 958 to 960
modelLower := strings.ToLower(modelID)
iMatch := strings.HasPrefix(modelLower, strings.ToLower(providers[i].name))
jMatch := strings.HasPrefix(modelLower, strings.ToLower(providers[j].name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve performance, consider moving the strings.ToLower(modelID) call outside the sort closure, as it's a repeated calculation on a constant value. The closure can be called O(N log N) times.

Similarly, strings.ToLower(providers[i].name) is also repeatedly calculated. This could be optimized by pre-calculating lowercase provider names when building the providers slice.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 551656f922

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 955 to 959
sort.Slice(providers, func(i, j int) bool {
// Native provider priority: provider name matching model prefix comes first.
// e.g., "claude-haiku-*" prefers "claude" provider over "antigravity".
modelLower := strings.ToLower(modelID)
iMatch := strings.HasPrefix(modelLower, strings.ToLower(providers[i].name))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ensure native-priority affects actual routing

This new prefix-based sort won’t change which provider actually executes requests because the runtime selection path ignores provider ordering: GetProviderName feeds the list into coreauth.Manager, but normalizeProviders just lowercases/dedupes and pickNextMixed builds a set of providers and lets the selector choose any candidate (sdk/cliproxy/auth/conductor.go:1010-1027, 1563-1571). In scenarios with multiple providers (e.g., both "claude" and an alias provider for a claude-* model), the routing will still pick any available auth, so the intended “native provider first” behavior won’t be honored except in logs. If you want native preference to affect real routing, the ordering needs to be passed into the selector or enforced before candidate selection.

Useful? React with 👍 / 👎.

Pre-calculate lowercase strings outside sort closure to avoid
O(N log N) repeated calls:
- Move strings.ToLower(modelID) before sort
- Store nameLower in providerCount struct

Addresses review feedback from Gemini Code Assist.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@wayfind
Copy link
Author

wayfind commented Jan 25, 2026

Thanks for the review feedback @gemini-code-assist!

The performance optimization has been addressed in commit 435492e:

  • Moved strings.ToLower(modelID) outside the sort closure
  • Pre-calculated lowercase provider names in the providerCount struct

This avoids O(N log N) repeated string operations during sorting.

@gemini-code-assist
Copy link
Contributor

Thanks for the update, @wayfind! I appreciate you addressing the performance optimization by pre-calculating modelLower and nameLower outside the sort closure. This is a great improvement that effectively avoids redundant string operations during sorting, enhancing efficiency as you described.

The previous GetModelProviders sorting was ineffective because
pickNextMixed converted the provider list to a set, losing order.

Now iterate by provider order when building candidates, ensuring
native providers (e.g., "claude" for claude-* models) are selected
before alias-mapped providers.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@wayfind
Copy link
Author

wayfind commented Jan 25, 2026

@chatgpt-codex-connector Great catch! You were absolutely right.

Problem identified: GetModelProviders sorting was ineffective because pickNextMixed converted the provider list to a map[string]struct{}, losing the order information.

Fixed in commit 65b3734: Modified pickNextMixed to iterate by provider order when building candidates:

// Before: providers → set (order lost) → iterate m.auths
// After:  providers → preserve order → iterate by provider priority
for _, providerKey := range normalizedProviders {
    for _, candidate := range m.auths {
        if candidate.Provider == providerKey {
            candidates = append(candidates, candidate)
        }
    }
}

Now native providers (e.g., "claude" for claude-* models) are genuinely prioritized over alias-mapped providers in actual routing, not just in logs.

👍 for the thorough analysis!

@chatgpt-codex-connector
Copy link

To use Codex here, create an environment for this repo.

@wayfind wayfind changed the title feat(registry): prioritize native provider by model name prefix feat(routing): native provider priority with automatic fallback Jan 25, 2026
@luispater
Copy link
Collaborator

No plan to support this.

@luispater luispater closed this Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants