1use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::str::FromStr;
11
12#[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#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
22pub enum Provider {
23 Gemini,
25 #[default]
27 OpenAI,
28 Anthropic,
30 Copilot,
32 DeepSeek,
34 OpenRouter,
36 Ollama,
38 LmStudio,
40 LlamaCpp,
42 Moonshot,
44 ZAI,
46 Minimax,
48 MiMo,
50 Mistral,
52 HuggingFace,
54 OpenCodeZen,
56 OpenCodeGo,
58 Qwen,
60 StepFun,
62 Evolink,
64 Poolside,
66}
67
68impl Provider {
69 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 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 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!(self, Provider::Ollama | Provider::LmStudio | Provider::LlamaCpp)
156 }
157
158 pub fn local_install_instructions(&self) -> Option<&'static str> {
159 match self {
160 Provider::Ollama => Some(
161 "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')",
162 ),
163 Provider::LmStudio => Some(
164 "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",
165 ),
166 Provider::LlamaCpp => Some(
167 "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",
168 ),
169 _ => None,
170 }
171 }
172
173 pub fn uses_managed_auth(&self) -> bool {
174 matches!(self, Provider::Copilot)
175 }
176}
177
178impl fmt::Display for Provider {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match self {
181 Provider::Gemini => write!(f, "gemini"),
182 Provider::OpenAI => write!(f, "openai"),
183 Provider::Anthropic => write!(f, "anthropic"),
184 Provider::Copilot => write!(f, "copilot"),
185 Provider::DeepSeek => write!(f, "deepseek"),
186 Provider::OpenRouter => write!(f, "openrouter"),
187 Provider::Ollama => write!(f, "ollama"),
188 Provider::LmStudio => write!(f, "lmstudio"),
189 Provider::LlamaCpp => write!(f, "llamacpp"),
190 Provider::Moonshot => write!(f, "moonshot"),
191 Provider::ZAI => write!(f, "zai"),
192 Provider::Minimax => write!(f, "minimax"),
193 Provider::MiMo => write!(f, "mimo"),
194 Provider::Mistral => write!(f, "mistral"),
195 Provider::HuggingFace => write!(f, "huggingface"),
196 Provider::OpenCodeZen => write!(f, "opencode-zen"),
197 Provider::OpenCodeGo => write!(f, "opencode-go"),
198 Provider::Qwen => write!(f, "qwen"),
199 Provider::StepFun => write!(f, "stepfun"),
200 Provider::Evolink => write!(f, "evolink"),
201 Provider::Poolside => write!(f, "poolside"),
202 }
203 }
204}
205
206impl AsRef<str> for Provider {
207 fn as_ref(&self) -> &str {
208 match self {
209 Provider::Gemini => "gemini",
210 Provider::OpenAI => "openai",
211 Provider::Anthropic => "anthropic",
212 Provider::Copilot => "copilot",
213 Provider::DeepSeek => "deepseek",
214 Provider::OpenRouter => "openrouter",
215 Provider::Ollama => "ollama",
216 Provider::LmStudio => "lmstudio",
217 Provider::LlamaCpp => "llamacpp",
218 Provider::Moonshot => "moonshot",
219 Provider::ZAI => "zai",
220 Provider::Minimax => "minimax",
221 Provider::MiMo => "mimo",
222 Provider::Mistral => "mistral",
223 Provider::HuggingFace => "huggingface",
224 Provider::OpenCodeZen => "opencode-zen",
225 Provider::OpenCodeGo => "opencode-go",
226 Provider::Qwen => "qwen",
227 Provider::StepFun => "stepfun",
228 Provider::Evolink => "evolink",
229 Provider::Poolside => "poolside",
230 }
231 }
232}
233
234impl FromStr for Provider {
235 type Err = ProviderParseError;
236
237 fn from_str(s: &str) -> Result<Self, Self::Err> {
238 match s.to_lowercase().as_str() {
239 "gemini" => Ok(Provider::Gemini),
240 "openai" => Ok(Provider::OpenAI),
241 "anthropic" => Ok(Provider::Anthropic),
242 "copilot" => Ok(Provider::Copilot),
243 "deepseek" => Ok(Provider::DeepSeek),
244 "openrouter" => Ok(Provider::OpenRouter),
245 "ollama" => Ok(Provider::Ollama),
246 "lmstudio" => Ok(Provider::LmStudio),
247 "llamacpp" | "llama.cpp" | "llama-cpp" => Ok(Provider::LlamaCpp),
248 "moonshot" => Ok(Provider::Moonshot),
249 "zai" => Ok(Provider::ZAI),
250 "minimax" => Ok(Provider::Minimax),
251 "mimo" => Ok(Provider::MiMo),
252 "mistral" => Ok(Provider::Mistral),
253 "huggingface" => Ok(Provider::HuggingFace),
254 "opencode-zen" | "opencodezen" => Ok(Provider::OpenCodeZen),
255 "opencode-go" | "opencodego" => Ok(Provider::OpenCodeGo),
256 "qwen" => Ok(Provider::Qwen),
257 "stepfun" => Ok(Provider::StepFun),
258 "evolink" => Ok(Provider::Evolink),
259 "poolside" => Ok(Provider::Poolside),
260 _ => Err(ProviderParseError::InvalidProvider(s.to_string())),
261 }
262 }
263}