100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
// Package models builds model catalogs for Anthropic clients.
|
|
package models
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
const claudeDDModelPrefix = "claude-fable-5-dd-"
|
|
|
|
// BuildResponse builds an Anthropic model response from available models.
|
|
func BuildResponse(availableModels []map[string]any, disableCloaking bool) map[string]any {
|
|
models := make([]map[string]any, len(availableModels))
|
|
for i, model := range availableModels {
|
|
models[i] = cloneModel(model)
|
|
if id, ok := models[i]["id"].(string); ok && !disableCloaking {
|
|
models[i]["id"] = EnsureClaudeModelIDPrefix(id)
|
|
}
|
|
}
|
|
|
|
sort.SliceStable(models, func(i, j int) bool {
|
|
displayNameI, _ := models[i]["display_name"].(string)
|
|
displayNameJ, _ := models[j]["display_name"].(string)
|
|
if displayNameI != displayNameJ {
|
|
return displayNameI < displayNameJ
|
|
}
|
|
idI, _ := models[i]["id"].(string)
|
|
idJ, _ := models[j]["id"].(string)
|
|
return idI < idJ
|
|
})
|
|
|
|
firstID := ""
|
|
lastID := ""
|
|
if len(models) > 0 {
|
|
firstID, _ = models[0]["id"].(string)
|
|
lastID, _ = models[len(models)-1]["id"].(string)
|
|
}
|
|
|
|
return map[string]any{
|
|
"data": models,
|
|
"has_more": false,
|
|
"first_id": firstID,
|
|
"last_id": lastID,
|
|
}
|
|
}
|
|
|
|
// EnsureClaudeModelIDPrefix rewrites model IDs for Anthropic model listings.
|
|
// IDs that already start with "claude-" are returned unchanged; all other IDs
|
|
// become "claude-fable-5-dd-" plus the original ID with its characters reversed.
|
|
func EnsureClaudeModelIDPrefix(id string) string {
|
|
if id == "" || strings.HasPrefix(id, "claude-") {
|
|
return id
|
|
}
|
|
return claudeDDModelPrefix + reverseModelID(id)
|
|
}
|
|
|
|
// ResolveClaudeModelIDPrefix reverses EnsureClaudeModelIDPrefix for request routing.
|
|
// Optional thinking suffixes in model(value) form are preserved.
|
|
func ResolveClaudeModelIDPrefix(id string) string {
|
|
if id == "" {
|
|
return id
|
|
}
|
|
base, suffix, hasSuffix := splitModelThinkingSuffix(id)
|
|
if !strings.HasPrefix(base, claudeDDModelPrefix) {
|
|
return id
|
|
}
|
|
encoded := base[len(claudeDDModelPrefix):]
|
|
if encoded == "" {
|
|
return id
|
|
}
|
|
resolved := reverseModelID(encoded)
|
|
if hasSuffix {
|
|
return resolved + "(" + suffix + ")"
|
|
}
|
|
return resolved
|
|
}
|
|
|
|
func cloneModel(model map[string]any) map[string]any {
|
|
cloned := make(map[string]any, len(model))
|
|
for key, value := range model {
|
|
cloned[key] = value
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func splitModelThinkingSuffix(model string) (base, suffix string, hasSuffix bool) {
|
|
lastOpen := strings.LastIndex(model, "(")
|
|
if lastOpen == -1 || !strings.HasSuffix(model, ")") {
|
|
return model, "", false
|
|
}
|
|
return model[:lastOpen], model[lastOpen+1 : len(model)-1], true
|
|
}
|
|
|
|
func reverseModelID(id string) string {
|
|
runes := []rune(id)
|
|
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
|
runes[i], runes[j] = runes[j], runes[i]
|
|
}
|
|
return string(runes)
|
|
}
|