package config import "strings" type ModelInfo struct { ID string `json:"id"` Object string `json:"object"` Created int64 `json:"created"` OwnedBy string `json:"owned_by"` Permission []any `json:"permission,omitempty"` } type ModelAliasReader interface { ModelAliases() map[string]string } const noThinkingModelSuffix = "-nothinking" var deepSeekBaseModels = []ModelInfo{ {ID: "deepseek-v4-flash", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}}, {ID: "deepseek-v4-pro", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}}, {ID: "deepseek-v4-flash-search", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}}, {ID: "deepseek-v4-pro-search", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}}, {ID: "deepseek-v4-vision", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}}, } var DeepSeekModels = appendNoThinkingVariants(deepSeekBaseModels) var claudeBaseModels = []ModelInfo{ // Current aliases {ID: "claude-opus-4-6", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-sonnet-4-6", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-haiku-4-5", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, // Claude 4.x snapshots and prior aliases kept for compatibility {ID: "claude-sonnet-4-5", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-opus-4-1", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-opus-4-1-20250805", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-opus-4-0", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-opus-4-20250514", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-sonnet-4-5-20250929", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-sonnet-4-0", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-sonnet-4-20250514", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-haiku-4-5-20251001", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, // Claude 3.x (legacy/deprecated snapshots and aliases) {ID: "claude-3-7-sonnet-latest", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-7-sonnet-20250219", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-5-sonnet-latest", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-5-sonnet-20240620", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-5-sonnet-20241022", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-opus-20240229", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-sonnet-20240229", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-5-haiku-latest", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-5-haiku-20241022", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, {ID: "claude-3-haiku-20240307", Object: "model", Created: 1715635200, OwnedBy: "anthropic"}, } var ClaudeModels = appendNoThinkingVariants(claudeBaseModels) func GetModelConfig(model string) (thinking bool, search bool, ok bool) { baseModel, noThinking := splitNoThinkingModel(model) if baseModel == "" { return false, false, false } switch baseModel { case "deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-vision": return !noThinking, false, true case "deepseek-v4-flash-search", "deepseek-v4-pro-search": return !noThinking, true, true default: return false, false, false } } func GetModelType(model string) (modelType string, ok bool) { baseModel, _ := splitNoThinkingModel(model) switch baseModel { case "deepseek-v4-flash", "deepseek-v4-flash-search": return "default", true case "deepseek-v4-pro", "deepseek-v4-pro-search": return "expert", true case "deepseek-v4-vision": return "vision", true default: return "", false } } func IsSupportedDeepSeekModel(model string) bool { _, _, ok := GetModelConfig(model) return ok } func IsNoThinkingModel(model string) bool { _, noThinking := splitNoThinkingModel(model) return noThinking } func DefaultModelAliases() map[string]string { return map[string]string{ // OpenAI GPT / ChatGPT families "chatgpt-4o": "deepseek-v4-flash", "gpt-4": "deepseek-v4-flash", "gpt-4-turbo": "deepseek-v4-flash", "gpt-4-turbo-preview": "deepseek-v4-flash", "gpt-4.5-preview": "deepseek-v4-flash", "gpt-4o": "deepseek-v4-flash", "gpt-4o-mini": "deepseek-v4-flash", "gpt-4.1": "deepseek-v4-flash", "gpt-4.1-mini": "deepseek-v4-flash", "gpt-4.1-nano": "deepseek-v4-flash", "gpt-5": "deepseek-v4-flash", "gpt-5-chat": "deepseek-v4-flash", "gpt-5.1": "deepseek-v4-flash", "gpt-5.1-chat": "deepseek-v4-flash", "gpt-5.2": "deepseek-v4-flash", "gpt-5.2-chat": "deepseek-v4-flash", "gpt-5.3-chat": "deepseek-v4-flash", "gpt-5.4": "deepseek-v4-flash", "gpt-5.5": "deepseek-v4-flash", "gpt-5-mini": "deepseek-v4-flash", "gpt-5-nano": "deepseek-v4-flash", "gpt-5.4-mini": "deepseek-v4-flash", "gpt-5.4-nano": "deepseek-v4-flash", "gpt-5-pro": "deepseek-v4-pro", "gpt-5.2-pro": "deepseek-v4-pro", "gpt-5.4-pro": "deepseek-v4-pro", "gpt-5.5-pro": "deepseek-v4-pro", "gpt-5-codex": "deepseek-v4-pro", "gpt-5.1-codex": "deepseek-v4-pro", "gpt-5.1-codex-mini": "deepseek-v4-pro", "gpt-5.1-codex-max": "deepseek-v4-pro", "gpt-5.2-codex": "deepseek-v4-pro", "gpt-5.3-codex": "deepseek-v4-pro", "codex-mini-latest": "deepseek-v4-pro", // OpenAI reasoning / research families "o1": "deepseek-v4-pro", "o1-preview": "deepseek-v4-pro", "o1-mini": "deepseek-v4-pro", "o1-pro": "deepseek-v4-pro", "o3": "deepseek-v4-pro", "o3-mini": "deepseek-v4-pro", "o3-pro": "deepseek-v4-pro", "o3-deep-research": "deepseek-v4-pro-search", "o4-mini": "deepseek-v4-pro", "o4-mini-deep-research": "deepseek-v4-pro-search", // Claude current and historical aliases "claude-opus-4-6": "deepseek-v4-pro", "claude-opus-4-1": "deepseek-v4-pro", "claude-opus-4-1-20250805": "deepseek-v4-pro", "claude-opus-4-0": "deepseek-v4-pro", "claude-opus-4-20250514": "deepseek-v4-pro", "claude-sonnet-4-6": "deepseek-v4-flash", "claude-sonnet-4-5": "deepseek-v4-flash", "claude-sonnet-4-5-20250929": "deepseek-v4-flash", "claude-sonnet-4-0": "deepseek-v4-flash", "claude-sonnet-4-20250514": "deepseek-v4-flash", "claude-haiku-4-5": "deepseek-v4-flash", "claude-haiku-4-5-20251001": "deepseek-v4-flash", "claude-3-7-sonnet": "deepseek-v4-flash", "claude-3-7-sonnet-latest": "deepseek-v4-flash", "claude-3-7-sonnet-20250219": "deepseek-v4-flash", "claude-3-5-sonnet": "deepseek-v4-flash", "claude-3-5-sonnet-latest": "deepseek-v4-flash", "claude-3-5-sonnet-20240620": "deepseek-v4-flash", "claude-3-5-sonnet-20241022": "deepseek-v4-flash", "claude-3-5-haiku": "deepseek-v4-flash", "claude-3-5-haiku-latest": "deepseek-v4-flash", "claude-3-5-haiku-20241022": "deepseek-v4-flash", "claude-3-opus": "deepseek-v4-pro", "claude-3-opus-20240229": "deepseek-v4-pro", "claude-3-sonnet": "deepseek-v4-flash", "claude-3-sonnet-20240229": "deepseek-v4-flash", "claude-3-haiku": "deepseek-v4-flash", "claude-3-haiku-20240307": "deepseek-v4-flash", // Gemini current and historical text / multimodal models "gemini-pro": "deepseek-v4-pro", "gemini-pro-vision": "deepseek-v4-vision", "gemini-pro-latest": "deepseek-v4-pro", "gemini-flash-latest": "deepseek-v4-flash", "gemini-1.5-pro": "deepseek-v4-pro", "gemini-1.5-flash": "deepseek-v4-flash", "gemini-1.5-flash-8b": "deepseek-v4-flash", "gemini-2.0-flash": "deepseek-v4-flash", "gemini-2.0-flash-lite": "deepseek-v4-flash", "gemini-2.5-pro": "deepseek-v4-pro", "gemini-2.5-flash": "deepseek-v4-flash", "gemini-2.5-flash-lite": "deepseek-v4-flash", "gemini-3.1-pro": "deepseek-v4-pro", "gemini-3-pro": "deepseek-v4-pro", "gemini-3-flash": "deepseek-v4-flash", "gemini-3.1-flash": "deepseek-v4-flash", "gemini-3.1-flash-lite": "deepseek-v4-flash", "llama-3.1-70b-instruct": "deepseek-v4-flash", "qwen-max": "deepseek-v4-flash", } } func ResolveModel(store ModelAliasReader, requested string) (string, bool) { model := lower(strings.TrimSpace(requested)) if model == "" { return "", false } aliases := loadModelAliases(store) if IsSupportedDeepSeekModel(model) { return model, true } if mapped, ok := aliases[model]; ok && IsSupportedDeepSeekModel(mapped) { return mapped, true } baseModel, noThinking := splitNoThinkingModel(model) resolvedModel, ok := resolveCanonicalModel(aliases, baseModel) if !ok { return "", false } return withNoThinkingVariant(resolvedModel, noThinking), true } func isRetiredHistoricalModel(model string) bool { switch { case strings.HasPrefix(model, "claude-1."): return true case strings.HasPrefix(model, "claude-2."): return true case strings.HasPrefix(model, "claude-instant-"): return true case strings.HasPrefix(model, "gpt-3.5"): return true default: return false } } func lower(s string) string { b := []byte(s) for i, c := range b { if c >= 'A' && c <= 'Z' { b[i] = c + 32 } } return string(b) } func OpenAIModelsResponse() map[string]any { return map[string]any{"object": "list", "data": DeepSeekModels} } func OpenAIModelByID(store ModelAliasReader, id string) (ModelInfo, bool) { canonical, ok := ResolveModel(store, id) if !ok { return ModelInfo{}, false } for _, model := range DeepSeekModels { if model.ID == canonical { return model, true } } return ModelInfo{}, false } func ClaudeModelsResponse() map[string]any { resp := map[string]any{"object": "list", "data": ClaudeModels} if len(ClaudeModels) > 0 { resp["first_id"] = ClaudeModels[0].ID resp["last_id"] = ClaudeModels[len(ClaudeModels)-1].ID } else { resp["first_id"] = nil resp["last_id"] = nil } resp["has_more"] = false return resp } func appendNoThinkingVariants(models []ModelInfo) []ModelInfo { out := make([]ModelInfo, 0, len(models)*2) for _, model := range models { out = append(out, model) variant := model variant.ID = withNoThinkingVariant(model.ID, true) out = append(out, variant) } return out } func splitNoThinkingModel(model string) (string, bool) { model = lower(strings.TrimSpace(model)) if strings.HasSuffix(model, noThinkingModelSuffix) { return strings.TrimSuffix(model, noThinkingModelSuffix), true } return model, false } func withNoThinkingVariant(model string, enabled bool) string { baseModel, _ := splitNoThinkingModel(model) if !enabled { return baseModel } if baseModel == "" { return "" } return baseModel + noThinkingModelSuffix } func loadModelAliases(store ModelAliasReader) map[string]string { aliases := DefaultModelAliases() if store != nil { for k, v := range store.ModelAliases() { aliases[lower(strings.TrimSpace(k))] = lower(strings.TrimSpace(v)) } } return aliases } func resolveCanonicalModel(aliases map[string]string, model string) (string, bool) { model = lower(strings.TrimSpace(model)) if model == "" { return "", false } if isRetiredHistoricalModel(model) { return "", false } if IsSupportedDeepSeekModel(model) { return model, true } if mapped, ok := aliases[model]; ok && IsSupportedDeepSeekModel(mapped) { return mapped, true } if strings.HasPrefix(model, "deepseek-") { return "", false } knownFamily := false for _, prefix := range []string{ "gpt-", "o1", "o3", "claude-", "gemini-", "llama-", "qwen-", "mistral-", "command-", } { if strings.HasPrefix(model, prefix) { knownFamily = true break } } if !knownFamily { return "", false } useVision := strings.Contains(model, "vision") useReasoner := strings.Contains(model, "reason") || strings.Contains(model, "reasoner") || strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") || strings.Contains(model, "opus") || strings.Contains(model, "slow") || strings.Contains(model, "r1") useSearch := strings.Contains(model, "search") switch { case useVision: return "deepseek-v4-vision", true case useReasoner && useSearch: return "deepseek-v4-pro-search", true case useReasoner: return "deepseek-v4-pro", true case useSearch: return "deepseek-v4-flash-search", true default: return "deepseek-v4-flash", true } }