Skip to main content

codetether_agent/provider/
init_config.rs

1//! Build a [`ProviderRegistry`](super::ProviderRegistry) from the app [`Config`].
2
3use super::anthropic;
4use super::bedrock;
5use super::fallback_policy;
6use super::google;
7use super::openai;
8use super::registry::ProviderRegistry;
9use anyhow::Result;
10use std::sync::Arc;
11
12impl ProviderRegistry {
13    /// Initialize with default providers from the TOML config file.
14    ///
15    /// Reads `[providers.<name>]` sections and common `*_API_KEY` env vars.
16    ///
17    /// # Examples
18    ///
19    /// ```rust,no_run
20    /// use codetether_agent::provider::ProviderRegistry;
21    /// # async fn demo(cfg: &codetether_agent::config::Config) {
22    /// let registry = ProviderRegistry::from_config(cfg).await.unwrap();
23    /// # }
24    /// ```
25    pub async fn from_config(config: &crate::config::Config) -> Result<Self> {
26        let mut registry = Self::new();
27        let disable_env = fallback_policy::env_fallback_disabled();
28
29        // ---- OpenAI ----
30        if let Some(pc) = config.providers.get("openai") {
31            if let Some(key) = &pc.api_key {
32                registry.register(Arc::new(openai::OpenAIProvider::new(key.clone())?));
33            }
34        } else if !disable_env && let Ok(key) = std::env::var("OPENAI_API_KEY") {
35            registry.register(Arc::new(openai::OpenAIProvider::new(key)?));
36        }
37
38        // ---- Anthropic ----
39        if let Some(pc) = config.providers.get("anthropic") {
40            if let Some(key) = &pc.api_key {
41                let p = if let Some(url) = pc.base_url.clone() {
42                    anthropic::AnthropicProvider::with_base_url(key.clone(), url, "anthropic")?
43                } else {
44                    anthropic::AnthropicProvider::new(key.clone())?
45                };
46                registry.register(Arc::new(p));
47            }
48        } else if !disable_env && let Ok(key) = std::env::var("ANTHROPIC_API_KEY") {
49            registry.register(Arc::new(anthropic::AnthropicProvider::new(key)?));
50        }
51
52        // ---- Google ----
53        if let Some(pc) = config.providers.get("google") {
54            if let Some(key) = &pc.api_key {
55                registry.register(Arc::new(google::GoogleProvider::new(key.clone())?));
56            }
57        } else if !disable_env && let Ok(key) = std::env::var("GOOGLE_API_KEY") {
58            registry.register(Arc::new(google::GoogleProvider::new(key)?));
59        }
60
61        // ---- Novita (OpenAI-compatible) ----
62        if let Some(pc) = config.providers.get("novita")
63            && let Some(key) = &pc.api_key
64        {
65            let url = pc
66                .base_url
67                .clone()
68                .unwrap_or_else(|| "https://api.novita.ai/openai/v1".into());
69            registry.register(Arc::new(openai::OpenAIProvider::with_base_url(
70                key.clone(),
71                url,
72                "novita",
73            )?));
74        }
75
76        // ---- Bedrock (auto-detect from env / ~/.aws) ----
77        if !disable_env && let Some(creds) = bedrock::AwsCredentials::from_environment() {
78            let region = bedrock::AwsCredentials::detect_region()
79                .unwrap_or_else(|| bedrock::DEFAULT_REGION.to_string());
80            match bedrock::BedrockProvider::with_credentials(creds, region) {
81                Ok(p) => registry.register(Arc::new(p)),
82                Err(e) => tracing::warn!("Failed to init bedrock from AWS credentials: {}", e),
83            }
84        }
85
86        // ---- Env-var fallback ----
87        if !disable_env {
88            super::init_env::register_env_fallbacks(&mut registry);
89        } else {
90            tracing::info!(
91                env = fallback_policy::DISABLE_ENV_FALLBACK,
92                "Env/AWS fallback disabled"
93            );
94        }
95
96        Ok(registry)
97    }
98}