construct/agent/operator/providers/
mod.rs1pub mod claude;
7pub mod gemini;
8pub mod ollama;
9pub mod openai;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Provider {
14 Claude,
16 OpenAi,
18 Local,
20 Gemini,
22}
23
24impl Provider {
25 pub fn detect(model_name: &str) -> Self {
33 let lower = model_name.to_lowercase();
34
35 let model = match lower.rfind('/') {
37 Some(pos) => &lower[pos + 1..],
38 None => &lower,
39 };
40
41 if model.starts_with("claude") {
42 Provider::Claude
43 } else if model.starts_with("gpt-")
44 || model.starts_with("gpt4")
45 || model.starts_with("o1")
46 || model.starts_with("o3")
47 || model.starts_with("o4")
48 || model.starts_with("codex")
49 || model.starts_with("chatgpt")
50 {
51 Provider::OpenAi
52 } else if model.starts_with("gemini") {
53 Provider::Gemini
54 } else if model.starts_with("llama")
55 || model.starts_with("mistral")
56 || model.starts_with("qwen")
57 || model.starts_with("phi")
58 || model.starts_with("deepseek")
59 || model.starts_with("command")
60 || model.contains(':')
61 {
63 Provider::Local
64 } else {
65 Provider::OpenAi
67 }
68 }
69
70 pub fn tool_layer(&self) -> &'static str {
72 match self {
73 Provider::Claude => claude::TOOL_LAYER,
74 Provider::OpenAi => openai::TOOL_LAYER,
75 Provider::Local => ollama::TOOL_LAYER,
76 Provider::Gemini => gemini::TOOL_LAYER,
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn detect_claude_models() {
87 assert_eq!(Provider::detect("claude-opus-4-6"), Provider::Claude);
88 assert_eq!(Provider::detect("claude-sonnet-4-6"), Provider::Claude);
89 assert_eq!(
90 Provider::detect("claude-haiku-4-5-20251001"),
91 Provider::Claude
92 );
93 assert_eq!(Provider::detect("Claude-Opus-4-6"), Provider::Claude);
94 }
95
96 #[test]
97 fn detect_openai_models() {
98 assert_eq!(Provider::detect("gpt-5.4"), Provider::OpenAi);
99 assert_eq!(Provider::detect("gpt-4-turbo"), Provider::OpenAi);
100 assert_eq!(Provider::detect("gpt4o"), Provider::OpenAi);
101 assert_eq!(Provider::detect("o1-preview"), Provider::OpenAi);
102 assert_eq!(Provider::detect("o3-mini"), Provider::OpenAi);
103 assert_eq!(Provider::detect("codex-mini"), Provider::OpenAi);
104 }
105
106 #[test]
107 fn detect_gemini_models() {
108 assert_eq!(Provider::detect("gemini-pro"), Provider::Gemini);
109 assert_eq!(Provider::detect("gemini-2.0-flash"), Provider::Gemini);
110 }
111
112 #[test]
113 fn detect_local_models() {
114 assert_eq!(Provider::detect("llama3:70b"), Provider::Local);
115 assert_eq!(Provider::detect("mistral-7b"), Provider::Local);
116 assert_eq!(Provider::detect("qwen2:14b"), Provider::Local);
117 assert_eq!(Provider::detect("deepseek-coder-v2"), Provider::Local);
118 assert_eq!(Provider::detect("phi-3-mini"), Provider::Local);
119 }
120
121 #[test]
122 fn detect_with_router_prefix() {
123 assert_eq!(
124 Provider::detect("openrouter/claude-opus-4-6"),
125 Provider::Claude
126 );
127 assert_eq!(Provider::detect("openrouter/gpt-5.4"), Provider::OpenAi);
128 assert_eq!(Provider::detect("together/llama3-70b"), Provider::Local);
129 }
130
131 #[test]
132 fn unknown_defaults_to_openai() {
133 assert_eq!(Provider::detect("some-unknown-model"), Provider::OpenAi);
134 }
135
136 #[test]
137 fn tool_layer_not_empty() {
138 for provider in [
139 Provider::Claude,
140 Provider::OpenAi,
141 Provider::Local,
142 Provider::Gemini,
143 ] {
144 let layer = provider.tool_layer();
145 assert!(!layer.is_empty(), "{provider:?} has empty tool layer");
146 assert!(
147 layer.contains("create_agent"),
148 "{provider:?} tool layer missing create_agent"
149 );
150 }
151 }
152}