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    /// OpenRouter marketplace models
35    OpenRouter,
36    /// Local Ollama models
37    Ollama,
38    /// LM Studio local models
39    LmStudio,
40    /// llama.cpp local models
41    LlamaCpp,
42    /// Moonshot.ai models
43    Moonshot,
44    /// Z.AI GLM models
45    ZAI,
46    /// MiniMax models
47    Minimax,
48    /// Xiaomi MiMo models
49    MiMo,
50    /// Mistral AI models
51    Mistral,
52    /// Hugging Face Inference Providers
53    HuggingFace,
54    /// OpenCode Zen gateway (pay-as-you-go)
55    OpenCodeZen,
56    /// OpenCode Go subscription
57    OpenCodeGo,
58    /// Alibaba Cloud Qwen models
59    Qwen,
60    /// StepFun models
61    StepFun,
62    /// Evolink multi-model gateway
63    Evolink,
64    /// Poolside AI models
65    Poolside,
66}
67
68impl Provider {
69    /// Get the default API key environment variable for this provider
70    pub fn default_api_key_env(&self) -> &'static str {
71        match self {
72            Provider::Gemini => "GEMINI_API_KEY",
73            Provider::OpenAI => "OPENAI_API_KEY",
74            Provider::Anthropic => "ANTHROPIC_API_KEY",
75            Provider::Copilot => "",
76            Provider::DeepSeek => "DEEPSEEK_API_KEY",
77            Provider::OpenRouter => "OPENROUTER_API_KEY",
78            Provider::Ollama => "OLLAMA_API_KEY",
79            Provider::LmStudio => "LMSTUDIO_API_KEY",
80            Provider::LlamaCpp => "LLAMACPP_API_KEY",
81            Provider::Moonshot => "MOONSHOT_API_KEY",
82            Provider::ZAI => "ZAI_API_KEY",
83            Provider::Minimax => "MINIMAX_API_KEY",
84            Provider::MiMo => "MIMO_API_KEY",
85            Provider::Mistral => "MISTRAL_API_KEY",
86            Provider::HuggingFace => "HF_TOKEN",
87            Provider::OpenCodeZen => "OPENCODE_ZEN_API_KEY",
88            Provider::OpenCodeGo => "OPENCODE_GO_API_KEY",
89            Provider::Qwen => "QWEN_API_KEY",
90            Provider::StepFun => "STEPFUN_API_KEY",
91            Provider::Evolink => "EVOLINK_API_KEY",
92            Provider::Poolside => "POOLSIDE_API_KEY",
93        }
94    }
95
96    /// Get all supported providers
97    pub fn all_providers() -> Vec<Provider> {
98        vec![
99            Provider::OpenAI,
100            Provider::Anthropic,
101            Provider::Copilot,
102            Provider::Minimax,
103            Provider::MiMo,
104            Provider::Mistral,
105            Provider::Gemini,
106            Provider::DeepSeek,
107            Provider::HuggingFace,
108            Provider::OpenRouter,
109            Provider::Ollama,
110            Provider::LmStudio,
111            Provider::LlamaCpp,
112            Provider::Moonshot,
113            Provider::ZAI,
114            Provider::OpenCodeZen,
115            Provider::OpenCodeGo,
116            Provider::Qwen,
117            Provider::StepFun,
118            Provider::Evolink,
119            Provider::Poolside,
120        ]
121    }
122
123    /// Human-friendly label for display purposes
124    pub fn label(&self) -> &'static str {
125        match self {
126            Provider::Gemini => "Gemini",
127            Provider::OpenAI => "OpenAI",
128            Provider::Anthropic => "Anthropic",
129            Provider::Copilot => "GitHub Copilot",
130            Provider::DeepSeek => "DeepSeek",
131            Provider::OpenRouter => "OpenRouter",
132            Provider::Ollama => "Ollama",
133            Provider::LmStudio => "LM Studio",
134            Provider::LlamaCpp => "llama.cpp",
135            Provider::Moonshot => "Moonshot",
136            Provider::ZAI => "Z.AI",
137            Provider::Minimax => "MiniMax",
138            Provider::MiMo => "Xiaomi MiMo",
139            Provider::Mistral => "Mistral",
140            Provider::HuggingFace => "Hugging Face",
141            Provider::OpenCodeZen => "OpenCode Zen",
142            Provider::OpenCodeGo => "OpenCode Go",
143            Provider::Qwen => "Qwen",
144            Provider::StepFun => "StepFun",
145            Provider::Evolink => "Evolink",
146            Provider::Poolside => "Poolside",
147        }
148    }
149
150    pub fn is_dynamic(&self) -> bool {
151        matches!(self, Provider::Copilot) || self.is_local()
152    }
153
154    pub fn is_local(&self) -> bool {
155        matches!(
156            self,
157            Provider::Ollama | Provider::LmStudio | Provider::LlamaCpp
158        )
159    }
160
161    pub fn local_install_instructions(&self) -> Option<&'static str> {
162        match self {
163            Provider::Ollama => Some(
164                "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')",
165            ),
166            Provider::LmStudio => Some(
167                "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",
168            ),
169            Provider::LlamaCpp => Some(
170                "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",
171            ),
172            _ => None,
173        }
174    }
175
176    pub fn uses_managed_auth(&self) -> bool {
177        matches!(self, Provider::Copilot)
178    }
179}
180
181impl fmt::Display for Provider {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        match self {
184            Provider::Gemini => write!(f, "gemini"),
185            Provider::OpenAI => write!(f, "openai"),
186            Provider::Anthropic => write!(f, "anthropic"),
187            Provider::Copilot => write!(f, "copilot"),
188            Provider::DeepSeek => write!(f, "deepseek"),
189            Provider::OpenRouter => write!(f, "openrouter"),
190            Provider::Ollama => write!(f, "ollama"),
191            Provider::LmStudio => write!(f, "lmstudio"),
192            Provider::LlamaCpp => write!(f, "llamacpp"),
193            Provider::Moonshot => write!(f, "moonshot"),
194            Provider::ZAI => write!(f, "zai"),
195            Provider::Minimax => write!(f, "minimax"),
196            Provider::MiMo => write!(f, "mimo"),
197            Provider::Mistral => write!(f, "mistral"),
198            Provider::HuggingFace => write!(f, "huggingface"),
199            Provider::OpenCodeZen => write!(f, "opencode-zen"),
200            Provider::OpenCodeGo => write!(f, "opencode-go"),
201            Provider::Qwen => write!(f, "qwen"),
202            Provider::StepFun => write!(f, "stepfun"),
203            Provider::Evolink => write!(f, "evolink"),
204            Provider::Poolside => write!(f, "poolside"),
205        }
206    }
207}
208
209impl AsRef<str> for Provider {
210    fn as_ref(&self) -> &str {
211        match self {
212            Provider::Gemini => "gemini",
213            Provider::OpenAI => "openai",
214            Provider::Anthropic => "anthropic",
215            Provider::Copilot => "copilot",
216            Provider::DeepSeek => "deepseek",
217            Provider::OpenRouter => "openrouter",
218            Provider::Ollama => "ollama",
219            Provider::LmStudio => "lmstudio",
220            Provider::LlamaCpp => "llamacpp",
221            Provider::Moonshot => "moonshot",
222            Provider::ZAI => "zai",
223            Provider::Minimax => "minimax",
224            Provider::MiMo => "mimo",
225            Provider::Mistral => "mistral",
226            Provider::HuggingFace => "huggingface",
227            Provider::OpenCodeZen => "opencode-zen",
228            Provider::OpenCodeGo => "opencode-go",
229            Provider::Qwen => "qwen",
230            Provider::StepFun => "stepfun",
231            Provider::Evolink => "evolink",
232            Provider::Poolside => "poolside",
233        }
234    }
235}
236
237impl FromStr for Provider {
238    type Err = ProviderParseError;
239
240    fn from_str(s: &str) -> Result<Self, Self::Err> {
241        match s.to_lowercase().as_str() {
242            "gemini" => Ok(Provider::Gemini),
243            "openai" => Ok(Provider::OpenAI),
244            "anthropic" => Ok(Provider::Anthropic),
245            "copilot" => Ok(Provider::Copilot),
246            "deepseek" => Ok(Provider::DeepSeek),
247            "openrouter" => Ok(Provider::OpenRouter),
248            "ollama" => Ok(Provider::Ollama),
249            "lmstudio" => Ok(Provider::LmStudio),
250            "llamacpp" | "llama.cpp" | "llama-cpp" => Ok(Provider::LlamaCpp),
251            "moonshot" => Ok(Provider::Moonshot),
252            "zai" => Ok(Provider::ZAI),
253            "minimax" => Ok(Provider::Minimax),
254            "mimo" => Ok(Provider::MiMo),
255            "mistral" => Ok(Provider::Mistral),
256            "huggingface" => Ok(Provider::HuggingFace),
257            "opencode-zen" | "opencodezen" => Ok(Provider::OpenCodeZen),
258            "opencode-go" | "opencodego" => Ok(Provider::OpenCodeGo),
259            "qwen" => Ok(Provider::Qwen),
260            "stepfun" => Ok(Provider::StepFun),
261            "evolink" => Ok(Provider::Evolink),
262            "poolside" => Ok(Provider::Poolside),
263            _ => Err(ProviderParseError::InvalidProvider(s.to_string())),
264        }
265    }
266}