1use crate::v1::assistant::{
2 AssistantFileObject, AssistantFileRequest, AssistantObject, AssistantRequest, DeletionStatus,
3 ListAssistant, ListAssistantFile,
4};
5use crate::v1::audio::{
6 AudioSpeechRequest, AudioSpeechResponse, AudioTranscriptionRequest, AudioTranscriptionResponse,
7 AudioTranslationRequest, AudioTranslationResponse,
8};
9use crate::v1::batch::{BatchResponse, CreateBatchRequest, ListBatchResponse};
10use crate::v1::chat_completion::{ChatCompletionRequest, ChatCompletionResponse};
11use crate::v1::common;
12use crate::v1::completion::{CompletionRequest, CompletionResponse};
13use crate::v1::edit::{EditRequest, EditResponse};
14use crate::v1::embedding::{EmbeddingRequest, EmbeddingResponse};
15use crate::v1::error::APIError;
16use crate::v1::file::{
17 FileDeleteRequest, FileDeleteResponse, FileListResponse, FileRetrieveResponse,
18 FileUploadRequest, FileUploadResponse,
19};
20use crate::v1::fine_tuning::{
21 CancelFineTuningJobRequest, CreateFineTuningJobRequest, FineTuningJobEvent,
22 FineTuningJobObject, FineTuningPagination, ListFineTuningJobEventsRequest,
23 RetrieveFineTuningJobRequest,
24};
25use crate::v1::image::{
26 ImageEditRequest, ImageEditResponse, ImageGenerationRequest, ImageGenerationResponse,
27 ImageVariationRequest, ImageVariationResponse,
28};
29use crate::v1::message::{
30 CreateMessageRequest, ListMessage, ListMessageFile, MessageFileObject, MessageObject,
31 ModifyMessageRequest,
32};
33use crate::v1::moderation::{CreateModerationRequest, CreateModerationResponse};
34use crate::v1::run::{
35 CreateRunRequest, CreateThreadAndRunRequest, ListRun, ListRunStep, ModifyRunRequest, RunObject,
36 RunStepObject,
37};
38use crate::v1::thread::{CreateThreadRequest, ModifyThreadRequest, ThreadObject};
39
40use bytes::Bytes;
41use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
42use reqwest::multipart::{Form, Part};
43use reqwest::{Client, Method, Response};
44use serde::Serialize;
45use serde_json::Value;
46use url::Url;
47
48use std::error::Error;
49use std::fs::{create_dir_all, File};
50use std::io::Read;
51use std::io::Write;
52use std::path::Path;
53
54const API_URL_V1: &str = "https://api.openai.com/v1";
55
56#[derive(Default)]
57pub struct OpenAIClientBuilder {
58 api_endpoint: Option<String>,
59 api_key: Option<String>,
60 organization: Option<String>,
61 proxy: Option<String>,
62 timeout: Option<u64>,
63 headers: Option<HeaderMap>,
64}
65
66#[derive(Debug)]
67pub struct OpenAIClient {
68 api_endpoint: String,
69 api_key: Option<String>,
70 organization: Option<String>,
71 proxy: Option<String>,
72 timeout: Option<u64>,
73 pub headers: Option<HeaderMap>,
74}
75
76impl OpenAIClientBuilder {
77 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
82 self.api_key = Some(api_key.into());
83 self
84 }
85
86 pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
87 self.api_endpoint = Some(endpoint.into());
88 self
89 }
90
91 pub fn with_organization(mut self, organization: impl Into<String>) -> Self {
92 self.organization = Some(organization.into());
93 self
94 }
95
96 pub fn with_proxy(mut self, proxy: impl Into<String>) -> Self {
97 self.proxy = Some(proxy.into());
98 self
99 }
100
101 pub fn with_timeout(mut self, timeout: u64) -> Self {
102 self.timeout = Some(timeout);
103 self
104 }
105
106 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
107 let headers = self.headers.get_or_insert_with(HeaderMap::new);
108 headers.insert(
109 HeaderName::from_bytes(key.into().as_bytes()).expect("Invalid header name"),
110 HeaderValue::from_str(&value.into()).expect("Invalid header value"),
111 );
112 self
113 }
114
115 pub fn build(self) -> Result<OpenAIClient, Box<dyn Error>> {
116 let api_endpoint = self.api_endpoint.unwrap_or_else(|| {
117 std::env::var("OPENAI_API_BASE").unwrap_or_else(|_| API_URL_V1.to_owned())
118 });
119
120 Ok(OpenAIClient {
121 api_endpoint,
122 api_key: self.api_key,
123 organization: self.organization,
124 proxy: self.proxy,
125 timeout: self.timeout,
126 headers: self.headers,
127 })
128 }
129}
130
131impl OpenAIClient {
132 pub fn builder() -> OpenAIClientBuilder {
133 OpenAIClientBuilder::new()
134 }
135
136 async fn build_request(&self, method: Method, path: &str) -> reqwest::RequestBuilder {
137 let url = self
138 .build_url_with_preserved_query(path)
139 .unwrap_or_else(|_| format!("{}/{}", self.api_endpoint, path));
140
141 let client = Client::builder();
142
143 #[cfg(feature = "rustls")]
144 let client = client.use_rustls_tls();
145
146 let client = if let Some(timeout) = self.timeout {
147 client.timeout(std::time::Duration::from_secs(timeout))
148 } else {
149 client
150 };
151
152 let client = if let Some(proxy) = &self.proxy {
153 client.proxy(reqwest::Proxy::all(proxy).unwrap())
154 } else {
155 client
156 };
157
158 let client = client.build().unwrap();
159
160 let mut request = client.request(method, url);
161
162 if let Some(api_key) = &self.api_key {
163 request = request.header("Authorization", format!("Bearer {}", api_key));
164 }
165
166 if let Some(organization) = &self.organization {
167 request = request.header("openai-organization", organization);
168 }
169
170 if let Some(headers) = &self.headers {
171 for (key, value) in headers {
172 request = request.header(key, value);
173 }
174 }
175
176 if Self::is_beta(path) {
177 request = request.header("OpenAI-Beta", "assistants=v2");
178 }
179
180 request
181 }
182
183 async fn post<T: serde::de::DeserializeOwned>(
184 &mut self,
185 path: &str,
186 body: &impl serde::ser::Serialize,
187 ) -> Result<T, APIError> {
188 let request = self.build_request(Method::POST, path).await;
189 let request = request.json(body);
190 let response = request.send().await?;
191 self.handle_response(response).await
192 }
193
194 async fn get<T: serde::de::DeserializeOwned>(&mut self, path: &str) -> Result<T, APIError> {
195 let request = self.build_request(Method::GET, path).await;
196 let response = request.send().await?;
197 self.handle_response(response).await
198 }
199
200 async fn get_raw(&self, path: &str) -> Result<Bytes, APIError> {
201 let request = self.build_request(Method::GET, path).await;
202 let response = request.send().await?;
203 Ok(response.bytes().await?)
204 }
205
206 async fn delete<T: serde::de::DeserializeOwned>(&mut self, path: &str) -> Result<T, APIError> {
207 let request = self.build_request(Method::DELETE, path).await;
208 let response = request.send().await?;
209 self.handle_response(response).await
210 }
211
212 async fn post_form<T: serde::de::DeserializeOwned>(
213 &mut self,
214 path: &str,
215 form: Form,
216 ) -> Result<T, APIError> {
217 let request = self.build_request(Method::POST, path).await;
218 let request = request.multipart(form);
219 let response = request.send().await?;
220 self.handle_response(response).await
221 }
222
223 async fn post_form_raw(&self, path: &str, form: Form) -> Result<Bytes, APIError> {
224 let request = self.build_request(Method::POST, path).await;
225 let request = request.multipart(form);
226 let response = request.send().await?;
227 Ok(response.bytes().await?)
228 }
229
230 async fn handle_response<T: serde::de::DeserializeOwned>(
231 &mut self,
232 response: Response,
233 ) -> Result<T, APIError> {
234 let status = response.status();
235 let headers = response.headers().clone();
236 if status.is_success() {
237 let text = response.text().await.unwrap_or_else(|_| "".to_string());
238 match serde_json::from_str::<T>(&text) {
239 Ok(parsed) => {
240 self.headers = Some(headers);
241 Ok(parsed)
242 }
243 Err(e) => Err(APIError::CustomError {
244 message: format!("Failed to parse JSON: {} / response {}", e, text),
245 }),
246 }
247 } else {
248 let error_message = response
249 .text()
250 .await
251 .unwrap_or_else(|_| "Unknown error".to_string());
252 Err(APIError::CustomError {
253 message: format!("{}: {}", status, error_message),
254 })
255 }
256 }
257
258 pub async fn completion(
259 &mut self,
260 req: CompletionRequest,
261 ) -> Result<CompletionResponse, APIError> {
262 self.post("completions", &req).await
263 }
264
265 pub async fn edit(&mut self, req: EditRequest) -> Result<EditResponse, APIError> {
266 self.post("edits", &req).await
267 }
268
269 pub async fn image_generation(
270 &mut self,
271 req: ImageGenerationRequest,
272 ) -> Result<ImageGenerationResponse, APIError> {
273 self.post("images/generations", &req).await
274 }
275
276 pub async fn image_edit(
277 &mut self,
278 req: ImageEditRequest,
279 ) -> Result<ImageEditResponse, APIError> {
280 self.post("images/edits", &req).await
281 }
282
283 pub async fn image_variation(
284 &mut self,
285 req: ImageVariationRequest,
286 ) -> Result<ImageVariationResponse, APIError> {
287 self.post("images/variations", &req).await
288 }
289
290 pub async fn embedding(
291 &mut self,
292 req: EmbeddingRequest,
293 ) -> Result<EmbeddingResponse, APIError> {
294 self.post("embeddings", &req).await
295 }
296
297 pub async fn file_list(&mut self) -> Result<FileListResponse, APIError> {
298 self.get("files").await
299 }
300
301 pub async fn upload_file(
302 &mut self,
303 req: FileUploadRequest,
304 ) -> Result<FileUploadResponse, APIError> {
305 let form = Self::create_form(&req, "file")?;
306 self.post_form("files", form).await
307 }
308
309 pub async fn delete_file(
310 &mut self,
311 req: FileDeleteRequest,
312 ) -> Result<FileDeleteResponse, APIError> {
313 self.delete(&format!("files/{}", req.file_id)).await
314 }
315
316 pub async fn retrieve_file(
317 &mut self,
318 file_id: String,
319 ) -> Result<FileRetrieveResponse, APIError> {
320 self.get(&format!("files/{}", file_id)).await
321 }
322
323 pub async fn retrieve_file_content(&self, file_id: String) -> Result<Bytes, APIError> {
324 self.get_raw(&format!("files/{}/content", file_id)).await
325 }
326
327 pub async fn chat_completion(
328 &mut self,
329 req: ChatCompletionRequest,
330 ) -> Result<ChatCompletionResponse, APIError> {
331 self.post("chat/completions", &req).await
332 }
333
334 pub async fn audio_transcription(
335 &mut self,
336 req: AudioTranscriptionRequest,
337 ) -> Result<AudioTranscriptionResponse, APIError> {
338 if let Some(response_format) = &req.response_format {
340 if response_format != "json" && response_format != "verbose_json" {
341 return Err(APIError::CustomError {
342 message: "response_format must be either 'json' or 'verbose_json' please use audio_transcription_raw".to_string(),
343 });
344 }
345 }
346 let form: Form;
347 if req.clone().file.is_some() {
348 form = Self::create_form(&req, "file")?;
349 } else if let Some(bytes) = req.clone().bytes {
350 form = Self::create_form_from_bytes(&req, bytes)?;
351 } else {
352 return Err(APIError::CustomError {
353 message: "Either file or bytes must be provided".to_string(),
354 });
355 }
356 self.post_form("audio/transcriptions", form).await
357 }
358
359 pub async fn audio_transcription_raw(
360 &mut self,
361 req: AudioTranscriptionRequest,
362 ) -> Result<Bytes, APIError> {
363 if let Some(response_format) = &req.response_format {
365 if response_format != "text" && response_format != "srt" && response_format != "vtt" {
366 return Err(APIError::CustomError {
367 message: "response_format must be either 'text', 'srt' or 'vtt', please use audio_transcription".to_string(),
368 });
369 }
370 }
371 let form: Form;
372 if req.clone().file.is_some() {
373 form = Self::create_form(&req, "file")?;
374 } else if let Some(bytes) = req.clone().bytes {
375 form = Self::create_form_from_bytes(&req, bytes)?;
376 } else {
377 return Err(APIError::CustomError {
378 message: "Either file or bytes must be provided".to_string(),
379 });
380 }
381 self.post_form_raw("audio/transcriptions", form).await
382 }
383
384 pub async fn audio_translation(
385 &mut self,
386 req: AudioTranslationRequest,
387 ) -> Result<AudioTranslationResponse, APIError> {
388 let form = Self::create_form(&req, "file")?;
389 self.post_form("audio/translations", form).await
390 }
391
392 pub async fn audio_speech(
393 &mut self,
394 req: AudioSpeechRequest,
395 ) -> Result<AudioSpeechResponse, APIError> {
396 let request = self.build_request(Method::POST, "audio/speech").await;
397 let request = request.json(&req);
398 let response = request.send().await?;
399 let headers = response.headers().clone();
400 let bytes = response.bytes().await?;
401 let path = Path::new(req.output.as_str());
402 if let Some(parent) = path.parent() {
403 match create_dir_all(parent) {
404 Ok(_) => {}
405 Err(e) => {
406 return Err(APIError::CustomError {
407 message: e.to_string(),
408 })
409 }
410 }
411 }
412 match File::create(path) {
413 Ok(mut file) => match file.write_all(&bytes) {
414 Ok(_) => {}
415 Err(e) => {
416 return Err(APIError::CustomError {
417 message: e.to_string(),
418 })
419 }
420 },
421 Err(e) => {
422 return Err(APIError::CustomError {
423 message: e.to_string(),
424 })
425 }
426 }
427
428 Ok(AudioSpeechResponse {
429 result: true,
430 headers: Some(headers),
431 })
432 }
433
434 pub async fn create_fine_tuning_job(
435 &mut self,
436 req: CreateFineTuningJobRequest,
437 ) -> Result<FineTuningJobObject, APIError> {
438 self.post("fine_tuning/jobs", &req).await
439 }
440
441 pub async fn list_fine_tuning_jobs(
442 &mut self,
443 ) -> Result<FineTuningPagination<FineTuningJobObject>, APIError> {
444 self.get("fine_tuning/jobs").await
445 }
446
447 pub async fn list_fine_tuning_job_events(
448 &mut self,
449 req: ListFineTuningJobEventsRequest,
450 ) -> Result<FineTuningPagination<FineTuningJobEvent>, APIError> {
451 self.get(&format!(
452 "fine_tuning/jobs/{}/events",
453 req.fine_tuning_job_id
454 ))
455 .await
456 }
457
458 pub async fn retrieve_fine_tuning_job(
459 &mut self,
460 req: RetrieveFineTuningJobRequest,
461 ) -> Result<FineTuningJobObject, APIError> {
462 self.get(&format!("fine_tuning/jobs/{}", req.fine_tuning_job_id))
463 .await
464 }
465
466 pub async fn cancel_fine_tuning_job(
467 &mut self,
468 req: CancelFineTuningJobRequest,
469 ) -> Result<FineTuningJobObject, APIError> {
470 self.post(
471 &format!("fine_tuning/jobs/{}/cancel", req.fine_tuning_job_id),
472 &req,
473 )
474 .await
475 }
476
477 pub async fn create_moderation(
478 &mut self,
479 req: CreateModerationRequest,
480 ) -> Result<CreateModerationResponse, APIError> {
481 self.post("moderations", &req).await
482 }
483
484 pub async fn create_assistant(
485 &mut self,
486 req: AssistantRequest,
487 ) -> Result<AssistantObject, APIError> {
488 self.post("assistants", &req).await
489 }
490
491 pub async fn retrieve_assistant(
492 &mut self,
493 assistant_id: String,
494 ) -> Result<AssistantObject, APIError> {
495 self.get(&format!("assistants/{}", assistant_id)).await
496 }
497
498 pub async fn modify_assistant(
499 &mut self,
500 assistant_id: String,
501 req: AssistantRequest,
502 ) -> Result<AssistantObject, APIError> {
503 self.post(&format!("assistants/{}", assistant_id), &req)
504 .await
505 }
506
507 pub async fn delete_assistant(
508 &mut self,
509 assistant_id: String,
510 ) -> Result<DeletionStatus, APIError> {
511 self.delete(&format!("assistants/{}", assistant_id)).await
512 }
513
514 pub async fn list_assistant(
515 &mut self,
516 limit: Option<i64>,
517 order: Option<String>,
518 after: Option<String>,
519 before: Option<String>,
520 ) -> Result<ListAssistant, APIError> {
521 let url = Self::query_params(limit, order, after, before, "assistants".to_string());
522 self.get(&url).await
523 }
524
525 pub async fn create_assistant_file(
526 &mut self,
527 assistant_id: String,
528 req: AssistantFileRequest,
529 ) -> Result<AssistantFileObject, APIError> {
530 self.post(&format!("assistants/{}/files", assistant_id), &req)
531 .await
532 }
533
534 pub async fn retrieve_assistant_file(
535 &mut self,
536 assistant_id: String,
537 file_id: String,
538 ) -> Result<AssistantFileObject, APIError> {
539 self.get(&format!("assistants/{}/files/{}", assistant_id, file_id))
540 .await
541 }
542
543 pub async fn delete_assistant_file(
544 &mut self,
545 assistant_id: String,
546 file_id: String,
547 ) -> Result<DeletionStatus, APIError> {
548 self.delete(&format!("assistants/{}/files/{}", assistant_id, file_id))
549 .await
550 }
551
552 pub async fn list_assistant_file(
553 &mut self,
554 assistant_id: String,
555 limit: Option<i64>,
556 order: Option<String>,
557 after: Option<String>,
558 before: Option<String>,
559 ) -> Result<ListAssistantFile, APIError> {
560 let url = Self::query_params(
561 limit,
562 order,
563 after,
564 before,
565 format!("assistants/{}/files", assistant_id),
566 );
567 self.get(&url).await
568 }
569
570 pub async fn create_thread(
571 &mut self,
572 req: CreateThreadRequest,
573 ) -> Result<ThreadObject, APIError> {
574 self.post("threads", &req).await
575 }
576
577 pub async fn retrieve_thread(&mut self, thread_id: String) -> Result<ThreadObject, APIError> {
578 self.get(&format!("threads/{}", thread_id)).await
579 }
580
581 pub async fn modify_thread(
582 &mut self,
583 thread_id: String,
584 req: ModifyThreadRequest,
585 ) -> Result<ThreadObject, APIError> {
586 self.post(&format!("threads/{}", thread_id), &req).await
587 }
588
589 pub async fn delete_thread(&mut self, thread_id: String) -> Result<DeletionStatus, APIError> {
590 self.delete(&format!("threads/{}", thread_id)).await
591 }
592
593 pub async fn create_message(
594 &mut self,
595 thread_id: String,
596 req: CreateMessageRequest,
597 ) -> Result<MessageObject, APIError> {
598 self.post(&format!("threads/{}/messages", thread_id), &req)
599 .await
600 }
601
602 pub async fn retrieve_message(
603 &mut self,
604 thread_id: String,
605 message_id: String,
606 ) -> Result<MessageObject, APIError> {
607 self.get(&format!("threads/{}/messages/{}", thread_id, message_id))
608 .await
609 }
610
611 pub async fn modify_message(
612 &mut self,
613 thread_id: String,
614 message_id: String,
615 req: ModifyMessageRequest,
616 ) -> Result<MessageObject, APIError> {
617 self.post(
618 &format!("threads/{}/messages/{}", thread_id, message_id),
619 &req,
620 )
621 .await
622 }
623
624 pub async fn list_messages(&mut self, thread_id: String) -> Result<ListMessage, APIError> {
625 self.get(&format!("threads/{}/messages", thread_id)).await
626 }
627
628 pub async fn retrieve_message_file(
629 &mut self,
630 thread_id: String,
631 message_id: String,
632 file_id: String,
633 ) -> Result<MessageFileObject, APIError> {
634 self.get(&format!(
635 "threads/{}/messages/{}/files/{}",
636 thread_id, message_id, file_id
637 ))
638 .await
639 }
640
641 pub async fn list_message_file(
642 &mut self,
643 thread_id: String,
644 message_id: String,
645 limit: Option<i64>,
646 order: Option<String>,
647 after: Option<String>,
648 before: Option<String>,
649 ) -> Result<ListMessageFile, APIError> {
650 let url = Self::query_params(
651 limit,
652 order,
653 after,
654 before,
655 format!("threads/{}/messages/{}/files", thread_id, message_id),
656 );
657 self.get(&url).await
658 }
659
660 pub async fn create_run(
661 &mut self,
662 thread_id: String,
663 req: CreateRunRequest,
664 ) -> Result<RunObject, APIError> {
665 self.post(&format!("threads/{}/runs", thread_id), &req)
666 .await
667 }
668
669 pub async fn retrieve_run(
670 &mut self,
671 thread_id: String,
672 run_id: String,
673 ) -> Result<RunObject, APIError> {
674 self.get(&format!("threads/{}/runs/{}", thread_id, run_id))
675 .await
676 }
677
678 pub async fn modify_run(
679 &mut self,
680 thread_id: String,
681 run_id: String,
682 req: ModifyRunRequest,
683 ) -> Result<RunObject, APIError> {
684 self.post(&format!("threads/{}/runs/{}", thread_id, run_id), &req)
685 .await
686 }
687
688 pub async fn list_run(
689 &mut self,
690 thread_id: String,
691 limit: Option<i64>,
692 order: Option<String>,
693 after: Option<String>,
694 before: Option<String>,
695 ) -> Result<ListRun, APIError> {
696 let url = Self::query_params(
697 limit,
698 order,
699 after,
700 before,
701 format!("threads/{}/runs", thread_id),
702 );
703 self.get(&url).await
704 }
705
706 pub async fn cancel_run(
707 &mut self,
708 thread_id: String,
709 run_id: String,
710 ) -> Result<RunObject, APIError> {
711 self.post(
712 &format!("threads/{}/runs/{}/cancel", thread_id, run_id),
713 &ModifyRunRequest::default(),
714 )
715 .await
716 }
717
718 pub async fn create_thread_and_run(
719 &mut self,
720 req: CreateThreadAndRunRequest,
721 ) -> Result<RunObject, APIError> {
722 self.post("threads/runs", &req).await
723 }
724
725 pub async fn retrieve_run_step(
726 &mut self,
727 thread_id: String,
728 run_id: String,
729 step_id: String,
730 ) -> Result<RunStepObject, APIError> {
731 self.get(&format!(
732 "threads/{}/runs/{}/steps/{}",
733 thread_id, run_id, step_id
734 ))
735 .await
736 }
737
738 pub async fn list_run_step(
739 &mut self,
740 thread_id: String,
741 run_id: String,
742 limit: Option<i64>,
743 order: Option<String>,
744 after: Option<String>,
745 before: Option<String>,
746 ) -> Result<ListRunStep, APIError> {
747 let url = Self::query_params(
748 limit,
749 order,
750 after,
751 before,
752 format!("threads/{}/runs/{}/steps", thread_id, run_id),
753 );
754 self.get(&url).await
755 }
756
757 pub async fn create_batch(
758 &mut self,
759 req: CreateBatchRequest,
760 ) -> Result<BatchResponse, APIError> {
761 self.post("batches", &req).await
762 }
763
764 pub async fn retrieve_batch(&mut self, batch_id: String) -> Result<BatchResponse, APIError> {
765 self.get(&format!("batches/{}", batch_id)).await
766 }
767
768 pub async fn cancel_batch(&mut self, batch_id: String) -> Result<BatchResponse, APIError> {
769 self.post(
770 &format!("batches/{}/cancel", batch_id),
771 &common::EmptyRequestBody {},
772 )
773 .await
774 }
775
776 pub async fn list_batch(
777 &mut self,
778 after: Option<String>,
779 limit: Option<i64>,
780 ) -> Result<ListBatchResponse, APIError> {
781 let url = Self::query_params(limit, None, after, None, "batches".to_string());
782 self.get(&url).await
783 }
784 fn build_url_with_preserved_query(&self, path: &str) -> Result<String, url::ParseError> {
785 let (base, query_opt) = match self.api_endpoint.split_once('?') {
786 Some((b, q)) => (b.trim_end_matches('/'), Some(q)),
787 None => (self.api_endpoint.trim_end_matches('/'), None),
788 };
789
790 let full_path = format!("{}/{}", base, path.trim_start_matches('/'));
791 let mut url = Url::parse(&full_path)?;
792
793 if let Some(query) = query_opt {
794 for (k, v) in url::form_urlencoded::parse(query.as_bytes()) {
795 url.query_pairs_mut().append_pair(&k, &v);
796 }
797 }
798 Ok(url.to_string())
799 }
800 fn query_params(
801 limit: Option<i64>,
802 order: Option<String>,
803 after: Option<String>,
804 before: Option<String>,
805 mut url: String,
806 ) -> String {
807 let mut params = vec![];
808 if let Some(limit) = limit {
809 params.push(format!("limit={}", limit));
810 }
811 if let Some(order) = order {
812 params.push(format!("order={}", order));
813 }
814 if let Some(after) = after {
815 params.push(format!("after={}", after));
816 }
817 if let Some(before) = before {
818 params.push(format!("before={}", before));
819 }
820 if !params.is_empty() {
821 url = format!("{}?{}", url, params.join("&"));
822 }
823 url
824 }
825
826 fn is_beta(path: &str) -> bool {
827 path.starts_with("assistants") || path.starts_with("threads")
828 }
829
830 fn create_form<T>(req: &T, file_field: &str) -> Result<Form, APIError>
831 where
832 T: Serialize,
833 {
834 let json = match serde_json::to_value(req) {
835 Ok(json) => json,
836 Err(e) => {
837 return Err(APIError::CustomError {
838 message: e.to_string(),
839 })
840 }
841 };
842 let file_path = if let Value::Object(map) = &json {
843 map.get(file_field)
844 .and_then(|v| v.as_str())
845 .ok_or(APIError::CustomError {
846 message: format!("Field '{}' not found or not a string", file_field),
847 })?
848 } else {
849 return Err(APIError::CustomError {
850 message: "Request is not a JSON object".to_string(),
851 });
852 };
853
854 let mut file = match File::open(file_path) {
855 Ok(file) => file,
856 Err(e) => {
857 return Err(APIError::CustomError {
858 message: e.to_string(),
859 })
860 }
861 };
862 let mut buffer = Vec::new();
863 match file.read_to_end(&mut buffer) {
864 Ok(_) => {}
865 Err(e) => {
866 return Err(APIError::CustomError {
867 message: e.to_string(),
868 })
869 }
870 }
871
872 let mut form =
873 Form::new().part("file", Part::bytes(buffer).file_name(file_path.to_string()));
874
875 if let Value::Object(map) = json {
876 for (key, value) in map.into_iter() {
877 if key != file_field {
878 match value {
879 Value::String(s) => {
880 form = form.text(key, s);
881 }
882 Value::Number(n) => {
883 form = form.text(key, n.to_string());
884 }
885 _ => {}
886 }
887 }
888 }
889 }
890
891 Ok(form)
892 }
893
894 fn create_form_from_bytes<T>(req: &T, bytes: Vec<u8>) -> Result<Form, APIError>
895 where
896 T: Serialize,
897 {
898 let json = match serde_json::to_value(req) {
899 Ok(json) => json,
900 Err(e) => {
901 return Err(APIError::CustomError {
902 message: e.to_string(),
903 })
904 }
905 };
906
907 let mut form = Form::new().part("file", Part::bytes(bytes.clone()).file_name("file.mp3"));
908
909 if let Value::Object(map) = json {
910 for (key, value) in map.into_iter() {
911 match value {
912 Value::String(s) => {
913 form = form.text(key, s);
914 }
915 Value::Number(n) => {
916 form = form.text(key, n.to_string());
917 }
918 _ => {}
919 }
920 }
921 }
922
923 Ok(form)
924 }
925}