refactor(config): rename oauth-model-mappings to oauth-model-alias

This commit is contained in:
hkfires
2026-01-15 18:03:26 +08:00
parent e0ffec885c
commit fe5b3c80cb
19 changed files with 761 additions and 269 deletions

View File

@@ -66,7 +66,7 @@ func TestLookupAPIKeyUpstreamModel(t *testing.T) {
}
}
func TestAPIKeyModelMappings_ConfigHotReload(t *testing.T) {
func TestAPIKeyModelAlias_ConfigHotReload(t *testing.T) {
cfg := &internalconfig.Config{
GeminiKey: []internalconfig.GeminiKey{
{
@@ -82,12 +82,12 @@ func TestAPIKeyModelMappings_ConfigHotReload(t *testing.T) {
ctx := context.Background()
_, _ = mgr.Register(ctx, &Auth{ID: "a1", Provider: "gemini", Attributes: map[string]string{"api_key": "k"}})
// Initial mapping
// Initial alias
if resolved := mgr.lookupAPIKeyUpstreamModel("a1", "g25p"); resolved != "gemini-2.5-pro-exp-03-25" {
t.Fatalf("before reload: got %q, want %q", resolved, "gemini-2.5-pro-exp-03-25")
}
// Hot reload with new mapping
// Hot reload with new alias
mgr.SetConfig(&internalconfig.Config{
GeminiKey: []internalconfig.GeminiKey{
{
@@ -97,13 +97,13 @@ func TestAPIKeyModelMappings_ConfigHotReload(t *testing.T) {
},
})
// New mapping should take effect
// New alias should take effect
if resolved := mgr.lookupAPIKeyUpstreamModel("a1", "g25p"); resolved != "gemini-2.5-flash" {
t.Fatalf("after reload: got %q, want %q", resolved, "gemini-2.5-flash")
}
}
func TestAPIKeyModelMappings_MultipleProviders(t *testing.T) {
func TestAPIKeyModelAlias_MultipleProviders(t *testing.T) {
cfg := &internalconfig.Config{
GeminiKey: []internalconfig.GeminiKey{{APIKey: "gemini-key", Models: []internalconfig.GeminiModel{{Name: "gemini-2.5-pro", Alias: "gp"}}}},
ClaudeKey: []internalconfig.ClaudeKey{{APIKey: "claude-key", Models: []internalconfig.ClaudeModel{{Name: "claude-sonnet-4", Alias: "cs4"}}}},
@@ -133,7 +133,7 @@ func TestAPIKeyModelMappings_MultipleProviders(t *testing.T) {
}
}
func TestApplyAPIKeyModelMapping(t *testing.T) {
func TestApplyAPIKeyModelAlias(t *testing.T) {
cfg := &internalconfig.Config{
GeminiKey: []internalconfig.GeminiKey{
{APIKey: "k", Models: []internalconfig.GeminiModel{{Name: "gemini-2.5-pro-exp-03-25", Alias: "g25p"}}},
@@ -170,7 +170,7 @@ func TestApplyAPIKeyModelMapping(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolvedModel := mgr.applyAPIKeyModelMapping(tt.auth, tt.inputModel)
resolvedModel := mgr.applyAPIKeyModelAlias(tt.auth, tt.inputModel)
if resolvedModel != tt.wantModel {
t.Errorf("model = %q, want %q", resolvedModel, tt.wantModel)

View File

@@ -119,17 +119,17 @@ type Manager struct {
requestRetry atomic.Int32
maxRetryInterval atomic.Int64
// modelNameMappings stores global model name alias mappings (alias -> upstream name) keyed by channel.
modelNameMappings atomic.Value
// oauthModelAlias stores global OAuth model alias mappings (alias -> upstream name) keyed by channel.
oauthModelAlias atomic.Value
// apiKeyModelAlias caches resolved model alias mappings for API-key auths.
// Keyed by auth.ID, value is alias(lower) -> upstream model (including suffix).
apiKeyModelAlias atomic.Value
// runtimeConfig stores the latest application config for request-time decisions.
// It is initialized in NewManager; never Load() before first Store().
runtimeConfig atomic.Value
// apiKeyModelMappings caches resolved model alias mappings for API-key auths.
// Keyed by auth.ID, value is alias(lower) -> upstream model (including suffix).
apiKeyModelMappings atomic.Value
// Optional HTTP RoundTripper provider injected by host.
rtProvider RoundTripperProvider
@@ -155,7 +155,7 @@ func NewManager(store Store, selector Selector, hook Hook) *Manager {
}
// atomic.Value requires non-nil initial value.
manager.runtimeConfig.Store(&internalconfig.Config{})
manager.apiKeyModelMappings.Store(apiKeyModelMappingTable(nil))
manager.apiKeyModelAlias.Store(apiKeyModelAliasTable(nil))
return manager
}
@@ -195,7 +195,7 @@ func (m *Manager) SetConfig(cfg *internalconfig.Config) {
cfg = &internalconfig.Config{}
}
m.runtimeConfig.Store(cfg)
m.rebuildAPIKeyModelMappingsFromRuntimeConfig()
m.rebuildAPIKeyModelAliasFromRuntimeConfig()
}
func (m *Manager) lookupAPIKeyUpstreamModel(authID, requestedModel string) string {
@@ -210,7 +210,7 @@ func (m *Manager) lookupAPIKeyUpstreamModel(authID, requestedModel string) strin
if requestedModel == "" {
return ""
}
table, _ := m.apiKeyModelMappings.Load().(apiKeyModelMappingTable)
table, _ := m.apiKeyModelAlias.Load().(apiKeyModelAliasTable)
if table == nil {
return ""
}
@@ -238,7 +238,7 @@ func (m *Manager) lookupAPIKeyUpstreamModel(authID, requestedModel string) strin
}
func (m *Manager) rebuildAPIKeyModelMappingsFromRuntimeConfig() {
func (m *Manager) rebuildAPIKeyModelAliasFromRuntimeConfig() {
if m == nil {
return
}
@@ -248,10 +248,10 @@ func (m *Manager) rebuildAPIKeyModelMappingsFromRuntimeConfig() {
}
m.mu.Lock()
defer m.mu.Unlock()
m.rebuildAPIKeyModelMappingsLocked(cfg)
m.rebuildAPIKeyModelAliasLocked(cfg)
}
func (m *Manager) rebuildAPIKeyModelMappingsLocked(cfg *internalconfig.Config) {
func (m *Manager) rebuildAPIKeyModelAliasLocked(cfg *internalconfig.Config) {
if m == nil {
return
}
@@ -259,7 +259,7 @@ func (m *Manager) rebuildAPIKeyModelMappingsLocked(cfg *internalconfig.Config) {
cfg = &internalconfig.Config{}
}
out := make(apiKeyModelMappingTable)
out := make(apiKeyModelAliasTable)
for _, auth := range m.auths {
if auth == nil {
continue
@@ -277,19 +277,19 @@ func (m *Manager) rebuildAPIKeyModelMappingsLocked(cfg *internalconfig.Config) {
switch provider {
case "gemini":
if entry := resolveGeminiAPIKeyConfig(cfg, auth); entry != nil {
compileAPIKeyModelMappingsForModels(byAlias, entry.Models)
compileAPIKeyModelAliasForModels(byAlias, entry.Models)
}
case "claude":
if entry := resolveClaudeAPIKeyConfig(cfg, auth); entry != nil {
compileAPIKeyModelMappingsForModels(byAlias, entry.Models)
compileAPIKeyModelAliasForModels(byAlias, entry.Models)
}
case "codex":
if entry := resolveCodexAPIKeyConfig(cfg, auth); entry != nil {
compileAPIKeyModelMappingsForModels(byAlias, entry.Models)
compileAPIKeyModelAliasForModels(byAlias, entry.Models)
}
case "vertex":
if entry := resolveVertexAPIKeyConfig(cfg, auth); entry != nil {
compileAPIKeyModelMappingsForModels(byAlias, entry.Models)
compileAPIKeyModelAliasForModels(byAlias, entry.Models)
}
default:
// OpenAI-compat uses config selection from auth.Attributes.
@@ -301,7 +301,7 @@ func (m *Manager) rebuildAPIKeyModelMappingsLocked(cfg *internalconfig.Config) {
}
if compatName != "" || strings.EqualFold(strings.TrimSpace(auth.Provider), "openai-compatibility") {
if entry := resolveOpenAICompatConfig(cfg, providerKey, compatName, auth.Provider); entry != nil {
compileAPIKeyModelMappingsForModels(byAlias, entry.Models)
compileAPIKeyModelAliasForModels(byAlias, entry.Models)
}
}
}
@@ -311,10 +311,10 @@ func (m *Manager) rebuildAPIKeyModelMappingsLocked(cfg *internalconfig.Config) {
}
}
m.apiKeyModelMappings.Store(out)
m.apiKeyModelAlias.Store(out)
}
func compileAPIKeyModelMappingsForModels[T interface {
func compileAPIKeyModelAliasForModels[T interface {
GetName() string
GetAlias() string
}](out map[string]string, models []T) {
@@ -408,7 +408,7 @@ func (m *Manager) Register(ctx context.Context, auth *Auth) (*Auth, error) {
m.mu.Lock()
m.auths[auth.ID] = auth.Clone()
m.mu.Unlock()
m.rebuildAPIKeyModelMappingsFromRuntimeConfig()
m.rebuildAPIKeyModelAliasFromRuntimeConfig()
_ = m.persist(ctx, auth)
m.hook.OnAuthRegistered(ctx, auth.Clone())
return auth.Clone(), nil
@@ -427,7 +427,7 @@ func (m *Manager) Update(ctx context.Context, auth *Auth) (*Auth, error) {
auth.EnsureIndex()
m.auths[auth.ID] = auth.Clone()
m.mu.Unlock()
m.rebuildAPIKeyModelMappingsFromRuntimeConfig()
m.rebuildAPIKeyModelAliasFromRuntimeConfig()
_ = m.persist(ctx, auth)
m.hook.OnAuthUpdated(ctx, auth.Clone())
return auth.Clone(), nil
@@ -456,7 +456,7 @@ func (m *Manager) Load(ctx context.Context) error {
if cfg == nil {
cfg = &internalconfig.Config{}
}
m.rebuildAPIKeyModelMappingsLocked(cfg)
m.rebuildAPIKeyModelAliasLocked(cfg)
return nil
}
@@ -592,8 +592,8 @@ func (m *Manager) executeMixedOnce(ctx context.Context, providers []string, req
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
resp, errExec := executor.Execute(execCtx, auth, execReq, opts)
result := Result{AuthID: auth.ID, Provider: provider, Model: routeModel, Success: errExec == nil}
if errExec != nil {
@@ -641,8 +641,8 @@ func (m *Manager) executeCountMixedOnce(ctx context.Context, providers []string,
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
resp, errExec := executor.CountTokens(execCtx, auth, execReq, opts)
result := Result{AuthID: auth.ID, Provider: provider, Model: routeModel, Success: errExec == nil}
if errExec != nil {
@@ -690,8 +690,8 @@ func (m *Manager) executeStreamMixedOnce(ctx context.Context, providers []string
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
chunks, errStream := executor.ExecuteStream(execCtx, auth, execReq, opts)
if errStream != nil {
rerr := &Error{Message: errStream.Error()}
@@ -756,8 +756,8 @@ func (m *Manager) executeWithProvider(ctx context.Context, provider string, req
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
resp, errExec := executor.Execute(execCtx, auth, execReq, opts)
result := Result{AuthID: auth.ID, Provider: provider, Model: routeModel, Success: errExec == nil}
if errExec != nil {
@@ -805,8 +805,8 @@ func (m *Manager) executeCountWithProvider(ctx context.Context, provider string,
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
resp, errExec := executor.CountTokens(execCtx, auth, execReq, opts)
result := Result{AuthID: auth.ID, Provider: provider, Model: routeModel, Success: errExec == nil}
if errExec != nil {
@@ -854,8 +854,8 @@ func (m *Manager) executeStreamWithProvider(ctx context.Context, provider string
}
execReq := req
execReq.Model = rewriteModelForAuth(routeModel, auth)
execReq.Model = m.applyOAuthModelMapping(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelMapping(auth, execReq.Model)
execReq.Model = m.applyOAuthModelAlias(auth, execReq.Model)
execReq.Model = m.applyAPIKeyModelAlias(auth, execReq.Model)
chunks, errStream := executor.ExecuteStream(execCtx, auth, execReq, opts)
if errStream != nil {
rerr := &Error{Message: errStream.Error()}
@@ -908,7 +908,7 @@ func rewriteModelForAuth(model string, auth *Auth) string {
return strings.TrimPrefix(model, needle)
}
func (m *Manager) applyAPIKeyModelMapping(auth *Auth, requestedModel string) string {
func (m *Manager) applyAPIKeyModelAlias(auth *Auth, requestedModel string) string {
if m == nil || auth == nil {
return requestedModel
}
@@ -1079,7 +1079,7 @@ func resolveUpstreamModelForOpenAICompatAPIKey(cfg *internalconfig.Config, auth
return resolveModelAliasFromConfigModels(requestedModel, asModelAliasEntries(entry.Models))
}
type apiKeyModelMappingTable map[string]map[string]string
type apiKeyModelAliasTable map[string]map[string]string
func resolveOpenAICompatConfig(cfg *internalconfig.Config, providerKey, compatName, authProvider string) *internalconfig.OpenAICompatibility {
if cfg == nil {
@@ -1109,11 +1109,11 @@ func resolveOpenAICompatConfig(cfg *internalconfig.Config, providerKey, compatNa
func asModelAliasEntries[T interface {
GetName() string
GetAlias() string
}](models []T) []modelMappingEntry {
}](models []T) []modelAliasEntry {
if len(models) == 0 {
return nil
}
out := make([]modelMappingEntry, 0, len(models))
out := make([]modelAliasEntry, 0, len(models))
for i := range models {
out = append(out, models[i])
}

View File

@@ -7,24 +7,24 @@ import (
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
)
type modelMappingEntry interface {
type modelAliasEntry interface {
GetName() string
GetAlias() string
}
type modelNameMappingTable struct {
type oauthModelAliasTable struct {
// reverse maps channel -> alias (lower) -> original upstream model name.
reverse map[string]map[string]string
}
func compileModelNameMappingTable(mappings map[string][]internalconfig.ModelNameMapping) *modelNameMappingTable {
if len(mappings) == 0 {
return &modelNameMappingTable{}
func compileOAuthModelAliasTable(aliases map[string][]internalconfig.OAuthModelAlias) *oauthModelAliasTable {
if len(aliases) == 0 {
return &oauthModelAliasTable{}
}
out := &modelNameMappingTable{
reverse: make(map[string]map[string]string, len(mappings)),
out := &oauthModelAliasTable{
reverse: make(map[string]map[string]string, len(aliases)),
}
for rawChannel, entries := range mappings {
for rawChannel, entries := range aliases {
channel := strings.ToLower(strings.TrimSpace(rawChannel))
if channel == "" || len(entries) == 0 {
continue
@@ -55,24 +55,24 @@ func compileModelNameMappingTable(mappings map[string][]internalconfig.ModelName
return out
}
// SetOAuthModelMappings updates the OAuth model name mapping table used during execution.
// The mapping is applied per-auth channel to resolve the upstream model name while keeping the
// SetOAuthModelAlias updates the OAuth model name alias table used during execution.
// The alias is applied per-auth channel to resolve the upstream model name while keeping the
// client-visible model name unchanged for translation/response formatting.
func (m *Manager) SetOAuthModelMappings(mappings map[string][]internalconfig.ModelNameMapping) {
func (m *Manager) SetOAuthModelAlias(aliases map[string][]internalconfig.OAuthModelAlias) {
if m == nil {
return
}
table := compileModelNameMappingTable(mappings)
table := compileOAuthModelAliasTable(aliases)
// atomic.Value requires non-nil store values.
if table == nil {
table = &modelNameMappingTable{}
table = &oauthModelAliasTable{}
}
m.modelNameMappings.Store(table)
m.oauthModelAlias.Store(table)
}
// applyOAuthModelMapping resolves the upstream model from OAuth model mappings.
// If a mapping exists, the returned model is the upstream model.
func (m *Manager) applyOAuthModelMapping(auth *Auth, requestedModel string) string {
// applyOAuthModelAlias resolves the upstream model from OAuth model alias.
// If an alias exists, the returned model is the upstream model.
func (m *Manager) applyOAuthModelAlias(auth *Auth, requestedModel string) string {
upstreamModel := m.resolveOAuthUpstreamModel(auth, requestedModel)
if upstreamModel == "" {
return requestedModel
@@ -80,7 +80,7 @@ func (m *Manager) applyOAuthModelMapping(auth *Auth, requestedModel string) stri
return upstreamModel
}
func resolveModelAliasFromConfigModels(requestedModel string, models []modelMappingEntry) string {
func resolveModelAliasFromConfigModels(requestedModel string, models []modelAliasEntry) string {
requestedModel = strings.TrimSpace(requestedModel)
if requestedModel == "" {
return ""
@@ -131,18 +131,18 @@ func resolveModelAliasFromConfigModels(requestedModel string, models []modelMapp
return ""
}
// resolveOAuthUpstreamModel resolves the upstream model name from OAuth model mappings.
// If a mapping exists, returns the original (upstream) model name that corresponds
// resolveOAuthUpstreamModel resolves the upstream model name from OAuth model alias.
// If an alias exists, returns the original (upstream) model name that corresponds
// to the requested alias.
//
// If the requested model contains a thinking suffix (e.g., "gemini-2.5-pro(8192)"),
// the suffix is preserved in the returned model name. However, if the mapping's
// the suffix is preserved in the returned model name. However, if the alias's
// original name already contains a suffix, the config suffix takes priority.
func (m *Manager) resolveOAuthUpstreamModel(auth *Auth, requestedModel string) string {
return resolveUpstreamModelFromMappingTable(m, auth, requestedModel, modelMappingChannel(auth))
return resolveUpstreamModelFromAliasTable(m, auth, requestedModel, modelAliasChannel(auth))
}
func resolveUpstreamModelFromMappingTable(m *Manager, auth *Auth, requestedModel, channel string) string {
func resolveUpstreamModelFromAliasTable(m *Manager, auth *Auth, requestedModel, channel string) string {
if m == nil || auth == nil {
return ""
}
@@ -160,8 +160,8 @@ func resolveUpstreamModelFromMappingTable(m *Manager, auth *Auth, requestedModel
candidates = append(candidates, requestedModel)
}
raw := m.modelNameMappings.Load()
table, _ := raw.(*modelNameMappingTable)
raw := m.oauthModelAlias.Load()
table, _ := raw.(*oauthModelAliasTable)
if table == nil || table.reverse == nil {
return ""
}
@@ -197,10 +197,10 @@ func resolveUpstreamModelFromMappingTable(m *Manager, auth *Auth, requestedModel
return ""
}
// modelMappingChannel extracts the OAuth model mapping channel from an Auth object.
// modelAliasChannel extracts the OAuth model alias channel from an Auth object.
// It determines the provider and auth kind from the Auth's attributes and delegates
// to OAuthModelMappingChannel for the actual channel resolution.
func modelMappingChannel(auth *Auth) string {
// to OAuthModelAliasChannel for the actual channel resolution.
func modelAliasChannel(auth *Auth) string {
if auth == nil {
return ""
}
@@ -214,20 +214,20 @@ func modelMappingChannel(auth *Auth) string {
authKind = "apikey"
}
}
return OAuthModelMappingChannel(provider, authKind)
return OAuthModelAliasChannel(provider, authKind)
}
// OAuthModelMappingChannel returns the OAuth model mapping channel name for a given provider
// OAuthModelAliasChannel returns the OAuth model alias channel name for a given provider
// and auth kind. Returns empty string if the provider/authKind combination doesn't support
// OAuth model mappings (e.g., API key authentication).
// OAuth model alias (e.g., API key authentication).
//
// Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow.
func OAuthModelMappingChannel(provider, authKind string) string {
func OAuthModelAliasChannel(provider, authKind string) string {
provider = strings.ToLower(strings.TrimSpace(provider))
authKind = strings.ToLower(strings.TrimSpace(authKind))
switch provider {
case "gemini":
// gemini provider uses gemini-api-key config, not oauth-model-mappings.
// gemini provider uses gemini-api-key config, not oauth-model-alias.
// OAuth-based gemini auth is converted to "gemini-cli" by the synthesizer.
return ""
case "vertex":

View File

@@ -10,15 +10,15 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
t.Parallel()
tests := []struct {
name string
mappings map[string][]internalconfig.ModelNameMapping
channel string
input string
want string
name string
aliases map[string][]internalconfig.OAuthModelAlias
channel string
input string
want string
}{
{
name: "numeric suffix preserved",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -27,7 +27,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "level suffix preserved",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"claude": {{Name: "claude-sonnet-4-5-20250514", Alias: "claude-sonnet-4-5"}},
},
channel: "claude",
@@ -36,7 +36,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "no suffix unchanged",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -45,7 +45,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "config suffix takes priority",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"claude": {{Name: "claude-sonnet-4-5-20250514(low)", Alias: "claude-sonnet-4-5"}},
},
channel: "claude",
@@ -54,7 +54,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "auto suffix preserved",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -63,7 +63,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "none suffix preserved",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -72,7 +72,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "case insensitive alias lookup with suffix",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "Gemini-2.5-Pro"}},
},
channel: "gemini-cli",
@@ -80,8 +80,8 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
want: "gemini-2.5-pro-exp-03-25(high)",
},
{
name: "no mapping returns empty",
mappings: map[string][]internalconfig.ModelNameMapping{
name: "no alias returns empty",
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -90,7 +90,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "wrong channel returns empty",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "claude",
@@ -99,7 +99,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "empty suffix filtered out",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
},
channel: "gemini-cli",
@@ -108,7 +108,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
},
{
name: "incomplete suffix treated as no suffix",
mappings: map[string][]internalconfig.ModelNameMapping{
aliases: map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro(high"}},
},
channel: "gemini-cli",
@@ -123,7 +123,7 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
mgr := NewManager(nil, nil, nil)
mgr.SetConfig(&internalconfig.Config{})
mgr.SetOAuthModelMappings(tt.mappings)
mgr.SetOAuthModelAlias(tt.aliases)
auth := createAuthForChannel(tt.channel)
got := mgr.resolveOAuthUpstreamModel(auth, tt.input)
@@ -157,21 +157,21 @@ func createAuthForChannel(channel string) *Auth {
}
}
func TestApplyOAuthModelMapping_SuffixPreservation(t *testing.T) {
func TestApplyOAuthModelAlias_SuffixPreservation(t *testing.T) {
t.Parallel()
mappings := map[string][]internalconfig.ModelNameMapping{
aliases := map[string][]internalconfig.OAuthModelAlias{
"gemini-cli": {{Name: "gemini-2.5-pro-exp-03-25", Alias: "gemini-2.5-pro"}},
}
mgr := NewManager(nil, nil, nil)
mgr.SetConfig(&internalconfig.Config{})
mgr.SetOAuthModelMappings(mappings)
mgr.SetOAuthModelAlias(aliases)
auth := &Auth{ID: "test-auth-id", Provider: "gemini-cli"}
resolvedModel := mgr.applyOAuthModelMapping(auth, "gemini-2.5-pro(8192)")
resolvedModel := mgr.applyOAuthModelAlias(auth, "gemini-2.5-pro(8192)")
if resolvedModel != "gemini-2.5-pro-exp-03-25(8192)" {
t.Errorf("applyOAuthModelMapping() model = %q, want %q", resolvedModel, "gemini-2.5-pro-exp-03-25(8192)")
t.Errorf("applyOAuthModelAlias() model = %q, want %q", resolvedModel, "gemini-2.5-pro-exp-03-25(8192)")
}
}

View File

@@ -216,7 +216,7 @@ func (b *Builder) Build() (*Service, error) {
// Attach a default RoundTripper provider so providers can opt-in per-auth transports.
coreManager.SetRoundTripperProvider(newDefaultRoundTripperProvider())
coreManager.SetConfig(b.cfg)
coreManager.SetOAuthModelMappings(b.cfg.OAuthModelMappings)
coreManager.SetOAuthModelAlias(b.cfg.OAuthModelAlias)
service := &Service{
cfg: b.cfg,

View File

@@ -554,7 +554,7 @@ func (s *Service) Run(ctx context.Context) error {
s.cfgMu.Unlock()
if s.coreManager != nil {
s.coreManager.SetConfig(newCfg)
s.coreManager.SetOAuthModelMappings(newCfg.OAuthModelMappings)
s.coreManager.SetOAuthModelAlias(newCfg.OAuthModelAlias)
}
s.rebindExecutors()
}
@@ -849,7 +849,7 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) {
}
}
}
models = applyOAuthModelMappings(s.cfg, provider, authKind, models)
models = applyOAuthModelAlias(s.cfg, provider, authKind, models)
if len(models) > 0 {
key := provider
if key == "" {
@@ -1222,28 +1222,28 @@ func rewriteModelInfoName(name, oldID, newID string) string {
return name
}
func applyOAuthModelMappings(cfg *config.Config, provider, authKind string, models []*ModelInfo) []*ModelInfo {
func applyOAuthModelAlias(cfg *config.Config, provider, authKind string, models []*ModelInfo) []*ModelInfo {
if cfg == nil || len(models) == 0 {
return models
}
channel := coreauth.OAuthModelMappingChannel(provider, authKind)
if channel == "" || len(cfg.OAuthModelMappings) == 0 {
channel := coreauth.OAuthModelAliasChannel(provider, authKind)
if channel == "" || len(cfg.OAuthModelAlias) == 0 {
return models
}
mappings := cfg.OAuthModelMappings[channel]
if len(mappings) == 0 {
aliases := cfg.OAuthModelAlias[channel]
if len(aliases) == 0 {
return models
}
type mappingEntry struct {
type aliasEntry struct {
alias string
fork bool
}
forward := make(map[string][]mappingEntry, len(mappings))
for i := range mappings {
name := strings.TrimSpace(mappings[i].Name)
alias := strings.TrimSpace(mappings[i].Alias)
forward := make(map[string][]aliasEntry, len(aliases))
for i := range aliases {
name := strings.TrimSpace(aliases[i].Name)
alias := strings.TrimSpace(aliases[i].Alias)
if name == "" || alias == "" {
continue
}
@@ -1251,7 +1251,7 @@ func applyOAuthModelMappings(cfg *config.Config, provider, authKind string, mode
continue
}
key := strings.ToLower(name)
forward[key] = append(forward[key], mappingEntry{alias: alias, fork: mappings[i].Fork})
forward[key] = append(forward[key], aliasEntry{alias: alias, fork: aliases[i].Fork})
}
if len(forward) == 0 {
return models

View File

@@ -6,9 +6,9 @@ import (
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
)
func TestApplyOAuthModelMappings_Rename(t *testing.T) {
func TestApplyOAuthModelAlias_Rename(t *testing.T) {
cfg := &config.Config{
OAuthModelMappings: map[string][]config.ModelNameMapping{
OAuthModelAlias: map[string][]config.OAuthModelAlias{
"codex": {
{Name: "gpt-5", Alias: "g5"},
},
@@ -18,7 +18,7 @@ func TestApplyOAuthModelMappings_Rename(t *testing.T) {
{ID: "gpt-5", Name: "models/gpt-5"},
}
out := applyOAuthModelMappings(cfg, "codex", "oauth", models)
out := applyOAuthModelAlias(cfg, "codex", "oauth", models)
if len(out) != 1 {
t.Fatalf("expected 1 model, got %d", len(out))
}
@@ -30,9 +30,9 @@ func TestApplyOAuthModelMappings_Rename(t *testing.T) {
}
}
func TestApplyOAuthModelMappings_ForkAddsAlias(t *testing.T) {
func TestApplyOAuthModelAlias_ForkAddsAlias(t *testing.T) {
cfg := &config.Config{
OAuthModelMappings: map[string][]config.ModelNameMapping{
OAuthModelAlias: map[string][]config.OAuthModelAlias{
"codex": {
{Name: "gpt-5", Alias: "g5", Fork: true},
},
@@ -42,7 +42,7 @@ func TestApplyOAuthModelMappings_ForkAddsAlias(t *testing.T) {
{ID: "gpt-5", Name: "models/gpt-5"},
}
out := applyOAuthModelMappings(cfg, "codex", "oauth", models)
out := applyOAuthModelAlias(cfg, "codex", "oauth", models)
if len(out) != 2 {
t.Fatalf("expected 2 models, got %d", len(out))
}
@@ -57,9 +57,9 @@ func TestApplyOAuthModelMappings_ForkAddsAlias(t *testing.T) {
}
}
func TestApplyOAuthModelMappings_ForkAddsMultipleAliases(t *testing.T) {
func TestApplyOAuthModelAlias_ForkAddsMultipleAliases(t *testing.T) {
cfg := &config.Config{
OAuthModelMappings: map[string][]config.ModelNameMapping{
OAuthModelAlias: map[string][]config.OAuthModelAlias{
"codex": {
{Name: "gpt-5", Alias: "g5", Fork: true},
{Name: "gpt-5", Alias: "g5-2", Fork: true},
@@ -70,7 +70,7 @@ func TestApplyOAuthModelMappings_ForkAddsMultipleAliases(t *testing.T) {
{ID: "gpt-5", Name: "models/gpt-5"},
}
out := applyOAuthModelMappings(cfg, "codex", "oauth", models)
out := applyOAuthModelAlias(cfg, "codex", "oauth", models)
if len(out) != 3 {
t.Fatalf("expected 3 models, got %d", len(out))
}