Skip to main content

vtcode_commons/
provider.rs

1//! Provider enum and core definitions shared across VT Code crates.
2//!
3//! This module provides the [`Provider`] enum and its pure methods. Methods
4//! that depend on model catalogs or vtcode-config internals
5//! (`supports_reasoning_effort`, `supports_service_tier`) remain in
6//! `vtcode-config` as extension methods.
7
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::str::FromStr;
11
12/// Error returned when parsing a provider string fails.
13#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
14pub enum ProviderParseError {
15    #[error("Invalid provider: '{}'. Supported providers: {}", .0, crate::provider::Provider::all_providers().iter().map(|p| p.to_string()).collect::<Vec<_>>().join(", "))]
16    InvalidProvider(String),
17}
18
19/// Supported AI model providers
20#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
22pub enum Provider {
23    /// Google Gemini models
24    Gemini,
25    /// OpenAI GPT models
26    #[default]
27    OpenAI,
28    /// Anthropic Claude models
29    Anthropic,
30    /// GitHub Copilot preview integration
31    Copilot,
32    /// DeepSeek native models
33    DeepSeek,
34    /// Official Meta AI Muse models
35    Meta,
36    /// OpenRouter marketplace models
37    OpenRouter,
38    /// Local Ollama models
39    Ollama,
40    /// Ollama Cloud hosted models (ollama.com)
41    OllamaCloud,
42    /// LM Studio local models
43    LmStudio,
44    /// llama.cpp local models
45    LlamaCpp,
46    /// Moonshot.ai models
47    Moonshot,
48    /// Z.AI GLM models
49    ZAI,
50    /// MiniMax models
51    Minimax,
52    /// Xiaomi MiMo models
53    MiMo,
54    /// Mistral AI models
55    Mistral,
56    /// Hugging Face Inference Providers
57    HuggingFace,
58    /// OpenCode Zen gateway (pay-as-you-go)
59    OpenCodeZen,
60    /// OpenCode Go subscription
61    OpenCodeGo,
62    /// Alibaba Cloud Qwen models
63    Qwen,
64    /// StepFun models
65    StepFun,
66    /// Evolink multi-model gateway
67    Evolink,
68    /// Poolside AI models
69    Poolside,
70    /// xAI Grok models
71    XAI,
72    /// NVIDIA NIM OpenAI-compatible models
73    NVIDIA,
74    /// Merge Gateway multi-provider routing gateway
75    MergeGateway,
76    /// Vercel AI Gateway multi-provider routing gateway
77    Vercel,
78}
79
80impl Provider {
81    /// Get the default API key environment variable for this provider
82    pub fn default_api_key_env(&self) -> &'static str {
83        match self {
84            Provider::Gemini => "GEMINI_API_KEY",
85            Provider::OpenAI => "OPENAI_API_KEY",
86            Provider::Anthropic => "ANTHROPIC_API_KEY",
87            Provider::Copilot => "",
88            Provider::DeepSeek => "DEEPSEEK_API_KEY",
89            Provider::Meta => "META_API_KEY",
90            Provider::OpenRouter => "OPENROUTER_API_KEY",
91            Provider::Ollama => "OLLAMA_API_KEY",
92            Provider::OllamaCloud => "OLLAMA_API_KEY",
93            Provider::LmStudio => "LMSTUDIO_API_KEY",
94            Provider::LlamaCpp => "LLAMACPP_API_KEY",
95            Provider::Moonshot => "MOONSHOT_API_KEY",
96            Provider::ZAI => "ZAI_API_KEY",
97            Provider::Minimax => "MINIMAX_API_KEY",
98            Provider::MiMo => "MIMO_API_KEY",
99            Provider::Mistral => "MISTRAL_API_KEY",
100            Provider::HuggingFace => "HF_TOKEN",
101            Provider::OpenCodeZen => "OPENCODE_ZEN_API_KEY",
102            Provider::OpenCodeGo => "OPENCODE_GO_API_KEY",
103            Provider::Qwen => "QWEN_API_KEY",
104            Provider::StepFun => "STEPFUN_API_KEY",
105            Provider::Evolink => "EVOLINK_API_KEY",
106            Provider::Poolside => "POOLSIDE_API_KEY",
107            Provider::XAI => "XAI_API_KEY",
108            Provider::NVIDIA => "NVIDIA_API_KEY",
109            Provider::MergeGateway => "MERGE_GATEWAY_API_KEY",
110            Provider::Vercel => "AI_GATEWAY_API_KEY",
111        }
112    }
113
114    /// Get all supported providers
115    pub fn all_providers() -> Vec<Provider> {
116        vec![
117            Provider::OpenAI,
118            Provider::Anthropic,
119            Provider::Copilot,
120            Provider::Minimax,
121            Provider::MiMo,
122            Provider::Mistral,
123            Provider::Gemini,
124            Provider::DeepSeek,
125            Provider::Meta,
126            Provider::HuggingFace,
127            Provider::OpenRouter,
128            Provider::Ollama,
129            Provider::OllamaCloud,
130            Provider::LmStudio,
131            Provider::LlamaCpp,
132            Provider::Moonshot,
133            Provider::ZAI,
134            Provider::OpenCodeZen,
135            Provider::OpenCodeGo,
136            Provider::Qwen,
137            Provider::StepFun,
138            Provider::Evolink,
139            Provider::Poolside,
140            Provider::XAI,
141            Provider::NVIDIA,
142            Provider::MergeGateway,
143            Provider::Vercel,
144        ]
145    }
146
147    /// Human-friendly label for display purposes
148    pub fn label(&self) -> &'static str {
149        match self {
150            Provider::Gemini => "Gemini",
151            Provider::OpenAI => "OpenAI",
152            Provider::Anthropic => "Anthropic",
153            Provider::Copilot => "GitHub Copilot",
154            Provider::DeepSeek => "DeepSeek",
155            Provider::Meta => "Meta AI",
156            Provider::OpenRouter => "OpenRouter",
157            Provider::Ollama => "Ollama",
158            Provider::OllamaCloud => "Ollama Cloud",
159            Provider::LmStudio => "LM Studio",
160            Provider::LlamaCpp => "llama.cpp",
161            Provider::Moonshot => "Moonshot",
162            Provider::ZAI => "Z.AI",
163            Provider::Minimax => "MiniMax",
164            Provider::MiMo => "Xiaomi MiMo",
165            Provider::Mistral => "Mistral",
166            Provider::HuggingFace => "Hugging Face",
167            Provider::OpenCodeZen => "OpenCode Zen",
168            Provider::OpenCodeGo => "OpenCode Go",
169            Provider::Qwen => "Qwen",
170            Provider::StepFun => "StepFun",
171            Provider::Evolink => "Evolink",
172            Provider::Poolside => "Poolside",
173            Provider::XAI => "xAI",
174            Provider::NVIDIA => "NVIDIA",
175            Provider::MergeGateway => "Merge Gateway",
176            Provider::Vercel => "Vercel AI Gateway",
177        }
178    }
179
180    pub fn is_dynamic(&self) -> bool {
181        matches!(self, Provider::Copilot) || self.is_local()
182    }
183
184    pub fn is_local(&self) -> bool {
185        matches!(self, Provider::Ollama | Provider::LmStudio | Provider::LlamaCpp)
186    }
187
188    pub fn local_install_instructions(&self) -> Option<&'static str> {
189        match self {
190            Provider::Ollama => Some(
191                "Ollama server is not running. To start:\n  1. Install Ollama from https://ollama.com\n  2. Run 'ollama serve' in a terminal\n  3. Pull models using 'ollama pull <model-name>' (e.g., 'ollama pull gpt-oss:20b')",
192            ),
193            Provider::LmStudio => Some(
194                "LM Studio server is not running. To start:\n  1. Install LM Studio from https://lmstudio.ai\n  2. Open LM Studio and start the Local Server on port 1234\n  3. Load the model you want to use",
195            ),
196            Provider::LlamaCpp => Some(
197                "llama.cpp server is not running. To start:\n  1. Install llama.cpp from https://llama.app or your package manager\n  2. Run 'llama-server -m /path/to/model.gguf --port 8080'\n  3. Keep the server running while VT Code connects",
198            ),
199            _ => None,
200        }
201    }
202
203    pub fn uses_managed_auth(&self) -> bool {
204        matches!(self, Provider::Copilot)
205    }
206
207    pub fn platform_url(&self) -> Option<&'static str> {
208        match self {
209            Provider::Gemini => Some("https://aistudio.google.com/app/apikey"),
210            Provider::OpenAI => Some("https://platform.openai.com/api-keys"),
211            Provider::Anthropic => Some("https://console.anthropic.com"),
212            Provider::Copilot => None,
213            Provider::DeepSeek => Some("https://platform.deepseek.com"),
214            Provider::Meta => Some("https://developer.meta.com/ai/models"),
215            Provider::OpenRouter => Some("https://openrouter.ai/api/v1/auth/keys"),
216            Provider::Ollama => None,
217            Provider::OllamaCloud => Some("https://ollama.com"),
218            Provider::LmStudio => None,
219            Provider::LlamaCpp => None,
220            Provider::Moonshot => Some("https://platform.kimi.ai/console/api-keys"),
221            Provider::ZAI => Some("https://z.ai/docs"),
222            Provider::Minimax => Some("https://platform.minimax.io/docs"),
223            Provider::MiMo => Some("https://platform.xiaomimimo.com/docs/en-US/welcome"),
224            Provider::Mistral => Some("https://console.mistral.ai"),
225            Provider::HuggingFace => Some("https://huggingface.co/settings/tokens"),
226            Provider::OpenCodeZen => Some("https://opencode.ai/docs/zen/"),
227            Provider::OpenCodeGo => Some("https://opencode.ai/docs/go/"),
228            Provider::Qwen => Some("https://dashscope.console.aliyun.com"),
229            Provider::StepFun => Some("https://platform.stepfun.com"),
230            Provider::Evolink => Some("https://evolink.ai/dashboard/keys"),
231            Provider::Poolside => Some("https://poolside.ai"),
232            Provider::XAI => Some("https://docs.x.ai"),
233            Provider::NVIDIA => Some("https://build.nvidia.com"),
234            Provider::MergeGateway => Some("https://dashboard.merge.dev"),
235            Provider::Vercel => Some("https://vercel.com/docs/ai-gateway/getting-started"),
236        }
237    }
238}
239
240impl fmt::Display for Provider {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            Provider::Gemini => write!(f, "gemini"),
244            Provider::OpenAI => write!(f, "openai"),
245            Provider::Anthropic => write!(f, "anthropic"),
246            Provider::Copilot => write!(f, "copilot"),
247            Provider::DeepSeek => write!(f, "deepseek"),
248            Provider::Meta => write!(f, "meta"),
249            Provider::OpenRouter => write!(f, "openrouter"),
250            Provider::Ollama => write!(f, "ollama"),
251            Provider::OllamaCloud => write!(f, "ollama-cloud"),
252            Provider::LmStudio => write!(f, "lmstudio"),
253            Provider::LlamaCpp => write!(f, "llamacpp"),
254            Provider::Moonshot => write!(f, "moonshot"),
255            Provider::ZAI => write!(f, "zai"),
256            Provider::Minimax => write!(f, "minimax"),
257            Provider::MiMo => write!(f, "mimo"),
258            Provider::Mistral => write!(f, "mistral"),
259            Provider::HuggingFace => write!(f, "huggingface"),
260            Provider::OpenCodeZen => write!(f, "opencode-zen"),
261            Provider::OpenCodeGo => write!(f, "opencode-go"),
262            Provider::Qwen => write!(f, "qwen"),
263            Provider::StepFun => write!(f, "stepfun"),
264            Provider::Evolink => write!(f, "evolink"),
265            Provider::Poolside => write!(f, "poolside"),
266            Provider::XAI => write!(f, "xai"),
267            Provider::NVIDIA => write!(f, "nvidia"),
268            Provider::MergeGateway => write!(f, "merge-gateway"),
269            Provider::Vercel => write!(f, "vercel"),
270        }
271    }
272}
273
274impl AsRef<str> for Provider {
275    fn as_ref(&self) -> &str {
276        match self {
277            Provider::Gemini => "gemini",
278            Provider::OpenAI => "openai",
279            Provider::Anthropic => "anthropic",
280            Provider::Copilot => "copilot",
281            Provider::DeepSeek => "deepseek",
282            Provider::Meta => "meta",
283            Provider::OpenRouter => "openrouter",
284            Provider::Ollama => "ollama",
285            Provider::OllamaCloud => "ollama-cloud",
286            Provider::LmStudio => "lmstudio",
287            Provider::LlamaCpp => "llamacpp",
288            Provider::Moonshot => "moonshot",
289            Provider::ZAI => "zai",
290            Provider::Minimax => "minimax",
291            Provider::MiMo => "mimo",
292            Provider::Mistral => "mistral",
293            Provider::HuggingFace => "huggingface",
294            Provider::OpenCodeZen => "opencode-zen",
295            Provider::OpenCodeGo => "opencode-go",
296            Provider::Qwen => "qwen",
297            Provider::StepFun => "stepfun",
298            Provider::Evolink => "evolink",
299            Provider::Poolside => "poolside",
300            Provider::XAI => "xai",
301            Provider::NVIDIA => "nvidia",
302            Provider::MergeGateway => "merge-gateway",
303            Provider::Vercel => "vercel",
304        }
305    }
306}
307
308impl FromStr for Provider {
309    type Err = ProviderParseError;
310
311    fn from_str(s: &str) -> Result<Self, Self::Err> {
312        match s.to_lowercase().as_str() {
313            "gemini" | "google" => Ok(Provider::Gemini),
314            "openai" => Ok(Provider::OpenAI),
315            "anthropic" => Ok(Provider::Anthropic),
316            "copilot" => Ok(Provider::Copilot),
317            "deepseek" => Ok(Provider::DeepSeek),
318            "meta" | "meta-ai" => Ok(Provider::Meta),
319            "openrouter" => Ok(Provider::OpenRouter),
320            "ollama" => Ok(Provider::Ollama),
321            "ollama-cloud" | "ollamacloud" | "ollama_cloud" => Ok(Provider::OllamaCloud),
322            "lmstudio" => Ok(Provider::LmStudio),
323            "llamacpp" | "llama.cpp" | "llama-cpp" => Ok(Provider::LlamaCpp),
324            "moonshot" => Ok(Provider::Moonshot),
325            "zai" => Ok(Provider::ZAI),
326            "minimax" => Ok(Provider::Minimax),
327            "mimo" => Ok(Provider::MiMo),
328            "mistral" => Ok(Provider::Mistral),
329            "huggingface" => Ok(Provider::HuggingFace),
330            "opencode-zen" | "opencodezen" => Ok(Provider::OpenCodeZen),
331            "opencode-go" | "opencodego" => Ok(Provider::OpenCodeGo),
332            "qwen" => Ok(Provider::Qwen),
333            "stepfun" => Ok(Provider::StepFun),
334            "evolink" => Ok(Provider::Evolink),
335            "poolside" => Ok(Provider::Poolside),
336            "xai" => Ok(Provider::XAI),
337            "nvidia" | "nvidia-nim" => Ok(Provider::NVIDIA),
338            "merge-gateway" | "merge_gateway" | "mergegateway" => Ok(Provider::MergeGateway),
339            "vercel" | "vercel-ai-gateway" | "ai-gateway" => Ok(Provider::Vercel),
340            _ => Err(ProviderParseError::InvalidProvider(s.to_string())),
341        }
342    }
343}