// Package registry provides model definitions and lookup helpers for various AI providers. // Static model metadata is loaded from the embedded models.json file and can be refreshed from network. package registry import ( "strings" ) const ( codexBuiltinImage15ModelID = "gpt-image-1.5" codexBuiltinImageModelID = "gpt-image-2" codexBuiltinImage25FlareModelID = "gpt-image-2.5-flare" codexBuiltinImage25SunburstModelID = "gpt-image-2.5-sunburst" codexBuiltinImage25ModelID = "gpt-image-2.5" codexBuiltinImage26FlareModelID = "gpt-image-2.6-flare" codexBuiltinImage26SunburstModelID = "gpt-image-2.6-sunburst" codexBuiltinImage26ModelID = "gpt-image-2.6" xaiBuiltinImageModelID = "grok-imagine-image" xaiBuiltinImageQualityModelID = "grok-imagine-image-quality" xaiBuiltinImage20ModelID = "grok-imagine-image-2.0" xaiBuiltinVideoModelID = "grok-imagine-video" xaiBuiltinVideo15ModelID = "grok-imagine-video-1.5" xaiBuiltinVideo15PreviewID = "grok-imagine-video-1.5-preview" xaiBuiltinVideo15LiteModelID = "grok-imagine-video-1.5-lite" xaiBuiltinSpeechModelID = "grok-tts" xaiBuiltinSpeechVoiceModelID = "grok-voice-tts-1.0" ) // staticModelsJSON mirrors the top-level structure of models.json. type staticModelsJSON struct { Claude []*ModelInfo `json:"claude"` Gemini []*ModelInfo `json:"gemini"` Vertex []*ModelInfo `json:"vertex"` AIStudio []*ModelInfo `json:"aistudio"` CodexFree []*ModelInfo `json:"codex-free"` CodexTeam []*ModelInfo `json:"codex-team"` CodexPlus []*ModelInfo `json:"codex-plus"` CodexPro []*ModelInfo `json:"codex-pro"` Kimi []*ModelInfo `json:"kimi"` Antigravity []*ModelInfo `json:"antigravity"` XAI []*ModelInfo `json:"xai"` Devin []*ModelInfo `json:"devin"` Meta []*ModelInfo `json:"meta"` } // GetClaudeModels returns the standard Claude model definitions. func GetClaudeModels() []*ModelInfo { return cloneModelInfos(getModels().Claude) } // GetGeminiModels returns the standard Gemini model definitions. func GetGeminiModels() []*ModelInfo { return cloneModelInfos(getModels().Gemini) } // GetGeminiVertexModels returns Gemini model definitions for Vertex AI. func GetGeminiVertexModels() []*ModelInfo { return cloneModelInfos(getModels().Vertex) } // GetAIStudioModels returns model definitions for AI Studio. func GetAIStudioModels() []*ModelInfo { return cloneModelInfos(getModels().AIStudio) } // GetCodexFreeModels returns model definitions for the Codex free plan tier. func GetCodexFreeModels() []*ModelInfo { return WithCodexBuiltins(cloneModelInfos(getModels().CodexFree)) } // GetCodexTeamModels returns model definitions for the Codex team plan tier. func GetCodexTeamModels() []*ModelInfo { return WithCodexBuiltins(cloneModelInfos(getModels().CodexTeam)) } // GetCodexPlusModels returns model definitions for the Codex plus plan tier. func GetCodexPlusModels() []*ModelInfo { return WithCodexBuiltins(cloneModelInfos(getModels().CodexPlus)) } // GetCodexProModels returns model definitions for the Codex pro plan tier. func GetCodexProModels() []*ModelInfo { return WithCodexBuiltins(cloneModelInfos(getModels().CodexPro)) } // GetKimiModels returns the standard Kimi (Moonshot AI) model definitions. func GetKimiModels() []*ModelInfo { return cloneModelInfos(getModels().Kimi) } // GetAntigravityModels returns the standard Antigravity model definitions. func GetAntigravityModels() []*ModelInfo { return cloneModelInfos(getModels().Antigravity) } var staticDevinModels = []*ModelInfo{ devinBuiltinSWE16SlowModelInfo(), { ID: "devin/swe-2", Type: "devin", OwnedBy: "cognition", DisplayName: "SWE-2", ContextLength: 262000, MaxCompletionTokens: 128000, Thinking: &ThinkingSupport{ Levels: []string{"medium", "high", "max"}, }, }, { ID: "devin/claude-fable-5-1", Type: "devin", OwnedBy: "anthropic", DisplayName: "Claude Fable 5.1", ContextLength: 1000000, MaxCompletionTokens: 64000, Thinking: &ThinkingSupport{ Levels: []string{"low", "medium", "high", "xhigh", "max"}, }, }, { ID: "devin/gpt-6-astra", Type: "devin", OwnedBy: "openai", DisplayName: "GPT-6 Astra", ContextLength: 1000000, MaxCompletionTokens: 64000, Thinking: &ThinkingSupport{ Levels: []string{"low", "medium", "high", "xhigh", "max"}, }, }, { ID: "devin/glm-5-2", Type: "devin", OwnedBy: "zhipu", DisplayName: "GLM-5.2", ContextLength: 200000, MaxCompletionTokens: 64000, Thinking: &ThinkingSupport{ Levels: []string{"none", "high"}, }, }, { ID: "devin/glm-5-3", Type: "devin", OwnedBy: "zhipu", DisplayName: "GLM-5.3", ContextLength: 1048576, MaxCompletionTokens: 128000, Thinking: &ThinkingSupport{ Levels: []string{"low", "high", "max"}, }, }, { ID: "devin/glm-5-3-flash", Type: "devin", OwnedBy: "zhipu", DisplayName: "GLM-5.3 Flash", ContextLength: 1000000, MaxCompletionTokens: 128000, Thinking: &ThinkingSupport{ Levels: []string{"low", "high", "max"}, }, }, { ID: "devin/gpt-5-6-sol", Type: "devin", OwnedBy: "openai", DisplayName: "GPT-5.6 Sol", ContextLength: 1000000, MaxCompletionTokens: 128000, Thinking: &ThinkingSupport{ Levels: []string{"none", "low", "medium", "high", "xhigh", "max"}, }, }, { ID: "devin/gemini-3-8-flash", Type: "devin", OwnedBy: "google", DisplayName: "Gemini 3.8 Flash", ContextLength: 1048576, MaxCompletionTokens: 65536, Thinking: &ThinkingSupport{ Levels: []string{"low", "medium", "high"}, }, }, { ID: "devin/grok-4-6", Type: "devin", OwnedBy: "xai", DisplayName: "Grok 4.6", ContextLength: 500000, MaxCompletionTokens: 131072, Thinking: &ThinkingSupport{ Levels: []string{"low", "medium", "high", "xhigh"}, }, }, { ID: "devin/deepseek-v4-flash", Type: "devin", OwnedBy: "deepseek", DisplayName: "DeepSeek V4 Flash", ContextLength: 1048576, MaxCompletionTokens: 64000, Thinking: &ThinkingSupport{ Levels: []string{"high", "max"}, }, }, { ID: "devin/deepseek-v4-1-flash", Type: "devin", OwnedBy: "deepseek", DisplayName: "DeepSeek V4.1 Flash", ContextLength: 1048576, MaxCompletionTokens: 64000, Thinking: &ThinkingSupport{ Levels: []string{"high", "max"}, }, }, } // AntigravityWebSearchModelFor returns the Antigravity model that should run a // native web search request for modelID. func AntigravityWebSearchModelFor(modelID string) string { modelID = normalizeAntigravityCapabilityModelID(modelID) if modelID == "" { return "" } for _, model := range GetGlobalRegistry().GetAvailableModelsByProvider("antigravity") { if model == nil { continue } currentModelID := normalizeAntigravityCapabilityModelID(model.ID) if currentModelID == "" { continue } if currentModelID == modelID { if model.SupportsWebSearch { return currentModelID } return "" } } return "" } // GetXAIModels returns the standard xAI Grok model definitions. func GetXAIModels() []*ModelInfo { return WithXAIBuiltins(cloneModelInfos(getModels().XAI)) } // WithCodexBuiltins injects hard-coded Codex-only model definitions that should // not depend on remote models.json updates. Built-ins replace any matching IDs // already present in the provided slice. func WithCodexBuiltins(models []*ModelInfo) []*ModelInfo { return upsertModelInfos(models, codexBuiltinImage15ModelInfo(), codexBuiltinImageModelInfo(), codexBuiltinImage25FlareModelInfo(), codexBuiltinImage25SunburstModelInfo(), codexBuiltinImage25ModelInfo(), codexBuiltinImage26FlareModelInfo(), codexBuiltinImage26SunburstModelInfo(), codexBuiltinImage26ModelInfo(), ) } // WithXAIBuiltins injects hard-coded xAI image, video, and speech model definitions // that should not depend on remote models.json updates. func WithXAIBuiltins(models []*ModelInfo) []*ModelInfo { return upsertModelInfos(models, xaiBuiltinImageModelInfo(), xaiBuiltinImageQualityModelInfo(), xaiBuiltinImage20ModelInfo(), xaiBuiltinVideoModelInfo(), xaiBuiltinVideo15ModelInfo(), xaiBuiltinVideo15PreviewModelInfo(), xaiBuiltinVideo15LiteModelInfo(), xaiBuiltinSpeechModelInfo(), xaiBuiltinSpeechVoiceModelInfo()) } func normalizeAntigravityCapabilityModelID(modelID string) string { modelID = strings.ToLower(strings.TrimSpace(modelID)) if open := strings.LastIndex(modelID, "("); open >= 0 && strings.HasSuffix(modelID, ")") { modelID = strings.TrimSpace(modelID[:open]) } return modelID } func codexBuiltinImage15ModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage15ModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 1.5", Version: codexBuiltinImage15ModelID, } } func codexBuiltinImageModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImageModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2", Version: codexBuiltinImageModelID, } } func codexBuiltinImage25FlareModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage25FlareModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.5 Flare", Version: codexBuiltinImage25FlareModelID, } } func codexBuiltinImage25SunburstModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage25SunburstModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.5 Sunburst", Version: codexBuiltinImage25SunburstModelID, } } func codexBuiltinImage25ModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage25ModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.5", Version: codexBuiltinImage25ModelID, } } func codexBuiltinImage26FlareModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage26FlareModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.6 Flare", Version: codexBuiltinImage26FlareModelID, } } func codexBuiltinImage26SunburstModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage26SunburstModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.6 Sunburst", Version: codexBuiltinImage26SunburstModelID, } } func codexBuiltinImage26ModelInfo() *ModelInfo { return &ModelInfo{ ID: codexBuiltinImage26ModelID, Object: "model", Created: 1704067200, // 2024-01-01 OwnedBy: "openai", Type: "openai", DisplayName: "GPT Image 2.6", Version: codexBuiltinImage26ModelID, } } func xaiBuiltinImageModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinImageModelID, Object: "model", Created: 1735689600, // 2025-01-01 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Image", Name: xaiBuiltinImageModelID, Description: "xAI Grok image generation model.", } } func xaiBuiltinImageQualityModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinImageQualityModelID, Object: "model", Created: 1735689600, // 2025-01-01 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Image Quality", Name: xaiBuiltinImageQualityModelID, Description: "xAI Grok higher-fidelity image generation model.", } } func xaiBuiltinImage20ModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinImage20ModelID, Object: "model", Created: 1786060800, // 2026-08-07 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Image 2.0", Name: xaiBuiltinImage20ModelID, Description: "xAI Grok image generation model.", } } func xaiBuiltinVideoModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinVideoModelID, Object: "model", Created: 1735689600, // 2025-01-01 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Video", Name: xaiBuiltinVideoModelID, Description: "xAI Grok video generation model.", } } func xaiBuiltinVideo15ModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinVideo15ModelID, Object: "model", Created: 1735689600, // 2025-01-01 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Video 1.5", Name: xaiBuiltinVideo15ModelID, Description: "xAI Grok video generation model.", } } func xaiBuiltinVideo15PreviewModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinVideo15PreviewID, Object: "model", Created: 1735689600, // 2025-01-01 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Video 1.5 Preview", Name: xaiBuiltinVideo15PreviewID, Description: "Compatibility alias for the xAI Grok video generation model.", } } func xaiBuiltinVideo15LiteModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinVideo15LiteModelID, Object: "model", Created: 1787702400, // 2026-08-26 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Imagine Video 1.5 Lite", Name: xaiBuiltinVideo15LiteModelID, Description: "xAI Grok Imagine Video 1.5 Lite supports text-to-video and image-to-video generation. Output pricing per second: $0.02 at 480p, $0.03 at 720p, and $0.14 at 1080p.", SupportedInputModalities: []string{"text", "image"}, SupportedOutputModalities: []string{"video"}, } } func xaiBuiltinSpeechModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinSpeechModelID, Object: "model", Created: 1773619200, // 2026-03-16 OwnedBy: "xai", Type: "xai", DisplayName: "Grok TTS", Name: xaiBuiltinSpeechModelID, Description: "xAI Grok unary text-to-speech model.", } } func xaiBuiltinSpeechVoiceModelInfo() *ModelInfo { return &ModelInfo{ ID: xaiBuiltinSpeechVoiceModelID, Object: "model", Created: 1773619200, // 2026-03-16 OwnedBy: "xai", Type: "xai", DisplayName: "Grok Voice TTS 1.0", Name: xaiBuiltinSpeechVoiceModelID, Description: "xAI Grok unary text-to-speech model.", } } func upsertModelInfos(models []*ModelInfo, extras ...*ModelInfo) []*ModelInfo { if len(extras) == 0 { return models } extraIDs := make(map[string]struct{}, len(extras)) extraList := make([]*ModelInfo, 0, len(extras)) for _, extra := range extras { if extra == nil { continue } id := strings.TrimSpace(extra.ID) if id == "" { continue } key := strings.ToLower(id) if _, exists := extraIDs[key]; exists { continue } extraIDs[key] = struct{}{} extraList = append(extraList, cloneModelInfo(extra)) } if len(extraList) == 0 { return models } filtered := make([]*ModelInfo, 0, len(models)+len(extraList)) for _, model := range models { if model == nil { continue } id := strings.TrimSpace(model.ID) if id == "" { continue } if _, exists := extraIDs[strings.ToLower(id)]; exists { continue } filtered = append(filtered, model) } filtered = append(filtered, extraList...) return filtered } // cloneModelInfos returns a shallow copy of the slice with each element deep-cloned. func cloneModelInfos(models []*ModelInfo) []*ModelInfo { if len(models) == 0 { return nil } out := make([]*ModelInfo, len(models)) for i, m := range models { out[i] = cloneModelInfo(m) } return out } // GetStaticModelDefinitionsByChannel returns static model definitions for a given channel/provider. // It returns nil when the channel is unknown. // // Supported channels: // - claude // - gemini // - gemini-interactions // - vertex // - aistudio // - codex // - kimi // - antigravity // - xai // - devin // - meta func GetStaticModelDefinitionsByChannel(channel string) []*ModelInfo { key := strings.ToLower(strings.TrimSpace(channel)) switch key { case "claude": return GetClaudeModels() case "gemini": return GetGeminiModels() case "gemini-interactions": return GetGeminiModels() case "vertex": return GetGeminiVertexModels() case "aistudio": return GetAIStudioModels() case "codex": return GetCodexProModels() case "kimi", "kimi-ai", "kimi.ai", "kimi.com": return GetKimiModels() case "antigravity": return GetAntigravityModels() case "xai", "x-ai", "grok": return GetXAIModels() case "devin": return GetDevinModels() case "meta", "muse": return GetMetaModels() default: return nil } } // LookupStaticModelInfoByChannel searches one provider-specific static section. // It does not fall back across providers, so callers can preserve provenance. func LookupStaticModelInfoByChannel(modelID, channel string) *ModelInfo { modelID = strings.TrimSpace(modelID) if modelID == "" { return nil } models := GetStaticModelDefinitionsByChannel(channel) for _, model := range models { if model != nil && model.ID == modelID { return cloneModelInfo(model) } } if strings.EqualFold(strings.TrimSpace(channel), "claude") { for _, familyID := range claudeFamilyModelIDs(modelID) { for _, model := range models { if model != nil && model.ID == familyID { return cloneModelInfo(model) } } } } return nil } // GetMetaModels returns the standard Meta Muse model definitions. func GetMetaModels() []*ModelInfo { return cloneModelInfos(getModels().Meta) } // LookupStaticModelInfo searches all static model definitions for a model by ID. // Returns nil if no matching model is found. func LookupStaticModelInfo(modelID string) *ModelInfo { if modelID == "" { return nil } data := getModels() allModels := [][]*ModelInfo{ data.Claude, data.Gemini, data.Vertex, data.AIStudio, data.CodexPro, data.Kimi, data.Antigravity, data.XAI, GetDevinModels(), GetMetaModels(), } for _, models := range allModels { for _, m := range models { if m != nil && m.ID == modelID { return cloneModelInfo(m) } } } // Dated and bracketed Claude variants (claude-sonnet-5-5-20261001, // claude-opus-5-5[1m]) share the capabilities of their catalog entry. for _, familyID := range claudeFamilyModelIDs(modelID) { for _, m := range data.Claude { if m != nil && m.ID == familyID { return cloneModelInfo(m) } } } return nil } // claudeFamilyModelIDs returns the catalog IDs a Claude model variant may belong // to: without a "[...]" context suffix, then also without a "-YYYYMMDD" date. func claudeFamilyModelIDs(modelID string) []string { if !strings.HasPrefix(modelID, "claude-") { return nil } var ids []string base := modelID if i := strings.IndexByte(base, '['); i > 0 && strings.HasSuffix(base, "]") { base = base[:i] ids = append(ids, base) } if n := len(base); n > 9 && base[n-9] == '-' && isEightDigits(base[n-8:]) { ids = append(ids, base[:n-9]) } return ids } func isEightDigits(s string) bool { if len(s) != 8 { return false } for _, r := range s { if r < '0' || r > '9' { return false } } return true }