{"repo": "ardhycrjr24/nexusrouter", "files": {"backend/internal/connectors/catalog.go": {"de8f67f1e7d0a0077151b777c5181716c0a4c04b": {"t": 1785746077.2544951, "n": 45162, "h": "33a9a3273440238f", "c": "package connectors\n\nimport \"github.com/ardhycrjr24/nexusrouter/backend/internal/core\"\n\n// RegionOption describes one selectable region for a provider that has\n// region-based endpoints (e.g. Xiaomi Token Plan SGP/CN/AMS).\ntype RegionOption struct {\n\tID string `json:\"id\"`\n\tLabel string `json:\"label\"`\n\tBaseURL string `json:\"base_url\"`\n}\n\n// ProviderSpec describes a built-in provider: its id, the wire dialect it\n// speaks, its default endpoint, the service kinds it serves, and the metadata\n// the dashboard renders (display name, alias, brand color, auth modes, etc.).\ntype ProviderSpec struct {\n\tID string\n\tDisplayName string\n\t// Alias is the short code accepted in model strings (e.g. \"ds\" -> deepseek).\n\tAlias string\n\tDialect core.Dialect\n\tBaseURL string\n\t// AuthKind is the default authentication mechanism (api_key, oauth, none).\n\tAuthKind string\n\t// AuthModes lists every supported auth mechanism (a provider may offer both\n\t// OAuth and API key). Defaults to [AuthKind] when empty.\n\tAuthModes []string\n\t// ServiceKinds enumerates the capabilities this provider serves. Empty means\n\t// LLM-only (the conservative default).\n\tServiceKinds []core.ServiceKind\n\t// Color is the brand color used by the dashboard (hex).\n\tColor string\n\t// Website is the provider's marketing/console URL.\n\tWebsite string\n\t// APIKeyURL is where a user mints an API key for this provider.\n\tAPIKeyURL string\n\t// Deprecated marks providers that carry usage risk (unofficial OAuth, etc.).\n\tDeprecated bool\n\t// Hidden hides the provider from the default dashboard listing.\n\tHidden bool\n\t// Pinned providers appear at the top of the dashboard listing.\n\tPinned bool\n\t// SkipValidation skips upstream credential probing during account creation.\n\t// Used for providers behind WAF/CDN that block server-side requests.\n\tSkipValidation bool\n\t// Notice is a short human-readable note shown in the dashboard.\n\tNotice string\n\t// Pricing (USD per million tokens) used for cost estimation. Zero means\n\t// free or unknown.\n\tInputPerM float64\n\tOutputPerM float64\n\t// Regions lists selectable endpoint regions for providers with region-based\n\t// URLs (e.g. Xiaomi Token Plan). When set, the dashboard shows a region\n\t// dropdown instead of a free-text base URL field.\n\tRegions []RegionOption `json:\"regions,omitempty\"`\n\t// DefaultRegion is the pre-selected region id when Regions is non-empty.\n\tDefaultRegion string `json:\"default_region,omitempty\"`\n}\n\n// llm is shorthand for the default LLM-only service kind slice.\nfunc llm(extra ...core.ServiceKind) []core.ServiceKind {\n\treturn append([]core.ServiceKind{core.ServiceLLM}, extra...)\n}\n\n// Catalog returns the built-in provider specs. It covers: free OAuth\n// providers, free-tier providers, OAuth providers, the\n// large API-key provider set, and dedicated media providers (image, STT, TTS,\n// web search, web fetch, embeddings).\n//\n// Dialect determines which connector backs a provider. Providers whose upstream\n// dialect NexusRouter does not yet drive natively remain in the catalog for\n// discovery and account management, but are not routable until a dedicated\n// connector lands.\nfunc Catalog() []ProviderSpec {\n\treturn append(append(append(append(append(\n\t\tpinnedProviders(),\n\t\tfreeProviders()...),\n\t\tfreeTierProviders()...),\n\t\toauthProviders()...),\n\t\tapiKeyProviders()...),\n\t\tmediaProviders()...)\n}\n\n// pinnedProviders are always-visible entries shown at the top of the listing.\nfunc pinnedProviders() []ProviderSpec {\n\treturn []ProviderSpec{\n\t\t{ID: \"custom-openai\", DisplayName: \"Custom (OpenAI-compatible)\", Alias: \"custom-openai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"\", AuthKind: \"api_key\", ServiceKinds: llm(), Pinned: true},\n\t\t{ID: \"custom-anthropic\", DisplayName: \"Custom (Anthropic-compatible)\", Alias: \"custom-anthropic\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"\", AuthKind: \"api_key\", ServiceKinds: llm(), Pinned: true},\n\t}\n}\n\n// freeProviders are subscription/OAuth-session providers (use at your own risk).\nfunc freeProviders() []ProviderSpec {\n\trisk := \"Uses a subscription/OAuth session not licensed for proxy use. Account may be restricted. Use at your own risk.\"\n\treturn []ProviderSpec{\n\t\t{ID: \"kiro\", DisplayName: \"Kiro AI\", Alias: \"kr\", Dialect: core.DialectKiro,\n\t\t\tBaseURL: \"https://codewhisperer.us-east-1.amazonaws.com/generateAssistantResponse\",\n\t\t\tAuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#FF6B35\", Website: \"https://kiro.dev\", Deprecated: true, Notice: risk,\n\t\t\tSkipValidation: true},\n\t\t{ID: \"gemini-cli\", DisplayName: \"Gemini CLI\", Alias: \"gc\", Dialect: core.DialectGeminiCLI,\n\t\t\tBaseURL: \"https://cloudcode-pa.googleapis.com/v1internal\",\n\t\t\tAuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#4285F4\", Website: \"https://github.com/google-gemini/gemini-cli\", Deprecated: true, Notice: risk},\n\t\t{ID: \"qoder\", DisplayName: \"Qoder\", Alias: \"qd\", Dialect: core.DialectQoder,\n\t\t\tBaseURL: \"https://api3.qoder.sh/algo/api/v2/service/pro/sse/agent_chat_generation\",\n\t\t\tAuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#EC4899\", Website: \"https://qoder.com\", Deprecated: true, Notice: risk},\n\t}\n}\n\n// freeTierProviders offer a free tier but may require an account or API key.\nfunc freeTierProviders() []ProviderSpec {\n\treturn []ProviderSpec{\n\t\t{ID: \"openrouter\", DisplayName: \"OpenRouter\", Alias: \"openrouter\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://openrouter.ai/api/v1\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceEmbedding), Color: \"#F97316\",\n\t\t\tWebsite: \"https://openrouter.ai\", APIKeyURL: \"https://openrouter.ai/settings/keys\",\n\t\t\tNotice: \"Free tier: 27+ free models, no credit card, 200 req/day.\"},\n\t\t{ID: \"nvidia\", DisplayName: \"NVIDIA NIM\", Alias: \"nvidia\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://integrate.api.nvidia.com/v1\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceTTS, core.ServiceEmbedding), Color: \"#76B900\",\n\t\t\tWebsite: \"https://developer.nvidia.com/nim\", APIKeyURL: \"https://build.nvidia.com/settings/api-keys\"},\n\t\t{ID: \"ollama\", DisplayName: \"Ollama Cloud\", Alias: \"ollama\", Dialect: core.DialectOllama,\n\t\t\tBaseURL: \"https://ollama.com\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#111111\", Website: \"https://ollama.com\", APIKeyURL: \"https://ollama.com/settings/keys\"},\n\t\t{ID: \"ollama-local\", DisplayName: \"Ollama Local\", Alias: \"ollama-local\", Dialect: core.DialectOllama,\n\t\t\tBaseURL: \"http://localhost:11434\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#111111\", Website: \"https://ollama.com\"},\n\t\t{ID: \"vllm\", DisplayName: \"vLLM\", Alias: \"vllm\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:8000/v1\", AuthKind: \"none\", AuthModes: []string{\"none\", \"api_key\"},\n\t\t\tServiceKinds: llm(core.ServiceEmbedding), Color: \"#FA5C1D\",\n\t\t\tWebsite: \"https://docs.vllm.ai\",\n\t\t\tNotice: \"Self-hosted vLLM OpenAI-compatible server. Set base URL to your vLLM endpoint; provide an API key only if you started vLLM with --api-key. Requires NEXUSROUTER_SECURITY__ALLOW_PRIVATE_BASE_URL=true when pointing at loopback or LAN hosts.\"},\n\t\t{ID: \"gemini\", DisplayName: \"Gemini\", Alias: \"gemini\", Dialect: core.DialectGemini,\n\t\t\tBaseURL: \"https://generativelanguage.googleapis.com/v1beta\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceEmbedding, core.ServiceImage, core.ServiceSearch, core.ServiceTTS, core.ServiceSTT),\n\t\t\tColor: \"#4285F4\", Website: \"https://ai.google.dev\", APIKeyURL: \"https://aistudio.google.com/app/apikey\"},\n\t\t{ID: \"vertex\", DisplayName: \"Vertex AI\", Alias: \"vx\", Dialect: core.DialectVertex,\n\t\t\tBaseURL: \"https://aiplatform.googleapis.com\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#4285F4\", Website: \"https://cloud.google.com/vertex-ai\", Hidden: true},\n\t\t{ID: \"vertex-partner\", DisplayName: \"Vertex Partner\", Alias: \"vxp\", Dialect: core.DialectVertex,\n\t\t\tBaseURL: \"https://aiplatform.googleapis.com\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#34A853\", Website: \"https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models\", Hidden: true},\n\t\t{ID: \"cloudflare-ai\", DisplayName: \"Cloudflare\", Alias: \"cf\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/v1\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceImage), Color: \"#F38020\",\n\t\t\tWebsite: \"https://developers.cloudflare.com/workers-ai/\", APIKeyURL: \"https://dash.cloudflare.com/profile/api-tokens\"},\n\t\t{ID: \"byteplus\", DisplayName: \"BytePlus ModelArk\", Alias: \"bpm\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ark.ap-southeast.bytepluses.com/api/coding/v3\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#2563EB\", Website: \"https://console.byteplus.com/ark\"},\n\t\t{ID: \"mimo-free\", DisplayName: \"MiMo Free\", Alias: \"mf\", Dialect: core.DialectMimoFree,\n\t\t\tBaseURL: mimoFreeDefaultBase, AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#FF6900\", Website: \"https://xiaomimimo.com\",\n\t\t\tNotice: \"Free MiMo models via anonymous bootstrap JWT. No API key required. Rate limits apply.\"},\n\t}\n}\n\n// oauthProviders authenticate via OAuth (some also accept an API key).\nfunc oauthProviders() []ProviderSpec {\n\trisk := \"Uses a subscription/OAuth session not licensed for proxy use. Account may be restricted. Use at your own risk.\"\n\treturn []ProviderSpec{\n\t\t{ID: \"claude\", DisplayName: \"Claude Code\", Alias: \"cc\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.anthropic.com/v1\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#D97757\", Website: \"https://claude.ai\", Deprecated: true, Notice: risk},\n\t\t{ID: \"antigravity\", DisplayName: \"Antigravity\", Alias: \"ag\", Dialect: core.DialectAntigravity,\n\t\t\tBaseURL: \"https://daily-cloudcode-pa.googleapis.com\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#F59E0B\", Website: \"https://antigravity.google\", Deprecated: true, Notice: risk},\n\t\t{ID: \"codex\", DisplayName: \"OpenAI Codex\", Alias: \"cx\", Dialect: core.DialectOpenAIResponses,\n\t\t\tBaseURL: \"https://chatgpt.com/backend-api/codex/responses\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"},\n\t\t\tServiceKinds: llm(core.ServiceImage), Color: \"#3B82F6\", Website: \"https://chatgpt.com/codex\", Deprecated: true, Notice: risk},\n\t\t{ID: \"github\", DisplayName: \"GitHub Copilot\", Alias: \"gh\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.githubcopilot.com\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"},\n\t\t\tServiceKinds: llm(core.ServiceEmbedding), Color: \"#333333\", Website: \"https://github.com/features/copilot\", Deprecated: true, Notice: risk},\n\t\t{ID: \"cursor\", DisplayName: \"Cursor IDE\", Alias: \"cu\", Dialect: core.DialectCursor,\n\t\t\tBaseURL: \"https://api2.cursor.sh\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#00D4AA\", Website: \"https://cursor.com\"},\n\t\t{ID: \"kilocode\", DisplayName: \"Kilo Code\", Alias: \"kc\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.kilo.ai/api/openrouter\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#FF6B35\", Website: \"https://kilocode.ai\"},\n\t\t{ID: \"cline\", DisplayName: \"Cline\", Alias: \"cl\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.cline.bot/api/v1\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#5B9BD5\", Website: \"https://cline.bot\"},\n\t\t{ID: \"qwen\", DisplayName: \"Qwen Code\", Alias: \"qwen\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://portal.qwen.ai/v1/chat/completions\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#615CED\", Website: \"https://chat.qwen.ai\", Deprecated: true, Notice: risk},\n\t\t{ID: \"iflow\", DisplayName: \"iFlow\", Alias: \"iflow\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://apis.iflow.cn/v1/chat/completions\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\", \"api_key\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#2563EB\", Website: \"https://iflow.cn\", Deprecated: true, Notice: risk},\n\t\t{ID: \"kimi-coding\", DisplayName: \"Kimi Coding\", Alias: \"kimi-coding\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.kimi.com/coding/v1\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\", \"api_key\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#1E3A8A\", Website: \"https://kimi.moonshot.cn\", Deprecated: true, Notice: risk},\n\t}\n}\n\n// apiKeyProviders is the large API-key-authenticated provider set.\nfunc apiKeyProviders() []ProviderSpec {\n\treturn []ProviderSpec{\n\t\t{ID: \"openai\", DisplayName: \"OpenAI\", Alias: \"openai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.openai.com/v1\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceEmbedding, core.ServiceTTS, core.ServiceSTT, core.ServiceImage, core.ServiceSearch),\n\t\t\tColor: \"#10A37F\", Website: \"https://platform.openai.com\", APIKeyURL: \"https://platform.openai.com/api-keys\",\n\t\t\tInputPerM: 2.5, OutputPerM: 10},\n\t\t{ID: \"anthropic\", DisplayName: \"Anthropic\", Alias: \"anthropic\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.anthropic.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#D97757\", Website: \"https://console.anthropic.com\", APIKeyURL: \"https://console.anthropic.com/settings/keys\",\n\t\t\tInputPerM: 3, OutputPerM: 15},\n\t\t{ID: \"deepseek\", DisplayName: \"DeepSeek\", Alias: \"ds\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.deepseek.com\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#4D6BFE\", Website: \"https://deepseek.com\", APIKeyURL: \"https://platform.deepseek.com/api_keys\",\n\t\t\tInputPerM: 0.27, OutputPerM: 1.1},\n\t\t{ID: \"glm\", DisplayName: \"GLM Coding\", Alias: \"glm\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.z.ai/api/anthropic/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#2563EB\", Website: \"https://open.bigmodel.cn\", APIKeyURL: \"https://open.bigmodel.cn/usercenter/apikeys\",\n\t\t\tInputPerM: 0.6, OutputPerM: 0.6},\n\t\t{ID: \"glm-cn\", DisplayName: \"GLM (China)\", Alias: \"glm-cn\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://open.bigmodel.cn/api/coding/paas/v4\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#DC2626\", Website: \"https://open.bigmodel.cn\", APIKeyURL: \"https://open.bigmodel.cn/usercenter/apikeys\",\n\t\t\tInputPerM: 0.6, OutputPerM: 0.6},\n\t\t{ID: \"kimi\", DisplayName: \"Kimi\", Alias: \"kimi\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.kimi.com/coding/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceSearch),\n\t\t\tColor: \"#1E3A8A\", Website: \"https://kimi.moonshot.cn\", APIKeyURL: \"https://platform.moonshot.ai/console/api-keys\"},\n\t\t{ID: \"minimax\", DisplayName: \"MiniMax Coding\", Alias: \"minimax\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.minimax.io/anthropic/v1\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: llm(core.ServiceImage, core.ServiceSearch, core.ServiceTTS), Color: \"#7C3AED\",\n\t\t\tWebsite: \"https://www.minimaxi.com\", APIKeyURL: \"https://platform.minimaxi.com/user-center/basic-information/interface-key\",\n\t\t\tInputPerM: 0.2, OutputPerM: 1.1},\n\t\t{ID: \"minimax-cn\", DisplayName: \"MiniMax (China)\", Alias: \"minimax-cn\", Dialect: core.DialectAnthropic,\n\t\t\tBaseURL: \"https://api.minimaxi.com/anthropic/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceTTS),\n\t\t\tColor: \"#DC2626\", Website: \"https://www.minimaxi.com\"},\n\t\t{ID: \"alicode\", DisplayName: \"Alibaba\", Alias: \"alicode\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://coding.dashscope.aliyuncs.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FF6A00\", Website: \"https://bailian.console.aliyun.com\"},\n\t\t{ID: \"alicode-intl\", DisplayName: \"Alibaba Intl\", Alias: \"alicode-intl\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://coding-intl.dashscope.aliyuncs.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FF6A00\", Website: \"https://modelstudio.console.alibabacloud.com\"},\n\t\t{ID: \"xiaomi-mimo\", DisplayName: \"Xiaomi MiMo\", Alias: \"mimo\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.xiaomimimo.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FF6900\", Website: \"https://xiaomimimo.com\",\n\t\t\tInputPerM: 0.2, OutputPerM: 0.6},\n\t\t{ID: \"xiaomi-tokenplan\", DisplayName: \"Xiaomi MiMo Token Plan\", Alias: \"mmtp\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://token-plan-sgp.xiaomimimo.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FF6900\", Website: \"https://xiaomimimo.com\",\n\t\t\tInputPerM: 0.2, OutputPerM: 0.6,\n\t\t\tNotice: \"Xiaomi MiMo Token Plan subscription (API key starts with tp-). Token Plan keys are cluster-specific — select the region matching your subscription.\",\n\t\t\tRegions: []RegionOption{\n\t\t\t\t{ID: \"sgp\", Label: \"Singapore\", BaseURL: \"https://token-plan-sgp.xiaomimimo.com/v1\"},\n\t\t\t\t{ID: \"cn\", Label: \"China\", BaseURL: \"https://token-plan-cn.xiaomimimo.com/v1\"},\n\t\t\t\t{ID: \"ams\", Label: \"Europe\", BaseURL: \"https://token-plan-ams.xiaomimimo.com/v1\"},\n\t\t\t}, DefaultRegion: \"sgp\"},\n\t\t{ID: \"volcengine-ark\", DisplayName: \"Volcengine Ark\", Alias: \"ark\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ark.cn-beijing.volces.com/api/coding/v3\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#1677FF\", Website: \"https://ark.cn-beijing.volces.com\"},\n\t\t{ID: \"vercel-ai-gateway\", DisplayName: \"Vercel AI Gateway\", Alias: \"vercel\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ai-gateway.vercel.sh/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#111827\", Website: \"https://vercel.com/ai-gateway\"},\n\t\t{ID: \"azure\", DisplayName: \"Azure OpenAI\", Alias: \"azure\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"\", AuthKind: \"api_key\", ServiceKinds: llm(), Color: \"#0078D4\",\n\t\t\tWebsite: \"https://azure.microsoft.com/en-us/products/ai-services/openai-service\"},\n\t\t{ID: \"commandcode\", DisplayName: \"Command Code\", Alias: \"cmc\", Dialect: core.DialectCommandCode,\n\t\t\tBaseURL: \"https://api.commandcode.ai/alpha/generate\",\n\t\t\tAuthKind: \"api_key\", AuthModes: []string{\"oauth\", \"api_key\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#000000\", Website: \"https://commandcode.ai\", APIKeyURL: \"https://commandcode.ai/studio\",\n\t\t\tNotice: \"Use your Command Code API key from commandcode.ai/studio or run `cmd login` to get a CLI token. CLI subscriptions (Go, Pro, Max, Ultra) are supported.\"},\n\t\t{ID: \"groq\", DisplayName: \"Groq\", Alias: \"groq\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.groq.com/openai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceSTT),\n\t\t\tColor: \"#F55036\", Website: \"https://groq.com\", APIKeyURL: \"https://console.groq.com/keys\",\n\t\t\tInputPerM: 0.59, OutputPerM: 0.79},\n\t\t{ID: \"xai\", DisplayName: \"xAI (Grok)\", Alias: \"xai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.x.ai/v1\", AuthKind: \"api_key\", AuthModes: []string{\"oauth\", \"api_key\"},\n\t\t\tServiceKinds: llm(core.ServiceSearch, core.ServiceImage), Color: \"#1DA1F2\", Website: \"https://x.ai\",\n\t\t\tAPIKeyURL: \"https://console.x.ai\"},\n\t\t{ID: \"mistral\", DisplayName: \"Mistral\", Alias: \"mistral\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.mistral.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceEmbedding),\n\t\t\tColor: \"#FF7000\", Website: \"https://mistral.ai\", APIKeyURL: \"https://console.mistral.ai/api-keys\"},\n\t\t{ID: \"perplexity\", DisplayName: \"Perplexity\", Alias: \"pplx\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.perplexity.ai\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceSearch),\n\t\t\tColor: \"#20808D\", Website: \"https://www.perplexity.ai\", APIKeyURL: \"https://www.perplexity.ai/settings/api\"},\n\t\t{ID: \"together\", DisplayName: \"Together AI\", Alias: \"together\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.together.xyz/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceEmbedding),\n\t\t\tColor: \"#0F6FFF\", Website: \"https://www.together.ai\", APIKeyURL: \"https://api.together.xyz/settings/api-keys\"},\n\t\t{ID: \"fireworks\", DisplayName: \"Fireworks AI\", Alias: \"fireworks\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.fireworks.ai/inference/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceEmbedding),\n\t\t\tColor: \"#7B2EF2\", Website: \"https://fireworks.ai\", APIKeyURL: \"https://fireworks.ai/account/api-keys\"},\n\t\t{ID: \"cerebras\", DisplayName: \"Cerebras\", Alias: \"cerebras\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.cerebras.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FF4F00\", Website: \"https://www.cerebras.ai\", APIKeyURL: \"https://cloud.cerebras.ai/platform\"},\n\t\t{ID: \"cohere\", DisplayName: \"Cohere\", Alias: \"cohere\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.cohere.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#39594D\", Website: \"https://cohere.com\", APIKeyURL: \"https://dashboard.cohere.com/api-keys\"},\n\t\t{ID: \"nebius\", DisplayName: \"Nebius AI\", Alias: \"nebius\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.studio.nebius.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceEmbedding),\n\t\t\tColor: \"#6C5CE7\", Website: \"https://nebius.com\", APIKeyURL: \"https://studio.nebius.com/settings/api-keys\"},\n\t\t{ID: \"siliconflow\", DisplayName: \"SiliconFlow\", Alias: \"siliconflow\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.siliconflow.cn/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#5B6EF5\", Website: \"https://cloud.siliconflow.com\", APIKeyURL: \"https://cloud.siliconflow.com/account/ak\"},\n\t\t{ID: \"hyperbolic\", DisplayName: \"Hyperbolic\", Alias: \"hyp\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.hyperbolic.xyz/v1\", AuthKind: \"api_key\", ServiceKinds: llm(core.ServiceTTS),\n\t\t\tColor: \"#00D4FF\", Website: \"https://hyperbolic.xyz\", APIKeyURL: \"https://app.hyperbolic.xyz/settings\"},\n\t\t{ID: \"blackbox\", DisplayName: \"Blackbox AI\", Alias: \"bb\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.blackbox.ai\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#5B5FEF\", Website: \"https://blackbox.ai\", APIKeyURL: \"https://www.blackbox.ai/api-management\"},\n\t\t{ID: \"chutes\", DisplayName: \"Chutes AI\", Alias: \"ch\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://llm.chutes.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#111111\", Website: \"https://chutes.ai\", APIKeyURL: \"https://chutes.ai/app/api\"},\n\t\t{ID: \"opencode\", DisplayName: \"OpenCode\", Alias: \"oc\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://opencode.ai/zen/go/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#E87040\", Website: \"https://opencode.ai\", APIKeyURL: \"https://opencode.ai/auth\"},\n\t\t{ID: \"opencode-go\", DisplayName: \"OpenCode Go\", Alias: \"ocg\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://opencode.ai/zen/go/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#E87040\", Website: \"https://opencode.ai/auth\", Hidden: true},\n\t\t// --- Additional providers ---\n\t\t{ID: \"gitlab\", DisplayName: \"GitLab Duo\", Alias: \"gitlab\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://gitlab.com/api/v4\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#FC6D26\", Website: \"https://gitlab.com\"},\n\t\t{ID: \"codebuddy\", DisplayName: \"CodeBuddy\", Alias: \"cb\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://copilot.tencent.com/v1\", AuthKind: \"oauth\", AuthModes: []string{\"oauth\", \"api_key\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#0052D9\", Website: \"https://copilot.tencent.com\"},\n\t\t// Browser-session cookie providers. Connected by pasting a session cookie\n\t\t// from the provider's web app. The web_cookie dialect has no connector\n\t\t// yet, so these are discovery/account-management only until one lands.\n\t\t{ID: \"grok-web\", DisplayName: \"Grok Web (Subscription)\", Alias: \"gw\", Dialect: core.DialectWebCookie,\n\t\t\tBaseURL: \"https://grok.com\", AuthKind: \"cookie\", AuthModes: []string{\"cookie\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#1DA1F2\", Website: \"https://grok.com\", SkipValidation: true,\n\t\t\tNotice: \"Paste your sso= cookie value from grok.com.\"},\n\t\t{ID: \"perplexity-web\", DisplayName: \"Perplexity Web (Pro/Max)\", Alias: \"pw\", Dialect: core.DialectWebCookie,\n\t\t\tBaseURL: \"https://www.perplexity.ai\", AuthKind: \"cookie\", AuthModes: []string{\"cookie\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#20808D\", Website: \"https://www.perplexity.ai\", SkipValidation: true,\n\t\t\tNotice: \"Paste your __Secure-next-auth.session-token cookie value from perplexity.ai.\"},\n\t\t{ID: \"agentrouter\", DisplayName: \"AgentRouter\", Alias: \"ar\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://agentrouter.org/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#10B981\", Website: \"https://agentrouter.org\", SkipValidation: true, Hidden: true},\n\t\t{ID: \"aimlapi\", DisplayName: \"AIML API\", Alias: \"aimlapi\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.aimlapi.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#6366F1\", Website: \"https://aimlapi.com\", APIKeyURL: \"https://aimlapi.com/api-keys\", Hidden: true},\n\t\t{ID: \"novita\", DisplayName: \"Novita AI\", Alias: \"novita\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.novita.ai/v3/openai\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#8B5CF6\", Website: \"https://novita.ai\", APIKeyURL: \"https://novita.ai/settings/keys\", Hidden: true},\n\t\t{ID: \"modal\", DisplayName: \"Modal\", Alias: \"modal\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.modal.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#4ADE80\", Website: \"https://modal.com\", Hidden: true},\n\t\t{ID: \"reka\", DisplayName: \"Reka AI\", Alias: \"reka\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.reka.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#EC4899\", Website: \"https://reka.ai\", Hidden: true},\n\t\t{ID: \"nlpcloud\", DisplayName: \"NLP Cloud\", Alias: \"nlpcloud\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.nlpcloud.io/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#3B82F6\", Website: \"https://nlpcloud.io\", Hidden: true},\n\t\t{ID: \"bazaarlink\", DisplayName: \"BazaarLink\", Alias: \"bazaarlink\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://bazaarlink.ai/api/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#F97316\", Website: \"https://bazaarlink.ai\", Hidden: true},\n\t\t{ID: \"completions\", DisplayName: \"Completions.me\", Alias: \"completions\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://completions.me/api/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#10B981\", Website: \"https://completions.me\", Hidden: true},\n\t\t{ID: \"enally\", DisplayName: \"Enally AI\", Alias: \"enally\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ai.enally.in/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#8B5CF6\", Website: \"https://enally.in\", Hidden: true},\n\t\t{ID: \"freetheai\", DisplayName: \"FreeTheAI\", Alias: \"freetheai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.freetheai.xyz/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#14B8A6\", Website: \"https://freetheai.xyz\", Hidden: true},\n\t\t{ID: \"llm7\", DisplayName: \"LLM7\", Alias: \"llm7\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.llm7.io/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#6366F1\", Website: \"https://llm7.io\", Hidden: true},\n\t\t{ID: \"lepton\", DisplayName: \"Lepton AI\", Alias: \"lepton\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.lepton.ai/api/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#3B82F6\", Website: \"https://lepton.ai\", Hidden: true},\n\t\t{ID: \"kluster\", DisplayName: \"Kluster AI\", Alias: \"kluster\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.kluster.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#EC4899\", Website: \"https://kluster.ai\", Hidden: true},\n\t\t{ID: \"ai21\", DisplayName: \"AI21 Labs\", Alias: \"ai21\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.ai21.com/studio/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#1E40AF\", Website: \"https://ai21.com\", APIKeyURL: \"https://studio.ai21.com/api-keys\"},\n\t\t{ID: \"inference-net\", DisplayName: \"Inference.net\", Alias: \"inference-net\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.inference.net/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#10B981\", Website: \"https://inference.net\", Hidden: true},\n\t\t{ID: \"predibase\", DisplayName: \"Predibase\", Alias: \"predibase\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://serving.app.predibase.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#7C3AED\", Website: \"https://predibase.com\", Hidden: true},\n\t\t{ID: \"bytez\", DisplayName: \"Bytez\", Alias: \"bytez\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.bytez.com/models/v2\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#3B82F6\", Website: \"https://bytez.com\", Hidden: true},\n\t\t{ID: \"morph\", DisplayName: \"MorphLLM\", Alias: \"morph\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.morphllm.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#8B5CF6\", Website: \"https://morphllm.com\", Hidden: true},\n\t\t{ID: \"longcat\", DisplayName: \"LongCat\", Alias: \"longcat\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.longcat.chat/openai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#F97316\", Website: \"https://longcat.chat\", Hidden: true},\n\t\t{ID: \"puter\", DisplayName: \"Puter AI\", Alias: \"puter\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.puter.com/puterai/openai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#6366F1\", Website: \"https://puter.com\", Hidden: true},\n\t\t{ID: \"uncloseai\", DisplayName: \"UncloseAI\", Alias: \"uncloseai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://hermes.ai.unturf.com/v1\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: llm(),\n\t\t\tColor: \"#10B981\", Website: \"https://unturf.com\", Notice: \"Free, no API key required.\", Hidden: true},\n\t\t{ID: \"scaleway\", DisplayName: \"Scaleway AI\", Alias: \"scaleway\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.scaleway.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#4F46E5\", Website: \"https://scaleway.com\"},\n\t\t{ID: \"deepinfra\", DisplayName: \"DeepInfra\", Alias: \"deepinfra\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.deepinfra.com/v1/openai\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#6366F1\", Website: \"https://deepinfra.com\", APIKeyURL: \"https://deepinfra.com/dash/api_keys\"},\n\t\t{ID: \"sambanova\", DisplayName: \"SambaNova\", Alias: \"sambanova\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.sambanova.ai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#F59E0B\", Website: \"https://sambanova.ai\", APIKeyURL: \"https://cloud.sambanova.ai/apis\"},\n\t\t{ID: \"nscale\", DisplayName: \"Nscale\", Alias: \"nscale\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://inference.api.nscale.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#3B82F6\", Website: \"https://nscale.com\"},\n\t\t{ID: \"baseten\", DisplayName: \"Baseten\", Alias: \"baseten\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://inference.baseten.co/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#8B5CF6\", Website: \"https://baseten.co\"},\n\t\t{ID: \"publicai\", DisplayName: \"PublicAI\", Alias: \"publicai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.publicai.co/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#10B981\", Website: \"https://publicai.co\", Hidden: true},\n\t\t{ID: \"nous-research\", DisplayName: \"Nous Research\", Alias: \"nous\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://inference-api.nousresearch.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#D97706\", Website: \"https://nousresearch.com\"},\n\t\t{ID: \"glhf\", DisplayName: \"GLHF\", Alias: \"glhf\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://glhf.chat/api/openai/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#EC4899\", Website: \"https://glhf.chat\", Hidden: true},\n\t\t{ID: \"sumopod\", DisplayName: \"SumoPod\", Alias: \"sumopod\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ai.sumopod.com/v1\", AuthKind: \"api_key\", ServiceKinds: llm(),\n\t\t\tColor: \"#3B82F6\", Website: \"https://sumopod.com\", APIKeyURL: \"https://sumopod.com\", Hidden: true},\n\t\t// Generic compatible endpoints are now in pinnedProviders().\n\t}\n}\n\n// mediaProviders serve non-LLM kinds: embeddings, image, STT, TTS, web search,\n// and web fetch. They are driven by the dedicated media connectors.\nfunc mediaProviders() []ProviderSpec {\n\treturn []ProviderSpec{\n\t\t// Speech-to-text.\n\t\t{ID: \"deepgram\", DisplayName: \"Deepgram\", Alias: \"dg\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.deepgram.com/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSTT, core.ServiceTTS},\n\t\t\tColor: \"#13EF93\", Website: \"https://deepgram.com\", APIKeyURL: \"https://console.deepgram.com/api-keys\"},\n\t\t{ID: \"assemblyai\", DisplayName: \"AssemblyAI\", Alias: \"aai\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.assemblyai.com/v2\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSTT},\n\t\t\tColor: \"#0062FF\", Website: \"https://assemblyai.com\", APIKeyURL: \"https://www.assemblyai.com/app/api-keys\"},\n\t\t// Text-to-speech.\n\t\t{ID: \"elevenlabs\", DisplayName: \"ElevenLabs\", Alias: \"el\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.elevenlabs.io/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#6C47FF\", Website: \"https://elevenlabs.io\", APIKeyURL: \"https://elevenlabs.io/app/settings/api-keys\"},\n\t\t{ID: \"inworld\", DisplayName: \"Inworld TTS\", Alias: \"inworld\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.inworld.ai/tts/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#FF6B6B\", Website: \"https://inworld.ai\", APIKeyURL: \"https://platform.inworld.ai/api-keys\"},\n\t\t{ID: \"cartesia\", DisplayName: \"Cartesia\", Alias: \"cartesia\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.cartesia.ai\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#FF4F8B\", Website: \"https://cartesia.ai\", APIKeyURL: \"https://play.cartesia.ai/keys\", Hidden: true},\n\t\t{ID: \"playht\", DisplayName: \"PlayHT\", Alias: \"playht\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.play.ht/api/v2\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#00B4D8\", Website: \"https://play.ht\", APIKeyURL: \"https://play.ht/studio/api-access\", Hidden: true},\n\t\t{ID: \"aws-polly\", DisplayName: \"AWS Polly\", Alias: \"polly\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://polly.us-east-1.amazonaws.com/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#FF9900\", Website: \"https://aws.amazon.com/polly/\", SkipValidation: true,\n\t\t\tNotice: \"Use AWS Secret Access Key as API key; set the access key id and region in metadata.\"},\n\t\t{ID: \"huggingface\", DisplayName: \"HuggingFace\", Alias: \"hf\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api-inference.huggingface.co/models\", AuthKind: \"api_key\",\n\t\t\tServiceKinds: []core.ServiceKind{core.ServiceImage, core.ServiceTTS, core.ServiceSTT},\n\t\t\tColor: \"#FFD21E\", Website: \"https://huggingface.co\", APIKeyURL: \"https://huggingface.co/settings/tokens\", SkipValidation: true},\n\t\t{ID: \"google-tts\", DisplayName: \"Google TTS\", Alias: \"google-tts\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#4285F4\", Website: \"https://cloud.google.com/text-to-speech\", SkipValidation: true},\n\t\t{ID: \"edge-tts\", DisplayName: \"Edge TTS\", Alias: \"edge-tts\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#0078D4\", Website: \"https://learn.microsoft.com/azure/ai-services/speech-service/\", SkipValidation: true},\n\t\t{ID: \"local-device\", DisplayName: \"Local Device\", Alias: \"local-device\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#64748B\", Website: \"\", SkipValidation: true},\n\t\t{ID: \"coqui\", DisplayName: \"Coqui TTS\", Alias: \"coqui\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:5002/api/tts\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#10B981\", Website: \"https://github.com/coqui-ai/TTS\", Hidden: true, SkipValidation: true},\n\t\t{ID: \"tortoise\", DisplayName: \"Tortoise TTS\", Alias: \"tortoise\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:5000/api/tts\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceTTS},\n\t\t\tColor: \"#7C3AED\", Website: \"https://github.com/neonbjb/tortoise-tts\", Hidden: true, SkipValidation: true},\n\t\t// Image generation.\n\t\t{ID: \"nanobanana\", DisplayName: \"NanoBanana API\", Alias: \"nb\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.nanobananaapi.ai/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#FFD700\", Website: \"https://nanobananaapi.ai\", APIKeyURL: \"https://nanobananaapi.ai/dashboard\"},\n\t\t{ID: \"fal-ai\", DisplayName: \"Fal.ai\", Alias: \"fal\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.fal.ai/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#2563EB\", Website: \"https://fal.ai\", APIKeyURL: \"https://fal.ai/dashboard/keys\"},\n\t\t{ID: \"stability-ai\", DisplayName: \"Stability AI\", Alias: \"stability\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.stability.ai\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#8B5CF6\", Website: \"https://stability.ai\", APIKeyURL: \"https://platform.stability.ai/account/keys\"},\n\t\t{ID: \"black-forest-labs\", DisplayName: \"Black Forest Labs\", Alias: \"bfl\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.bfl.ai/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#111827\", Website: \"https://blackforestlabs.ai\"},\n\t\t{ID: \"recraft\", DisplayName: \"Recraft\", Alias: \"recraft\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://external.api.recraft.ai/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#EC4899\", Website: \"https://recraft.ai\", APIKeyURL: \"https://www.recraft.ai/profile/api\"},\n\t\t{ID: \"topaz\", DisplayName: \"Topaz\", Alias: \"topaz\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.topazlabs.com/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#059669\", Website: \"https://topazlabs.com\", APIKeyURL: \"https://topazlabs.com/account\", SkipValidation: true},\n\t\t{ID: \"runwayml\", DisplayName: \"Runway ML\", Alias: \"runway\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.dev.runwayml.com/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#000000\", Website: \"https://runwayml.com\", APIKeyURL: \"https://dev.runwayml.com\", SkipValidation: true},\n\t\t{ID: \"sdwebui\", DisplayName: \"SD WebUI\", Alias: \"sdwebui\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:7860\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#FF7043\", Website: \"https://github.com/AUTOMATIC1111/stable-diffusion-webui\", SkipValidation: true},\n\t\t{ID: \"comfyui\", DisplayName: \"ComfyUI\", Alias: \"comfyui\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:8188\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceImage},\n\t\t\tColor: \"#4CAF50\", Website: \"https://github.com/comfyanonymous/ComfyUI\", SkipValidation: true},\n\t\t// Embeddings.\n\t\t{ID: \"voyage-ai\", DisplayName: \"Voyage AI\", Alias: \"voyage\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.voyageai.com/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceEmbedding},\n\t\t\tColor: \"#0EA5E9\", Website: \"https://www.voyageai.com\", APIKeyURL: \"https://dash.voyageai.com/api-keys\"},\n\t\t{ID: \"jina-ai\", DisplayName: \"Jina AI\", Alias: \"jina\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.jina.ai/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceEmbedding},\n\t\t\tColor: \"#2563EB\", Website: \"https://jina.ai\", APIKeyURL: \"https://jina.ai/?sui=apikey\"},\n\t\t// Web search.\n\t\t{ID: \"tavily\", DisplayName: \"Tavily\", Alias: \"tavily\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.tavily.com\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch, core.ServiceFetch},\n\t\t\tColor: \"#5B21B6\", Website: \"https://tavily.com\", APIKeyURL: \"https://app.tavily.com/home\"},\n\t\t{ID: \"brave-search\", DisplayName: \"Brave Search\", Alias: \"brave\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.search.brave.com/res/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#FB542B\", Website: \"https://brave.com/search/api\", APIKeyURL: \"https://api-dashboard.search.brave.com/app/keys\"},\n\t\t{ID: \"serper\", DisplayName: \"Serper\", Alias: \"serper\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://google.serper.dev\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#4F46E5\", Website: \"https://serper.dev\", APIKeyURL: \"https://serper.dev/api-key\"},\n\t\t{ID: \"exa\", DisplayName: \"Exa\", Alias: \"exa\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.exa.ai\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch, core.ServiceFetch},\n\t\t\tColor: \"#2563EB\", Website: \"https://exa.ai\", APIKeyURL: \"https://dashboard.exa.ai/api-keys\"},\n\t\t{ID: \"searxng\", DisplayName: \"SearXNG\", Alias: \"searxng\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"http://localhost:8888\", AuthKind: \"none\", AuthModes: []string{\"none\"}, ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#3B82F6\", Website: \"https://docs.searxng.org\", Hidden: true},\n\t\t{ID: \"google-pse\", DisplayName: \"Google PSE\", Alias: \"gpse\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://www.googleapis.com/customsearch/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#4285F4\", Website: \"https://programmablesearchengine.google.com\", SkipValidation: true},\n\t\t{ID: \"linkup\", DisplayName: \"Linkup\", Alias: \"linkup\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.linkup.so/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#0EA5E9\", Website: \"https://linkup.so\", APIKeyURL: \"https://app.linkup.so/api-keys\", SkipValidation: true, Hidden: true},\n\t\t{ID: \"searchapi\", DisplayName: \"SearchAPI\", Alias: \"searchapi\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://www.searchapi.io/api/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#0EA5A4\", Website: \"https://www.searchapi.io\", APIKeyURL: \"https://www.searchapi.io/dashboard\", SkipValidation: true, Hidden: true},\n\t\t{ID: \"youcom\", DisplayName: \"You.com Search\", Alias: \"youcom\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://ydc-index.io/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceSearch},\n\t\t\tColor: \"#7C3AED\", Website: \"https://you.com\", APIKeyURL: \"https://api.you.com\", SkipValidation: true, Hidden: true},\n\t\t// Web fetch.\n\t\t{ID: \"firecrawl\", DisplayName: \"Firecrawl\", Alias: \"firecrawl\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://api.firecrawl.dev/v1\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceFetch},\n\t\t\tColor: \"#F59E0B\", Website: \"https://firecrawl.dev\", APIKeyURL: \"https://www.firecrawl.dev/app/api-keys\"},\n\t\t{ID: \"jina-reader\", DisplayName: \"Jina Reader\", Alias: \"jina-reader\", Dialect: core.DialectOpenAI,\n\t\t\tBaseURL: \"https://r.jina.ai\", AuthKind: \"api_key\", ServiceKinds: []core.ServiceKind{core.ServiceFetch},\n\t\t\tColor: \"#111111\", Website: \"https://jina.ai/reader\", APIKeyURL: \"https://jina.ai/?sui=apikey\", Hidden: true},\n\t}\n}\n\n// SpecByID returns the catalog spec for a provider id, or false if unknown.\nfunc SpecByID(id string) (ProviderSpec, bool) {\n\tfor _, p := range Catalog() {\n\t\tif p.ID == id {\n\t\t\treturn p, true\n\t\t}\n\t}\n\treturn ProviderSpec{}, false\n}\n\n// SpecByAlias resolves a provider by its short alias or id.\nfunc SpecByAlias(aliasOrID string) (ProviderSpec, bool) {\n\tfor _, p := range Catalog() {\n\t\tif p.ID == aliasOrID || p.Alias == aliasOrID {\n\t\t\treturn p, true\n\t\t}\n\t}\n\treturn ProviderSpec{}, false\n}\n\n// SpecsByKind returns the catalog specs that serve a given service kind,\n// excluding hidden providers.\nfunc SpecsByKind(kind core.ServiceKind) []ProviderSpec {\n\tvar out []ProviderSpec\n\tfor _, p := range Catalog() {\n\t\tif p.Hidden {\n\t\t\tcontinue\n\t\t}\n\t\tif core.HasServiceKind(p.ServiceKinds, kind) {\n\t\t\tout = append(out, p)\n\t\t}\n\t}\n\treturn out\n}\n\n// ResolveRegionBaseURL returns the base URL for the given region of a provider.\n// If the region is empty or unknown, the default region's URL is returned.\n// Returns \"\" if the provider has no regions.\nfunc ResolveRegionBaseURL(providerID, regionID string) string {\n\tspec, ok := SpecByID(providerID)\n\tif !ok || len(spec.Regions) == 0 {\n\t\treturn \"\"\n\t}\n\tfor _, r := range spec.Regions {\n\t\tif r.ID == regionID {\n\t\t\treturn r.BaseURL\n\t\t}\n\t}\n\t// Fall back to default region.\n\tif spec.DefaultRegion != \"\" {\n\t\tfor _, r := range spec.Regions {\n\t\t\tif r.ID == spec.DefaultRegion {\n\t\t\t\treturn r.BaseURL\n\t\t\t}\n\t\t}\n\t}\n\t// Fall back to first region.\n\treturn spec.Regions[0].BaseURL\n}\n\n// AuthModesOf returns the auth modes for a spec, defaulting to [AuthKind].\nfunc (p ProviderSpec) AuthModesOf() []string {\n\tif len(p.AuthModes) > 0 {\n\t\treturn p.AuthModes\n\t}\n\tif p.AuthKind != \"\" {\n\t\treturn []string{p.AuthKind}\n\t}\n\treturn []string{\"api_key\"}\n}\n"}}}}