Skip to main content

vtcode_core/llm/
provider_config.rs

1use crate::config::TimeoutsConfig;
2use crate::config::core::{GeminiPromptCacheSettings, PromptCachingConfig};
3use crate::llm::cgp::{CanDescribeProvider, ProviderMetadataProvider};
4use crate::llm::factory::{self, ProviderConfig as FactoryProviderConfig};
5use crate::llm::provider::{LLMError, LLMProvider};
6use crate::llm::provider_builder::ProviderConfig as LegacyProviderConfig;
7
8macro_rules! define_provider_config {
9    ($(#[$meta:meta])* $name:ident, $key:literal, $display:literal, $default_model:expr, $api_base:expr, $env_var:expr, $prompt_cache_settings:ty) => {
10        $(#[$meta])*
11        pub struct $name;
12
13        impl ProviderMetadataProvider<$name> for $name {
14            const PROVIDER_KEY: &'static str = $key;
15            const DISPLAY_NAME: &'static str = $display;
16            const DEFAULT_MODEL: &'static str = $default_model;
17            const API_BASE_URL: &'static str = $api_base;
18            const BASE_URL_ENV_VAR: Option<&'static str> = $env_var;
19        }
20
21        impl LegacyProviderConfig for $name {
22            const PROVIDER_KEY: &'static str = <Self as CanDescribeProvider>::PROVIDER_KEY;
23            const DISPLAY_NAME: &'static str = <Self as CanDescribeProvider>::DISPLAY_NAME;
24            const DEFAULT_MODEL: &'static str = <Self as CanDescribeProvider>::DEFAULT_MODEL;
25            const API_BASE_URL: &'static str = <Self as CanDescribeProvider>::API_BASE_URL;
26            const BASE_URL_ENV_VAR: Option<&'static str> =
27                <Self as CanDescribeProvider>::BASE_URL_ENV_VAR;
28
29            type PromptCacheSettings = $prompt_cache_settings;
30        }
31    };
32}
33
34define_provider_config!(
35    /// Provider configuration for Google Gemini.
36    GeminiProviderConfig,
37    "gemini",
38    "Gemini",
39    crate::config::constants::models::google::GEMINI_3_FLASH_PREVIEW,
40    crate::config::constants::urls::GEMINI_API_BASE,
41    Some(crate::config::constants::env_vars::GEMINI_BASE_URL),
42    GeminiPromptCacheSettings
43);
44define_provider_config!(
45    /// Provider configuration for Anthropic.
46    AnthropicProviderConfig,
47    "anthropic",
48    "Anthropic",
49    crate::config::constants::models::anthropic::DEFAULT_MODEL,
50    crate::config::constants::urls::ANTHROPIC_API_BASE,
51    Some(crate::config::constants::env_vars::ANTHROPIC_BASE_URL),
52    ()
53);
54define_provider_config!(
55    /// Provider configuration for GitHub Copilot.
56    CopilotProviderConfig,
57    "copilot",
58    "GitHub Copilot",
59    crate::config::constants::models::copilot::DEFAULT_MODEL,
60    "",
61    None,
62    ()
63);
64define_provider_config!(
65    /// Provider configuration for OpenAI.
66    OpenAIProviderConfig,
67    "openai",
68    "OpenAI",
69    crate::config::constants::models::openai::DEFAULT_MODEL,
70    crate::config::constants::urls::OPENAI_API_BASE,
71    Some(crate::config::constants::env_vars::OPENAI_BASE_URL),
72    ()
73);
74define_provider_config!(
75    /// Provider configuration for HuggingFace Inference API.
76    HuggingFaceProviderConfig,
77    "huggingface",
78    "HuggingFace",
79    crate::config::constants::models::huggingface::DEFAULT_MODEL,
80    crate::config::constants::urls::HUGGINGFACE_API_BASE,
81    Some(crate::config::constants::env_vars::HUGGINGFACE_BASE_URL),
82    ()
83);
84define_provider_config!(
85    /// Provider configuration for DeepSeek.
86    DeepSeekProviderConfig,
87    "deepseek",
88    "DeepSeek",
89    crate::config::constants::models::deepseek::DEEPSEEK_FLASH,
90    crate::config::constants::urls::DEEPSEEK_API_BASE,
91    Some(crate::config::constants::env_vars::DEEPSEEK_BASE_URL),
92    ()
93);
94define_provider_config!(
95    /// Provider configuration for the official Meta AI API.
96    MetaProviderConfig,
97    "meta",
98    "Meta AI",
99    crate::config::constants::models::meta::DEFAULT_MODEL,
100    crate::config::constants::urls::META_API_BASE,
101    Some(crate::config::constants::env_vars::META_BASE_URL),
102    ()
103);
104define_provider_config!(
105    /// Provider configuration for Mistral AI.
106    MistralProviderConfig,
107    "mistral",
108    "Mistral",
109    crate::config::constants::models::mistral::MISTRAL_LARGE_3,
110    crate::config::constants::urls::MISTRAL_API_BASE,
111    Some(crate::config::constants::env_vars::MISTRAL_BASE_URL),
112    ()
113);
114define_provider_config!(
115    /// Provider configuration for Moonshot AI.
116    MoonshotProviderConfig,
117    "moonshot",
118    "Moonshot",
119    crate::config::constants::models::moonshot::DEFAULT_MODEL,
120    crate::config::constants::urls::MOONSHOT_API_BASE,
121    Some(crate::config::constants::env_vars::MOONSHOT_BASE_URL),
122    ()
123);
124define_provider_config!(
125    /// Provider configuration for Z.AI (GLM).
126    ZAIProviderConfig,
127    "zai",
128    "Z.AI",
129    crate::config::constants::models::zai::DEFAULT_MODEL,
130    crate::config::constants::urls::Z_AI_API_BASE,
131    Some(crate::config::constants::env_vars::ZAI_BASE_URL),
132    ()
133);
134define_provider_config!(
135    /// Provider configuration for OpenRouter.
136    OpenRouterProviderConfig,
137    "openrouter",
138    "OpenRouter",
139    "openrouter/auto",
140    crate::config::constants::urls::OPENROUTER_API_BASE,
141    Some(crate::config::constants::env_vars::OPENROUTER_BASE_URL),
142    ()
143);
144define_provider_config!(
145    /// Provider configuration for OpenResponses.
146    OpenResponsesProviderConfig,
147    "openresponses",
148    "OpenResponses",
149    crate::config::constants::models::openresponses::DEFAULT_MODEL,
150    crate::config::constants::urls::OPENRESPONSES_API_BASE,
151    Some(crate::config::constants::env_vars::OPENRESPONSES_BASE_URL),
152    ()
153);
154define_provider_config!(
155    /// Provider configuration for Ollama (local inference).
156    OllamaProviderConfig,
157    "ollama",
158    "Ollama",
159    "gpt-oss:20b",
160    "http://localhost:11434",
161    None,
162    ()
163);
164define_provider_config!(
165    /// Provider configuration for Ollama Cloud.
166    OllamaCloudProviderConfig,
167    "ollama-cloud",
168    "Ollama Cloud",
169    crate::config::constants::models::ollama::DEFAULT_CLOUD_MODEL,
170    crate::config::constants::urls::OLLAMA_CLOUD_API_BASE,
171    Some(crate::config::constants::env_vars::OLLAMA_BASE_URL),
172    ()
173);
174define_provider_config!(
175    /// Provider configuration for LM Studio (local inference).
176    LmStudioProviderConfig,
177    "lmstudio",
178    "LM Studio",
179    crate::config::constants::models::lmstudio::DEFAULT_MODEL,
180    crate::config::constants::urls::LMSTUDIO_API_BASE,
181    Some(crate::config::constants::env_vars::LMSTUDIO_BASE_URL),
182    ()
183);
184define_provider_config!(
185    /// Provider configuration for llama.cpp (local inference).
186    LlamaCppProviderConfig,
187    "llamacpp",
188    "llama.cpp",
189    crate::config::constants::models::llamacpp::DEFAULT_MODEL,
190    crate::config::constants::urls::LLAMACPP_API_BASE,
191    Some(crate::config::constants::env_vars::LLAMACPP_BASE_URL),
192    ()
193);
194define_provider_config!(
195    /// Provider configuration for Xiaomi MiMo.
196    MiMoProviderConfig,
197    "mimo",
198    "Xiaomi MiMo",
199    crate::config::constants::models::mimo::DEFAULT_MODEL,
200    crate::config::constants::urls::MIMO_API_BASE,
201    Some(crate::config::constants::env_vars::MIMO_BASE_URL),
202    ()
203);
204define_provider_config!(
205    /// Provider configuration for Minimax.
206    MinimaxProviderConfig,
207    "minimax",
208    "Minimax",
209    crate::config::constants::models::minimax::DEFAULT_MODEL,
210    crate::config::constants::urls::MINIMAX_API_BASE,
211    Some(crate::config::constants::env_vars::MINIMAX_BASE_URL),
212    ()
213);
214define_provider_config!(
215    /// Provider configuration for OpenCode Zen.
216    OpenCodeZenProviderConfig,
217    "opencode-zen",
218    "OpenCode Zen",
219    crate::config::constants::models::opencode_zen::DEFAULT_MODEL,
220    crate::config::constants::urls::OPENCODE_ZEN_API_BASE,
221    Some(crate::config::constants::env_vars::OPENCODE_ZEN_BASE_URL),
222    ()
223);
224define_provider_config!(
225    /// Provider configuration for OpenCode Go.
226    OpenCodeGoProviderConfig,
227    "opencode-go",
228    "OpenCode Go",
229    crate::config::constants::models::opencode_go::DEFAULT_MODEL,
230    crate::config::constants::urls::OPENCODE_GO_API_BASE,
231    Some(crate::config::constants::env_vars::OPENCODE_GO_BASE_URL),
232    ()
233);
234define_provider_config!(
235    /// Provider configuration for Qwen.
236    QwenProviderConfig,
237    "qwen",
238    "Qwen",
239    crate::config::constants::models::qwen::DEFAULT_MODEL,
240    crate::config::constants::urls::QWEN_API_BASE,
241    Some(crate::config::constants::env_vars::QWEN_BASE_URL),
242    ()
243);
244define_provider_config!(
245    /// Provider configuration for StepFun.
246    StepFunProviderConfig,
247    "stepfun",
248    "StepFun",
249    crate::config::constants::models::stepfun::DEFAULT_MODEL,
250    crate::config::constants::urls::STEPFUN_API_BASE,
251    Some(crate::config::constants::env_vars::STEPFUN_BASE_URL),
252    ()
253);
254define_provider_config!(
255    /// Provider configuration for Evolink.
256    EvolinkProviderConfig,
257    "evolink",
258    "Evolink",
259    crate::config::constants::models::evolink::DEFAULT_MODEL,
260    crate::config::constants::urls::EVOLINK_API_BASE,
261    Some(crate::config::constants::env_vars::EVOLINK_BASE_URL),
262    ()
263);
264define_provider_config!(
265    /// Provider configuration for xAI (Grok).
266    XAIProviderConfig,
267    "xai",
268    "xAI",
269    crate::config::constants::models::xai::DEFAULT_MODEL,
270    crate::config::constants::urls::XAI_API_BASE,
271    Some(crate::config::constants::env_vars::XAI_BASE_URL),
272    ()
273);
274define_provider_config!(
275    /// Provider configuration for Poolside.
276    PoolsideProviderConfig,
277    "poolside",
278    "Poolside",
279    crate::config::constants::models::poolside::DEFAULT_MODEL,
280    crate::config::constants::urls::POOLSIDE_API_BASE,
281    Some(crate::config::constants::env_vars::POOLSIDE_BASE_URL),
282    ()
283);
284define_provider_config!(
285    /// Provider configuration for the Vercel AI Gateway.
286    VercelProviderConfig,
287    "vercel",
288    "Vercel AI Gateway",
289    crate::config::constants::models::vercel::DEFAULT_MODEL,
290    crate::config::constants::urls::VERCEL_AI_GATEWAY_API_BASE,
291    Some(crate::config::constants::env_vars::VERCEL_AI_GATEWAY_BASE_URL),
292    ()
293);
294define_provider_config!(
295    /// Provider configuration for NVIDIA NIM.
296    NvidiaProviderConfig,
297    "nvidia",
298    "NVIDIA",
299    crate::config::constants::models::nvidia::DEFAULT_MODEL,
300    crate::config::constants::urls::NVIDIA_API_BASE,
301    Some(crate::config::constants::env_vars::NVIDIA_BASE_URL),
302    ()
303);
304define_provider_config!(
305    /// Provider configuration for Merge Gateway.
306    MergeGatewayProviderConfig,
307    "merge-gateway",
308    "Merge Gateway",
309    crate::config::constants::models::merge_gateway::DEFAULT_MODEL,
310    crate::config::constants::urls::MERGE_GATEWAY_NATIVE_API_BASE,
311    Some(crate::config::constants::env_vars::MERGE_GATEWAY_BASE_URL),
312    ()
313);
314
315/// Macro kept for source compatibility with older builder-based call sites.
316#[macro_export]
317macro_rules! create_provider_builder {
318    ($config_type:ty) => {
319        $crate::llm::provider_builder::ProviderBuilder::<$config_type>::new()
320    };
321}
322
323fn non_empty(value: Option<String>) -> Option<String> {
324    value.and_then(|value| {
325        let trimmed = value.trim();
326        if trimmed.is_empty() {
327            None
328        } else if trimmed.len() == value.len() {
329            Some(value)
330        } else {
331            Some(trimmed.to_string())
332        }
333    })
334}
335
336/// Compatibility shim that forwards legacy builder/config callers into the
337/// canonical provider factory.
338pub fn create_provider_unified(
339    provider_name: &str,
340    api_key: Option<String>,
341    model: Option<String>,
342    base_url: Option<String>,
343    prompt_cache: Option<PromptCachingConfig>,
344    timeouts: Option<TimeoutsConfig>,
345) -> Result<Box<dyn LLMProvider>, LLMError> {
346    factory::create_provider_with_config(
347        provider_name,
348        FactoryProviderConfig {
349            api_key: non_empty(api_key),
350            openai_chatgpt_auth: None,
351            copilot_auth: None,
352            base_url: non_empty(base_url),
353            model: non_empty(model),
354            prompt_cache,
355            timeouts,
356            openai: None,
357            anthropic: None,
358            model_behavior: None,
359            workspace_root: None,
360        },
361    )
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367    use crate::llm::provider_builder::ProviderBuilder;
368
369    #[test]
370    fn unified_provider_creation_supports_huggingface_through_factory() {
371        let provider = create_provider_unified(
372            "huggingface",
373            Some("test-key".to_string()),
374            Some("openai/gpt-oss-20b".to_string()),
375            None,
376            None,
377            None,
378        )
379        .expect("shim should route through the factory");
380
381        assert_eq!(provider.name(), "huggingface");
382    }
383
384    #[test]
385    fn builder_shim_routes_through_factory() {
386        let provider = ProviderBuilder::<OpenAIProviderConfig>::new()
387            .api_key("test-key".to_string())
388            .model(crate::config::constants::models::openai::DEFAULT_MODEL.to_string())
389            .try_build()
390            .expect("builder shim should resolve via the factory");
391
392        assert_eq!(provider.name(), "openai");
393    }
394
395    #[test]
396    fn legacy_provider_config_create_provider_routes_through_factory() {
397        let provider = <OpenAIProviderConfig as crate::llm::provider_builder::ProviderConfig>::create_provider(
398            "test-key".to_string(),
399            crate::config::constants::models::openai::DEFAULT_MODEL.to_string(),
400            crate::config::constants::urls::OPENAI_API_BASE.to_string(),
401            false,
402            (),
403            TimeoutsConfig::default(),
404        );
405
406        assert_eq!(provider.name(), "openai");
407    }
408
409    #[test]
410    fn unified_provider_creation_matches_factory_behavior() {
411        let shim = create_provider_unified(
412            "ollama",
413            None,
414            Some("gpt-oss:20b".to_string()),
415            Some("http://localhost:11434".to_string()),
416            None,
417            Some(TimeoutsConfig::default()),
418        )
419        .expect("shim provider should build");
420
421        let factory = factory::create_provider_with_config(
422            "ollama",
423            FactoryProviderConfig {
424                api_key: None,
425                openai_chatgpt_auth: None,
426                copilot_auth: None,
427                base_url: Some("http://localhost:11434".to_string()),
428                model: Some("gpt-oss:20b".to_string()),
429                prompt_cache: None,
430                timeouts: Some(TimeoutsConfig::default()),
431                openai: None,
432                anthropic: None,
433                model_behavior: None,
434                workspace_root: None,
435            },
436        )
437        .expect("factory provider should build");
438
439        assert_eq!(shim.name(), factory.name());
440        assert_eq!(shim.supported_models(), factory.supported_models());
441    }
442}