ai_lib/
config.rs

1use std::time::Duration;
2use crate::circuit_breaker::CircuitBreakerConfig;
3use crate::rate_limiter::RateLimiterConfig;
4use crate::error_handling::ErrorThresholds;
5
6/// Minimal explicit connection/configuration options.
7///
8/// Library users can pass an instance of this struct to `AiClient::with_options` to
9/// explicitly control base URL, proxy, API key and timeout without relying exclusively
10/// on environment variables. Any field left as `None` will fall back to existing
11/// environment variable behavior or library defaults.
12#[derive(Clone, Debug, Default)]
13pub struct ConnectionOptions {
14    pub base_url: Option<String>,
15    pub proxy: Option<String>,
16    pub api_key: Option<String>,
17    pub timeout: Option<Duration>,
18    pub disable_proxy: bool,
19}
20
21// Default derived above
22
23impl ConnectionOptions {
24    /// Hydrate unset fields from environment variables (lightweight fallback logic).
25    ///
26    /// `provider_env_prefix` may be something like `OPENAI`, `GROQ`, etc., used to look up
27    /// a provider specific API key prior to the generic fallback `AI_API_KEY`.
28    pub fn hydrate_with_env(mut self, provider_env_prefix: &str) -> Self {
29        // API key precedence: explicit > <PROVIDER>_API_KEY > AI_API_KEY
30        if self.api_key.is_none() {
31            let specific = format!("{}_API_KEY", provider_env_prefix);
32            self.api_key = std::env::var(&specific)
33                .ok()
34                .or_else(|| std::env::var("AI_API_KEY").ok());
35        }
36        // Base URL precedence: explicit > AI_BASE_URL (generic) > leave None (caller/adapter handles default)
37        if self.base_url.is_none() {
38            if let Ok(v) = std::env::var("AI_BASE_URL") {
39                self.base_url = Some(v);
40            }
41        }
42        // Proxy precedence: explicit > AI_PROXY_URL
43        if self.proxy.is_none() && !self.disable_proxy {
44            self.proxy = std::env::var("AI_PROXY_URL").ok();
45        }
46        // Timeout precedence: explicit > AI_TIMEOUT_SECS > default handled by caller
47        if self.timeout.is_none() {
48            if let Ok(v) = std::env::var("AI_TIMEOUT_SECS") {
49                if let Ok(secs) = v.parse::<u64>() {
50                    self.timeout = Some(Duration::from_secs(secs));
51                }
52            }
53        }
54        self
55    }
56}
57
58/// Resilience configuration for advanced error handling and rate limiting
59#[derive(Debug, Clone, Default)]
60pub struct ResilienceConfig {
61    pub circuit_breaker: Option<CircuitBreakerConfig>,
62    pub rate_limiter: Option<RateLimiterConfig>,
63    pub backpressure: Option<BackpressureConfig>,
64    pub error_handling: Option<ErrorHandlingConfig>,
65}
66
67/// Backpressure configuration
68#[derive(Debug, Clone)]
69pub struct BackpressureConfig {
70    pub max_concurrent_requests: usize,
71}
72
73/// Error handling configuration
74#[derive(Debug, Clone)]
75pub struct ErrorHandlingConfig {
76    pub enable_recovery: bool,
77    pub enable_monitoring: bool,
78    pub error_thresholds: ErrorThresholds,
79}
80
81// Default derived above
82
83impl Default for BackpressureConfig {
84    fn default() -> Self {
85        Self {
86            max_concurrent_requests: 100,
87        }
88    }
89}
90
91impl Default for ErrorHandlingConfig {
92    fn default() -> Self {
93        Self {
94            enable_recovery: true,
95            enable_monitoring: true,
96            error_thresholds: ErrorThresholds::default(),
97        }
98    }
99}
100
101impl ResilienceConfig {
102    /// Create smart defaults for production use
103    pub fn smart_defaults() -> Self {
104        Self {
105            circuit_breaker: Some(CircuitBreakerConfig::default()),
106            rate_limiter: Some(RateLimiterConfig::default()),
107            backpressure: Some(BackpressureConfig::default()),
108            error_handling: Some(ErrorHandlingConfig::default()),
109        }
110    }
111
112    /// Create production-ready configuration
113    pub fn production() -> Self {
114        Self {
115            circuit_breaker: Some(CircuitBreakerConfig::production()),
116            rate_limiter: Some(RateLimiterConfig::production()),
117            backpressure: Some(BackpressureConfig {
118                max_concurrent_requests: 50,
119            }),
120            error_handling: Some(ErrorHandlingConfig {
121                enable_recovery: true,
122                enable_monitoring: true,
123                error_thresholds: ErrorThresholds {
124                    error_rate_threshold: 0.05, // 5% error rate
125                    consecutive_errors: 3,
126                    time_window: Duration::from_secs(30),
127                },
128            }),
129        }
130    }
131
132    /// Create development configuration
133    pub fn development() -> Self {
134        Self {
135            circuit_breaker: Some(CircuitBreakerConfig::development()),
136            rate_limiter: Some(RateLimiterConfig::development()),
137            backpressure: Some(BackpressureConfig {
138                max_concurrent_requests: 200,
139            }),
140            error_handling: Some(ErrorHandlingConfig {
141                enable_recovery: false,
142                enable_monitoring: false,
143                error_thresholds: ErrorThresholds::default(),
144            }),
145        }
146    }
147}