mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
refactor(config): rename oauth-model-mappings to oauth-model-alias
This commit is contained in:
@@ -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)
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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":
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user