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