async_openai/types/
impls.rs

1use std::{
2    fmt::Display,
3    path::{Path, PathBuf},
4};
5
6use crate::{
7    download::{download_url, save_b64},
8    error::OpenAIError,
9    traits::AsyncTryFrom,
10    types::{
11        assistants::CreateMessageRequestContent,
12        audio::{
13            AudioInput, AudioResponseFormat, CreateSpeechResponse, CreateTranscriptionRequest,
14            CreateTranslationRequest, TimestampGranularity, TranscriptionInclude,
15        },
16        audio::{TranscriptionChunkingStrategy, TranslationResponseFormat},
17        chat::{
18            ChatCompletionFunctionCall, ChatCompletionFunctions, ChatCompletionNamedToolChoice,
19        },
20        chat::{
21            ChatCompletionRequestAssistantMessage, ChatCompletionRequestAssistantMessageContent,
22            ChatCompletionRequestDeveloperMessage, ChatCompletionRequestDeveloperMessageContent,
23            ChatCompletionRequestFunctionMessage, ChatCompletionRequestMessage,
24            ChatCompletionRequestMessageContentPartAudio,
25            ChatCompletionRequestMessageContentPartImage,
26            ChatCompletionRequestMessageContentPartText, ChatCompletionRequestSystemMessage,
27            ChatCompletionRequestSystemMessageContent, ChatCompletionRequestToolMessage,
28            ChatCompletionRequestToolMessageContent, ChatCompletionRequestUserMessage,
29            ChatCompletionRequestUserMessageContent, ChatCompletionRequestUserMessageContentPart,
30            FunctionName, ImageUrl, Prompt, Role, StopConfiguration,
31        },
32        containers::CreateContainerFileRequest,
33        embeddings::EmbeddingInput,
34        files::{CreateFileRequest, FileExpirationAfterAnchor, FileInput, FilePurpose},
35        images::{
36            CreateImageEditRequest, CreateImageVariationRequest, DallE2ImageSize, Image,
37            ImageInput, ImageModel, ImageResponseFormat, ImageSize, ImagesResponse,
38        },
39        images::{ImageBackground, ImageEditInput, ImageOutputFormat, ImageQuality, InputFidelity},
40        moderations::ModerationInput,
41        responses::EasyInputContent,
42        uploads::AddUploadPartRequest,
43        videos::{CreateVideoRequest, VideoSize},
44        InputSource,
45    },
46    util::{create_all_dir, create_file_part},
47};
48
49use bytes::Bytes;
50
51/// for `impl_from!(T, Enum)`, implements
52/// - `From<T>`
53/// - `From<Vec<T>>`
54/// - `From<&Vec<T>>`
55/// - `From<[T; N]>`
56/// - `From<&[T; N]>`
57///
58/// for `T: Into<String>` and `Enum` having variants `String(String)` and `StringArray(Vec<String>)`
59macro_rules! impl_from {
60    ($from_typ:ty, $to_typ:ty) => {
61        // From<T> -> String variant
62        impl From<$from_typ> for $to_typ {
63            fn from(value: $from_typ) -> Self {
64                <$to_typ>::String(value.into())
65            }
66        }
67
68        // From<Vec<T>> -> StringArray variant
69        impl From<Vec<$from_typ>> for $to_typ {
70            fn from(value: Vec<$from_typ>) -> Self {
71                <$to_typ>::StringArray(value.iter().map(|v| v.to_string()).collect())
72            }
73        }
74
75        // From<&Vec<T>> -> StringArray variant
76        impl From<&Vec<$from_typ>> for $to_typ {
77            fn from(value: &Vec<$from_typ>) -> Self {
78                <$to_typ>::StringArray(value.iter().map(|v| v.to_string()).collect())
79            }
80        }
81
82        // From<[T; N]> -> StringArray variant
83        impl<const N: usize> From<[$from_typ; N]> for $to_typ {
84            fn from(value: [$from_typ; N]) -> Self {
85                <$to_typ>::StringArray(value.into_iter().map(|v| v.to_string()).collect())
86            }
87        }
88
89        // From<&[T; N]> -> StringArray variatn
90        impl<const N: usize> From<&[$from_typ; N]> for $to_typ {
91            fn from(value: &[$from_typ; N]) -> Self {
92                <$to_typ>::StringArray(value.into_iter().map(|v| v.to_string()).collect())
93            }
94        }
95    };
96}
97
98// From String "family" to Prompt
99impl_from!(&str, Prompt);
100impl_from!(String, Prompt);
101impl_from!(&String, Prompt);
102
103// From String "family" to StopConfiguration
104impl_from!(&str, StopConfiguration);
105impl_from!(String, StopConfiguration);
106impl_from!(&String, StopConfiguration);
107
108// From String "family" to ModerationInput
109impl_from!(&str, ModerationInput);
110impl_from!(String, ModerationInput);
111impl_from!(&String, ModerationInput);
112
113// From String "family" to EmbeddingInput
114impl_from!(&str, EmbeddingInput);
115impl_from!(String, EmbeddingInput);
116impl_from!(&String, EmbeddingInput);
117
118/// for `impl_default!(Enum)`, implements `Default` for `Enum` as `Enum::String("")` where `Enum` has `String` variant
119macro_rules! impl_default {
120    ($for_typ:ty) => {
121        impl Default for $for_typ {
122            fn default() -> Self {
123                Self::String("".into())
124            }
125        }
126    };
127}
128
129impl_default!(Prompt);
130impl_default!(ModerationInput);
131impl_default!(EmbeddingInput);
132
133impl Default for InputSource {
134    fn default() -> Self {
135        InputSource::Path {
136            path: PathBuf::new(),
137        }
138    }
139}
140
141/// for `impl_input!(Struct)` where
142/// ```text
143/// Struct {
144///     source: InputSource
145/// }
146/// ```
147/// implements methods `from_bytes` and `from_vec_u8`,
148/// and `From<P>` for `P: AsRef<Path>`
149macro_rules! impl_input {
150    ($for_typ:ty) => {
151        impl $for_typ {
152            pub fn from_bytes(filename: String, bytes: Bytes) -> Self {
153                Self {
154                    source: InputSource::Bytes { filename, bytes },
155                }
156            }
157
158            pub fn from_vec_u8(filename: String, vec: Vec<u8>) -> Self {
159                Self {
160                    source: InputSource::VecU8 { filename, vec },
161                }
162            }
163        }
164
165        impl<P: AsRef<Path>> From<P> for $for_typ {
166            fn from(path: P) -> Self {
167                let path_buf = path.as_ref().to_path_buf();
168                Self {
169                    source: InputSource::Path { path: path_buf },
170                }
171            }
172        }
173    };
174}
175
176impl_input!(AudioInput);
177impl_input!(FileInput);
178impl_input!(ImageInput);
179
180impl Default for ImageEditInput {
181    fn default() -> Self {
182        Self::Image(ImageInput::default())
183    }
184}
185
186impl From<ImageInput> for ImageEditInput {
187    fn from(value: ImageInput) -> Self {
188        Self::Image(value)
189    }
190}
191
192impl From<Vec<ImageInput>> for ImageEditInput {
193    fn from(value: Vec<ImageInput>) -> Self {
194        Self::Images(value)
195    }
196}
197
198// Single path-like values
199impl From<&str> for ImageEditInput {
200    fn from(value: &str) -> Self {
201        Self::Image(value.into())
202    }
203}
204
205impl From<String> for ImageEditInput {
206    fn from(value: String) -> Self {
207        Self::Image(value.into())
208    }
209}
210
211impl From<&Path> for ImageEditInput {
212    fn from(value: &Path) -> Self {
213        Self::Image(value.into())
214    }
215}
216
217impl From<PathBuf> for ImageEditInput {
218    fn from(value: PathBuf) -> Self {
219        Self::Image(value.into())
220    }
221}
222
223// Arrays of path-like values
224impl<const N: usize> From<[&str; N]> for ImageEditInput {
225    fn from(value: [&str; N]) -> Self {
226        Self::Images(value.into_iter().map(ImageInput::from).collect())
227    }
228}
229
230impl<const N: usize> From<[String; N]> for ImageEditInput {
231    fn from(value: [String; N]) -> Self {
232        Self::Images(value.into_iter().map(ImageInput::from).collect())
233    }
234}
235
236impl<const N: usize> From<[&Path; N]> for ImageEditInput {
237    fn from(value: [&Path; N]) -> Self {
238        Self::Images(value.into_iter().map(ImageInput::from).collect())
239    }
240}
241
242impl<const N: usize> From<[PathBuf; N]> for ImageEditInput {
243    fn from(value: [PathBuf; N]) -> Self {
244        Self::Images(value.into_iter().map(ImageInput::from).collect())
245    }
246}
247
248// Vectors of path-like values
249impl<'a> From<Vec<&'a str>> for ImageEditInput {
250    fn from(value: Vec<&'a str>) -> Self {
251        Self::Images(value.into_iter().map(ImageInput::from).collect())
252    }
253}
254
255impl From<Vec<String>> for ImageEditInput {
256    fn from(value: Vec<String>) -> Self {
257        Self::Images(value.into_iter().map(ImageInput::from).collect())
258    }
259}
260
261impl From<Vec<&Path>> for ImageEditInput {
262    fn from(value: Vec<&Path>) -> Self {
263        Self::Images(value.into_iter().map(ImageInput::from).collect())
264    }
265}
266
267impl From<Vec<PathBuf>> for ImageEditInput {
268    fn from(value: Vec<PathBuf>) -> Self {
269        Self::Images(value.into_iter().map(ImageInput::from).collect())
270    }
271}
272
273impl Display for VideoSize {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        write!(
276            f,
277            "{}",
278            match self {
279                Self::S720x1280 => "720x1280",
280                Self::S1280x720 => "1280x720",
281                Self::S1024x1792 => "1024x1792",
282                Self::S1792x1024 => "1792x1024",
283            }
284        )
285    }
286}
287
288impl Display for ImageSize {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        write!(
291            f,
292            "{}",
293            match self {
294                Self::S256x256 => "256x256",
295                Self::S512x512 => "512x512",
296                Self::S1024x1024 => "1024x1024",
297                Self::S1792x1024 => "1792x1024",
298                Self::S1024x1792 => "1024x1792",
299                Self::S1536x1024 => "1536x1024",
300                Self::S1024x1536 => "1024x1536",
301                Self::Auto => "auto",
302            }
303        )
304    }
305}
306
307impl Display for DallE2ImageSize {
308    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309        write!(
310            f,
311            "{}",
312            match self {
313                Self::S256x256 => "256x256",
314                Self::S512x512 => "512x512",
315                Self::S1024x1024 => "1024x1024",
316            }
317        )
318    }
319}
320
321impl Display for ImageModel {
322    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323        write!(
324            f,
325            "{}",
326            match self {
327                Self::DallE2 => "dall-e-2",
328                Self::DallE3 => "dall-e-3",
329                Self::GptImage1 => "gpt-image-1",
330                Self::GptImage1Mini => "gpt-image-1-mini",
331                Self::Other(other) => other,
332            }
333        )
334    }
335}
336
337impl Display for ImageBackground {
338    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339        write!(
340            f,
341            "{}",
342            match self {
343                Self::Transparent => "transparent",
344                Self::Opaque => "opaque",
345                Self::Auto => "auto",
346            }
347        )
348    }
349}
350
351impl Display for ImageOutputFormat {
352    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353        write!(
354            f,
355            "{}",
356            match self {
357                Self::Png => "png",
358                Self::Jpeg => "jpeg",
359                Self::Webp => "webp",
360            }
361        )
362    }
363}
364
365impl Display for InputFidelity {
366    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367        write!(
368            f,
369            "{}",
370            match self {
371                Self::High => "high",
372                Self::Low => "low",
373            }
374        )
375    }
376}
377
378impl Display for ImageQuality {
379    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380        write!(
381            f,
382            "{}",
383            match self {
384                Self::Low => "low",
385                Self::Medium => "medium",
386                Self::High => "high",
387                Self::Auto => "auto",
388                Self::Standard => "standard",
389                Self::HD => "hd",
390            }
391        )
392    }
393}
394
395impl Display for ImageResponseFormat {
396    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397        write!(
398            f,
399            "{}",
400            match self {
401                Self::Url => "url",
402                Self::B64Json => "b64_json",
403            }
404        )
405    }
406}
407
408impl Display for AudioResponseFormat {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        write!(
411            f,
412            "{}",
413            match self {
414                AudioResponseFormat::Json => "json",
415                AudioResponseFormat::Srt => "srt",
416                AudioResponseFormat::Text => "text",
417                AudioResponseFormat::VerboseJson => "verbose_json",
418                AudioResponseFormat::Vtt => "vtt",
419                AudioResponseFormat::DiarizedJson => "diarized_json",
420            }
421        )
422    }
423}
424
425impl Display for TranslationResponseFormat {
426    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427        write!(
428            f,
429            "{}",
430            match self {
431                TranslationResponseFormat::Json => "json",
432                TranslationResponseFormat::Srt => "srt",
433                TranslationResponseFormat::Text => "text",
434                TranslationResponseFormat::VerboseJson => "verbose_json",
435                TranslationResponseFormat::Vtt => "vtt",
436            }
437        )
438    }
439}
440
441impl Display for TimestampGranularity {
442    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443        write!(
444            f,
445            "{}",
446            match self {
447                TimestampGranularity::Word => "word",
448                TimestampGranularity::Segment => "segment",
449            }
450        )
451    }
452}
453
454impl Display for TranscriptionInclude {
455    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
456        write!(
457            f,
458            "{}",
459            match self {
460                TranscriptionInclude::Logprobs => "logprobs",
461            }
462        )
463    }
464}
465
466impl Display for Role {
467    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468        write!(
469            f,
470            "{}",
471            match self {
472                Role::User => "user",
473                Role::System => "system",
474                Role::Assistant => "assistant",
475                Role::Function => "function",
476                Role::Tool => "tool",
477            }
478        )
479    }
480}
481
482impl Display for FilePurpose {
483    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484        write!(
485            f,
486            "{}",
487            match self {
488                Self::Assistants => "assistants",
489                Self::Batch => "batch",
490                Self::FineTune => "fine-tune",
491                Self::Vision => "vision",
492                Self::UserData => "user_data",
493                Self::Evals => "evals",
494            }
495        )
496    }
497}
498
499impl Display for FileExpirationAfterAnchor {
500    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501        write!(
502            f,
503            "{}",
504            match self {
505                Self::CreatedAt => "created_at",
506            }
507        )
508    }
509}
510
511impl ImagesResponse {
512    /// Save each image in a dedicated Tokio task and return paths to saved files.
513    /// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.
514    pub async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>, OpenAIError> {
515        create_all_dir(dir.as_ref())?;
516
517        let mut handles = vec![];
518        for id in self.data.clone() {
519            let dir_buf = PathBuf::from(dir.as_ref());
520            handles.push(tokio::spawn(async move { id.save(dir_buf).await }));
521        }
522
523        let results = futures::future::join_all(handles).await;
524        let mut errors = vec![];
525        let mut paths = vec![];
526
527        for result in results {
528            match result {
529                Ok(inner) => match inner {
530                    Ok(path) => paths.push(path),
531                    Err(e) => errors.push(e),
532                },
533                Err(e) => errors.push(OpenAIError::FileSaveError(e.to_string())),
534            }
535        }
536
537        if errors.is_empty() {
538            Ok(paths)
539        } else {
540            Err(OpenAIError::FileSaveError(
541                errors
542                    .into_iter()
543                    .map(|e| e.to_string())
544                    .collect::<Vec<String>>()
545                    .join("; "),
546            ))
547        }
548    }
549}
550
551impl CreateSpeechResponse {
552    pub async fn save<P: AsRef<Path>>(&self, file_path: P) -> Result<(), OpenAIError> {
553        let dir = file_path.as_ref().parent();
554
555        if let Some(dir) = dir {
556            create_all_dir(dir)?;
557        }
558
559        tokio::fs::write(file_path, &self.bytes)
560            .await
561            .map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
562
563        Ok(())
564    }
565}
566
567impl Image {
568    async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, OpenAIError> {
569        match self {
570            Image::Url { url, .. } => download_url(url, dir).await,
571            Image::B64Json { b64_json, .. } => save_b64(b64_json, dir).await,
572        }
573    }
574}
575
576macro_rules! impl_from_for_integer_array {
577    ($from_typ:ty, $to_typ:ty) => {
578        impl<const N: usize> From<[$from_typ; N]> for $to_typ {
579            fn from(value: [$from_typ; N]) -> Self {
580                Self::IntegerArray(value.to_vec())
581            }
582        }
583
584        impl<const N: usize> From<&[$from_typ; N]> for $to_typ {
585            fn from(value: &[$from_typ; N]) -> Self {
586                Self::IntegerArray(value.to_vec())
587            }
588        }
589
590        impl From<Vec<$from_typ>> for $to_typ {
591            fn from(value: Vec<$from_typ>) -> Self {
592                Self::IntegerArray(value)
593            }
594        }
595
596        impl From<&Vec<$from_typ>> for $to_typ {
597            fn from(value: &Vec<$from_typ>) -> Self {
598                Self::IntegerArray(value.clone())
599            }
600        }
601    };
602}
603
604impl_from_for_integer_array!(u32, EmbeddingInput);
605impl_from_for_integer_array!(u32, Prompt);
606
607macro_rules! impl_from_for_array_of_integer_array {
608    ($from_typ:ty, $to_typ:ty) => {
609        impl From<Vec<Vec<$from_typ>>> for $to_typ {
610            fn from(value: Vec<Vec<$from_typ>>) -> Self {
611                Self::ArrayOfIntegerArray(value)
612            }
613        }
614
615        impl From<&Vec<Vec<$from_typ>>> for $to_typ {
616            fn from(value: &Vec<Vec<$from_typ>>) -> Self {
617                Self::ArrayOfIntegerArray(value.clone())
618            }
619        }
620
621        impl<const M: usize, const N: usize> From<[[$from_typ; N]; M]> for $to_typ {
622            fn from(value: [[$from_typ; N]; M]) -> Self {
623                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
624            }
625        }
626
627        impl<const M: usize, const N: usize> From<[&[$from_typ; N]; M]> for $to_typ {
628            fn from(value: [&[$from_typ; N]; M]) -> Self {
629                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
630            }
631        }
632
633        impl<const M: usize, const N: usize> From<&[[$from_typ; N]; M]> for $to_typ {
634            fn from(value: &[[$from_typ; N]; M]) -> Self {
635                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
636            }
637        }
638
639        impl<const M: usize, const N: usize> From<&[&[$from_typ; N]; M]> for $to_typ {
640            fn from(value: &[&[$from_typ; N]; M]) -> Self {
641                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
642            }
643        }
644
645        impl<const N: usize> From<[Vec<$from_typ>; N]> for $to_typ {
646            fn from(value: [Vec<$from_typ>; N]) -> Self {
647                Self::ArrayOfIntegerArray(value.to_vec())
648            }
649        }
650
651        impl<const N: usize> From<&[Vec<$from_typ>; N]> for $to_typ {
652            fn from(value: &[Vec<$from_typ>; N]) -> Self {
653                Self::ArrayOfIntegerArray(value.to_vec())
654            }
655        }
656
657        impl<const N: usize> From<[&Vec<$from_typ>; N]> for $to_typ {
658            fn from(value: [&Vec<$from_typ>; N]) -> Self {
659                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.clone()).collect())
660            }
661        }
662
663        impl<const N: usize> From<&[&Vec<$from_typ>; N]> for $to_typ {
664            fn from(value: &[&Vec<$from_typ>; N]) -> Self {
665                Self::ArrayOfIntegerArray(
666                    value
667                        .to_vec()
668                        .into_iter()
669                        .map(|inner| inner.clone())
670                        .collect(),
671                )
672            }
673        }
674
675        impl<const N: usize> From<Vec<[$from_typ; N]>> for $to_typ {
676            fn from(value: Vec<[$from_typ; N]>) -> Self {
677                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
678            }
679        }
680
681        impl<const N: usize> From<&Vec<[$from_typ; N]>> for $to_typ {
682            fn from(value: &Vec<[$from_typ; N]>) -> Self {
683                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
684            }
685        }
686
687        impl<const N: usize> From<Vec<&[$from_typ; N]>> for $to_typ {
688            fn from(value: Vec<&[$from_typ; N]>) -> Self {
689                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
690            }
691        }
692
693        impl<const N: usize> From<&Vec<&[$from_typ; N]>> for $to_typ {
694            fn from(value: &Vec<&[$from_typ; N]>) -> Self {
695                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
696            }
697        }
698    };
699}
700
701impl_from_for_array_of_integer_array!(u32, EmbeddingInput);
702impl_from_for_array_of_integer_array!(u32, Prompt);
703
704impl From<&str> for ChatCompletionFunctionCall {
705    fn from(value: &str) -> Self {
706        match value {
707            "auto" => Self::Auto,
708            "none" => Self::None,
709            _ => Self::Function { name: value.into() },
710        }
711    }
712}
713
714impl From<&str> for FunctionName {
715    fn from(value: &str) -> Self {
716        Self { name: value.into() }
717    }
718}
719
720impl From<String> for FunctionName {
721    fn from(value: String) -> Self {
722        Self { name: value }
723    }
724}
725
726impl From<&str> for ChatCompletionNamedToolChoice {
727    fn from(value: &str) -> Self {
728        Self {
729            function: value.into(),
730        }
731    }
732}
733
734impl From<String> for ChatCompletionNamedToolChoice {
735    fn from(value: String) -> Self {
736        Self {
737            function: value.into(),
738        }
739    }
740}
741
742impl From<(String, serde_json::Value)> for ChatCompletionFunctions {
743    fn from(value: (String, serde_json::Value)) -> Self {
744        Self {
745            name: value.0,
746            description: None,
747            parameters: value.1,
748        }
749    }
750}
751
752// todo: write macro for bunch of same looking From trait implementations below
753
754impl From<ChatCompletionRequestUserMessage> for ChatCompletionRequestMessage {
755    fn from(value: ChatCompletionRequestUserMessage) -> Self {
756        Self::User(value)
757    }
758}
759
760impl From<ChatCompletionRequestSystemMessage> for ChatCompletionRequestMessage {
761    fn from(value: ChatCompletionRequestSystemMessage) -> Self {
762        Self::System(value)
763    }
764}
765
766impl From<ChatCompletionRequestDeveloperMessage> for ChatCompletionRequestMessage {
767    fn from(value: ChatCompletionRequestDeveloperMessage) -> Self {
768        Self::Developer(value)
769    }
770}
771
772impl From<ChatCompletionRequestAssistantMessage> for ChatCompletionRequestMessage {
773    fn from(value: ChatCompletionRequestAssistantMessage) -> Self {
774        Self::Assistant(value)
775    }
776}
777
778impl From<ChatCompletionRequestFunctionMessage> for ChatCompletionRequestMessage {
779    fn from(value: ChatCompletionRequestFunctionMessage) -> Self {
780        Self::Function(value)
781    }
782}
783
784impl From<ChatCompletionRequestToolMessage> for ChatCompletionRequestMessage {
785    fn from(value: ChatCompletionRequestToolMessage) -> Self {
786        Self::Tool(value)
787    }
788}
789
790impl From<ChatCompletionRequestUserMessageContent> for ChatCompletionRequestUserMessage {
791    fn from(value: ChatCompletionRequestUserMessageContent) -> Self {
792        Self {
793            content: value,
794            name: None,
795        }
796    }
797}
798
799impl From<ChatCompletionRequestSystemMessageContent> for ChatCompletionRequestSystemMessage {
800    fn from(value: ChatCompletionRequestSystemMessageContent) -> Self {
801        Self {
802            content: value,
803            name: None,
804        }
805    }
806}
807
808impl From<ChatCompletionRequestDeveloperMessageContent> for ChatCompletionRequestDeveloperMessage {
809    fn from(value: ChatCompletionRequestDeveloperMessageContent) -> Self {
810        Self {
811            content: value,
812            name: None,
813        }
814    }
815}
816
817impl From<ChatCompletionRequestAssistantMessageContent> for ChatCompletionRequestAssistantMessage {
818    fn from(value: ChatCompletionRequestAssistantMessageContent) -> Self {
819        Self {
820            content: Some(value),
821            ..Default::default()
822        }
823    }
824}
825
826impl From<&str> for ChatCompletionRequestUserMessageContent {
827    fn from(value: &str) -> Self {
828        ChatCompletionRequestUserMessageContent::Text(value.into())
829    }
830}
831
832impl From<String> for ChatCompletionRequestUserMessageContent {
833    fn from(value: String) -> Self {
834        ChatCompletionRequestUserMessageContent::Text(value)
835    }
836}
837
838impl From<&str> for ChatCompletionRequestSystemMessageContent {
839    fn from(value: &str) -> Self {
840        ChatCompletionRequestSystemMessageContent::Text(value.into())
841    }
842}
843
844impl From<String> for ChatCompletionRequestSystemMessageContent {
845    fn from(value: String) -> Self {
846        ChatCompletionRequestSystemMessageContent::Text(value)
847    }
848}
849
850impl From<&str> for ChatCompletionRequestDeveloperMessageContent {
851    fn from(value: &str) -> Self {
852        ChatCompletionRequestDeveloperMessageContent::Text(value.into())
853    }
854}
855
856impl From<String> for ChatCompletionRequestDeveloperMessageContent {
857    fn from(value: String) -> Self {
858        ChatCompletionRequestDeveloperMessageContent::Text(value)
859    }
860}
861
862impl From<&str> for ChatCompletionRequestAssistantMessageContent {
863    fn from(value: &str) -> Self {
864        ChatCompletionRequestAssistantMessageContent::Text(value.into())
865    }
866}
867
868impl From<String> for ChatCompletionRequestAssistantMessageContent {
869    fn from(value: String) -> Self {
870        ChatCompletionRequestAssistantMessageContent::Text(value)
871    }
872}
873
874impl From<&str> for ChatCompletionRequestToolMessageContent {
875    fn from(value: &str) -> Self {
876        ChatCompletionRequestToolMessageContent::Text(value.into())
877    }
878}
879
880impl From<String> for ChatCompletionRequestToolMessageContent {
881    fn from(value: String) -> Self {
882        ChatCompletionRequestToolMessageContent::Text(value)
883    }
884}
885
886impl From<&str> for ChatCompletionRequestUserMessage {
887    fn from(value: &str) -> Self {
888        ChatCompletionRequestUserMessageContent::Text(value.into()).into()
889    }
890}
891
892impl From<String> for ChatCompletionRequestUserMessage {
893    fn from(value: String) -> Self {
894        value.as_str().into()
895    }
896}
897
898impl From<&str> for ChatCompletionRequestSystemMessage {
899    fn from(value: &str) -> Self {
900        ChatCompletionRequestSystemMessageContent::Text(value.into()).into()
901    }
902}
903
904impl From<&str> for ChatCompletionRequestDeveloperMessage {
905    fn from(value: &str) -> Self {
906        ChatCompletionRequestDeveloperMessageContent::Text(value.into()).into()
907    }
908}
909
910impl From<String> for ChatCompletionRequestSystemMessage {
911    fn from(value: String) -> Self {
912        value.as_str().into()
913    }
914}
915
916impl From<String> for ChatCompletionRequestDeveloperMessage {
917    fn from(value: String) -> Self {
918        value.as_str().into()
919    }
920}
921
922impl From<&str> for ChatCompletionRequestAssistantMessage {
923    fn from(value: &str) -> Self {
924        ChatCompletionRequestAssistantMessageContent::Text(value.into()).into()
925    }
926}
927
928impl From<String> for ChatCompletionRequestAssistantMessage {
929    fn from(value: String) -> Self {
930        value.as_str().into()
931    }
932}
933
934impl From<Vec<ChatCompletionRequestUserMessageContentPart>>
935    for ChatCompletionRequestUserMessageContent
936{
937    fn from(value: Vec<ChatCompletionRequestUserMessageContentPart>) -> Self {
938        ChatCompletionRequestUserMessageContent::Array(value)
939    }
940}
941
942impl From<ChatCompletionRequestMessageContentPartText>
943    for ChatCompletionRequestUserMessageContentPart
944{
945    fn from(value: ChatCompletionRequestMessageContentPartText) -> Self {
946        ChatCompletionRequestUserMessageContentPart::Text(value)
947    }
948}
949
950impl From<ChatCompletionRequestMessageContentPartImage>
951    for ChatCompletionRequestUserMessageContentPart
952{
953    fn from(value: ChatCompletionRequestMessageContentPartImage) -> Self {
954        ChatCompletionRequestUserMessageContentPart::ImageUrl(value)
955    }
956}
957
958impl From<ChatCompletionRequestMessageContentPartAudio>
959    for ChatCompletionRequestUserMessageContentPart
960{
961    fn from(value: ChatCompletionRequestMessageContentPartAudio) -> Self {
962        ChatCompletionRequestUserMessageContentPart::InputAudio(value)
963    }
964}
965
966impl From<&str> for ChatCompletionRequestMessageContentPartText {
967    fn from(value: &str) -> Self {
968        ChatCompletionRequestMessageContentPartText { text: value.into() }
969    }
970}
971
972impl From<String> for ChatCompletionRequestMessageContentPartText {
973    fn from(value: String) -> Self {
974        ChatCompletionRequestMessageContentPartText { text: value }
975    }
976}
977
978impl From<&str> for ImageUrl {
979    fn from(value: &str) -> Self {
980        Self {
981            url: value.into(),
982            detail: Default::default(),
983        }
984    }
985}
986
987impl From<String> for ImageUrl {
988    fn from(value: String) -> Self {
989        Self {
990            url: value,
991            detail: Default::default(),
992        }
993    }
994}
995
996impl From<String> for CreateMessageRequestContent {
997    fn from(value: String) -> Self {
998        Self::Content(value)
999    }
1000}
1001
1002impl From<&str> for CreateMessageRequestContent {
1003    fn from(value: &str) -> Self {
1004        Self::Content(value.to_string())
1005    }
1006}
1007
1008impl Default for ChatCompletionRequestUserMessageContent {
1009    fn default() -> Self {
1010        ChatCompletionRequestUserMessageContent::Text("".into())
1011    }
1012}
1013
1014impl Default for CreateMessageRequestContent {
1015    fn default() -> Self {
1016        Self::Content("".into())
1017    }
1018}
1019
1020impl Default for ChatCompletionRequestDeveloperMessageContent {
1021    fn default() -> Self {
1022        ChatCompletionRequestDeveloperMessageContent::Text("".into())
1023    }
1024}
1025
1026impl Default for ChatCompletionRequestSystemMessageContent {
1027    fn default() -> Self {
1028        ChatCompletionRequestSystemMessageContent::Text("".into())
1029    }
1030}
1031
1032impl Default for ChatCompletionRequestToolMessageContent {
1033    fn default() -> Self {
1034        ChatCompletionRequestToolMessageContent::Text("".into())
1035    }
1036}
1037
1038// start: types to multipart from
1039
1040impl AsyncTryFrom<CreateTranscriptionRequest> for reqwest::multipart::Form {
1041    type Error = OpenAIError;
1042
1043    async fn try_from(request: CreateTranscriptionRequest) -> Result<Self, Self::Error> {
1044        let audio_part = create_file_part(request.file.source).await?;
1045
1046        let mut form = reqwest::multipart::Form::new()
1047            .part("file", audio_part)
1048            .text("model", request.model);
1049
1050        if let Some(language) = request.language {
1051            form = form.text("language", language);
1052        }
1053
1054        if let Some(prompt) = request.prompt {
1055            form = form.text("prompt", prompt);
1056        }
1057
1058        if let Some(response_format) = request.response_format {
1059            form = form.text("response_format", response_format.to_string())
1060        }
1061
1062        if let Some(temperature) = request.temperature {
1063            form = form.text("temperature", temperature.to_string())
1064        }
1065
1066        if let Some(include) = request.include {
1067            for inc in include {
1068                form = form.text("include[]", inc.to_string());
1069            }
1070        }
1071
1072        if let Some(timestamp_granularities) = request.timestamp_granularities {
1073            for tg in timestamp_granularities {
1074                form = form.text("timestamp_granularities[]", tg.to_string());
1075            }
1076        }
1077
1078        if let Some(stream) = request.stream {
1079            form = form.text("stream", stream.to_string());
1080        }
1081
1082        if let Some(chunking_strategy) = request.chunking_strategy {
1083            match chunking_strategy {
1084                TranscriptionChunkingStrategy::Auto => {
1085                    form = form.text("chunking_strategy", "auto");
1086                }
1087                TranscriptionChunkingStrategy::ServerVad(vad_config) => {
1088                    form = form.text(
1089                        "chunking_strategy",
1090                        serde_json::to_string(&vad_config).unwrap().to_string(),
1091                    );
1092                }
1093            }
1094        }
1095
1096        if let Some(known_speaker_names) = request.known_speaker_names {
1097            for kn in known_speaker_names {
1098                form = form.text("known_speaker_names[]", kn.to_string());
1099            }
1100        }
1101
1102        if let Some(known_speaker_references) = request.known_speaker_references {
1103            for kn in known_speaker_references {
1104                form = form.text("known_speaker_references[]", kn.to_string());
1105            }
1106        }
1107
1108        Ok(form)
1109    }
1110}
1111
1112impl AsyncTryFrom<CreateTranslationRequest> for reqwest::multipart::Form {
1113    type Error = OpenAIError;
1114
1115    async fn try_from(request: CreateTranslationRequest) -> Result<Self, Self::Error> {
1116        let audio_part = create_file_part(request.file.source).await?;
1117
1118        let mut form = reqwest::multipart::Form::new()
1119            .part("file", audio_part)
1120            .text("model", request.model);
1121
1122        if let Some(prompt) = request.prompt {
1123            form = form.text("prompt", prompt);
1124        }
1125
1126        if let Some(response_format) = request.response_format {
1127            form = form.text("response_format", response_format.to_string())
1128        }
1129
1130        if let Some(temperature) = request.temperature {
1131            form = form.text("temperature", temperature.to_string())
1132        }
1133        Ok(form)
1134    }
1135}
1136
1137impl AsyncTryFrom<CreateImageEditRequest> for reqwest::multipart::Form {
1138    type Error = OpenAIError;
1139
1140    async fn try_from(request: CreateImageEditRequest) -> Result<Self, Self::Error> {
1141        let mut form = reqwest::multipart::Form::new().text("prompt", request.prompt);
1142
1143        match request.image {
1144            ImageEditInput::Image(image) => {
1145                let image_part = create_file_part(image.source).await?;
1146                form = form.part("image", image_part);
1147            }
1148            ImageEditInput::Images(images) => {
1149                for image in images {
1150                    let image_part = create_file_part(image.source).await?;
1151                    form = form.part("image[]", image_part);
1152                }
1153            }
1154        }
1155
1156        if let Some(mask) = request.mask {
1157            let mask_part = create_file_part(mask.source).await?;
1158            form = form.part("mask", mask_part);
1159        }
1160
1161        if let Some(background) = request.background {
1162            form = form.text("background", background.to_string())
1163        }
1164
1165        if let Some(model) = request.model {
1166            form = form.text("model", model.to_string())
1167        }
1168
1169        if let Some(n) = request.n {
1170            form = form.text("n", n.to_string())
1171        }
1172
1173        if let Some(size) = request.size {
1174            form = form.text("size", size.to_string())
1175        }
1176
1177        if let Some(response_format) = request.response_format {
1178            form = form.text("response_format", response_format.to_string())
1179        }
1180
1181        if let Some(output_format) = request.output_format {
1182            form = form.text("output_format", output_format.to_string())
1183        }
1184
1185        if let Some(output_compression) = request.output_compression {
1186            form = form.text("output_compression", output_compression.to_string())
1187        }
1188
1189        if let Some(output_compression) = request.output_compression {
1190            form = form.text("output_compression", output_compression.to_string())
1191        }
1192
1193        if let Some(user) = request.user {
1194            form = form.text("user", user)
1195        }
1196
1197        if let Some(input_fidelity) = request.input_fidelity {
1198            form = form.text("input_fidelity", input_fidelity.to_string())
1199        }
1200
1201        if let Some(stream) = request.stream {
1202            form = form.text("stream", stream.to_string())
1203        }
1204
1205        if let Some(partial_images) = request.partial_images {
1206            form = form.text("partial_images", partial_images.to_string())
1207        }
1208
1209        if let Some(quality) = request.quality {
1210            form = form.text("quality", quality.to_string())
1211        }
1212
1213        Ok(form)
1214    }
1215}
1216
1217impl AsyncTryFrom<CreateImageVariationRequest> for reqwest::multipart::Form {
1218    type Error = OpenAIError;
1219
1220    async fn try_from(request: CreateImageVariationRequest) -> Result<Self, Self::Error> {
1221        let image_part = create_file_part(request.image.source).await?;
1222
1223        let mut form = reqwest::multipart::Form::new().part("image", image_part);
1224
1225        if let Some(model) = request.model {
1226            form = form.text("model", model.to_string())
1227        }
1228
1229        if request.n.is_some() {
1230            form = form.text("n", request.n.unwrap().to_string())
1231        }
1232
1233        if request.size.is_some() {
1234            form = form.text("size", request.size.unwrap().to_string())
1235        }
1236
1237        if request.response_format.is_some() {
1238            form = form.text(
1239                "response_format",
1240                request.response_format.unwrap().to_string(),
1241            )
1242        }
1243
1244        if request.user.is_some() {
1245            form = form.text("user", request.user.unwrap())
1246        }
1247        Ok(form)
1248    }
1249}
1250
1251impl AsyncTryFrom<CreateFileRequest> for reqwest::multipart::Form {
1252    type Error = OpenAIError;
1253
1254    async fn try_from(request: CreateFileRequest) -> Result<Self, Self::Error> {
1255        let file_part = create_file_part(request.file.source).await?;
1256        let mut form = reqwest::multipart::Form::new()
1257            .part("file", file_part)
1258            .text("purpose", request.purpose.to_string());
1259
1260        if let Some(expires_after) = request.expires_after {
1261            form = form
1262                .text("expires_after[anchor]", expires_after.anchor.to_string())
1263                .text("expires_after[seconds]", expires_after.seconds.to_string());
1264        }
1265        Ok(form)
1266    }
1267}
1268
1269impl AsyncTryFrom<AddUploadPartRequest> for reqwest::multipart::Form {
1270    type Error = OpenAIError;
1271
1272    async fn try_from(request: AddUploadPartRequest) -> Result<Self, Self::Error> {
1273        let file_part = create_file_part(request.data).await?;
1274        let form = reqwest::multipart::Form::new().part("data", file_part);
1275        Ok(form)
1276    }
1277}
1278
1279impl AsyncTryFrom<CreateContainerFileRequest> for reqwest::multipart::Form {
1280    type Error = OpenAIError;
1281
1282    async fn try_from(request: CreateContainerFileRequest) -> Result<Self, Self::Error> {
1283        let mut form = reqwest::multipart::Form::new();
1284
1285        // Either file or file_id should be provided
1286        if let Some(file_source) = request.file {
1287            let file_part = create_file_part(file_source).await?;
1288            form = form.part("file", file_part);
1289        } else if let Some(file_id) = request.file_id {
1290            form = form.text("file_id", file_id);
1291        }
1292
1293        Ok(form)
1294    }
1295}
1296
1297impl AsyncTryFrom<CreateVideoRequest> for reqwest::multipart::Form {
1298    type Error = OpenAIError;
1299
1300    async fn try_from(request: CreateVideoRequest) -> Result<Self, Self::Error> {
1301        let mut form = reqwest::multipart::Form::new().text("model", request.model);
1302
1303        form = form.text("prompt", request.prompt);
1304
1305        if request.size.is_some() {
1306            form = form.text("size", request.size.unwrap().to_string());
1307        }
1308
1309        if request.seconds.is_some() {
1310            form = form.text("seconds", request.seconds.unwrap());
1311        }
1312
1313        if request.input_reference.is_some() {
1314            let image_part = create_file_part(request.input_reference.unwrap().source).await?;
1315            form = form.part("input_reference", image_part);
1316        }
1317
1318        Ok(form)
1319    }
1320}
1321
1322#[cfg(feature = "realtime")]
1323impl AsyncTryFrom<crate::types::realtime::RealtimeCallCreateRequest> for reqwest::multipart::Form {
1324    type Error = OpenAIError;
1325
1326    async fn try_from(
1327        request: crate::types::realtime::RealtimeCallCreateRequest,
1328    ) -> Result<Self, Self::Error> {
1329        use reqwest::multipart::Part;
1330
1331        // Create SDP part with content type application/sdp
1332        let sdp_part = Part::text(request.sdp)
1333            .mime_str("application/sdp")
1334            .map_err(|e| OpenAIError::InvalidArgument(format!("Invalid content type: {}", e)))?;
1335
1336        let mut form = reqwest::multipart::Form::new().part("sdp", sdp_part);
1337
1338        // Add session as JSON if present
1339        if let Some(session) = request.session {
1340            let session_json = serde_json::to_string(&session).map_err(|e| {
1341                OpenAIError::InvalidArgument(format!("Failed to serialize session: {}", e))
1342            })?;
1343            let session_part = Part::text(session_json)
1344                .mime_str("application/json")
1345                .map_err(|e| {
1346                    OpenAIError::InvalidArgument(format!("Invalid content type: {}", e))
1347                })?;
1348            form = form.part("session", session_part);
1349        }
1350
1351        Ok(form)
1352    }
1353}
1354
1355// end: types to multipart form
1356
1357impl Default for EasyInputContent {
1358    fn default() -> Self {
1359        Self::Text("".to_string())
1360    }
1361}
1362
1363impl From<String> for EasyInputContent {
1364    fn from(value: String) -> Self {
1365        Self::Text(value)
1366    }
1367}
1368
1369impl From<&str> for EasyInputContent {
1370    fn from(value: &str) -> Self {
1371        Self::Text(value.to_owned())
1372    }
1373}