1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5use super::{ModelId, ModelParseError};
6
7#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
10pub enum Provider {
11 #[default]
13 Gemini,
14 OpenAI,
16 Anthropic,
18 DeepSeek,
20 OpenRouter,
22 Ollama,
24 LmStudio,
26 Moonshot,
28 XAI,
30 ZAI,
32 Minimax,
34 HuggingFace,
36}
37
38impl Provider {
39 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 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 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 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 => false,
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}