Merge pull request #1465 from router-for-me/kimi-fix

fix(kimi): add OAuth model-alias channel support and cover OAuth excl…
This commit is contained in:
Luis Pater
2026-02-07 01:27:30 +08:00
committed by GitHub
5 changed files with 96 additions and 3 deletions

View File

@@ -221,7 +221,7 @@ nonstream-keepalive-interval: 0
# Global OAuth model name aliases (per channel) # Global OAuth model name aliases (per channel)
# These aliases rename model IDs for both model listing and request routing. # These aliases rename model IDs for both model listing and request routing.
# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow. # Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow, kimi.
# NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode. # NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode.
# You can repeat the same name with different aliases to expose multiple client model names. # You can repeat the same name with different aliases to expose multiple client model names.
oauth-model-alias: oauth-model-alias:
@@ -262,6 +262,9 @@ oauth-model-alias:
# iflow: # iflow:
# - name: "glm-4.7" # - name: "glm-4.7"
# alias: "glm-god" # alias: "glm-god"
# kimi:
# - name: "kimi-k2.5"
# alias: "k2.5"
# OAuth provider excluded models # OAuth provider excluded models
# oauth-excluded-models: # oauth-excluded-models:
@@ -284,6 +287,8 @@ oauth-model-alias:
# - "vision-model" # - "vision-model"
# iflow: # iflow:
# - "tstars2.0" # - "tstars2.0"
# kimi:
# - "kimi-k2-thinking"
# Optional payload configuration # Optional payload configuration
# payload: # payload:

View File

@@ -221,7 +221,7 @@ func modelAliasChannel(auth *Auth) string {
// and auth kind. Returns empty string if the provider/authKind combination doesn't support // and auth kind. Returns empty string if the provider/authKind combination doesn't support
// OAuth model alias (e.g., API key authentication). // OAuth model alias (e.g., API key authentication).
// //
// Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow. // Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow, kimi.
func OAuthModelAliasChannel(provider, authKind string) string { func OAuthModelAliasChannel(provider, authKind string) string {
provider = strings.ToLower(strings.TrimSpace(provider)) provider = strings.ToLower(strings.TrimSpace(provider))
authKind = strings.ToLower(strings.TrimSpace(authKind)) authKind = strings.ToLower(strings.TrimSpace(authKind))
@@ -245,7 +245,7 @@ func OAuthModelAliasChannel(provider, authKind string) string {
return "" return ""
} }
return "codex" return "codex"
case "gemini-cli", "aistudio", "antigravity", "qwen", "iflow": case "gemini-cli", "aistudio", "antigravity", "qwen", "iflow", "kimi":
return provider return provider
default: default:
return "" return ""

View File

@@ -70,6 +70,15 @@ func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
input: "gemini-2.5-pro(none)", input: "gemini-2.5-pro(none)",
want: "gemini-2.5-pro-exp-03-25(none)", want: "gemini-2.5-pro-exp-03-25(none)",
}, },
{
name: "kimi suffix preserved",
aliases: map[string][]internalconfig.OAuthModelAlias{
"kimi": {{Name: "kimi-k2.5", Alias: "k2.5"}},
},
channel: "kimi",
input: "k2.5(high)",
want: "kimi-k2.5(high)",
},
{ {
name: "case insensitive alias lookup with suffix", name: "case insensitive alias lookup with suffix",
aliases: map[string][]internalconfig.OAuthModelAlias{ aliases: map[string][]internalconfig.OAuthModelAlias{
@@ -152,11 +161,21 @@ func createAuthForChannel(channel string) *Auth {
return &Auth{Provider: "qwen"} return &Auth{Provider: "qwen"}
case "iflow": case "iflow":
return &Auth{Provider: "iflow"} return &Auth{Provider: "iflow"}
case "kimi":
return &Auth{Provider: "kimi"}
default: default:
return &Auth{Provider: channel} return &Auth{Provider: channel}
} }
} }
func TestOAuthModelAliasChannel_Kimi(t *testing.T) {
t.Parallel()
if got := OAuthModelAliasChannel("kimi", "oauth"); got != "kimi" {
t.Fatalf("OAuthModelAliasChannel() = %q, want %q", got, "kimi")
}
}
func TestApplyOAuthModelAlias_SuffixPreservation(t *testing.T) { func TestApplyOAuthModelAlias_SuffixPreservation(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -0,0 +1,45 @@
package cliproxy
import (
"testing"
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
)
func TestOAuthExcludedModels_KimiOAuth(t *testing.T) {
t.Parallel()
svc := &Service{
cfg: &config.Config{
OAuthExcludedModels: map[string][]string{
"kimi": {"kimi-k2-thinking", "kimi-k2.5"},
},
},
}
got := svc.oauthExcludedModels("kimi", "oauth")
if len(got) != 2 {
t.Fatalf("expected 2 excluded models, got %d", len(got))
}
if got[0] != "kimi-k2-thinking" || got[1] != "kimi-k2.5" {
t.Fatalf("unexpected excluded models: %#v", got)
}
}
func TestOAuthExcludedModels_KimiAPIKeyReturnsNil(t *testing.T) {
t.Parallel()
svc := &Service{
cfg: &config.Config{
OAuthExcludedModels: map[string][]string{
"kimi": {"kimi-k2-thinking"},
},
},
}
got := svc.oauthExcludedModels("kimi", "apikey")
if got != nil {
t.Fatalf("expected nil for apikey auth kind, got %#v", got)
}
}

View File

@@ -90,3 +90,27 @@ func TestApplyOAuthModelAlias_ForkAddsMultipleAliases(t *testing.T) {
t.Fatalf("expected forked model name %q, got %q", "models/g5-2", out[2].Name) t.Fatalf("expected forked model name %q, got %q", "models/g5-2", out[2].Name)
} }
} }
func TestApplyOAuthModelAlias_KimiRename(t *testing.T) {
cfg := &config.Config{
OAuthModelAlias: map[string][]config.OAuthModelAlias{
"kimi": {
{Name: "kimi-k2.5", Alias: "k2.5"},
},
},
}
models := []*ModelInfo{
{ID: "kimi-k2.5", Name: "models/kimi-k2.5"},
}
out := applyOAuthModelAlias(cfg, "kimi", "oauth", models)
if len(out) != 1 {
t.Fatalf("expected 1 model, got %d", len(out))
}
if out[0].ID != "k2.5" {
t.Fatalf("expected model id %q, got %q", "k2.5", out[0].ID)
}
if out[0].Name != "models/k2.5" {
t.Fatalf("expected model name %q, got %q", "models/k2.5", out[0].Name)
}
}