1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum ModelProvider {
7 Gemini,
8 Openai,
9 Anthropic,
10 Deepseek,
11 Groq,
12 Ollama,
13}
14
15impl ModelProvider {
16 pub const ALL: [Self; 6] =
18 [Self::Gemini, Self::Openai, Self::Anthropic, Self::Deepseek, Self::Groq, Self::Ollama];
19
20 pub const fn all() -> &'static [Self] {
22 &Self::ALL
23 }
24
25 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::Gemini => "gemini",
29 Self::Openai => "openai",
30 Self::Anthropic => "anthropic",
31 Self::Deepseek => "deepseek",
32 Self::Groq => "groq",
33 Self::Ollama => "ollama",
34 }
35 }
36
37 pub const fn default_model(self) -> &'static str {
39 match self {
40 Self::Gemini => "gemini-3.1-flash-lite-preview",
41 Self::Openai => "gpt-5-mini",
42 Self::Anthropic => "claude-sonnet-4-5-20250929",
43 Self::Deepseek => "deepseek-chat",
44 Self::Groq => "llama-3.3-70b-versatile",
45 Self::Ollama => "qwen3.5",
46 }
47 }
48
49 pub const fn env_var(self) -> &'static str {
51 match self {
52 Self::Gemini => "GOOGLE_API_KEY",
53 Self::Openai => "OPENAI_API_KEY",
54 Self::Anthropic => "ANTHROPIC_API_KEY",
55 Self::Deepseek => "DEEPSEEK_API_KEY",
56 Self::Groq => "GROQ_API_KEY",
57 Self::Ollama => "",
58 }
59 }
60
61 pub const fn alt_env_var(self) -> Option<&'static str> {
63 match self {
64 Self::Gemini => Some("GEMINI_API_KEY"),
65 _ => None,
66 }
67 }
68
69 pub const fn requires_key(self) -> bool {
71 !matches!(self, Self::Ollama)
72 }
73
74 pub const fn display_name(self) -> &'static str {
76 match self {
77 Self::Gemini => "Gemini (Google)",
78 Self::Openai => "OpenAI",
79 Self::Anthropic => "Anthropic (Claude)",
80 Self::Deepseek => "DeepSeek",
81 Self::Groq => "Groq",
82 Self::Ollama => "Ollama (local, no key needed)",
83 }
84 }
85}
86
87impl Display for ModelProvider {
88 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89 f.write_str(self.as_str())
90 }
91}
92
93impl FromStr for ModelProvider {
94 type Err = String;
95
96 fn from_str(value: &str) -> Result<Self, Self::Err> {
97 match value {
98 "gemini" => Ok(Self::Gemini),
99 "openai" => Ok(Self::Openai),
100 "anthropic" => Ok(Self::Anthropic),
101 "deepseek" => Ok(Self::Deepseek),
102 "groq" => Ok(Self::Groq),
103 "ollama" => Ok(Self::Ollama),
104 other => Err(format!("unsupported provider: {other}")),
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::ModelProvider;
112 use std::str::FromStr;
113
114 #[test]
115 fn provider_roundtrips_from_machine_name() {
116 for provider in ModelProvider::all() {
117 let parsed = ModelProvider::from_str(provider.as_str()).expect("provider should parse");
118 assert_eq!(*provider, parsed);
119 }
120 }
121}