Skip to content

Conversation

@mcowger
Copy link

@mcowger mcowger commented Dec 25, 2025

Overview

This PR introduces a significant enhancement to the model aliasing system, moving from a simple 1:1 mapping on a per provider basis to a robust multi-target aggregation engine. This allows a single alias to point to multiple underlying models, effectively creating a unified "virtual model" that pools providers from all targets.

Key Features

  • Multi-Target Aliases: Aliases can now be defined as a list of model IDs. For example, a balanced-basic alias can point to both claude-3-5-sonnet and gemini-1.5-pro.
  • Provider Aggregation (Union Logic): The proxy now calculates the union of all available providers across all targets of an alias. If an alias name overlaps with a real model ID, the real model's providers are also included in the pool.
  • Intelligent Model Resolution: When a client makes a request using an alias, the system automatically resolves it to the first target model that the specific client (OpenAI, Anthropic, or Gemini) supports.
  • Flexible YAML Configuration: Updated the configuration parser to support both legacy string mappings (alias: target) and new sequence mappings (alias: [t1, t2]) using a custom YAML unmarshaler.
  • Management API Support: The Management API has been updated to allow creating and updating multi-target aliases via the PUT /aliases/:alias endpoint.

Technical Changes

  • internal/config/config.go: Changed Aliases type to map[string][]string and implemented UnmarshalYAML for backward compatibility.
  • internal/registry/model_registry.go:
    • Updated GetModelProviders and GetModelCount to aggregate data across all alias targets.
    • Updated ResolveModelForClient to iterate through targets and find the first compatible match.
    • Updated isModelAvailableLocked to check availability across the entire target set.
  • internal/api/handlers/management/config_basic.go: Updated handlers to accept targets (slice) or target (string) in JSON payloads.
  • internal/util/provider.go: Updated global utility functions to handle the new slice-based alias structure.

Testing

Example Configuration

aliases:
  "claude-sonnet-4-5":
    - "claude-sonnet-4-5-20250929"
    - "gemini-claude-sonnet-4-5"
  "universal-pro":
    - "gpt-4o"
    - "claude-3-5-sonnet-20241022"
    - "gemini-1.5-pro"

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @mcowger, 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 introduces a fundamental upgrade to the model aliasing mechanism, transforming it from a simple one-to-one mapping into a sophisticated multi-target aggregation engine. This change empowers users to define aliases that represent a collection of models, enabling the system to pool providers from all associated targets. The primary impact is increased flexibility and resilience in model routing, as requests can now dynamically resolve to the most suitable available model among a defined set, enhancing the overall reliability and performance of the proxy.

Highlights

  • Multi-Target Model Aliases: The model aliasing system has been significantly enhanced to support aliases pointing to multiple underlying model IDs. This allows for the creation of 'virtual models' that aggregate capabilities from several real models.
  • Provider Aggregation (Union Logic): The proxy now intelligently calculates the union of all available providers across all target models specified by an alias. If an alias name overlaps with a real model ID, that real model's providers are also included in the pool, ensuring comprehensive provider coverage.
  • Intelligent Model Resolution for Clients: When a client requests a model using an alias, the system automatically resolves it to the first compatible target model that the specific client (e.g., OpenAI, Anthropic, Gemini) supports, optimizing model selection.
  • Flexible YAML Configuration: The configuration parser has been updated to support both legacy single-string alias mappings and new sequence (list) mappings for multi-target aliases, providing greater flexibility in defining aliases.
  • Management API Support for Aliases: The Management API now includes endpoints to create, retrieve, update, and delete multi-target aliases, allowing for dynamic configuration of the aliasing system.
  • Go Version Update: The project's Go version has been updated to 1.25.5, ensuring compatibility with the latest language features and improvements.

🧠 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 significant and well-implemented feature for multi-target model aliases, allowing a single alias to map to multiple underlying models and aggregating providers. The core logic changes in model_registry.go are robust and supported by a comprehensive new test suite. The management API has also been updated to support this new alias structure.

My review focuses on improving consistency and robustness in the new management API handlers and simplifying some of the new utility functions. I've identified a couple of potential race conditions/inconsistency issues in the management handlers and opportunities to remove redundant code and clarify comments in provider.go.

Comment on lines +268 to +273
h.cfg.Aliases = *body.Aliases
h.persist(c)
// Update global aliases after persisting
if h.cfg.Aliases != nil {
util.SetAliases(h.cfg.Aliases)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The return value of h.persist(c) is not being checked here. Other handlers in this file, such as PutAlias and DeleteAlias, correctly check this return value and abort if persistence fails. This could lead to this endpoint returning a success status even when the configuration change was not saved.

h.cfg.Aliases = *body.Aliases
	if !h.persist(c) {
		return
	}
	// Update global aliases after persisting
	if h.cfg.Aliases != nil {
		util.SetAliases(h.cfg.Aliases)
	}

Comment on lines +322 to +325
util.SetAliases(h.cfg.Aliases)
if !h.persist(c) {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The order of operations in DeleteAlias is inconsistent with other handlers like PutAlias. Here, the global in-memory alias state is updated via util.SetAliases before the configuration is persisted to disk. If h.persist(c) fails, the in-memory state will be inconsistent with the persisted configuration. To ensure atomicity, you should persist the changes first and only update the in-memory state upon successful persistence.

	if !h.persist(c) {
		return
	}
	util.SetAliases(h.cfg.Aliases)

Comment on lines +202 to +214
payload:
default: # Default rules only set parameters when they are missing in the payload.
- models:
- name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex
params: # JSON path (gjson/sjson syntax) -> value
"generationConfig.thinkingConfig.thinkingBudget": 32768
override: # Override rules always set parameters, overwriting any existing values.
- models:
- name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex
params: # JSON path (gjson/sjson syntax) -> value
"reasoning.effort": "high"
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The payload configuration section has been uncommented in this pull request. If this change is unrelated to the multi-target alias feature, it might be better to revert it or move it to a separate PR to keep this one focused on a single concern.

Comment on lines +33 to +42
// GetProviderNameAndModel determines all AI service providers capable of serving a model,
// and returns the resolved model name if an alias was used.
// It considers all possible sources: direct model name and global aliases.
//
// Parameters:
// - modelName: The name of the model to identify providers for.
//
// Returns:
// - []string: All provider identifiers capable of serving the model.
// - string: The resolved model name (original or alias target).
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The documentation for GetProviderNameAndModel is misleading. It states that the function "returns the resolved model name if an alias was used", but the implementation simply returns the original modelName input. This can cause confusion for future maintainers. Please update the comment to accurately describe the function's behavior.

Suggested change
// GetProviderNameAndModel determines all AI service providers capable of serving a model,
// and returns the resolved model name if an alias was used.
// It considers all possible sources: direct model name and global aliases.
//
// Parameters:
// - modelName: The name of the model to identify providers for.
//
// Returns:
// - []string: All provider identifiers capable of serving the model.
// - string: The resolved model name (original or alias target).
// GetProviderNameAndModel determines all AI service providers capable of serving a model,
// and returns the original model name that was passed in.
// It considers all possible sources: direct model name and global aliases.
//
// Parameters:
// - modelName: The name of the model to identify providers for.
//
// Returns:
// - []string: All provider identifiers capable of serving the model.
// - string: The original model name.

Comment on lines +267 to +273
func SetAliases(aliases map[string][]string) {
if aliases == nil {
aliases = make(map[string][]string)
}
aliasValue.Store(aliases)
registry.GetGlobalRegistry().SetAliases(aliases)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The aliasValue global variable (defined on line 17) and the ResolveAlias function (lines 275-297) appear to be redundant. The alias data is already stored and managed within the ModelRegistry. This atomic.Value creates a duplicate state, and since util.ResolveAlias is unused, it's effectively dead code.

To simplify, you could remove aliasValue and ResolveAlias. SetAliases can also be simplified, as the nil check is already handled inside registry.SetAliases.

func SetAliases(aliases map[string][]string) {
	registry.GetGlobalRegistry().SetAliases(aliases)
}

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.

1 participant