Skip to main content

vtcode_config/models/
provider.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5use super::{ModelId, ModelParseError};
6
7/// Supported AI model providers
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
10pub enum Provider {
11    /// Google Gemini models
12    #[default]
13    Gemini,
14    /// OpenAI GPT models
15    OpenAI,
16    /// Anthropic Claude models
17    Anthropic,
18    /// DeepSeek native models
19    DeepSeek,
20    /// OpenRouter marketplace models
21    OpenRouter,
22    /// Local Ollama models
23    Ollama,
24    /// LM Studio local server (OpenAI-compatible)
25    LmStudio,
26    /// Moonshot.ai models
27    Moonshot,
28    /// xAI Grok models
29    XAI,
30    /// Z.AI GLM models
31    ZAI,
32    /// MiniMax models
33    Minimax,
34    /// Hugging Face Inference Providers
35    HuggingFace,
36}
37
38impl Provider {
39    /// Get the default API key environment variable for this provider
40    pub fn default_api_key_env(&self) -> &'static str {
41        match self {
42            Provider::Gemini => "GEMINI_API_KEY",
43            Provider::OpenAI => "OPENAI_API_KEY",
44            Provider::Anthropic => "ANTHROPIC_API_KEY",
45            Provider::DeepSeek => "DEEPSEEK_API_KEY",
46            Provider::OpenRouter => "OPENROUTER_API_KEY",
47            Provider::Ollama => "OLLAMA_API_KEY",
48            Provider::LmStudio => "LMSTUDIO_API_KEY",
49            Provider::Moonshot => "MOONSHOT_API_KEY",
50            Provider::XAI => "XAI_API_KEY",
51            Provider::ZAI => "ZAI_API_KEY",
52            Provider::Minimax => "MINIMAX_API_KEY",
53            Provider::HuggingFace => "HF_TOKEN",
54        }
55    }
56
57    /// Get all supported providers
58    pub fn all_providers() -> Vec<Provider> {
59        vec![
60            Provider::OpenAI,
61            Provider::Anthropic,
62            Provider::Gemini,
63            Provider::DeepSeek,
64            Provider::OpenRouter,
65            Provider::Ollama,
66            Provider::LmStudio,
67            Provider::Moonshot,
68            Provider::XAI,
69            Provider::ZAI,
70            Provider::Minimax,
71            Provider::HuggingFace,
72        ]
73    }
74
75    /// Human-friendly label for display purposes
76    pub fn label(&self) -> &'static str {
77        match self {
78            Provider::Gemini => "Gemini",
79            Provider::OpenAI => "OpenAI",
80            Provider::Anthropic => "Anthropic",
81            Provider::DeepSeek => "DeepSeek",
82            Provider::OpenRouter => "OpenRouter",
83            Provider::Ollama => "Ollama",
84            Provider::LmStudio => "LM Studio",
85            Provider::Moonshot => "Moonshot",
86            Provider::XAI => "xAI",
87            Provider::ZAI => "Z.AI",
88            Provider::Minimax => "MiniMax",
89            Provider::HuggingFace => "Hugging Face",
90        }
91    }
92
93    /// Determine if the provider supports configurable reasoning effort for the model
94    pub fn supports_reasoning_effort(&self, model: &str) -> bool {
95        use crate::constants::models;
96
97        match self {
98            Provider::Gemini => models::google::REASONING_MODELS.contains(&model),
99            Provider::OpenAI => models::openai::REASONING_MODELS.contains(&model),
100            Provider::Anthropic => models::anthropic::REASONING_MODELS.contains(&model),
101            Provider::DeepSeek => model == models::deepseek::DEEPSEEK_REASONER,
102            Provider::OpenRouter => {
103                if let Ok(model_id) = ModelId::from_str(model) {
104                    return model_id.is_reasoning_variant();
105                }
106                models::openrouter::REASONING_MODELS.contains(&model)
107            }
108            Provider::Ollama => models::ollama::REASONING_LEVEL_MODELS.contains(&model),
109            Provider::LmStudio => false,
110            Provider::Moonshot => models::moonshot::REASONING_MODELS.contains(&model),
111            Provider::XAI => model == models::xai::GROK_4 || model == models::xai::GROK_4_CODE,
112            Provider::ZAI => model == models::zai::GLM_4_6,
113            Provider::Minimax => {
114                model == models::minimax::MINIMAX_M2_1
115                    || model == models::minimax::MINIMAX_M2_1_LIGHTNING
116                    || model == models::minimax::MINIMAX_M2
117            }
118            Provider::HuggingFace => models::huggingface::REASONING_MODELS.contains(&model),
119        }
120    }
121}
122
123impl fmt::Display for Provider {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        match self {
126            Provider::Gemini => write!(f, "gemini"),
127            Provider::OpenAI => write!(f, "openai"),
128            Provider::Anthropic => write!(f, "anthropic"),
129            Provider::DeepSeek => write!(f, "deepseek"),
130            Provider::OpenRouter => write!(f, "openrouter"),
131            Provider::Ollama => write!(f, "ollama"),
132            Provider::LmStudio => write!(f, "lmstudio"),
133            Provider::Moonshot => write!(f, "moonshot"),
134            Provider::XAI => write!(f, "xai"),
135            Provider::ZAI => write!(f, "zai"),
136            Provider::Minimax => write!(f, "minimax"),
137            Provider::HuggingFace => write!(f, "huggingface"),
138        }
139    }
140}
141
142impl FromStr for Provider {
143    type Err = ModelParseError;
144
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        match s.to_lowercase().as_str() {
147            "gemini" => Ok(Provider::Gemini),
148            "openai" => Ok(Provider::OpenAI),
149            "anthropic" => Ok(Provider::Anthropic),
150            "deepseek" => Ok(Provider::DeepSeek),
151            "openrouter" => Ok(Provider::OpenRouter),
152            "ollama" => Ok(Provider::Ollama),
153            "lmstudio" => Ok(Provider::LmStudio),
154            "moonshot" => Ok(Provider::Moonshot),
155            "xai" => Ok(Provider::XAI),
156            "zai" => Ok(Provider::ZAI),
157            "minimax" => Ok(Provider::Minimax),
158            "huggingface" => Ok(Provider::HuggingFace),
159            _ => Err(ModelParseError::InvalidProvider(s.to_string())),
160        }
161    }
162}