2026-03-30 14:09:15 +00:00
|
|
|
package models
|
|
|
|
|
|
2026-04-01 00:53:34 +00:00
|
|
|
import (
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
2026-03-30 14:09:15 +00:00
|
|
|
|
|
|
|
|
var anthropicToCursor = map[string]string{
|
2026-04-01 00:53:34 +00:00
|
|
|
"claude-opus-4-6": "opus-4.6",
|
|
|
|
|
"claude-opus-4.6": "opus-4.6",
|
|
|
|
|
"claude-sonnet-4-6": "sonnet-4.6",
|
|
|
|
|
"claude-sonnet-4.6": "sonnet-4.6",
|
|
|
|
|
"claude-opus-4-5": "opus-4.5",
|
|
|
|
|
"claude-opus-4.5": "opus-4.5",
|
|
|
|
|
"claude-sonnet-4-5": "sonnet-4.5",
|
|
|
|
|
"claude-sonnet-4.5": "sonnet-4.5",
|
|
|
|
|
"claude-opus-4": "opus-4.6",
|
|
|
|
|
"claude-sonnet-4": "sonnet-4.6",
|
|
|
|
|
"claude-haiku-4-5-20251001": "sonnet-4.5",
|
|
|
|
|
"claude-haiku-4-5": "sonnet-4.5",
|
|
|
|
|
"claude-haiku-4-6": "sonnet-4.6",
|
|
|
|
|
"claude-haiku-4": "sonnet-4.5",
|
|
|
|
|
"claude-opus-4-6-thinking": "opus-4.6-thinking",
|
|
|
|
|
"claude-sonnet-4-6-thinking": "sonnet-4.6-thinking",
|
|
|
|
|
"claude-opus-4-5-thinking": "opus-4.5-thinking",
|
|
|
|
|
"claude-sonnet-4-5-thinking": "sonnet-4.5-thinking",
|
2026-03-30 14:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ModelAlias struct {
|
|
|
|
|
CursorID string
|
|
|
|
|
AnthropicID string
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cursorToAnthropicAlias = []ModelAlias{
|
|
|
|
|
{"opus-4.6", "claude-opus-4-6", "Claude 4.6 Opus"},
|
|
|
|
|
{"opus-4.6-thinking", "claude-opus-4-6-thinking", "Claude 4.6 Opus (Thinking)"},
|
|
|
|
|
{"sonnet-4.6", "claude-sonnet-4-6", "Claude 4.6 Sonnet"},
|
|
|
|
|
{"sonnet-4.6-thinking", "claude-sonnet-4-6-thinking", "Claude 4.6 Sonnet (Thinking)"},
|
|
|
|
|
{"opus-4.5", "claude-opus-4-5", "Claude 4.5 Opus"},
|
|
|
|
|
{"opus-4.5-thinking", "claude-opus-4-5-thinking", "Claude 4.5 Opus (Thinking)"},
|
|
|
|
|
{"sonnet-4.5", "claude-sonnet-4-5", "Claude 4.5 Sonnet"},
|
|
|
|
|
{"sonnet-4.5-thinking", "claude-sonnet-4-5-thinking", "Claude 4.5 Sonnet (Thinking)"},
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 00:53:34 +00:00
|
|
|
// cursorModelPattern matches cursor model IDs like "opus-4.6", "sonnet-4.7-thinking".
|
|
|
|
|
var cursorModelPattern = regexp.MustCompile(`^([a-zA-Z]+)-(\d+)\.(\d+)(-thinking)?$`)
|
|
|
|
|
|
|
|
|
|
// reverseDynamicPattern matches dynamically generated anthropic aliases
|
|
|
|
|
// like "claude-opus-4-7", "claude-sonnet-4-7-thinking".
|
|
|
|
|
var reverseDynamicPattern = regexp.MustCompile(`^claude-([a-zA-Z]+)-(\d+)-(\d+)(-thinking)?$`)
|
|
|
|
|
|
|
|
|
|
func generateDynamicAlias(cursorID string) (AnthropicAlias, bool) {
|
|
|
|
|
m := cursorModelPattern.FindStringSubmatch(cursorID)
|
|
|
|
|
if m == nil {
|
|
|
|
|
return AnthropicAlias{}, false
|
|
|
|
|
}
|
|
|
|
|
family := m[1]
|
|
|
|
|
major := m[2]
|
|
|
|
|
minor := m[3]
|
|
|
|
|
thinking := m[4]
|
|
|
|
|
|
|
|
|
|
anthropicID := "claude-" + family + "-" + major + "-" + minor + thinking
|
|
|
|
|
capFamily := strings.ToUpper(family[:1]) + family[1:]
|
|
|
|
|
name := capFamily + " " + major + "." + minor
|
|
|
|
|
if thinking == "-thinking" {
|
|
|
|
|
name += " (Thinking)"
|
|
|
|
|
}
|
|
|
|
|
return AnthropicAlias{ID: anthropicID, Name: name}, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reverseDynamicAlias(anthropicID string) (string, bool) {
|
|
|
|
|
m := reverseDynamicPattern.FindStringSubmatch(anthropicID)
|
|
|
|
|
if m == nil {
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
return m[1] + "-" + m[2] + "." + m[3] + m[4], true
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 14:09:15 +00:00
|
|
|
func ResolveToCursorModel(requested string) string {
|
|
|
|
|
if strings.TrimSpace(requested) == "" {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
key := strings.ToLower(strings.TrimSpace(requested))
|
|
|
|
|
if v, ok := anthropicToCursor[key]; ok {
|
|
|
|
|
return v
|
|
|
|
|
}
|
2026-04-01 00:53:34 +00:00
|
|
|
if v, ok := reverseDynamicAlias(key); ok {
|
|
|
|
|
return v
|
|
|
|
|
}
|
2026-03-30 14:09:15 +00:00
|
|
|
return strings.TrimSpace(requested)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AnthropicAlias struct {
|
|
|
|
|
ID string
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetAnthropicModelAliases(availableCursorIDs []string) []AnthropicAlias {
|
|
|
|
|
set := make(map[string]bool, len(availableCursorIDs))
|
|
|
|
|
for _, id := range availableCursorIDs {
|
|
|
|
|
set[id] = true
|
|
|
|
|
}
|
2026-04-01 00:53:34 +00:00
|
|
|
|
|
|
|
|
staticSet := make(map[string]bool, len(cursorToAnthropicAlias))
|
2026-03-30 14:09:15 +00:00
|
|
|
var result []AnthropicAlias
|
|
|
|
|
for _, a := range cursorToAnthropicAlias {
|
|
|
|
|
if set[a.CursorID] {
|
2026-04-01 00:53:34 +00:00
|
|
|
staticSet[a.CursorID] = true
|
2026-03-30 14:09:15 +00:00
|
|
|
result = append(result, AnthropicAlias{ID: a.AnthropicID, Name: a.Name})
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-01 00:53:34 +00:00
|
|
|
|
|
|
|
|
for _, id := range availableCursorIDs {
|
|
|
|
|
if staticSet[id] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if alias, ok := generateDynamicAlias(id); ok {
|
|
|
|
|
result = append(result, alias)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-30 14:09:15 +00:00
|
|
|
return result
|
|
|
|
|
}
|