litellm_rs/core/traits/provider/config.rs
1//! Provider configuration trait
2//!
3//! Defines the configuration interface for all AI providers
4
5use std::fmt::Debug;
6
7use crate::core::net::ProviderEndpointAccess;
8
9/// Provider configuration trait
10///
11/// All provider configurations must implement this trait to ensure consistent
12/// validation and access to common settings.
13///
14/// # Design Principles
15/// - Type-safe configuration management
16/// - Validation before provider initialization
17/// - Common settings abstraction (API key, timeout, retries)
18/// - Clone and Debug for testability
19///
20/// # Example
21/// ```rust
22/// use std::time::Duration;
23/// use litellm_rs::core::traits::provider::ProviderConfig;
24///
25/// #[derive(Debug, Clone)]
26/// struct MyProviderConfig {
27/// api_key: String,
28/// api_base: Option<String>,
29/// timeout_secs: u64,
30/// max_retries: u32,
31/// }
32///
33/// impl ProviderConfig for MyProviderConfig {
34/// fn validate(&self) -> Result<(), String> {
35/// if self.api_key.is_empty() {
36/// return Err("API key is required".to_string());
37/// }
38/// Ok(())
39/// }
40///
41/// fn api_key(&self) -> Option<&str> {
42/// Some(&self.api_key)
43/// }
44///
45/// fn api_base(&self) -> Option<&str> {
46/// self.api_base.as_deref()
47/// }
48///
49/// fn timeout(&self) -> Duration {
50/// Duration::from_secs(self.timeout_secs)
51/// }
52///
53/// fn max_retries(&self) -> u32 {
54/// self.max_retries
55/// }
56/// }
57/// ```
58pub trait ProviderConfig: Send + Sync + Clone + Debug + 'static {
59 /// Validate configuration
60 ///
61 /// # Returns
62 /// `Ok(())` if configuration is valid, `Err(String)` with error message otherwise
63 ///
64 /// # Implementation Notes
65 /// - Check required fields are present
66 /// - Validate field formats (e.g., URL format)
67 /// - Verify value ranges are acceptable
68 /// - Called before provider initialization
69 fn validate(&self) -> Result<(), String>;
70
71 /// Get API key
72 ///
73 /// # Returns
74 /// Optional API key for authentication with the provider
75 ///
76 /// # Note
77 /// Returns `None` if the provider doesn't require an API key
78 fn api_key(&self) -> Option<&str>;
79
80 /// Get API base URL
81 ///
82 /// # Returns
83 /// Optional base URL for the provider API
84 ///
85 /// # Use Cases
86 /// - Custom API endpoints (e.g., Azure OpenAI)
87 /// - Self-hosted models
88 /// - Proxy configurations
89 /// - Development/testing environments
90 fn api_base(&self) -> Option<&str>;
91
92 /// Get request timeout
93 ///
94 /// # Returns
95 /// Maximum duration to wait for a request to complete
96 ///
97 /// # Implementation Notes
98 /// - Should include both connection and read timeouts
99 /// - Typical values: 30-120 seconds for chat completion
100 /// - Consider longer timeouts for streaming requests
101 fn timeout(&self) -> std::time::Duration;
102
103 /// Get maximum retry attempts
104 ///
105 /// # Returns
106 /// Number of times to retry failed requests
107 ///
108 /// # Implementation Notes
109 /// - Retries should use exponential backoff
110 /// - Typical values: 2-5 retries
111 /// - Consider rate limits when setting this value
112 fn max_retries(&self) -> u32;
113
114 /// Network scope allowed for this provider's configured endpoint.
115 fn endpoint_access(&self) -> ProviderEndpointAccess {
116 ProviderEndpointAccess::PublicOnly
117 }
118
119 /// Whether this provider requires an SSRF-safe HTTP client.
120 ///
121 /// Return `true` for providers whose endpoint URL is user-controlled.
122 /// The SSRF-safe client re-validates the resolved IP on every request,
123 /// preventing DNS-rebinding attacks.
124 fn use_ssrf_safe_client(&self) -> bool {
125 false
126 }
127
128 /// Standard validation: API key required, timeout > 0, max_retries <= 10.
129 ///
130 /// Call from `validate()` to avoid repeating common checks.
131 /// Providers with optional API keys or custom fields should implement
132 /// `validate()` directly instead.
133 fn validate_standard(&self, provider_name: &str) -> Result<(), String> {
134 if self.api_key().is_none_or(|k| k.is_empty()) {
135 return Err(format!("{} API key is required", provider_name));
136 }
137 if self.timeout().as_secs() == 0 {
138 return Err("Timeout must be greater than 0".to_string());
139 }
140 if self.max_retries() > 10 {
141 return Err("Max retries should not exceed 10".to_string());
142 }
143 Ok(())
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::ProviderConfig;
150 use crate::core::net::ProviderEndpointAccess;
151 use crate::core::providers::mistral::MistralConfig;
152
153 #[test]
154 fn endpoint_access_defaults_to_public_only() {
155 assert_eq!(
156 MistralConfig::default().endpoint_access(),
157 ProviderEndpointAccess::PublicOnly
158 );
159 }
160}