From c3f1cdd7e501889e954a5606a419bfebb4f2fd27 Mon Sep 17 00:00:00 2001 From: y Date: Tue, 10 Feb 2026 19:01:07 +0800 Subject: [PATCH] feat(config): add default Kiro model aliases for standard Claude model names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kiro models are exposed with kiro- prefix (e.g., kiro-claude-sonnet-4-5), which prevents clients like Claude Code from using standard model names (e.g., claude-sonnet-4-20250514). This change injects default oauth-model-alias entries for the kiro channel when no user-configured aliases exist, following the same pattern as the existing Antigravity defaults. The aliases map standard Claude model names (both with and without date suffixes) to their kiro-prefixed counterparts. Default aliases added: - claude-sonnet-4-5-20250929 / claude-sonnet-4-5 → kiro-claude-sonnet-4-5 - claude-sonnet-4-20250514 / claude-sonnet-4 → kiro-claude-sonnet-4 - claude-opus-4-6 → kiro-claude-opus-4-6 - claude-opus-4-5-20251101 / claude-opus-4-5 → kiro-claude-opus-4-5 - claude-haiku-4-5-20251001 / claude-haiku-4-5 → kiro-claude-haiku-4-5 All aliases use fork: true to preserve the original kiro-* names. User-configured kiro aliases are respected and not overridden. Closes router-for-me/CLIProxyAPIPlus#208 --- internal/config/config.go | 26 +++++- .../config/oauth_model_alias_migration.go | 22 +++++ internal/config/oauth_model_alias_test.go | 85 +++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index cd9f6884..50b3cbd5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -736,8 +736,32 @@ func payloadRawString(value any) ([]byte, bool) { // SanitizeOAuthModelAlias normalizes and deduplicates global OAuth model name aliases. // It trims whitespace, normalizes channel keys to lower-case, drops empty entries, // allows multiple aliases per upstream name, and ensures aliases are unique within each channel. +// It also injects default aliases for channels that have built-in defaults (e.g., kiro) +// when no user-configured aliases exist for those channels. func (cfg *Config) SanitizeOAuthModelAlias() { - if cfg == nil || len(cfg.OAuthModelAlias) == 0 { + if cfg == nil { + return + } + + // Inject default Kiro aliases if no user-configured kiro aliases exist + if cfg.OAuthModelAlias == nil { + cfg.OAuthModelAlias = make(map[string][]OAuthModelAlias) + } + if _, hasKiro := cfg.OAuthModelAlias["kiro"]; !hasKiro { + // Check case-insensitive too + found := false + for k := range cfg.OAuthModelAlias { + if strings.EqualFold(strings.TrimSpace(k), "kiro") { + found = true + break + } + } + if !found { + cfg.OAuthModelAlias["kiro"] = defaultKiroAliases() + } + } + + if len(cfg.OAuthModelAlias) == 0 { return } out := make(map[string][]OAuthModelAlias, len(cfg.OAuthModelAlias)) diff --git a/internal/config/oauth_model_alias_migration.go b/internal/config/oauth_model_alias_migration.go index f52df27a..639cbccd 100644 --- a/internal/config/oauth_model_alias_migration.go +++ b/internal/config/oauth_model_alias_migration.go @@ -20,6 +20,28 @@ var antigravityModelConversionTable = map[string]string{ "gemini-claude-opus-4-6-thinking": "claude-opus-4-6-thinking", } +// defaultKiroAliases returns the default oauth-model-alias configuration +// for the kiro channel. Maps kiro-prefixed model names to standard Claude model +// names so that clients like Claude Code can use standard names directly. +func defaultKiroAliases() []OAuthModelAlias { + return []OAuthModelAlias{ + // Sonnet 4.5 + {Name: "kiro-claude-sonnet-4-5", Alias: "claude-sonnet-4-5-20250929", Fork: true}, + {Name: "kiro-claude-sonnet-4-5", Alias: "claude-sonnet-4-5", Fork: true}, + // Sonnet 4 + {Name: "kiro-claude-sonnet-4", Alias: "claude-sonnet-4-20250514", Fork: true}, + {Name: "kiro-claude-sonnet-4", Alias: "claude-sonnet-4", Fork: true}, + // Opus 4.6 + {Name: "kiro-claude-opus-4-6", Alias: "claude-opus-4-6", Fork: true}, + // Opus 4.5 + {Name: "kiro-claude-opus-4-5", Alias: "claude-opus-4-5-20251101", Fork: true}, + {Name: "kiro-claude-opus-4-5", Alias: "claude-opus-4-5", Fork: true}, + // Haiku 4.5 + {Name: "kiro-claude-haiku-4-5", Alias: "claude-haiku-4-5-20251001", Fork: true}, + {Name: "kiro-claude-haiku-4-5", Alias: "claude-haiku-4-5", Fork: true}, + } +} + // defaultAntigravityAliases returns the default oauth-model-alias configuration // for the antigravity channel when neither field exists. func defaultAntigravityAliases() []OAuthModelAlias { diff --git a/internal/config/oauth_model_alias_test.go b/internal/config/oauth_model_alias_test.go index a5886474..7497eec8 100644 --- a/internal/config/oauth_model_alias_test.go +++ b/internal/config/oauth_model_alias_test.go @@ -54,3 +54,88 @@ func TestSanitizeOAuthModelAlias_AllowsMultipleAliasesForSameName(t *testing.T) } } } + +func TestSanitizeOAuthModelAlias_InjectsDefaultKiroAliases(t *testing.T) { + // When no kiro aliases are configured, defaults should be injected + cfg := &Config{ + OAuthModelAlias: map[string][]OAuthModelAlias{ + "codex": { + {Name: "gpt-5", Alias: "g5"}, + }, + }, + } + + cfg.SanitizeOAuthModelAlias() + + kiroAliases := cfg.OAuthModelAlias["kiro"] + if len(kiroAliases) == 0 { + t.Fatal("expected default kiro aliases to be injected") + } + + // Check that standard Claude model names are present + aliasSet := make(map[string]bool) + for _, a := range kiroAliases { + aliasSet[a.Alias] = true + } + expectedAliases := []string{ + "claude-sonnet-4-5-20250929", + "claude-sonnet-4-5", + "claude-sonnet-4-20250514", + "claude-sonnet-4", + "claude-opus-4-6", + "claude-opus-4-5-20251101", + "claude-opus-4-5", + "claude-haiku-4-5-20251001", + "claude-haiku-4-5", + } + for _, expected := range expectedAliases { + if !aliasSet[expected] { + t.Fatalf("expected default kiro alias %q to be present", expected) + } + } + + // All should have fork=true + for _, a := range kiroAliases { + if !a.Fork { + t.Fatalf("expected all default kiro aliases to have fork=true, got fork=false for %q", a.Alias) + } + } + + // Codex aliases should still be preserved + if len(cfg.OAuthModelAlias["codex"]) != 1 { + t.Fatal("expected codex aliases to be preserved") + } +} + +func TestSanitizeOAuthModelAlias_DoesNotOverrideUserKiroAliases(t *testing.T) { + // When user has configured kiro aliases, defaults should NOT be injected + cfg := &Config{ + OAuthModelAlias: map[string][]OAuthModelAlias{ + "kiro": { + {Name: "kiro-claude-sonnet-4", Alias: "my-custom-sonnet", Fork: true}, + }, + }, + } + + cfg.SanitizeOAuthModelAlias() + + kiroAliases := cfg.OAuthModelAlias["kiro"] + if len(kiroAliases) != 1 { + t.Fatalf("expected 1 user-configured kiro alias, got %d", len(kiroAliases)) + } + if kiroAliases[0].Alias != "my-custom-sonnet" { + t.Fatalf("expected user alias to be preserved, got %q", kiroAliases[0].Alias) + } +} + +func TestSanitizeOAuthModelAlias_InjectsDefaultKiroWhenEmpty(t *testing.T) { + // When OAuthModelAlias is nil, kiro defaults should still be injected + cfg := &Config{} + + cfg.SanitizeOAuthModelAlias() + + kiroAliases := cfg.OAuthModelAlias["kiro"] + if len(kiroAliases) == 0 { + t.Fatal("expected default kiro aliases to be injected when OAuthModelAlias is nil") + } +}