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 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), max_retries: 2,
33 log_level: LogLevel::Warn,
34 auth_method: AuthMethod::Anthropic,
35 }
36 }
37
38 pub fn from_env() -> Result<Self> {
40 dotenv().ok(); 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 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 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 _ => {} }
74 }
75
76 Ok(config)
77 }
78
79 pub fn with_timeout(mut self, timeout: Duration) -> Self {
81 self.timeout = timeout;
82 self
83 }
84
85 pub fn with_max_retries(mut self, max_retries: u32) -> Self {
87 self.max_retries = max_retries;
88 self
89 }
90
91 pub fn with_log_level(mut self, log_level: LogLevel) -> Self {
93 self.log_level = log_level;
94 self
95 }
96
97 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 pub fn with_auth_method(mut self, auth_method: AuthMethod) -> Self {
105 self.auth_method = auth_method;
106 self
107 }
108
109 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 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