// This file defines the central provider registry (US-001): a single source of // truth for every built-in provider's metadata — its name, the environment // variables that carry its API key (in precedence order), its default base URL, // wire protocol, auth scheme, any extra headers, and provider-specific base-URL // override env vars. // // The registry is deliberately additive and self-contained: later nodes wire // auth resolution (auth.go), the --provider flag (main.go), base_url overrides, // and per-provider construction (providers.go) to READ from it. This node only // introduces the data + a lookup, so it does not change existing behavior. // // Data source: the PRD "Technical Considerations" table // (tasks/prd-provider-env-parity.md), derived from pi's env-api-keys.ts and the // per-provider *.models.ts files. // // Security: the registry holds only env var NAMES, never secret values. Keys are // resolved from the environment at request time (see auth.go) and never logged. package provider // ProviderSpec is the metadata describing one built-in provider. It is the // single source of truth consumed by auth resolution, the --provider flag, // base_url override handling, and per-provider wiring. type ProviderSpec struct { // Name is the canonical provider name (e.g. "deepseek", "zai-coding-cn"). Name string // EnvVars lists the environment variables checked (in precedence order) for // this provider's API key. The first non-empty value wins. EnvVars []string // DefaultBaseURL is the provider's default API endpoint. It may be a template // (containing placeholders like {region}) for providers whose endpoint is // composed from additional parameters (Bedrock, Vertex, Cloudflare, Azure). DefaultBaseURL string // Protocol is the wire protocol the provider speaks: "openai" (OpenAI Chat // Completions) or "anthropic" (Anthropic Messages). Protocol string // AuthScheme names how credentials are attached: "bearer", "x-api-key", // "aws", "azure", or "special". AuthScheme string // ExtraHeaders are provider-specific headers attached to every request (may // be nil). ExtraHeaders map[string]string // BaseURLEnvVars lists provider-specific base-URL override environment // variables (e.g. AZURE_OPENAI_BASE_URL), in precedence order. May be empty; // the generic _BASE_URL convention is handled by callers. BaseURLEnvVars []string } // Protocol values. const ( ProtocolOpenAI = "openai" ProtocolAnthropic = "anthropic" ) // AuthScheme values. const ( AuthBearer = "bearer" AuthXAPIKey = "x-api-key" AuthAWS = "aws" AuthAzure = "azure" AuthSpecial = "special" ) // providerRegistry is the ordered list of all built-in provider specs. Order is // stable so callers that enumerate providers (e.g. --help) get a deterministic // list. LookupProviderSpec indexes it by name. var providerRegistry = []ProviderSpec{ { Name: "anthropic", EnvVars: []string{"ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY", "CLAUDE_API_KEY"}, DefaultBaseURL: anthropicBaseURL, // https://api.anthropic.com/v1 Protocol: ProtocolAnthropic, AuthScheme: AuthXAPIKey, }, { Name: "openai", EnvVars: []string{"OPENAI_API_KEY"}, DefaultBaseURL: "https://api.openai.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "ant-ling", EnvVars: []string{"ANT_LING_API_KEY"}, DefaultBaseURL: "https://api.ant-ling.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "deepseek", EnvVars: []string{"DEEPSEEK_API_KEY"}, DefaultBaseURL: "https://api.deepseek.com", Protocol: ProtocolOpenAIResponses, AuthScheme: AuthBearer, }, { Name: "nvidia", EnvVars: []string{"NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY"}, DefaultBaseURL: nvidiaBaseURL, // https://integrate.api.nvidia.com/v1 Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "google", EnvVars: []string{"GEMINI_API_KEY", "GOOGLE_API_KEY"}, DefaultBaseURL: "https://generativelanguage.googleapis.com/v1beta", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "groq", EnvVars: []string{"GROQ_API_KEY"}, DefaultBaseURL: "https://api.groq.com/openai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "cerebras", EnvVars: []string{"CEREBRAS_API_KEY"}, DefaultBaseURL: "https://api.cerebras.ai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "xai", EnvVars: []string{"XAI_API_KEY"}, DefaultBaseURL: "https://api.x.ai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "openrouter", EnvVars: []string{"OPENROUTER_API_KEY"}, DefaultBaseURL: openRouterBaseURL, // https://openrouter.ai/api/v1 Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "vercel-ai-gateway", EnvVars: []string{"AI_GATEWAY_API_KEY"}, DefaultBaseURL: "https://ai-gateway.vercel.sh", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "zai", EnvVars: []string{"ZAI_API_KEY"}, DefaultBaseURL: "https://api.z.ai/api/coding/paas/v4", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "zai-coding-cn", EnvVars: []string{"ZAI_CODING_CN_API_KEY"}, DefaultBaseURL: "https://open.bigmodel.cn/api/coding/paas/v4", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "mistral", EnvVars: []string{"MISTRAL_API_KEY"}, DefaultBaseURL: "https://api.mistral.ai", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "minimax", EnvVars: []string{"MINIMAX_API_KEY"}, DefaultBaseURL: "https://api.minimax.io/anthropic", Protocol: ProtocolAnthropic, AuthScheme: AuthXAPIKey, }, { Name: "minimax-cn", EnvVars: []string{"MINIMAX_CN_API_KEY"}, DefaultBaseURL: "https://api.minimaxi.com/anthropic", Protocol: ProtocolAnthropic, AuthScheme: AuthXAPIKey, }, { Name: "moonshotai", EnvVars: []string{"MOONSHOT_API_KEY"}, DefaultBaseURL: "https://api.moonshot.ai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "moonshotai-cn", EnvVars: []string{"MOONSHOT_API_KEY"}, DefaultBaseURL: "https://api.moonshot.cn/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "huggingface", EnvVars: []string{"HF_TOKEN"}, DefaultBaseURL: "https://router.huggingface.co/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "fireworks", EnvVars: []string{"FIREWORKS_API_KEY"}, DefaultBaseURL: "https://api.fireworks.ai/inference", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "together", EnvVars: []string{"TOGETHER_API_KEY"}, DefaultBaseURL: "https://api.together.ai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "opencode", EnvVars: []string{"OPENCODE_API_KEY"}, DefaultBaseURL: "https://opencode.ai/zen", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "opencode-go", EnvVars: []string{"OPENCODE_API_KEY"}, DefaultBaseURL: "https://opencode.ai/zen/go", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "kimi-coding", EnvVars: []string{"KIMI_API_KEY"}, DefaultBaseURL: "https://api.kimi.com/coding", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "xiaomi", EnvVars: []string{"XIAOMI_API_KEY"}, DefaultBaseURL: "https://api.xiaomimimo.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "xiaomi-token-plan-cn", EnvVars: []string{"XIAOMI_TOKEN_PLAN_CN_API_KEY"}, DefaultBaseURL: "https://token-plan-cn.xiaomimimo.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "xiaomi-token-plan-ams", EnvVars: []string{"XIAOMI_TOKEN_PLAN_AMS_API_KEY"}, DefaultBaseURL: "https://token-plan-ams.xiaomimimo.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "xiaomi-token-plan-sgp", EnvVars: []string{"XIAOMI_TOKEN_PLAN_SGP_API_KEY"}, DefaultBaseURL: "https://token-plan-sgp.xiaomimimo.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, // Chinese cloud LLM platforms. All four expose OpenAI-compatible endpoints // authenticated with a plain Bearer API key, so they reuse the standard // OpenAI-compatible driver with no bespoke auth. Base URLs are the platforms' // OpenAI-compatible endpoints as documented at implementation time. { // Baidu AI Cloud Qianfan (Baidu Qianfan). Name: "qianfan", EnvVars: []string{"QIANFAN_API_KEY"}, DefaultBaseURL: "https://qianfan.baidubce.com/v2", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { // ByteDance Volcengine Ark (Volcengine Ark). ARK_API_KEY is the platform's // conventional variable; VOLCENGINE_API_KEY is accepted as a fallback. Name: "volcengine", EnvVars: []string{"ARK_API_KEY", "VOLCENGINE_API_KEY"}, DefaultBaseURL: "https://ark.cn-beijing.volces.com/api/v3", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { // Alibaba Cloud DashScope (DashScope), OpenAI-compatible mode. Name: "dashscope", EnvVars: []string{"DASHSCOPE_API_KEY"}, DefaultBaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { // Tencent Hunyuan (Hunyuan), OpenAI-compatible endpoint. Name: "hunyuan", EnvVars: []string{"HUNYUAN_API_KEY"}, DefaultBaseURL: "https://api.hunyuan.cloud.tencent.com/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "azure-openai-responses", EnvVars: []string{"AZURE_OPENAI_API_KEY"}, // Endpoint is composed from AZURE_OPENAI_BASE_URL / AZURE_OPENAI_RESOURCE_NAME // per the Azure OpenAI convention; there is no fixed public default. DefaultBaseURL: "", Protocol: ProtocolOpenAI, AuthScheme: AuthAzure, BaseURLEnvVars: []string{"AZURE_OPENAI_BASE_URL"}, }, { Name: "amazon-bedrock", EnvVars: []string{"AWS_BEARER_TOKEN_BEDROCK"}, // Region-specific runtime endpoint; {AWS_REGION} defaults to us-east-1. DefaultBaseURL: "https://bedrock-runtime.{AWS_REGION}.amazonaws.com", Protocol: ProtocolAnthropic, AuthScheme: AuthAWS, }, { Name: "google-vertex", EnvVars: []string{"GOOGLE_CLOUD_API_KEY"}, // Location-specific endpoint; protocol varies by model (Gemini vs Claude). DefaultBaseURL: "https://{location}-aiplatform.googleapis.com", Protocol: ProtocolOpenAI, AuthScheme: AuthSpecial, }, { Name: "cloudflare-workers-ai", EnvVars: []string{"CLOUDFLARE_API_KEY"}, // {id} is CLOUDFLARE_ACCOUNT_ID. DefaultBaseURL: "https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1", Protocol: ProtocolOpenAI, AuthScheme: AuthBearer, }, { Name: "cloudflare-ai-gateway", EnvVars: []string{"CLOUDFLARE_API_KEY"}, // {acct}/{gw} are CLOUDFLARE_ACCOUNT_ID / CLOUDFLARE_GATEWAY_ID. DefaultBaseURL: "https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/anthropic", Protocol: ProtocolAnthropic, AuthScheme: AuthXAPIKey, }, } // providerRegistryByName indexes providerRegistry by provider name for O(1) // lookup. Built once at package init. var providerRegistryByName = func() map[string]ProviderSpec { m := make(map[string]ProviderSpec, len(providerRegistry)) for _, spec := range providerRegistry { m[spec.Name] = spec } return m }() // LookupProviderSpec returns the ProviderSpec for a provider name and whether it // is a known built-in provider. The returned spec is a copy; mutating its slice // or map fields is discouraged as they are shared with the registry. func LookupProviderSpec(name string) (ProviderSpec, bool) { spec, ok := providerRegistryByName[name] return spec, ok } // ProviderSpecs returns all built-in provider specs in registry (display) order. // Callers must not mutate the returned specs' slice/map fields. func ProviderSpecs() []ProviderSpec { out := make([]ProviderSpec, len(providerRegistry)) copy(out, providerRegistry) return out } // ProviderNames returns all built-in provider names in registry order. func ProviderNames() []string { out := make([]string, len(providerRegistry)) for i, spec := range providerRegistry { out[i] = spec.Name } return out }