1pub mod config;
2pub mod config_file;
3#[cfg(all(feature = "native-http", feature = "tower"))]
4pub mod managed;
5
6use std::future::Future;
7use std::pin::Pin;
8#[cfg(any(feature = "native-http", feature = "wasm-http"))]
9use std::sync::Arc;
10
11use futures_core::Stream;
12
13use crate::error::Result;
14use crate::types::audio::{CreateSpeechRequest, CreateTranscriptionRequest, TranscriptionResponse};
15use crate::types::batch::{BatchListQuery, BatchListResponse, BatchObject, CreateBatchRequest};
16use crate::types::files::{CreateFileRequest, DeleteResponse, FileListQuery, FileListResponse, FileObject};
17use crate::types::image::{CreateImageRequest, ImagesResponse};
18use crate::types::moderation::{ModerationRequest, ModerationResponse};
19use crate::types::ocr::{OcrRequest, OcrResponse};
20use crate::types::raw::{RawExchange, RawStreamExchange};
21use crate::types::rerank::{RerankRequest, RerankResponse};
22use crate::types::responses::{CreateResponseRequest, ResponseObject};
23use crate::types::search::{SearchRequest, SearchResponse};
24use crate::types::{
25 ChatCompletionChunk, ChatCompletionRequest, ChatCompletionResponse, EmbeddingRequest, EmbeddingResponse,
26 ModelsListResponse,
27};
28
29#[cfg(any(feature = "native-http", feature = "wasm-http"))]
31use crate::auth::Credential;
32#[cfg(any(feature = "native-http", feature = "wasm-http"))]
33use crate::error::LiterLlmError;
34#[cfg(any(feature = "native-http", feature = "wasm-http"))]
35use crate::http;
36#[cfg(any(feature = "native-http", feature = "wasm-http"))]
37use crate::provider::{self, OpenAiCompatibleProvider, OpenAiProvider, Provider};
38#[cfg(any(feature = "native-http", feature = "wasm-http"))]
39use secrecy::ExposeSecret;
40
41pub use config::{ClientConfig, ClientConfigBuilder};
42pub use config_file::FileConfig;
43
44#[cfg(not(target_arch = "wasm32"))]
46pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
47
48#[cfg(target_arch = "wasm32")]
50pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
51
52#[cfg(not(target_arch = "wasm32"))]
54pub type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + Send + 'a>>;
55
56#[cfg(target_arch = "wasm32")]
58pub type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + 'a>>;
59
60#[cfg(any(feature = "native-http", feature = "wasm-http"))]
74struct PreparedRequest {
75 url: String,
76 provider: Arc<dyn Provider>,
77 body_json: serde_json::Value,
78 body_bytes: bytes::Bytes,
79}
80
81#[cfg(any(feature = "native-http", feature = "wasm-http"))]
86fn str_pair(pair: &(String, String)) -> (&str, &str) {
87 (pair.0.as_str(), pair.1.as_str())
88}
89
90#[cfg(not(target_arch = "wasm32"))]
92pub trait LlmClient: Send + Sync {
93 fn chat(&self, req: ChatCompletionRequest) -> BoxFuture<'_, Result<ChatCompletionResponse>>;
95
96 fn chat_stream(
98 &self,
99 req: ChatCompletionRequest,
100 ) -> BoxFuture<'_, Result<BoxStream<'static, Result<ChatCompletionChunk>>>>;
101
102 fn embed(&self, req: EmbeddingRequest) -> BoxFuture<'_, Result<EmbeddingResponse>>;
104
105 fn list_models(&self) -> BoxFuture<'_, Result<ModelsListResponse>>;
107
108 fn image_generate(&self, req: CreateImageRequest) -> BoxFuture<'_, Result<ImagesResponse>>;
110
111 fn speech(&self, req: CreateSpeechRequest) -> BoxFuture<'_, Result<bytes::Bytes>>;
113
114 fn transcribe(&self, req: CreateTranscriptionRequest) -> BoxFuture<'_, Result<TranscriptionResponse>>;
116
117 fn moderate(&self, req: ModerationRequest) -> BoxFuture<'_, Result<ModerationResponse>>;
119
120 fn rerank(&self, req: RerankRequest) -> BoxFuture<'_, Result<RerankResponse>>;
122
123 fn search(&self, req: SearchRequest) -> BoxFuture<'_, Result<SearchResponse>>;
125
126 fn ocr(&self, req: OcrRequest) -> BoxFuture<'_, Result<OcrResponse>>;
128}
129
130#[cfg(target_arch = "wasm32")]
132pub trait LlmClient {
133 fn chat(&self, req: ChatCompletionRequest) -> BoxFuture<'_, Result<ChatCompletionResponse>>;
135
136 fn chat_stream(
138 &self,
139 req: ChatCompletionRequest,
140 ) -> BoxFuture<'_, Result<BoxStream<'static, Result<ChatCompletionChunk>>>>;
141
142 fn embed(&self, req: EmbeddingRequest) -> BoxFuture<'_, Result<EmbeddingResponse>>;
144
145 fn list_models(&self) -> BoxFuture<'_, Result<ModelsListResponse>>;
147
148 fn image_generate(&self, req: CreateImageRequest) -> BoxFuture<'_, Result<ImagesResponse>>;
150
151 fn speech(&self, req: CreateSpeechRequest) -> BoxFuture<'_, Result<bytes::Bytes>>;
153
154 fn transcribe(&self, req: CreateTranscriptionRequest) -> BoxFuture<'_, Result<TranscriptionResponse>>;
156
157 fn moderate(&self, req: ModerationRequest) -> BoxFuture<'_, Result<ModerationResponse>>;
159
160 fn rerank(&self, req: RerankRequest) -> BoxFuture<'_, Result<RerankResponse>>;
162
163 fn search(&self, req: SearchRequest) -> BoxFuture<'_, Result<SearchResponse>>;
165
166 fn ocr(&self, req: OcrRequest) -> BoxFuture<'_, Result<OcrResponse>>;
168}
169
170pub trait LlmClientRaw: LlmClient {
179 fn chat_raw(&self, req: ChatCompletionRequest) -> BoxFuture<'_, Result<RawExchange<ChatCompletionResponse>>>;
185
186 fn chat_stream_raw(
191 &self,
192 req: ChatCompletionRequest,
193 ) -> BoxFuture<'_, Result<RawStreamExchange<BoxStream<'static, Result<ChatCompletionChunk>>>>>;
194
195 fn embed_raw(&self, req: EmbeddingRequest) -> BoxFuture<'_, Result<RawExchange<EmbeddingResponse>>>;
197
198 fn image_generate_raw(&self, req: CreateImageRequest) -> BoxFuture<'_, Result<RawExchange<ImagesResponse>>>;
200
201 fn transcribe_raw(
203 &self,
204 req: CreateTranscriptionRequest,
205 ) -> BoxFuture<'_, Result<RawExchange<TranscriptionResponse>>>;
206
207 fn moderate_raw(&self, req: ModerationRequest) -> BoxFuture<'_, Result<RawExchange<ModerationResponse>>>;
209
210 fn rerank_raw(&self, req: RerankRequest) -> BoxFuture<'_, Result<RawExchange<RerankResponse>>>;
212
213 fn search_raw(&self, req: SearchRequest) -> BoxFuture<'_, Result<RawExchange<SearchResponse>>>;
215
216 fn ocr_raw(&self, req: OcrRequest) -> BoxFuture<'_, Result<RawExchange<OcrResponse>>>;
218}
219
220#[cfg(not(target_arch = "wasm32"))]
222pub trait FileClient: Send + Sync {
223 fn create_file(&self, req: CreateFileRequest) -> BoxFuture<'_, Result<FileObject>>;
225
226 fn retrieve_file(&self, file_id: &str) -> BoxFuture<'_, Result<FileObject>>;
228
229 fn delete_file(&self, file_id: &str) -> BoxFuture<'_, Result<DeleteResponse>>;
231
232 fn list_files(&self, query: Option<FileListQuery>) -> BoxFuture<'_, Result<FileListResponse>>;
234
235 fn file_content(&self, file_id: &str) -> BoxFuture<'_, Result<bytes::Bytes>>;
237}
238
239#[cfg(target_arch = "wasm32")]
241pub trait FileClient {
242 fn create_file(&self, req: CreateFileRequest) -> BoxFuture<'_, Result<FileObject>>;
244
245 fn retrieve_file(&self, file_id: &str) -> BoxFuture<'_, Result<FileObject>>;
247
248 fn delete_file(&self, file_id: &str) -> BoxFuture<'_, Result<DeleteResponse>>;
250
251 fn list_files(&self, query: Option<FileListQuery>) -> BoxFuture<'_, Result<FileListResponse>>;
253
254 fn file_content(&self, file_id: &str) -> BoxFuture<'_, Result<bytes::Bytes>>;
256}
257
258#[cfg(not(target_arch = "wasm32"))]
260pub trait BatchClient: Send + Sync {
261 fn create_batch(&self, req: CreateBatchRequest) -> BoxFuture<'_, Result<BatchObject>>;
263
264 fn retrieve_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>>;
266
267 fn list_batches(&self, query: Option<BatchListQuery>) -> BoxFuture<'_, Result<BatchListResponse>>;
269
270 fn cancel_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>>;
272}
273
274#[cfg(target_arch = "wasm32")]
276pub trait BatchClient {
277 fn create_batch(&self, req: CreateBatchRequest) -> BoxFuture<'_, Result<BatchObject>>;
279
280 fn retrieve_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>>;
282
283 fn list_batches(&self, query: Option<BatchListQuery>) -> BoxFuture<'_, Result<BatchListResponse>>;
285
286 fn cancel_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>>;
288}
289
290#[cfg(not(target_arch = "wasm32"))]
292pub trait ResponseClient: Send + Sync {
293 fn create_response(&self, req: CreateResponseRequest) -> BoxFuture<'_, Result<ResponseObject>>;
295
296 fn retrieve_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>>;
298
299 fn cancel_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>>;
301}
302
303#[cfg(target_arch = "wasm32")]
305pub trait ResponseClient {
306 fn create_response(&self, req: CreateResponseRequest) -> BoxFuture<'_, Result<ResponseObject>>;
308
309 fn retrieve_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>>;
311
312 fn cancel_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>>;
314}
315
316#[cfg(any(feature = "native-http", feature = "wasm-http"))]
330#[derive(Clone)]
331pub struct DefaultClient {
332 config: ClientConfig,
333 http: reqwest::Client,
334 provider: Arc<dyn Provider>,
337 cached_auth_header: Option<(String, String)>,
340 cached_extra_headers: Vec<(String, String)>,
343}
344
345#[cfg(any(feature = "native-http", feature = "wasm-http"))]
346impl DefaultClient {
347 pub fn new(mut config: ClientConfig, model_hint: Option<&str>) -> Result<Self> {
360 let provider = build_provider(&config, model_hint);
361 provider.validate()?;
364
365 #[cfg(not(target_arch = "wasm32"))]
369 if config.load_env
370 && config.api_key.expose_secret().is_empty()
371 && let Some(env_var_name) = provider.env_var()
372 {
373 match std::env::var(env_var_name) {
374 Ok(val) if !val.is_empty() => {
375 config.api_key = secrecy::SecretString::from(val);
376 }
377 _ => {
378 return Err(LiterLlmError::Authentication {
379 message: format!("no API key provided and environment variable {env_var_name} is not set"),
380 });
381 }
382 }
383 }
384
385 let mut header_map = reqwest::header::HeaderMap::new();
390 for (k, v) in config.headers() {
391 let name =
392 reqwest::header::HeaderName::from_bytes(k.as_bytes()).map_err(|_| LiterLlmError::InvalidHeader {
393 name: k.clone(),
394 reason: "pre-validated header name became invalid".into(),
395 })?;
396 let val = reqwest::header::HeaderValue::from_str(v).map_err(|_| LiterLlmError::InvalidHeader {
397 name: k.clone(),
398 reason: "pre-validated header value became invalid".into(),
399 })?;
400 header_map.insert(name, val);
401 }
402
403 let http = {
404 let builder = reqwest::Client::builder().default_headers(header_map);
405 #[cfg(not(target_arch = "wasm32"))]
408 let builder = builder.timeout(config.timeout);
409 builder.build().map_err(LiterLlmError::from)?
410 };
411
412 let cached_auth_header = provider
415 .auth_header(config.api_key.expose_secret())
416 .map(|(name, value)| (name.into_owned(), value.into_owned()));
417
418 let cached_extra_headers = provider
421 .extra_headers()
422 .iter()
423 .map(|&(name, value)| (name.to_owned(), value.to_owned()))
424 .collect();
425
426 Ok(Self {
427 config,
428 http,
429 provider,
430 cached_auth_header,
431 cached_extra_headers,
432 })
433 }
434
435 fn resolve_provider_for_model(&self, model: &str) -> Arc<dyn Provider> {
441 if self.config.base_url.is_some() {
445 return Arc::clone(&self.provider);
446 }
447 if self.provider.matches_model(model) {
449 return Arc::clone(&self.provider);
450 }
451 if let Some(detected) = provider::detect_provider(model) {
453 return Arc::from(detected);
454 }
455 Arc::clone(&self.provider)
457 }
458
459 async fn resolve_auth_header_for_provider(&self, prov: &dyn Provider) -> Result<Option<(String, String)>> {
462 if let Some(ref cp) = self.config.credential_provider {
463 let credential = cp.resolve().await?;
464 match credential {
465 Credential::BearerToken(token) => Ok(Some((
466 "Authorization".to_owned(),
467 format!("Bearer {}", token.expose_secret()),
468 ))),
469 Credential::AwsCredentials { .. } => Ok(None),
470 }
471 } else {
472 Ok(prov
474 .auth_header(self.config.api_key.expose_secret())
475 .map(|(name, value)| (name.into_owned(), value.into_owned())))
476 }
477 }
478
479 fn all_headers_for_provider(
481 &self,
482 prov: &dyn Provider,
483 method: &str,
484 url: &str,
485 body_json: &serde_json::Value,
486 body_bytes: &[u8],
487 ) -> Vec<(String, String)> {
488 let mut headers = prov.signing_headers(method, url, body_bytes);
489 headers.extend(
490 prov.extra_headers()
491 .iter()
492 .map(|&(name, value)| (name.to_owned(), value.to_owned())),
493 );
494 headers.extend(prov.dynamic_headers(body_json));
495 headers
496 }
497
498 fn prepare_request(
509 &self,
510 serializable: &impl serde::Serialize,
511 endpoint_fn: impl FnOnce(&dyn Provider) -> &str,
512 model: &str,
513 stream: Option<bool>,
514 ) -> Result<PreparedRequest> {
515 if model.is_empty() {
516 return Err(LiterLlmError::BadRequest {
517 message: "model must not be empty".into(),
518 });
519 }
520
521 let prov = self.resolve_provider_for_model(model);
522 let bare_model = prov.strip_model_prefix(model).to_owned();
523 let endpoint_path = endpoint_fn(prov.as_ref());
526 let url = prov.build_url(endpoint_path, &bare_model);
527
528 let mut body = serde_json::to_value(serializable)?;
529 if let Some(obj) = body.as_object_mut() {
530 obj.insert("model".into(), serde_json::Value::String(bare_model));
531 if let Some(s) = stream {
532 obj.insert("stream".into(), serde_json::Value::Bool(s));
533 }
534 }
535 prov.transform_request(&mut body)?;
536
537 let body_bytes = bytes::Bytes::from(serde_json::to_vec(&body)?);
541
542 Ok(PreparedRequest {
543 url,
544 provider: prov,
545 body_json: body,
546 body_bytes,
547 })
548 }
549
550 async fn resolve_auth_header(&self) -> Result<Option<(String, String)>> {
556 if let Some(ref cp) = self.config.credential_provider {
557 let credential = cp.resolve().await?;
558 match credential {
559 Credential::BearerToken(token) => Ok(Some((
560 "Authorization".to_owned(),
561 format!("Bearer {}", token.expose_secret()),
562 ))),
563 Credential::AwsCredentials { .. } => Ok(None),
564 }
565 } else {
566 Ok(self.cached_auth_header.clone())
567 }
568 }
569
570 fn all_headers(
574 &self,
575 method: &str,
576 url: &str,
577 body_json: &serde_json::Value,
578 body_bytes: &[u8],
579 ) -> Vec<(String, String)> {
580 let mut headers = self.provider.signing_headers(method, url, body_bytes);
581 headers.extend(self.cached_extra_headers.iter().cloned());
582 headers.extend(self.provider.dynamic_headers(body_json));
583 headers
584 }
585}
586
587#[cfg(any(feature = "native-http", feature = "wasm-http"))]
588fn build_provider(config: &ClientConfig, model_hint: Option<&str>) -> Arc<dyn Provider> {
595 if let Some(ref base_url) = config.base_url {
596 return Arc::new(OpenAiCompatibleProvider {
597 name: "custom".into(),
598 base_url: base_url.clone(),
599 env_var: None,
600 model_prefixes: vec![],
601 });
602 }
603
604 if let Some(model) = model_hint
605 && let Some(p) = provider::detect_provider(model)
606 {
607 return Arc::from(p);
609 }
610
611 Arc::new(OpenAiProvider)
612}
613
614#[cfg(any(feature = "native-http", feature = "wasm-http"))]
615impl LlmClient for DefaultClient {
616 fn chat(&self, req: ChatCompletionRequest) -> BoxFuture<'_, Result<ChatCompletionResponse>> {
617 Box::pin(async move {
618 let prepared = self.prepare_request(&req, |p| p.chat_completions_path(), &req.model, Some(false))?;
620
621 let auth_header = self
622 .resolve_auth_header_for_provider(prepared.provider.as_ref())
623 .await?;
624 let all_headers = self.all_headers_for_provider(
625 prepared.provider.as_ref(),
626 "POST",
627 &prepared.url,
628 &prepared.body_json,
629 &prepared.body_bytes,
630 );
631 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
632
633 let auth = auth_header.as_ref().map(str_pair);
634 let mut raw = http::request::post_json_raw(
635 &self.http,
636 &prepared.url,
637 auth,
638 &extra,
639 prepared.body_bytes,
640 self.config.max_retries,
641 )
642 .await?;
643 prepared.provider.transform_response(&mut raw)?;
644 serde_json::from_value::<ChatCompletionResponse>(raw).map_err(LiterLlmError::from)
645 })
646 }
647
648 fn chat_stream(
649 &self,
650 req: ChatCompletionRequest,
651 ) -> BoxFuture<'_, Result<BoxStream<'static, Result<ChatCompletionChunk>>>> {
652 Box::pin(async move {
653 let prepared = self.prepare_request(&req, |p| p.chat_completions_path(), &req.model, Some(true))?;
656
657 let bare_model = prepared.provider.strip_model_prefix(&req.model);
659 let url = prepared
660 .provider
661 .build_stream_url(prepared.provider.chat_completions_path(), bare_model);
662
663 let auth_header = self
664 .resolve_auth_header_for_provider(prepared.provider.as_ref())
665 .await?;
666 let all_headers = self.all_headers_for_provider(
667 prepared.provider.as_ref(),
668 "POST",
669 &url,
670 &prepared.body_json,
671 &prepared.body_bytes,
672 );
673 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
674 let auth = auth_header.as_ref().map(str_pair);
675
676 match prepared.provider.stream_format() {
677 provider::StreamFormat::Sse => {
678 let provider = Arc::clone(&prepared.provider);
679 let parse_event = move |data: &str| provider.parse_stream_event(data);
680 let stream = http::streaming::post_stream(
681 &self.http,
682 &url,
683 auth,
684 &extra,
685 prepared.body_bytes,
686 self.config.max_retries,
687 parse_event,
688 )
689 .await?;
690 Ok(stream)
691 }
692 provider::StreamFormat::AwsEventStream => {
693 let stream = http::eventstream::post_eventstream(
694 &self.http,
695 &url,
696 auth,
697 &extra,
698 prepared.body_bytes,
699 self.config.max_retries,
700 provider::bedrock::parse_bedrock_stream_event,
701 )
702 .await?;
703 Ok(stream)
704 }
705 }
706 })
707 }
708
709 fn embed(&self, req: EmbeddingRequest) -> BoxFuture<'_, Result<EmbeddingResponse>> {
710 Box::pin(async move {
711 let prepared = self.prepare_request(&req, |p| p.embeddings_path(), &req.model, None)?;
713
714 let auth_header = self
715 .resolve_auth_header_for_provider(prepared.provider.as_ref())
716 .await?;
717 let all_headers = self.all_headers_for_provider(
718 prepared.provider.as_ref(),
719 "POST",
720 &prepared.url,
721 &prepared.body_json,
722 &prepared.body_bytes,
723 );
724 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
725
726 let auth = auth_header.as_ref().map(str_pair);
727 let mut raw = http::request::post_json_raw(
728 &self.http,
729 &prepared.url,
730 auth,
731 &extra,
732 prepared.body_bytes,
733 self.config.max_retries,
734 )
735 .await?;
736 prepared.provider.transform_response(&mut raw)?;
737 serde_json::from_value::<EmbeddingResponse>(raw).map_err(LiterLlmError::from)
738 })
739 }
740
741 fn list_models(&self) -> BoxFuture<'_, Result<ModelsListResponse>> {
742 Box::pin(async move {
743 let url = self.provider.build_url(self.provider.models_path(), "");
745 let auth_header = self.resolve_auth_header().await?;
746 let auth = auth_header.as_ref().map(str_pair);
747 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
748 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
749
750 let mut raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
751 self.provider.transform_response(&mut raw)?;
752 serde_json::from_value::<ModelsListResponse>(raw).map_err(LiterLlmError::from)
753 })
754 }
755
756 fn image_generate(&self, req: CreateImageRequest) -> BoxFuture<'_, Result<ImagesResponse>> {
757 Box::pin(async move {
758 let model = req.model.as_deref().unwrap_or_default();
759 let prepared = self.prepare_request(&req, |p| p.image_generations_path(), model, None)?;
760
761 let auth_header = self
762 .resolve_auth_header_for_provider(prepared.provider.as_ref())
763 .await?;
764 let all_headers = self.all_headers_for_provider(
765 prepared.provider.as_ref(),
766 "POST",
767 &prepared.url,
768 &prepared.body_json,
769 &prepared.body_bytes,
770 );
771 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
772
773 let auth = auth_header.as_ref().map(str_pair);
774 let mut raw = http::request::post_json_raw(
775 &self.http,
776 &prepared.url,
777 auth,
778 &extra,
779 prepared.body_bytes,
780 self.config.max_retries,
781 )
782 .await?;
783 prepared.provider.transform_response(&mut raw)?;
784 serde_json::from_value::<ImagesResponse>(raw).map_err(LiterLlmError::from)
785 })
786 }
787
788 fn speech(&self, req: CreateSpeechRequest) -> BoxFuture<'_, Result<bytes::Bytes>> {
789 Box::pin(async move {
790 let prepared = self.prepare_request(&req, |p| p.audio_speech_path(), &req.model, None)?;
791
792 let auth_header = self
793 .resolve_auth_header_for_provider(prepared.provider.as_ref())
794 .await?;
795 let all_headers = self.all_headers_for_provider(
796 prepared.provider.as_ref(),
797 "POST",
798 &prepared.url,
799 &prepared.body_json,
800 &prepared.body_bytes,
801 );
802 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
803
804 let auth = auth_header.as_ref().map(str_pair);
805 http::request::post_binary(
806 &self.http,
807 &prepared.url,
808 auth,
809 &extra,
810 prepared.body_bytes,
811 self.config.max_retries,
812 )
813 .await
814 })
815 }
816
817 fn transcribe(&self, req: CreateTranscriptionRequest) -> BoxFuture<'_, Result<TranscriptionResponse>> {
818 Box::pin(async move {
819 let prepared = self.prepare_request(&req, |p| p.audio_transcriptions_path(), &req.model, None)?;
820
821 let auth_header = self
822 .resolve_auth_header_for_provider(prepared.provider.as_ref())
823 .await?;
824 let all_headers = self.all_headers_for_provider(
825 prepared.provider.as_ref(),
826 "POST",
827 &prepared.url,
828 &prepared.body_json,
829 &prepared.body_bytes,
830 );
831 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
832
833 let auth = auth_header.as_ref().map(str_pair);
834 let mut raw = http::request::post_json_raw(
835 &self.http,
836 &prepared.url,
837 auth,
838 &extra,
839 prepared.body_bytes,
840 self.config.max_retries,
841 )
842 .await?;
843 prepared.provider.transform_response(&mut raw)?;
844 serde_json::from_value::<TranscriptionResponse>(raw).map_err(LiterLlmError::from)
845 })
846 }
847
848 fn moderate(&self, req: ModerationRequest) -> BoxFuture<'_, Result<ModerationResponse>> {
849 Box::pin(async move {
850 let model = req.model.as_deref().unwrap_or_default();
851 let prepared = self.prepare_request(&req, |p| p.moderations_path(), model, None)?;
852
853 let auth_header = self
854 .resolve_auth_header_for_provider(prepared.provider.as_ref())
855 .await?;
856 let all_headers = self.all_headers_for_provider(
857 prepared.provider.as_ref(),
858 "POST",
859 &prepared.url,
860 &prepared.body_json,
861 &prepared.body_bytes,
862 );
863 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
864
865 let auth = auth_header.as_ref().map(str_pair);
866 let mut raw = http::request::post_json_raw(
867 &self.http,
868 &prepared.url,
869 auth,
870 &extra,
871 prepared.body_bytes,
872 self.config.max_retries,
873 )
874 .await?;
875 prepared.provider.transform_response(&mut raw)?;
876 serde_json::from_value::<ModerationResponse>(raw).map_err(LiterLlmError::from)
877 })
878 }
879
880 fn rerank(&self, req: RerankRequest) -> BoxFuture<'_, Result<RerankResponse>> {
881 Box::pin(async move {
882 let prepared = self.prepare_request(&req, |p| p.rerank_path(), &req.model, None)?;
883
884 let auth_header = self
885 .resolve_auth_header_for_provider(prepared.provider.as_ref())
886 .await?;
887 let all_headers = self.all_headers_for_provider(
888 prepared.provider.as_ref(),
889 "POST",
890 &prepared.url,
891 &prepared.body_json,
892 &prepared.body_bytes,
893 );
894 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
895
896 let auth = auth_header.as_ref().map(str_pair);
897 let mut raw = http::request::post_json_raw(
898 &self.http,
899 &prepared.url,
900 auth,
901 &extra,
902 prepared.body_bytes,
903 self.config.max_retries,
904 )
905 .await?;
906 prepared.provider.transform_response(&mut raw)?;
907 serde_json::from_value::<RerankResponse>(raw).map_err(LiterLlmError::from)
908 })
909 }
910
911 fn search(&self, req: SearchRequest) -> BoxFuture<'_, Result<SearchResponse>> {
912 Box::pin(async move {
913 let prepared = self.prepare_request(&req, |p| p.search_path(), &req.model, None)?;
914
915 let auth_header = self
916 .resolve_auth_header_for_provider(prepared.provider.as_ref())
917 .await?;
918 let all_headers = self.all_headers_for_provider(
919 prepared.provider.as_ref(),
920 "POST",
921 &prepared.url,
922 &prepared.body_json,
923 &prepared.body_bytes,
924 );
925 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
926
927 let auth = auth_header.as_ref().map(str_pair);
928 let mut raw = http::request::post_json_raw(
929 &self.http,
930 &prepared.url,
931 auth,
932 &extra,
933 prepared.body_bytes,
934 self.config.max_retries,
935 )
936 .await?;
937 prepared.provider.transform_response(&mut raw)?;
938 serde_json::from_value::<SearchResponse>(raw).map_err(LiterLlmError::from)
939 })
940 }
941
942 fn ocr(&self, req: OcrRequest) -> BoxFuture<'_, Result<OcrResponse>> {
943 Box::pin(async move {
944 let prepared = self.prepare_request(&req, |p| p.ocr_path(), &req.model, None)?;
945
946 let auth_header = self
947 .resolve_auth_header_for_provider(prepared.provider.as_ref())
948 .await?;
949 let all_headers = self.all_headers_for_provider(
950 prepared.provider.as_ref(),
951 "POST",
952 &prepared.url,
953 &prepared.body_json,
954 &prepared.body_bytes,
955 );
956 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
957
958 let auth = auth_header.as_ref().map(str_pair);
959 let mut raw = http::request::post_json_raw(
960 &self.http,
961 &prepared.url,
962 auth,
963 &extra,
964 prepared.body_bytes,
965 self.config.max_retries,
966 )
967 .await?;
968 prepared.provider.transform_response(&mut raw)?;
969 serde_json::from_value::<OcrResponse>(raw).map_err(LiterLlmError::from)
970 })
971 }
972}
973
974#[cfg(any(feature = "native-http", feature = "wasm-http"))]
975impl LlmClientRaw for DefaultClient {
976 fn chat_raw(&self, req: ChatCompletionRequest) -> BoxFuture<'_, Result<RawExchange<ChatCompletionResponse>>> {
977 Box::pin(async move {
978 let prepared = self.prepare_request(&req, |p| p.chat_completions_path(), &req.model, Some(false))?;
979 let raw_request = prepared.body_json.clone();
980
981 let auth_header = self
982 .resolve_auth_header_for_provider(prepared.provider.as_ref())
983 .await?;
984 let all_headers = self.all_headers_for_provider(
985 prepared.provider.as_ref(),
986 "POST",
987 &prepared.url,
988 &prepared.body_json,
989 &prepared.body_bytes,
990 );
991 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
992
993 let auth = auth_header.as_ref().map(str_pair);
994 let mut raw = http::request::post_json_raw(
995 &self.http,
996 &prepared.url,
997 auth,
998 &extra,
999 prepared.body_bytes,
1000 self.config.max_retries,
1001 )
1002 .await?;
1003
1004 let raw_response = Some(raw.clone());
1005 prepared.provider.transform_response(&mut raw)?;
1006 let data = serde_json::from_value::<ChatCompletionResponse>(raw).map_err(LiterLlmError::from)?;
1007
1008 Ok(RawExchange {
1009 data,
1010 raw_request,
1011 raw_response,
1012 })
1013 })
1014 }
1015
1016 fn chat_stream_raw(
1017 &self,
1018 req: ChatCompletionRequest,
1019 ) -> BoxFuture<'_, Result<RawStreamExchange<BoxStream<'static, Result<ChatCompletionChunk>>>>> {
1020 Box::pin(async move {
1021 let prepared = self.prepare_request(&req, |p| p.chat_completions_path(), &req.model, Some(true))?;
1022 let raw_request = prepared.body_json.clone();
1023
1024 let bare_model = prepared.provider.strip_model_prefix(&req.model);
1025 let url = prepared
1026 .provider
1027 .build_stream_url(prepared.provider.chat_completions_path(), bare_model);
1028
1029 let auth_header = self
1030 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1031 .await?;
1032 let all_headers = self.all_headers_for_provider(
1033 prepared.provider.as_ref(),
1034 "POST",
1035 &url,
1036 &prepared.body_json,
1037 &prepared.body_bytes,
1038 );
1039 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1040 let auth = auth_header.as_ref().map(str_pair);
1041
1042 let stream = match prepared.provider.stream_format() {
1043 provider::StreamFormat::Sse => {
1044 let provider = Arc::clone(&prepared.provider);
1045 let parse_event = move |data: &str| provider.parse_stream_event(data);
1046 http::streaming::post_stream(
1047 &self.http,
1048 &url,
1049 auth,
1050 &extra,
1051 prepared.body_bytes,
1052 self.config.max_retries,
1053 parse_event,
1054 )
1055 .await?
1056 }
1057 provider::StreamFormat::AwsEventStream => {
1058 http::eventstream::post_eventstream(
1059 &self.http,
1060 &url,
1061 auth,
1062 &extra,
1063 prepared.body_bytes,
1064 self.config.max_retries,
1065 provider::bedrock::parse_bedrock_stream_event,
1066 )
1067 .await?
1068 }
1069 };
1070
1071 Ok(RawStreamExchange { stream, raw_request })
1072 })
1073 }
1074
1075 fn embed_raw(&self, req: EmbeddingRequest) -> BoxFuture<'_, Result<RawExchange<EmbeddingResponse>>> {
1076 Box::pin(async move {
1077 let prepared = self.prepare_request(&req, |p| p.embeddings_path(), &req.model, None)?;
1078 let raw_request = prepared.body_json.clone();
1079
1080 let auth_header = self
1081 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1082 .await?;
1083 let all_headers = self.all_headers_for_provider(
1084 prepared.provider.as_ref(),
1085 "POST",
1086 &prepared.url,
1087 &prepared.body_json,
1088 &prepared.body_bytes,
1089 );
1090 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1091
1092 let auth = auth_header.as_ref().map(str_pair);
1093 let mut raw = http::request::post_json_raw(
1094 &self.http,
1095 &prepared.url,
1096 auth,
1097 &extra,
1098 prepared.body_bytes,
1099 self.config.max_retries,
1100 )
1101 .await?;
1102
1103 let raw_response = Some(raw.clone());
1104 prepared.provider.transform_response(&mut raw)?;
1105 let data = serde_json::from_value::<EmbeddingResponse>(raw).map_err(LiterLlmError::from)?;
1106
1107 Ok(RawExchange {
1108 data,
1109 raw_request,
1110 raw_response,
1111 })
1112 })
1113 }
1114
1115 fn image_generate_raw(&self, req: CreateImageRequest) -> BoxFuture<'_, Result<RawExchange<ImagesResponse>>> {
1116 Box::pin(async move {
1117 let model = req.model.as_deref().unwrap_or_default();
1118 let prepared = self.prepare_request(&req, |p| p.image_generations_path(), model, None)?;
1119 let raw_request = prepared.body_json.clone();
1120
1121 let auth_header = self
1122 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1123 .await?;
1124 let all_headers = self.all_headers_for_provider(
1125 prepared.provider.as_ref(),
1126 "POST",
1127 &prepared.url,
1128 &prepared.body_json,
1129 &prepared.body_bytes,
1130 );
1131 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1132
1133 let auth = auth_header.as_ref().map(str_pair);
1134 let mut raw = http::request::post_json_raw(
1135 &self.http,
1136 &prepared.url,
1137 auth,
1138 &extra,
1139 prepared.body_bytes,
1140 self.config.max_retries,
1141 )
1142 .await?;
1143
1144 let raw_response = Some(raw.clone());
1145 prepared.provider.transform_response(&mut raw)?;
1146 let data = serde_json::from_value::<ImagesResponse>(raw).map_err(LiterLlmError::from)?;
1147
1148 Ok(RawExchange {
1149 data,
1150 raw_request,
1151 raw_response,
1152 })
1153 })
1154 }
1155
1156 fn transcribe_raw(
1157 &self,
1158 req: CreateTranscriptionRequest,
1159 ) -> BoxFuture<'_, Result<RawExchange<TranscriptionResponse>>> {
1160 Box::pin(async move {
1161 let prepared = self.prepare_request(&req, |p| p.audio_transcriptions_path(), &req.model, None)?;
1162 let raw_request = prepared.body_json.clone();
1163
1164 let auth_header = self
1165 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1166 .await?;
1167 let all_headers = self.all_headers_for_provider(
1168 prepared.provider.as_ref(),
1169 "POST",
1170 &prepared.url,
1171 &prepared.body_json,
1172 &prepared.body_bytes,
1173 );
1174 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1175
1176 let auth = auth_header.as_ref().map(str_pair);
1177 let mut raw = http::request::post_json_raw(
1178 &self.http,
1179 &prepared.url,
1180 auth,
1181 &extra,
1182 prepared.body_bytes,
1183 self.config.max_retries,
1184 )
1185 .await?;
1186
1187 let raw_response = Some(raw.clone());
1188 prepared.provider.transform_response(&mut raw)?;
1189 let data = serde_json::from_value::<TranscriptionResponse>(raw).map_err(LiterLlmError::from)?;
1190
1191 Ok(RawExchange {
1192 data,
1193 raw_request,
1194 raw_response,
1195 })
1196 })
1197 }
1198
1199 fn moderate_raw(&self, req: ModerationRequest) -> BoxFuture<'_, Result<RawExchange<ModerationResponse>>> {
1200 Box::pin(async move {
1201 let model = req.model.as_deref().unwrap_or_default();
1202 let prepared = self.prepare_request(&req, |p| p.moderations_path(), model, None)?;
1203 let raw_request = prepared.body_json.clone();
1204
1205 let auth_header = self
1206 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1207 .await?;
1208 let all_headers = self.all_headers_for_provider(
1209 prepared.provider.as_ref(),
1210 "POST",
1211 &prepared.url,
1212 &prepared.body_json,
1213 &prepared.body_bytes,
1214 );
1215 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1216
1217 let auth = auth_header.as_ref().map(str_pair);
1218 let mut raw = http::request::post_json_raw(
1219 &self.http,
1220 &prepared.url,
1221 auth,
1222 &extra,
1223 prepared.body_bytes,
1224 self.config.max_retries,
1225 )
1226 .await?;
1227
1228 let raw_response = Some(raw.clone());
1229 prepared.provider.transform_response(&mut raw)?;
1230 let data = serde_json::from_value::<ModerationResponse>(raw).map_err(LiterLlmError::from)?;
1231
1232 Ok(RawExchange {
1233 data,
1234 raw_request,
1235 raw_response,
1236 })
1237 })
1238 }
1239
1240 fn rerank_raw(&self, req: RerankRequest) -> BoxFuture<'_, Result<RawExchange<RerankResponse>>> {
1241 Box::pin(async move {
1242 let prepared = self.prepare_request(&req, |p| p.rerank_path(), &req.model, None)?;
1243 let raw_request = prepared.body_json.clone();
1244
1245 let auth_header = self
1246 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1247 .await?;
1248 let all_headers = self.all_headers_for_provider(
1249 prepared.provider.as_ref(),
1250 "POST",
1251 &prepared.url,
1252 &prepared.body_json,
1253 &prepared.body_bytes,
1254 );
1255 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1256
1257 let auth = auth_header.as_ref().map(str_pair);
1258 let mut raw = http::request::post_json_raw(
1259 &self.http,
1260 &prepared.url,
1261 auth,
1262 &extra,
1263 prepared.body_bytes,
1264 self.config.max_retries,
1265 )
1266 .await?;
1267
1268 let raw_response = Some(raw.clone());
1269 prepared.provider.transform_response(&mut raw)?;
1270 let data = serde_json::from_value::<RerankResponse>(raw).map_err(LiterLlmError::from)?;
1271
1272 Ok(RawExchange {
1273 data,
1274 raw_request,
1275 raw_response,
1276 })
1277 })
1278 }
1279
1280 fn search_raw(&self, req: SearchRequest) -> BoxFuture<'_, Result<RawExchange<SearchResponse>>> {
1281 Box::pin(async move {
1282 let prepared = self.prepare_request(&req, |p| p.search_path(), &req.model, None)?;
1283 let raw_request = prepared.body_json.clone();
1284
1285 let auth_header = self
1286 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1287 .await?;
1288 let all_headers = self.all_headers_for_provider(
1289 prepared.provider.as_ref(),
1290 "POST",
1291 &prepared.url,
1292 &prepared.body_json,
1293 &prepared.body_bytes,
1294 );
1295 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1296
1297 let auth = auth_header.as_ref().map(str_pair);
1298 let mut raw = http::request::post_json_raw(
1299 &self.http,
1300 &prepared.url,
1301 auth,
1302 &extra,
1303 prepared.body_bytes,
1304 self.config.max_retries,
1305 )
1306 .await?;
1307
1308 let raw_response = Some(raw.clone());
1309 prepared.provider.transform_response(&mut raw)?;
1310 let data = serde_json::from_value::<SearchResponse>(raw).map_err(LiterLlmError::from)?;
1311
1312 Ok(RawExchange {
1313 data,
1314 raw_request,
1315 raw_response,
1316 })
1317 })
1318 }
1319
1320 fn ocr_raw(&self, req: OcrRequest) -> BoxFuture<'_, Result<RawExchange<OcrResponse>>> {
1321 Box::pin(async move {
1322 let prepared = self.prepare_request(&req, |p| p.ocr_path(), &req.model, None)?;
1323 let raw_request = prepared.body_json.clone();
1324
1325 let auth_header = self
1326 .resolve_auth_header_for_provider(prepared.provider.as_ref())
1327 .await?;
1328 let all_headers = self.all_headers_for_provider(
1329 prepared.provider.as_ref(),
1330 "POST",
1331 &prepared.url,
1332 &prepared.body_json,
1333 &prepared.body_bytes,
1334 );
1335 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1336
1337 let auth = auth_header.as_ref().map(str_pair);
1338 let mut raw = http::request::post_json_raw(
1339 &self.http,
1340 &prepared.url,
1341 auth,
1342 &extra,
1343 prepared.body_bytes,
1344 self.config.max_retries,
1345 )
1346 .await?;
1347
1348 let raw_response = Some(raw.clone());
1349 prepared.provider.transform_response(&mut raw)?;
1350 let data = serde_json::from_value::<OcrResponse>(raw).map_err(LiterLlmError::from)?;
1351
1352 Ok(RawExchange {
1353 data,
1354 raw_request,
1355 raw_response,
1356 })
1357 })
1358 }
1359}
1360
1361#[cfg(any(feature = "native-http", feature = "wasm-http"))]
1362impl FileClient for DefaultClient {
1363 fn create_file(&self, req: CreateFileRequest) -> BoxFuture<'_, Result<FileObject>> {
1364 Box::pin(async move {
1365 let url = self.provider.build_url(self.provider.files_path(), "");
1366 let auth_header = self.resolve_auth_header().await?;
1367 let auth = auth_header.as_ref().map(str_pair);
1368 let all_headers = self.all_headers("POST", &url, &serde_json::Value::Null, &[]);
1369 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1370
1371 use base64::Engine;
1373 let file_bytes = base64::engine::general_purpose::STANDARD
1374 .decode(&req.file)
1375 .map_err(|e| LiterLlmError::BadRequest {
1376 message: format!("invalid base64 file data: {e}"),
1377 })?;
1378
1379 let filename = req.filename.unwrap_or_else(|| "upload".to_owned());
1380 let file_part = reqwest::multipart::Part::bytes(file_bytes).file_name(filename);
1381 let purpose_str = serde_json::to_value(&req.purpose)?
1382 .as_str()
1383 .unwrap_or_default()
1384 .to_owned();
1385 let form = reqwest::multipart::Form::new()
1386 .part("file", file_part)
1387 .text("purpose", purpose_str);
1388
1389 let raw = http::request::post_multipart(&self.http, &url, auth, &extra, form).await?;
1390 serde_json::from_value::<FileObject>(raw).map_err(LiterLlmError::from)
1391 })
1392 }
1393
1394 fn retrieve_file(&self, file_id: &str) -> BoxFuture<'_, Result<FileObject>> {
1395 let file_id = file_id.to_owned();
1396 Box::pin(async move {
1397 let url = format!(
1398 "{}/{}",
1399 self.provider.build_url(self.provider.files_path(), ""),
1400 file_id
1401 );
1402 let auth_header = self.resolve_auth_header().await?;
1403 let auth = auth_header.as_ref().map(str_pair);
1404 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1405 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1406
1407 let raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1408 serde_json::from_value::<FileObject>(raw).map_err(LiterLlmError::from)
1409 })
1410 }
1411
1412 fn delete_file(&self, file_id: &str) -> BoxFuture<'_, Result<DeleteResponse>> {
1413 let file_id = file_id.to_owned();
1414 Box::pin(async move {
1415 let url = format!(
1416 "{}/{}",
1417 self.provider.build_url(self.provider.files_path(), ""),
1418 file_id
1419 );
1420 let auth_header = self.resolve_auth_header().await?;
1421 let auth = auth_header.as_ref().map(str_pair);
1422 let all_headers = self.all_headers("DELETE", &url, &serde_json::Value::Null, &[]);
1423 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1424
1425 let raw = http::request::delete_json(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1426 serde_json::from_value::<DeleteResponse>(raw).map_err(LiterLlmError::from)
1427 })
1428 }
1429
1430 fn list_files(&self, query: Option<FileListQuery>) -> BoxFuture<'_, Result<FileListResponse>> {
1431 Box::pin(async move {
1432 let base_url = self.provider.build_url(self.provider.files_path(), "");
1433 let url = if let Some(ref q) = query {
1434 let mut params = Vec::new();
1435 if let Some(ref purpose) = q.purpose {
1436 params.push(format!("purpose={purpose}"));
1437 }
1438 if let Some(limit) = q.limit {
1439 params.push(format!("limit={limit}"));
1440 }
1441 if let Some(ref after) = q.after {
1442 params.push(format!("after={after}"));
1443 }
1444 if params.is_empty() {
1445 base_url
1446 } else {
1447 format!("{base_url}?{}", params.join("&"))
1448 }
1449 } else {
1450 base_url
1451 };
1452 let auth_header = self.resolve_auth_header().await?;
1453 let auth = auth_header.as_ref().map(str_pair);
1454 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1455 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1456
1457 let raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1458 serde_json::from_value::<FileListResponse>(raw).map_err(LiterLlmError::from)
1459 })
1460 }
1461
1462 fn file_content(&self, file_id: &str) -> BoxFuture<'_, Result<bytes::Bytes>> {
1463 let file_id = file_id.to_owned();
1464 Box::pin(async move {
1465 let url = format!(
1466 "{}/{}/content",
1467 self.provider.build_url(self.provider.files_path(), ""),
1468 file_id
1469 );
1470 let auth_header = self.resolve_auth_header().await?;
1471 let auth = auth_header.as_ref().map(str_pair);
1472 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1473 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1474
1475 http::request::get_binary(&self.http, &url, auth, &extra, self.config.max_retries).await
1476 })
1477 }
1478}
1479
1480#[cfg(any(feature = "native-http", feature = "wasm-http"))]
1481impl BatchClient for DefaultClient {
1482 fn create_batch(&self, req: CreateBatchRequest) -> BoxFuture<'_, Result<BatchObject>> {
1483 Box::pin(async move {
1484 let url = self.provider.build_url(self.provider.batches_path(), "");
1485 let body_bytes = bytes::Bytes::from(serde_json::to_vec(&req)?);
1486 let body_json = serde_json::to_value(&req)?;
1487
1488 let auth_header = self.resolve_auth_header().await?;
1489 let all_headers = self.all_headers("POST", &url, &body_json, &body_bytes);
1490 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1491 let auth = auth_header.as_ref().map(str_pair);
1492
1493 let raw = http::request::post_json_raw(&self.http, &url, auth, &extra, body_bytes, self.config.max_retries)
1494 .await?;
1495 serde_json::from_value::<BatchObject>(raw).map_err(LiterLlmError::from)
1496 })
1497 }
1498
1499 fn retrieve_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>> {
1500 let batch_id = batch_id.to_owned();
1501 Box::pin(async move {
1502 let url = format!(
1503 "{}/{}",
1504 self.provider.build_url(self.provider.batches_path(), ""),
1505 batch_id
1506 );
1507 let auth_header = self.resolve_auth_header().await?;
1508 let auth = auth_header.as_ref().map(str_pair);
1509 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1510 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1511
1512 let raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1513 serde_json::from_value::<BatchObject>(raw).map_err(LiterLlmError::from)
1514 })
1515 }
1516
1517 fn list_batches(&self, query: Option<BatchListQuery>) -> BoxFuture<'_, Result<BatchListResponse>> {
1518 Box::pin(async move {
1519 let base_url = self.provider.build_url(self.provider.batches_path(), "");
1520 let url = if let Some(ref q) = query {
1521 let mut params = Vec::new();
1522 if let Some(limit) = q.limit {
1523 params.push(format!("limit={limit}"));
1524 }
1525 if let Some(ref after) = q.after {
1526 params.push(format!("after={after}"));
1527 }
1528 if params.is_empty() {
1529 base_url
1530 } else {
1531 format!("{base_url}?{}", params.join("&"))
1532 }
1533 } else {
1534 base_url
1535 };
1536 let auth_header = self.resolve_auth_header().await?;
1537 let auth = auth_header.as_ref().map(str_pair);
1538 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1539 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1540
1541 let raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1542 serde_json::from_value::<BatchListResponse>(raw).map_err(LiterLlmError::from)
1543 })
1544 }
1545
1546 fn cancel_batch(&self, batch_id: &str) -> BoxFuture<'_, Result<BatchObject>> {
1547 let batch_id = batch_id.to_owned();
1548 Box::pin(async move {
1549 let url = format!(
1550 "{}/{}/cancel",
1551 self.provider.build_url(self.provider.batches_path(), ""),
1552 batch_id
1553 );
1554 let auth_header = self.resolve_auth_header().await?;
1555 let body_json = serde_json::Value::Null;
1556 let body_bytes = bytes::Bytes::new();
1557 let all_headers = self.all_headers("POST", &url, &body_json, &body_bytes);
1558 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1559 let auth = auth_header.as_ref().map(str_pair);
1560
1561 let raw = http::request::post_json_raw(&self.http, &url, auth, &extra, body_bytes, self.config.max_retries)
1562 .await?;
1563 serde_json::from_value::<BatchObject>(raw).map_err(LiterLlmError::from)
1564 })
1565 }
1566}
1567
1568#[cfg(any(feature = "native-http", feature = "wasm-http"))]
1569impl ResponseClient for DefaultClient {
1570 fn create_response(&self, req: CreateResponseRequest) -> BoxFuture<'_, Result<ResponseObject>> {
1571 Box::pin(async move {
1572 let url = self.provider.build_url(self.provider.responses_path(), "");
1573 let body_bytes = bytes::Bytes::from(serde_json::to_vec(&req)?);
1574 let body_json = serde_json::to_value(&req)?;
1575
1576 let auth_header = self.resolve_auth_header().await?;
1577 let all_headers = self.all_headers("POST", &url, &body_json, &body_bytes);
1578 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1579 let auth = auth_header.as_ref().map(str_pair);
1580
1581 let raw = http::request::post_json_raw(&self.http, &url, auth, &extra, body_bytes, self.config.max_retries)
1582 .await?;
1583 serde_json::from_value::<ResponseObject>(raw).map_err(LiterLlmError::from)
1584 })
1585 }
1586
1587 fn retrieve_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>> {
1588 let id = id.to_owned();
1589 Box::pin(async move {
1590 let url = format!("{}/{}", self.provider.build_url(self.provider.responses_path(), ""), id);
1591 let auth_header = self.resolve_auth_header().await?;
1592 let auth = auth_header.as_ref().map(str_pair);
1593 let all_headers = self.all_headers("GET", &url, &serde_json::Value::Null, &[]);
1594 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1595
1596 let raw = http::request::get_json_raw(&self.http, &url, auth, &extra, self.config.max_retries).await?;
1597 serde_json::from_value::<ResponseObject>(raw).map_err(LiterLlmError::from)
1598 })
1599 }
1600
1601 fn cancel_response(&self, id: &str) -> BoxFuture<'_, Result<ResponseObject>> {
1602 let id = id.to_owned();
1603 Box::pin(async move {
1604 let url = format!(
1605 "{}/{}/cancel",
1606 self.provider.build_url(self.provider.responses_path(), ""),
1607 id
1608 );
1609 let auth_header = self.resolve_auth_header().await?;
1610 let body_json = serde_json::Value::Null;
1611 let body_bytes = bytes::Bytes::new();
1612 let all_headers = self.all_headers("POST", &url, &body_json, &body_bytes);
1613 let extra: Vec<(&str, &str)> = all_headers.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
1614 let auth = auth_header.as_ref().map(str_pair);
1615
1616 let raw = http::request::post_json_raw(&self.http, &url, auth, &extra, body_bytes, self.config.max_retries)
1617 .await?;
1618 serde_json::from_value::<ResponseObject>(raw).map_err(LiterLlmError::from)
1619 })
1620 }
1621}