Skip to content

Commit e8b1c58

Browse files
committed
fix(copilot): map hyphen-style Claude model names to dot-style upstream IDs
Clients (Claude Code, Cursor, etc.) request Claude models with hyphen-only IDs like 'claude-haiku-4-5', but GitHub Copilot publishes them with dots (e.g. 'claude-haiku-4.5'). Without alias mapping, requests fail with: 502 unknown provider for model claude-haiku-4-5 Three additions: 1. internal/config/oauth_model_alias_defaults.go (new): static defaults mapping dot IDs to hyphen aliases for the 6 known Claude Copilot models, plus GitHubCopilotAliasesFromModels() helper for dynamic alias generation from fetched model lists. 2. internal/config/config.go SanitizeOAuthModelAlias: inject the github-copilot defaults if the user has not configured any github-copilot aliases. Runs before the existing dedup pass. 3. sdk/cliproxy/auth/oauth_model_alias.go OAuthModelAliasChannel: add 'github-copilot' to the list of channels where aliases are honored. Previously fell to the default branch and aliases were ignored. Origin: ported from the archived CLIProxyAPIPlus fork's internal/config/oauth_model_alias_defaults.go (Kiro defaults skipped).
1 parent d376402 commit e8b1c58

3 files changed

Lines changed: 62 additions & 2 deletions

File tree

internal/config/config.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,26 @@ func (cfg *Config) SanitizeClaudeHeaderDefaults() {
830830
// It trims whitespace, normalizes channel keys to lower-case, drops empty entries,
831831
// allows multiple aliases per upstream name, and ensures aliases are unique within each channel.
832832
func (cfg *Config) SanitizeOAuthModelAlias() {
833-
if cfg == nil || len(cfg.OAuthModelAlias) == 0 {
833+
if cfg == nil {
834+
return
835+
}
836+
// Inject channel defaults when the channel is absent in user config.
837+
// Presence is checked case-insensitively.
838+
if cfg.OAuthModelAlias == nil {
839+
cfg.OAuthModelAlias = make(map[string][]OAuthModelAlias)
840+
}
841+
hasChannel := func(channel string) bool {
842+
for k := range cfg.OAuthModelAlias {
843+
if strings.EqualFold(strings.TrimSpace(k), channel) {
844+
return true
845+
}
846+
}
847+
return false
848+
}
849+
if !hasChannel("github-copilot") {
850+
cfg.OAuthModelAlias["github-copilot"] = defaultGitHubCopilotAliases()
851+
}
852+
if len(cfg.OAuthModelAlias) == 0 {
834853
return
835854
}
836855
out := make(map[string][]OAuthModelAlias, len(cfg.OAuthModelAlias))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package config
2+
3+
import "strings"
4+
5+
// defaultGitHubCopilotAliases returns default oauth-model-alias entries for
6+
// GitHub Copilot Claude models. It exposes hyphen-style IDs used by clients
7+
// (e.g. claude-haiku-4-5) in addition to the dot-style IDs that GitHub
8+
// Copilot upstream actually publishes (e.g. claude-haiku-4.5).
9+
func defaultGitHubCopilotAliases() []OAuthModelAlias {
10+
return []OAuthModelAlias{
11+
{Name: "claude-haiku-4.5", Alias: "claude-haiku-4-5", Fork: true},
12+
{Name: "claude-opus-4.1", Alias: "claude-opus-4-1", Fork: true},
13+
{Name: "claude-opus-4.5", Alias: "claude-opus-4-5", Fork: true},
14+
{Name: "claude-opus-4.6", Alias: "claude-opus-4-6", Fork: true},
15+
{Name: "claude-sonnet-4.5", Alias: "claude-sonnet-4-5", Fork: true},
16+
{Name: "claude-sonnet-4.6", Alias: "claude-sonnet-4-6", Fork: true},
17+
}
18+
}
19+
20+
// GitHubCopilotAliasesFromModels generates oauth-model-alias entries from a
21+
// dynamic list of model IDs fetched from the Copilot API. It auto-creates
22+
// hyphen aliases for any model whose ID contains a dot (e.g.
23+
// "claude-opus-4.7" -> "claude-opus-4-7"), which is the convention used by
24+
// Claude models served via GitHub Copilot.
25+
func GitHubCopilotAliasesFromModels(modelIDs []string) []OAuthModelAlias {
26+
var aliases []OAuthModelAlias
27+
seen := make(map[string]struct{})
28+
for _, id := range modelIDs {
29+
if !strings.Contains(id, ".") {
30+
continue
31+
}
32+
hyphenID := strings.ReplaceAll(id, ".", "-")
33+
key := id + "->" + hyphenID
34+
if _, ok := seen[key]; ok {
35+
continue
36+
}
37+
seen[key] = struct{}{}
38+
aliases = append(aliases, OAuthModelAlias{Name: id, Alias: hyphenID, Fork: true})
39+
}
40+
return aliases
41+
}

sdk/cliproxy/auth/oauth_model_alias.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ func OAuthModelAliasChannel(provider, authKind string) string {
289289
return ""
290290
}
291291
return "codex"
292-
case "gemini-cli", "aistudio", "antigravity", "kimi":
292+
case "gemini-cli", "aistudio", "antigravity", "kimi", "github-copilot":
293293
return provider
294294
default:
295295
return ""

0 commit comments

Comments
 (0)