Skip to main content

agentik_sdk/
config.rs

1use crate::http::auth::AuthMethod;
2use crate::types::errors::{AnthropicError, Result};
3use dotenvy::dotenv;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
7pub struct ClientConfig {
8    pub api_key: String,
9    pub base_url: String,
10    pub timeout: Duration,
11    pub max_retries: u32,
12    pub log_level: LogLevel,
13    pub auth_method: AuthMethod,
14}
15
16#[derive(Debug, Clone)]
17pub enum LogLevel {
18    Error,
19    Warn,
20    Info,
21    Debug,
22    Off,
23}
24
25impl ClientConfig {
26    /// Create a new client configuration with the provided API key
27    pub fn new(api_key: impl Into<String>) -> Self {
28        Self {
29            api_key: api_key.into(),
30            base_url: "https://api.anthropic.com".to_string(),
31            timeout: Duration::from_secs(600), // 10 minutes default
32            max_retries: 2,
33            log_level: LogLevel::Warn,
34            auth_method: AuthMethod::Anthropic,
35        }
36    }
37
38    /// Create a client configuration from environment variables
39    pub fn from_env() -> Result<Self> {
40        dotenv().ok(); // Load .env file if present
41
42        let api_key =
43            std::env::var("ANTHROPIC_API_KEY").map_err(|_| AnthropicError::Configuration {
44                message: "ANTHROPIC_API_KEY environment variable not set".to_string(),
45            })?;
46
47        let mut config = Self::new(api_key);
48
49        // Optional environment variable overrides
50        if let Ok(base_url) = std::env::var("ANTHROPIC_BASE_URL") {
51            config.base_url = base_url;
52        }
53
54        if let Ok(timeout_str) = std::env::var("ANTHROPIC_TIMEOUT") {
55            if let Ok(timeout_secs) = timeout_str.parse::<u64>() {
56                config.timeout = Duration::from_secs(timeout_secs);
57            }
58        }
59
60        if let Ok(retries_str) = std::env::var("ANTHROPIC_MAX_RETRIES") {
61            if let Ok(retries) = retries_str.parse::<u32>() {
62                config.max_retries = retries;
63            }
64        }
65
66        // Check for authentication method override
67        if let Ok(auth_method) = std::env::var("ANTHROPIC_AUTH_METHOD") {
68            match auth_method.to_lowercase().as_str() {
69                "bearer" => config.auth_method = AuthMethod::Bearer,
70                "token" => config.auth_method = AuthMethod::Token,
71                "anthropic" => config.auth_method = AuthMethod::Anthropic,
72                _ => {} // Keep default
73            }
74        }
75
76        Ok(config)
77    }
78
79    /// Set the request timeout
80    pub fn with_timeout(mut self, timeout: Duration) -> Self {
81        self.timeout = timeout;
82        self
83    }
84
85    /// Set the maximum number of retries
86    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
87        self.max_retries = max_retries;
88        self
89    }
90
91    /// Set the log level
92    pub fn with_log_level(mut self, log_level: LogLevel) -> Self {
93        self.log_level = log_level;
94        self
95    }
96
97    /// Set the base URL
98    pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
99        self.base_url = base_url.into();
100        self
101    }
102
103    /// Set the authentication method
104    pub fn with_auth_method(mut self, auth_method: AuthMethod) -> Self {
105        self.auth_method = auth_method;
106        self
107    }
108
109    /// Configure for custom gateway (Bearer token + base URL)
110    pub fn for_custom_gateway(mut self, base_url: impl Into<String>) -> Self {
111        self.base_url = base_url.into();
112        self.auth_method = AuthMethod::Bearer;
113        self
114    }
115
116    /// Validate the configuration
117    pub fn validate(&self) -> Result<()> {
118        if self.api_key.is_empty() {
119            return Err(AnthropicError::Configuration {
120                message: "API key cannot be empty".to_string(),
121            });
122        }
123
124        if self.base_url.is_empty() {
125            return Err(AnthropicError::Configuration {
126                message: "Base URL cannot be empty".to_string(),
127            });
128        }
129
130        if !self.base_url.starts_with("http://") && !self.base_url.starts_with("https://") {
131            return Err(AnthropicError::Configuration {
132                message: "Base URL must start with http:// or https://".to_string(),
133            });
134        }
135
136        Ok(())
137    }
138}
139