1use std::sync::Arc;
31use std::time::Duration;
32
33use secrecy::SecretString;
34
35use crate::auth::CredentialProvider;
36#[cfg(any(feature = "native-http", feature = "wasm-http"))]
37use crate::error::Result;
38use crate::http::transport::TransportConfig;
39#[cfg(feature = "tower")]
40use crate::tower::{BudgetConfig, CacheConfig, CacheStore, InFlightLimitConfig, LlmHook, RateLimitConfig};
41
42pub struct NoApiKey;
44pub struct WithApiKey;
46pub struct NoProvider;
48pub struct WithProvider;
50
51#[must_use = "call .build() to construct the client"]
86pub struct ClientBuilder<K = NoApiKey, P = NoProvider> {
87 api_key: SecretString,
88 provider_hint: String,
89 base_url: Option<String>,
90 timeout: Duration,
91 max_retries: u32,
92 max_response_bytes: Option<usize>,
93 transport: TransportConfig,
94 load_env: bool,
95 credential_provider: Option<Arc<dyn CredentialProvider>>,
96 #[cfg(feature = "tower")]
97 cache_config: Option<CacheConfig>,
98 #[cfg(feature = "tower")]
99 cache_store: Option<Arc<dyn CacheStore>>,
100 #[cfg(feature = "tower")]
101 budget_config: Option<BudgetConfig>,
102 #[cfg(feature = "tower")]
103 hooks: Vec<Arc<dyn LlmHook>>,
104 #[cfg(feature = "tower")]
105 cooldown_duration: Option<Duration>,
106 #[cfg(feature = "tower")]
107 rate_limit_config: Option<RateLimitConfig>,
108 #[cfg(feature = "tower")]
109 in_flight_limit_config: Option<InFlightLimitConfig>,
110 #[cfg(feature = "tower")]
111 health_check_interval: Option<Duration>,
112 #[cfg(feature = "tower")]
113 enable_cost_tracking: bool,
114 #[cfg(feature = "tower")]
115 enable_tracing: bool,
116 _key_state: std::marker::PhantomData<K>,
117 _provider_state: std::marker::PhantomData<P>,
118}
119
120impl ClientBuilder<NoApiKey, NoProvider> {
121 pub fn new() -> Self {
126 Self {
127 api_key: SecretString::from(String::new()),
128 provider_hint: String::new(),
129 base_url: None,
130 timeout: Duration::from_secs(60),
131 max_retries: 3,
132 max_response_bytes: None,
133 transport: TransportConfig::default(),
134 load_env: false,
135 credential_provider: None,
136 #[cfg(feature = "tower")]
137 cache_config: None,
138 #[cfg(feature = "tower")]
139 cache_store: None,
140 #[cfg(feature = "tower")]
141 budget_config: None,
142 #[cfg(feature = "tower")]
143 hooks: Vec::new(),
144 #[cfg(feature = "tower")]
145 cooldown_duration: None,
146 #[cfg(feature = "tower")]
147 rate_limit_config: None,
148 #[cfg(feature = "tower")]
149 in_flight_limit_config: None,
150 #[cfg(feature = "tower")]
151 health_check_interval: None,
152 #[cfg(feature = "tower")]
153 enable_cost_tracking: false,
154 #[cfg(feature = "tower")]
155 enable_tracing: false,
156 _key_state: std::marker::PhantomData,
157 _provider_state: std::marker::PhantomData,
158 }
159 }
160}
161
162impl Default for ClientBuilder<NoApiKey, NoProvider> {
163 fn default() -> Self {
164 Self::new()
165 }
166}
167
168impl<K, P> ClientBuilder<K, P> {
169 pub fn api_key(self, key: impl Into<String>) -> ClientBuilder<WithApiKey, P> {
177 ClientBuilder {
178 api_key: SecretString::from(key.into()),
179 provider_hint: self.provider_hint,
180 base_url: self.base_url,
181 timeout: self.timeout,
182 max_retries: self.max_retries,
183 max_response_bytes: self.max_response_bytes,
184 transport: self.transport,
185 load_env: self.load_env,
186 credential_provider: self.credential_provider,
187 #[cfg(feature = "tower")]
188 cache_config: self.cache_config,
189 #[cfg(feature = "tower")]
190 cache_store: self.cache_store,
191 #[cfg(feature = "tower")]
192 budget_config: self.budget_config,
193 #[cfg(feature = "tower")]
194 hooks: self.hooks,
195 #[cfg(feature = "tower")]
196 cooldown_duration: self.cooldown_duration,
197 #[cfg(feature = "tower")]
198 rate_limit_config: self.rate_limit_config,
199 #[cfg(feature = "tower")]
200 in_flight_limit_config: self.in_flight_limit_config,
201 #[cfg(feature = "tower")]
202 health_check_interval: self.health_check_interval,
203 #[cfg(feature = "tower")]
204 enable_cost_tracking: self.enable_cost_tracking,
205 #[cfg(feature = "tower")]
206 enable_tracing: self.enable_tracing,
207 _key_state: std::marker::PhantomData,
208 _provider_state: self._provider_state,
209 }
210 }
211
212 pub fn provider(self, provider_name: impl Into<String>) -> ClientBuilder<K, WithProvider> {
222 ClientBuilder {
223 api_key: self.api_key,
224 provider_hint: provider_name.into(),
225 base_url: self.base_url,
226 timeout: self.timeout,
227 max_retries: self.max_retries,
228 max_response_bytes: self.max_response_bytes,
229 transport: self.transport,
230 load_env: self.load_env,
231 credential_provider: self.credential_provider,
232 #[cfg(feature = "tower")]
233 cache_config: self.cache_config,
234 #[cfg(feature = "tower")]
235 cache_store: self.cache_store,
236 #[cfg(feature = "tower")]
237 budget_config: self.budget_config,
238 #[cfg(feature = "tower")]
239 hooks: self.hooks,
240 #[cfg(feature = "tower")]
241 cooldown_duration: self.cooldown_duration,
242 #[cfg(feature = "tower")]
243 rate_limit_config: self.rate_limit_config,
244 #[cfg(feature = "tower")]
245 in_flight_limit_config: self.in_flight_limit_config,
246 #[cfg(feature = "tower")]
247 health_check_interval: self.health_check_interval,
248 #[cfg(feature = "tower")]
249 enable_cost_tracking: self.enable_cost_tracking,
250 #[cfg(feature = "tower")]
251 enable_tracing: self.enable_tracing,
252 _key_state: self._key_state,
253 _provider_state: std::marker::PhantomData,
254 }
255 }
256
257 pub fn base_url(mut self, url: impl Into<String>) -> Self {
262 let url = url.into();
263 self.base_url = Some(url.trim_end_matches('/').to_string());
264 self
265 }
266
267 pub fn timeout(mut self, timeout: Duration) -> Self {
269 self.timeout = timeout;
270 self
271 }
272
273 pub fn max_retries(mut self, retries: u32) -> Self {
275 self.max_retries = retries;
276 self
277 }
278
279 #[cfg(all(feature = "native-http", not(target_arch = "wasm32")))]
284 pub fn max_response_bytes(mut self, limit: usize) -> Result<Self> {
285 super::config::validate_response_limit(Some(limit))?;
286 self.max_response_bytes = Some(limit);
287 Ok(self)
288 }
289
290 pub fn transport(mut self, config: TransportConfig) -> Self {
292 self.transport = config;
293 self
294 }
295
296 pub fn load_env(mut self, enabled: bool) -> Self {
305 self.load_env = enabled;
306 self
307 }
308
309 pub fn credential_provider(mut self, provider: Arc<dyn CredentialProvider>) -> Self {
314 self.credential_provider = Some(provider);
315 self
316 }
317
318 #[cfg(feature = "tower")]
320 pub fn cache(mut self, config: CacheConfig) -> Self {
321 self.cache_config = Some(config);
322 self
323 }
324
325 #[cfg(feature = "tower")]
327 pub fn cache_store(mut self, store: Arc<dyn CacheStore>) -> Self {
328 self.cache_store = Some(store);
329 self
330 }
331
332 #[cfg(feature = "tower")]
334 pub fn budget(mut self, config: BudgetConfig) -> Self {
335 self.budget_config = Some(config);
336 self
337 }
338
339 #[cfg(feature = "tower")]
341 pub fn hook(mut self, hook: Arc<dyn LlmHook>) -> Self {
342 self.hooks.push(hook);
343 self
344 }
345
346 #[cfg(feature = "tower")]
348 pub fn hooks(mut self, hooks: Vec<Arc<dyn LlmHook>>) -> Self {
349 self.hooks = hooks;
350 self
351 }
352
353 #[cfg(feature = "tower")]
355 pub fn cooldown(mut self, duration: Duration) -> Self {
356 self.cooldown_duration = Some(duration);
357 self
358 }
359
360 #[cfg(feature = "tower")]
362 pub fn rate_limit(mut self, config: RateLimitConfig) -> Self {
363 self.rate_limit_config = Some(config);
364 self
365 }
366
367 #[cfg(feature = "tower")]
369 pub fn in_flight_limit(mut self, config: InFlightLimitConfig) -> Self {
370 self.in_flight_limit_config = Some(config);
371 self
372 }
373
374 #[cfg(feature = "tower")]
376 pub fn health_check(mut self, interval: Duration) -> Self {
377 self.health_check_interval = Some(interval);
378 self
379 }
380
381 #[cfg(feature = "tower")]
383 pub fn cost_tracking(mut self, enabled: bool) -> Self {
384 self.enable_cost_tracking = enabled;
385 self
386 }
387
388 #[cfg(feature = "tower")]
390 pub fn tracing(mut self, enabled: bool) -> Self {
391 self.enable_tracing = enabled;
392 self
393 }
394}
395
396#[cfg(any(feature = "native-http", feature = "wasm-http"))]
397impl ClientBuilder<WithApiKey, WithProvider> {
398 pub fn build(self) -> Result<super::DefaultClient> {
413 use super::config::ClientConfig;
414
415 let config = ClientConfig {
416 api_key: self.api_key,
417 base_url: self.base_url,
418 timeout: self.timeout,
419 max_retries: self.max_retries,
420 max_response_bytes: self.max_response_bytes,
421 extra_headers: Vec::new(),
422 credential_provider: self.credential_provider,
423 load_env: self.load_env,
424 transport: self.transport,
425 bedrock_region: None,
426 bedrock_cross_region_prefix: None,
427 bedrock_access_key_id: None,
428 bedrock_secret_access_key: None,
429 bedrock_session_token: None,
430 #[cfg(feature = "tower")]
431 cache_config: self.cache_config,
432 #[cfg(feature = "tower")]
433 cache_store: self.cache_store,
434 #[cfg(feature = "tower")]
435 budget_config: self.budget_config,
436 #[cfg(feature = "tower")]
437 hooks: self.hooks,
438 #[cfg(feature = "tower")]
439 cooldown_duration: self.cooldown_duration,
440 #[cfg(feature = "tower")]
441 rate_limit_config: self.rate_limit_config,
442 #[cfg(feature = "tower")]
443 in_flight_limit_config: self.in_flight_limit_config,
444 #[cfg(feature = "tower")]
445 health_check_interval: self.health_check_interval,
446 #[cfg(feature = "tower")]
447 enable_cost_tracking: self.enable_cost_tracking,
448 #[cfg(feature = "tower")]
449 enable_tracing: self.enable_tracing,
450 };
451
452 let hint = if self.provider_hint.is_empty() {
453 None
454 } else {
455 Some(self.provider_hint.as_str())
456 };
457
458 super::DefaultClient::new(config, hint)
459 }
460}