Skip to main content

dynamo_async_openai/types/
impls.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Based on https://github.com/64bit/async-openai/ by Himanshu Neema
5// Original Copyright (c) 2022 Himanshu Neema
6// Licensed under MIT License (see ATTRIBUTIONS-Rust.md)
7//
8// Modifications Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES.
9// Licensed under Apache 2.0
10
11use std::{
12    fmt::Display,
13    path::{Path, PathBuf},
14};
15
16use crate::{
17    download::{download_url, save_b64},
18    error::OpenAIError,
19    traits::AsyncTryFrom,
20    types::InputSource,
21    util::{create_all_dir, create_file_part},
22};
23
24use bytes::Bytes;
25
26use super::{
27    AddUploadPartRequest,
28    AudioInput,
29    AudioResponseFormat,
30    AudioUrl,
31    ChatCompletionFunctionCall,
32    ChatCompletionFunctions,
33    ChatCompletionNamedToolChoice,
34    ChatCompletionRequestAssistantMessage,
35    ChatCompletionRequestAssistantMessageContent,
36    ChatCompletionRequestDeveloperMessage,
37    ChatCompletionRequestDeveloperMessageContent,
38    ChatCompletionRequestFunctionMessage,
39    ChatCompletionRequestMessage,
40    ChatCompletionRequestMessageContentPartAudio,
41    ChatCompletionRequestMessageContentPartAudioUrl,
42    ChatCompletionRequestMessageContentPartImage,
43    ChatCompletionRequestMessageContentPartText,
44    ChatCompletionRequestMessageContentPartVideo,
45    ChatCompletionRequestSystemMessage,
46    ChatCompletionRequestSystemMessageContent,
47    ChatCompletionRequestToolMessage,
48    ChatCompletionRequestToolMessageContent,
49    ChatCompletionRequestUserMessage,
50    ChatCompletionRequestUserMessageContent,
51    ChatCompletionRequestUserMessageContentPart,
52    ChatCompletionToolChoiceOption,
53    CreateFileRequest,
54    CreateImageEditRequest,
55    CreateImageVariationRequest,
56    CreateMessageRequestContent,
57    CreateSpeechResponse,
58    CreateTranscriptionRequest,
59    CreateTranslationRequest,
60    DallE2ImageSize,
61    EmbeddingInput,
62    FileInput,
63    FilePurpose,
64    FunctionName,
65    Image,
66    ImageInput,
67    ImageModel,
68    ImageResponseFormat,
69    ImageSize,
70    ImageUrl,
71    ImagesResponse,
72    ModerationInput,
73    Prompt,
74    Role,
75    Stop,
76    TimestampGranularity,
77    VideoUrl,
78    // responses types now have their own impls in responses/impls.rs
79};
80
81/// for `impl_from!(T, Enum)`, implements
82/// - `From<T>`
83/// - `From<Vec<T>>`
84/// - `From<&Vec<T>>`
85/// - `From<[T; N]>`
86/// - `From<&[T; N]>`
87///
88/// for `T: Into<String>` and `Enum` having variants `String(String)` and `StringArray(Vec<String>)`
89macro_rules! impl_from {
90    ($from_typ:ty, $to_typ:ty) => {
91        // From<T> -> String variant
92        impl From<$from_typ> for $to_typ {
93            fn from(value: $from_typ) -> Self {
94                <$to_typ>::String(value.into())
95            }
96        }
97
98        // From<Vec<T>> -> StringArray variant
99        impl From<Vec<$from_typ>> for $to_typ {
100            fn from(value: Vec<$from_typ>) -> Self {
101                <$to_typ>::StringArray(value.iter().map(|v| v.to_string()).collect())
102            }
103        }
104
105        // From<&Vec<T>> -> StringArray variant
106        impl From<&Vec<$from_typ>> for $to_typ {
107            fn from(value: &Vec<$from_typ>) -> Self {
108                <$to_typ>::StringArray(value.iter().map(|v| v.to_string()).collect())
109            }
110        }
111
112        // From<[T; N]> -> StringArray variant
113        impl<const N: usize> From<[$from_typ; N]> for $to_typ {
114            fn from(value: [$from_typ; N]) -> Self {
115                <$to_typ>::StringArray(value.into_iter().map(|v| v.to_string()).collect())
116            }
117        }
118
119        // From<&[T; N]> -> StringArray variatn
120        impl<const N: usize> From<&[$from_typ; N]> for $to_typ {
121            fn from(value: &[$from_typ; N]) -> Self {
122                <$to_typ>::StringArray(value.into_iter().map(|v| v.to_string()).collect())
123            }
124        }
125    };
126}
127
128// From String "family" to Prompt
129impl_from!(&str, Prompt);
130impl_from!(String, Prompt);
131impl_from!(&String, Prompt);
132
133// From String "family" to Stop
134impl_from!(&str, Stop);
135impl_from!(String, Stop);
136impl_from!(&String, Stop);
137
138// From String "family" to ModerationInput
139impl_from!(&str, ModerationInput);
140impl_from!(String, ModerationInput);
141impl_from!(&String, ModerationInput);
142
143// From String "family" to EmbeddingInput
144impl_from!(&str, EmbeddingInput);
145impl_from!(String, EmbeddingInput);
146impl_from!(&String, EmbeddingInput);
147
148/// for `impl_default!(Enum)`, implements `Default` for `Enum` as `Enum::String("")` where `Enum` has `String` variant
149macro_rules! impl_default {
150    ($for_typ:ty) => {
151        impl Default for $for_typ {
152            fn default() -> Self {
153                Self::String("".into())
154            }
155        }
156    };
157}
158
159impl_default!(Prompt);
160impl_default!(ModerationInput);
161impl_default!(EmbeddingInput);
162
163impl Default for InputSource {
164    fn default() -> Self {
165        InputSource::Path {
166            path: PathBuf::new(),
167        }
168    }
169}
170
171/// for `impl_input!(Struct)` where
172/// ```text
173/// Struct {
174///     source: InputSource
175/// }
176/// ```
177/// implements methods `from_bytes` and `from_vec_u8`,
178/// and `From<P>` for `P: AsRef<Path>`
179macro_rules! impl_input {
180    ($for_typ:ty) => {
181        impl $for_typ {
182            pub fn from_bytes(filename: String, bytes: Bytes) -> Self {
183                Self {
184                    source: InputSource::Bytes { filename, bytes },
185                }
186            }
187
188            pub fn from_vec_u8(filename: String, vec: Vec<u8>) -> Self {
189                Self {
190                    source: InputSource::VecU8 { filename, vec },
191                }
192            }
193        }
194
195        impl<P: AsRef<Path>> From<P> for $for_typ {
196            fn from(path: P) -> Self {
197                let path_buf = path.as_ref().to_path_buf();
198                Self {
199                    source: InputSource::Path { path: path_buf },
200                }
201            }
202        }
203    };
204}
205
206impl_input!(AudioInput);
207impl_input!(FileInput);
208impl_input!(ImageInput);
209
210impl Display for ImageSize {
211    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212        write!(
213            f,
214            "{}",
215            match self {
216                Self::S256x256 => "256x256",
217                Self::S512x512 => "512x512",
218                Self::S1024x1024 => "1024x1024",
219                Self::S1792x1024 => "1792x1024",
220                Self::S1024x1792 => "1024x1792",
221            }
222        )
223    }
224}
225
226impl Display for DallE2ImageSize {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        write!(
229            f,
230            "{}",
231            match self {
232                Self::S256x256 => "256x256",
233                Self::S512x512 => "512x512",
234                Self::S1024x1024 => "1024x1024",
235            }
236        )
237    }
238}
239
240impl Display for ImageModel {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        write!(
243            f,
244            "{}",
245            match self {
246                Self::DallE2 => "dall-e-2",
247                Self::DallE3 => "dall-e-3",
248                Self::Other(other) => other,
249            }
250        )
251    }
252}
253
254impl Display for ImageResponseFormat {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        write!(
257            f,
258            "{}",
259            match self {
260                Self::Url => "url",
261                Self::B64Json => "b64_json",
262            }
263        )
264    }
265}
266
267impl Display for AudioResponseFormat {
268    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269        write!(
270            f,
271            "{}",
272            match self {
273                AudioResponseFormat::Json => "json",
274                AudioResponseFormat::Srt => "srt",
275                AudioResponseFormat::Text => "text",
276                AudioResponseFormat::VerboseJson => "verbose_json",
277                AudioResponseFormat::Vtt => "vtt",
278            }
279        )
280    }
281}
282
283impl Display for TimestampGranularity {
284    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285        write!(
286            f,
287            "{}",
288            match self {
289                TimestampGranularity::Word => "word",
290                TimestampGranularity::Segment => "segment",
291            }
292        )
293    }
294}
295
296impl Display for Role {
297    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298        write!(
299            f,
300            "{}",
301            match self {
302                Role::User => "user",
303                Role::System => "system",
304                Role::Assistant => "assistant",
305                Role::Function => "function",
306                Role::Tool => "tool",
307            }
308        )
309    }
310}
311
312impl Display for FilePurpose {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        write!(
315            f,
316            "{}",
317            match self {
318                Self::Assistants => "assistants",
319                Self::Batch => "batch",
320                Self::FineTune => "fine-tune",
321                Self::Vision => "vision",
322            }
323        )
324    }
325}
326
327impl ImagesResponse {
328    /// Save each image in a dedicated Tokio task and return paths to saved files.
329    /// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.
330    pub async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>, OpenAIError> {
331        create_all_dir(dir.as_ref())?;
332
333        let mut handles = vec![];
334        for id in self.data.clone() {
335            let dir_buf = PathBuf::from(dir.as_ref());
336            handles.push(tokio::spawn(async move { id.save(dir_buf).await }));
337        }
338
339        let results = futures::future::join_all(handles).await;
340        let mut errors = vec![];
341        let mut paths = vec![];
342
343        for result in results {
344            match result {
345                Ok(inner) => match inner {
346                    Ok(path) => paths.push(path),
347                    Err(e) => errors.push(e),
348                },
349                Err(e) => errors.push(OpenAIError::FileSaveError(e.to_string())),
350            }
351        }
352
353        if errors.is_empty() {
354            Ok(paths)
355        } else {
356            Err(OpenAIError::FileSaveError(
357                errors
358                    .into_iter()
359                    .map(|e| e.to_string())
360                    .collect::<Vec<String>>()
361                    .join("; "),
362            ))
363        }
364    }
365}
366
367impl CreateSpeechResponse {
368    pub async fn save<P: AsRef<Path>>(&self, file_path: P) -> Result<(), OpenAIError> {
369        let dir = file_path.as_ref().parent();
370
371        if let Some(dir) = dir {
372            create_all_dir(dir)?;
373        }
374
375        tokio::fs::write(file_path, &self.bytes)
376            .await
377            .map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
378
379        Ok(())
380    }
381}
382
383impl Image {
384    async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, OpenAIError> {
385        match self {
386            Image::Url { url, .. } => download_url(url, dir).await,
387            Image::B64Json { b64_json, .. } => save_b64(b64_json, dir).await,
388        }
389    }
390}
391
392macro_rules! impl_from_for_integer_array {
393    ($from_typ:ty, $to_typ:ty) => {
394        impl<const N: usize> From<[$from_typ; N]> for $to_typ {
395            fn from(value: [$from_typ; N]) -> Self {
396                Self::IntegerArray(value.to_vec())
397            }
398        }
399
400        impl<const N: usize> From<&[$from_typ; N]> for $to_typ {
401            fn from(value: &[$from_typ; N]) -> Self {
402                Self::IntegerArray(value.to_vec())
403            }
404        }
405
406        impl From<Vec<$from_typ>> for $to_typ {
407            fn from(value: Vec<$from_typ>) -> Self {
408                Self::IntegerArray(value)
409            }
410        }
411
412        impl From<&Vec<$from_typ>> for $to_typ {
413            fn from(value: &Vec<$from_typ>) -> Self {
414                Self::IntegerArray(value.clone())
415            }
416        }
417    };
418}
419
420impl_from_for_integer_array!(u32, EmbeddingInput);
421impl_from_for_integer_array!(u32, Prompt);
422
423macro_rules! impl_from_for_array_of_integer_array {
424    ($from_typ:ty, $to_typ:ty) => {
425        impl From<Vec<Vec<$from_typ>>> for $to_typ {
426            fn from(value: Vec<Vec<$from_typ>>) -> Self {
427                Self::ArrayOfIntegerArray(value)
428            }
429        }
430
431        impl From<&Vec<Vec<$from_typ>>> for $to_typ {
432            fn from(value: &Vec<Vec<$from_typ>>) -> Self {
433                Self::ArrayOfIntegerArray(value.clone())
434            }
435        }
436
437        impl<const M: usize, const N: usize> From<[[$from_typ; N]; M]> for $to_typ {
438            fn from(value: [[$from_typ; N]; M]) -> Self {
439                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
440            }
441        }
442
443        impl<const M: usize, const N: usize> From<[&[$from_typ; N]; M]> for $to_typ {
444            fn from(value: [&[$from_typ; N]; M]) -> Self {
445                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
446            }
447        }
448
449        impl<const M: usize, const N: usize> From<&[[$from_typ; N]; M]> for $to_typ {
450            fn from(value: &[[$from_typ; N]; M]) -> Self {
451                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
452            }
453        }
454
455        impl<const M: usize, const N: usize> From<&[&[$from_typ; N]; M]> for $to_typ {
456            fn from(value: &[&[$from_typ; N]; M]) -> Self {
457                Self::ArrayOfIntegerArray(value.iter().map(|inner| inner.to_vec()).collect())
458            }
459        }
460
461        impl<const N: usize> From<[Vec<$from_typ>; N]> for $to_typ {
462            fn from(value: [Vec<$from_typ>; N]) -> Self {
463                Self::ArrayOfIntegerArray(value.to_vec())
464            }
465        }
466
467        impl<const N: usize> From<&[Vec<$from_typ>; N]> for $to_typ {
468            fn from(value: &[Vec<$from_typ>; N]) -> Self {
469                Self::ArrayOfIntegerArray(value.to_vec())
470            }
471        }
472
473        impl<const N: usize> From<[&Vec<$from_typ>; N]> for $to_typ {
474            fn from(value: [&Vec<$from_typ>; N]) -> Self {
475                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.clone()).collect())
476            }
477        }
478
479        impl<const N: usize> From<&[&Vec<$from_typ>; N]> for $to_typ {
480            fn from(value: &[&Vec<$from_typ>; N]) -> Self {
481                Self::ArrayOfIntegerArray(
482                    value
483                        .to_vec()
484                        .into_iter()
485                        .map(|inner| inner.clone())
486                        .collect(),
487                )
488            }
489        }
490
491        impl<const N: usize> From<Vec<[$from_typ; N]>> for $to_typ {
492            fn from(value: Vec<[$from_typ; N]>) -> Self {
493                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
494            }
495        }
496
497        impl<const N: usize> From<&Vec<[$from_typ; N]>> for $to_typ {
498            fn from(value: &Vec<[$from_typ; N]>) -> Self {
499                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
500            }
501        }
502
503        impl<const N: usize> From<Vec<&[$from_typ; N]>> for $to_typ {
504            fn from(value: Vec<&[$from_typ; N]>) -> Self {
505                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
506            }
507        }
508
509        impl<const N: usize> From<&Vec<&[$from_typ; N]>> for $to_typ {
510            fn from(value: &Vec<&[$from_typ; N]>) -> Self {
511                Self::ArrayOfIntegerArray(value.into_iter().map(|inner| inner.to_vec()).collect())
512            }
513        }
514    };
515}
516
517impl_from_for_array_of_integer_array!(u32, EmbeddingInput);
518impl_from_for_array_of_integer_array!(u32, Prompt);
519
520impl From<&str> for ChatCompletionFunctionCall {
521    fn from(value: &str) -> Self {
522        match value {
523            "auto" => Self::Auto,
524            "none" => Self::None,
525            _ => Self::Function { name: value.into() },
526        }
527    }
528}
529
530impl From<&str> for FunctionName {
531    fn from(value: &str) -> Self {
532        Self { name: value.into() }
533    }
534}
535
536impl From<String> for FunctionName {
537    fn from(value: String) -> Self {
538        Self { name: value }
539    }
540}
541
542impl From<&str> for ChatCompletionNamedToolChoice {
543    fn from(value: &str) -> Self {
544        Self {
545            r#type: super::ChatCompletionToolType::Function,
546            function: value.into(),
547        }
548    }
549}
550
551impl From<String> for ChatCompletionNamedToolChoice {
552    fn from(value: String) -> Self {
553        Self {
554            r#type: super::ChatCompletionToolType::Function,
555            function: value.into(),
556        }
557    }
558}
559
560impl From<&str> for ChatCompletionToolChoiceOption {
561    fn from(value: &str) -> Self {
562        match value {
563            "auto" => Self::Auto,
564            "none" => Self::None,
565            _ => Self::Named(value.into()),
566        }
567    }
568}
569
570impl From<String> for ChatCompletionToolChoiceOption {
571    fn from(value: String) -> Self {
572        match value.as_str() {
573            "auto" => Self::Auto,
574            "none" => Self::None,
575            _ => Self::Named(value.into()),
576        }
577    }
578}
579
580impl From<(String, serde_json::Value)> for ChatCompletionFunctions {
581    fn from(value: (String, serde_json::Value)) -> Self {
582        Self {
583            name: value.0,
584            description: None,
585            parameters: value.1,
586        }
587    }
588}
589
590// todo: write macro for bunch of same looking From trait implementations below
591
592impl From<ChatCompletionRequestUserMessage> for ChatCompletionRequestMessage {
593    fn from(value: ChatCompletionRequestUserMessage) -> Self {
594        Self::User(value)
595    }
596}
597
598impl From<ChatCompletionRequestSystemMessage> for ChatCompletionRequestMessage {
599    fn from(value: ChatCompletionRequestSystemMessage) -> Self {
600        Self::System(value)
601    }
602}
603
604impl From<ChatCompletionRequestDeveloperMessage> for ChatCompletionRequestMessage {
605    fn from(value: ChatCompletionRequestDeveloperMessage) -> Self {
606        Self::Developer(value)
607    }
608}
609
610impl From<ChatCompletionRequestAssistantMessage> for ChatCompletionRequestMessage {
611    fn from(value: ChatCompletionRequestAssistantMessage) -> Self {
612        Self::Assistant(value)
613    }
614}
615
616impl From<ChatCompletionRequestFunctionMessage> for ChatCompletionRequestMessage {
617    fn from(value: ChatCompletionRequestFunctionMessage) -> Self {
618        Self::Function(value)
619    }
620}
621
622impl From<ChatCompletionRequestToolMessage> for ChatCompletionRequestMessage {
623    fn from(value: ChatCompletionRequestToolMessage) -> Self {
624        Self::Tool(value)
625    }
626}
627
628impl From<ChatCompletionRequestUserMessageContent> for ChatCompletionRequestUserMessage {
629    fn from(value: ChatCompletionRequestUserMessageContent) -> Self {
630        Self {
631            content: value,
632            name: None,
633        }
634    }
635}
636
637impl From<ChatCompletionRequestSystemMessageContent> for ChatCompletionRequestSystemMessage {
638    fn from(value: ChatCompletionRequestSystemMessageContent) -> Self {
639        Self {
640            content: value,
641            name: None,
642        }
643    }
644}
645
646impl From<ChatCompletionRequestDeveloperMessageContent> for ChatCompletionRequestDeveloperMessage {
647    fn from(value: ChatCompletionRequestDeveloperMessageContent) -> Self {
648        Self {
649            content: value,
650            name: None,
651        }
652    }
653}
654
655impl From<ChatCompletionRequestAssistantMessageContent> for ChatCompletionRequestAssistantMessage {
656    fn from(value: ChatCompletionRequestAssistantMessageContent) -> Self {
657        Self {
658            content: Some(value),
659            ..Default::default()
660        }
661    }
662}
663
664impl From<&str> for ChatCompletionRequestUserMessageContent {
665    fn from(value: &str) -> Self {
666        ChatCompletionRequestUserMessageContent::Text(value.into())
667    }
668}
669
670impl From<String> for ChatCompletionRequestUserMessageContent {
671    fn from(value: String) -> Self {
672        ChatCompletionRequestUserMessageContent::Text(value)
673    }
674}
675
676impl From<&str> for ChatCompletionRequestSystemMessageContent {
677    fn from(value: &str) -> Self {
678        ChatCompletionRequestSystemMessageContent::Text(value.into())
679    }
680}
681
682impl From<String> for ChatCompletionRequestSystemMessageContent {
683    fn from(value: String) -> Self {
684        ChatCompletionRequestSystemMessageContent::Text(value)
685    }
686}
687
688impl From<&str> for ChatCompletionRequestDeveloperMessageContent {
689    fn from(value: &str) -> Self {
690        ChatCompletionRequestDeveloperMessageContent::Text(value.into())
691    }
692}
693
694impl From<String> for ChatCompletionRequestDeveloperMessageContent {
695    fn from(value: String) -> Self {
696        ChatCompletionRequestDeveloperMessageContent::Text(value)
697    }
698}
699
700impl From<&str> for ChatCompletionRequestAssistantMessageContent {
701    fn from(value: &str) -> Self {
702        ChatCompletionRequestAssistantMessageContent::Text(value.into())
703    }
704}
705
706impl From<String> for ChatCompletionRequestAssistantMessageContent {
707    fn from(value: String) -> Self {
708        ChatCompletionRequestAssistantMessageContent::Text(value)
709    }
710}
711
712impl From<&str> for ChatCompletionRequestToolMessageContent {
713    fn from(value: &str) -> Self {
714        ChatCompletionRequestToolMessageContent::Text(value.into())
715    }
716}
717
718impl From<String> for ChatCompletionRequestToolMessageContent {
719    fn from(value: String) -> Self {
720        ChatCompletionRequestToolMessageContent::Text(value)
721    }
722}
723
724impl From<&str> for ChatCompletionRequestUserMessage {
725    fn from(value: &str) -> Self {
726        ChatCompletionRequestUserMessageContent::Text(value.into()).into()
727    }
728}
729
730impl From<String> for ChatCompletionRequestUserMessage {
731    fn from(value: String) -> Self {
732        value.as_str().into()
733    }
734}
735
736impl From<&str> for ChatCompletionRequestSystemMessage {
737    fn from(value: &str) -> Self {
738        ChatCompletionRequestSystemMessageContent::Text(value.into()).into()
739    }
740}
741
742impl From<&str> for ChatCompletionRequestDeveloperMessage {
743    fn from(value: &str) -> Self {
744        ChatCompletionRequestDeveloperMessageContent::Text(value.into()).into()
745    }
746}
747
748impl From<String> for ChatCompletionRequestSystemMessage {
749    fn from(value: String) -> Self {
750        value.as_str().into()
751    }
752}
753
754impl From<String> for ChatCompletionRequestDeveloperMessage {
755    fn from(value: String) -> Self {
756        value.as_str().into()
757    }
758}
759
760impl From<&str> for ChatCompletionRequestAssistantMessage {
761    fn from(value: &str) -> Self {
762        ChatCompletionRequestAssistantMessageContent::Text(value.into()).into()
763    }
764}
765
766impl From<String> for ChatCompletionRequestAssistantMessage {
767    fn from(value: String) -> Self {
768        value.as_str().into()
769    }
770}
771
772impl From<Vec<ChatCompletionRequestUserMessageContentPart>>
773    for ChatCompletionRequestUserMessageContent
774{
775    fn from(value: Vec<ChatCompletionRequestUserMessageContentPart>) -> Self {
776        ChatCompletionRequestUserMessageContent::Array(value)
777    }
778}
779
780impl From<ChatCompletionRequestMessageContentPartText>
781    for ChatCompletionRequestUserMessageContentPart
782{
783    fn from(value: ChatCompletionRequestMessageContentPartText) -> Self {
784        ChatCompletionRequestUserMessageContentPart::Text(value)
785    }
786}
787
788impl From<ChatCompletionRequestMessageContentPartImage>
789    for ChatCompletionRequestUserMessageContentPart
790{
791    fn from(value: ChatCompletionRequestMessageContentPartImage) -> Self {
792        ChatCompletionRequestUserMessageContentPart::ImageUrl(value)
793    }
794}
795
796impl From<ChatCompletionRequestMessageContentPartAudio>
797    for ChatCompletionRequestUserMessageContentPart
798{
799    fn from(value: ChatCompletionRequestMessageContentPartAudio) -> Self {
800        ChatCompletionRequestUserMessageContentPart::InputAudio(value)
801    }
802}
803
804impl From<ChatCompletionRequestMessageContentPartVideo>
805    for ChatCompletionRequestUserMessageContentPart
806{
807    fn from(value: ChatCompletionRequestMessageContentPartVideo) -> Self {
808        ChatCompletionRequestUserMessageContentPart::VideoUrl(value)
809    }
810}
811
812impl From<ChatCompletionRequestMessageContentPartAudioUrl>
813    for ChatCompletionRequestUserMessageContentPart
814{
815    fn from(value: ChatCompletionRequestMessageContentPartAudioUrl) -> Self {
816        ChatCompletionRequestUserMessageContentPart::AudioUrl(value)
817    }
818}
819
820impl From<&str> for ChatCompletionRequestMessageContentPartText {
821    fn from(value: &str) -> Self {
822        ChatCompletionRequestMessageContentPartText { text: value.into() }
823    }
824}
825
826impl From<String> for ChatCompletionRequestMessageContentPartText {
827    fn from(value: String) -> Self {
828        ChatCompletionRequestMessageContentPartText { text: value }
829    }
830}
831
832impl From<&str> for ImageUrl {
833    fn from(value: &str) -> Self {
834        Self {
835            url: value.parse().expect("Invalid URL"),
836            detail: Default::default(),
837            uuid: None,
838        }
839    }
840}
841
842impl From<String> for ImageUrl {
843    fn from(value: String) -> Self {
844        Self {
845            url: value.parse().expect("Invalid URL"),
846            detail: Default::default(),
847            uuid: None,
848        }
849    }
850}
851
852impl From<&str> for VideoUrl {
853    fn from(value: &str) -> Self {
854        Self {
855            url: value.parse().expect("Invalid URL"),
856            detail: Default::default(),
857            uuid: None,
858        }
859    }
860}
861
862impl From<String> for VideoUrl {
863    fn from(value: String) -> Self {
864        Self {
865            url: value.parse().expect("Invalid URL"),
866            detail: Default::default(),
867            uuid: None,
868        }
869    }
870}
871
872impl From<&str> for AudioUrl {
873    fn from(value: &str) -> Self {
874        Self {
875            url: value.parse().expect("Invalid URL"),
876            uuid: None,
877        }
878    }
879}
880
881impl From<String> for AudioUrl {
882    fn from(value: String) -> Self {
883        Self {
884            url: value.parse().expect("Invalid URL"),
885            uuid: None,
886        }
887    }
888}
889
890impl From<String> for CreateMessageRequestContent {
891    fn from(value: String) -> Self {
892        Self::Content(value)
893    }
894}
895
896impl From<&str> for CreateMessageRequestContent {
897    fn from(value: &str) -> Self {
898        Self::Content(value.to_string())
899    }
900}
901
902impl Default for ChatCompletionRequestUserMessageContent {
903    fn default() -> Self {
904        ChatCompletionRequestUserMessageContent::Text("".into())
905    }
906}
907
908impl Default for CreateMessageRequestContent {
909    fn default() -> Self {
910        Self::Content("".into())
911    }
912}
913
914impl Default for ChatCompletionRequestDeveloperMessageContent {
915    fn default() -> Self {
916        ChatCompletionRequestDeveloperMessageContent::Text("".into())
917    }
918}
919
920impl Default for ChatCompletionRequestSystemMessageContent {
921    fn default() -> Self {
922        ChatCompletionRequestSystemMessageContent::Text("".into())
923    }
924}
925
926impl Default for ChatCompletionRequestToolMessageContent {
927    fn default() -> Self {
928        ChatCompletionRequestToolMessageContent::Text("".into())
929    }
930}
931
932// start: types to multipart from
933
934impl AsyncTryFrom<CreateTranscriptionRequest> for reqwest::multipart::Form {
935    type Error = OpenAIError;
936
937    async fn try_from(request: CreateTranscriptionRequest) -> Result<Self, Self::Error> {
938        let audio_part = create_file_part(request.file.source).await?;
939
940        let mut form = reqwest::multipart::Form::new()
941            .part("file", audio_part)
942            .text("model", request.model);
943
944        if let Some(prompt) = request.prompt {
945            form = form.text("prompt", prompt);
946        }
947
948        if let Some(response_format) = request.response_format {
949            form = form.text("response_format", response_format.to_string())
950        }
951
952        if let Some(temperature) = request.temperature {
953            form = form.text("temperature", temperature.to_string())
954        }
955
956        if let Some(language) = request.language {
957            form = form.text("language", language);
958        }
959
960        if let Some(timestamp_granularities) = request.timestamp_granularities {
961            for tg in timestamp_granularities {
962                form = form.text("timestamp_granularities[]", tg.to_string());
963            }
964        }
965
966        Ok(form)
967    }
968}
969
970impl AsyncTryFrom<CreateTranslationRequest> for reqwest::multipart::Form {
971    type Error = OpenAIError;
972
973    async fn try_from(request: CreateTranslationRequest) -> Result<Self, Self::Error> {
974        let audio_part = create_file_part(request.file.source).await?;
975
976        let mut form = reqwest::multipart::Form::new()
977            .part("file", audio_part)
978            .text("model", request.model);
979
980        if let Some(prompt) = request.prompt {
981            form = form.text("prompt", prompt);
982        }
983
984        if let Some(response_format) = request.response_format {
985            form = form.text("response_format", response_format.to_string())
986        }
987
988        if let Some(temperature) = request.temperature {
989            form = form.text("temperature", temperature.to_string())
990        }
991        Ok(form)
992    }
993}
994
995impl AsyncTryFrom<CreateImageEditRequest> for reqwest::multipart::Form {
996    type Error = OpenAIError;
997
998    async fn try_from(request: CreateImageEditRequest) -> Result<Self, Self::Error> {
999        let image_part = create_file_part(request.image.source).await?;
1000
1001        let mut form = reqwest::multipart::Form::new()
1002            .part("image", image_part)
1003            .text("prompt", request.prompt);
1004
1005        if let Some(mask) = request.mask {
1006            let mask_part = create_file_part(mask.source).await?;
1007            form = form.part("mask", mask_part);
1008        }
1009
1010        if let Some(model) = request.model {
1011            form = form.text("model", model.to_string())
1012        }
1013
1014        if request.n.is_some() {
1015            form = form.text("n", request.n.unwrap().to_string())
1016        }
1017
1018        if request.size.is_some() {
1019            form = form.text("size", request.size.unwrap().to_string())
1020        }
1021
1022        if request.response_format.is_some() {
1023            form = form.text(
1024                "response_format",
1025                request.response_format.unwrap().to_string(),
1026            )
1027        }
1028
1029        if request.user.is_some() {
1030            form = form.text("user", request.user.unwrap())
1031        }
1032        Ok(form)
1033    }
1034}
1035
1036impl AsyncTryFrom<CreateImageVariationRequest> for reqwest::multipart::Form {
1037    type Error = OpenAIError;
1038
1039    async fn try_from(request: CreateImageVariationRequest) -> Result<Self, Self::Error> {
1040        let image_part = create_file_part(request.image.source).await?;
1041
1042        let mut form = reqwest::multipart::Form::new().part("image", image_part);
1043
1044        if let Some(model) = request.model {
1045            form = form.text("model", model.to_string())
1046        }
1047
1048        if request.n.is_some() {
1049            form = form.text("n", request.n.unwrap().to_string())
1050        }
1051
1052        if request.size.is_some() {
1053            form = form.text("size", request.size.unwrap().to_string())
1054        }
1055
1056        if request.response_format.is_some() {
1057            form = form.text(
1058                "response_format",
1059                request.response_format.unwrap().to_string(),
1060            )
1061        }
1062
1063        if request.user.is_some() {
1064            form = form.text("user", request.user.unwrap())
1065        }
1066        Ok(form)
1067    }
1068}
1069
1070impl AsyncTryFrom<CreateFileRequest> for reqwest::multipart::Form {
1071    type Error = OpenAIError;
1072
1073    async fn try_from(request: CreateFileRequest) -> Result<Self, Self::Error> {
1074        let file_part = create_file_part(request.file.source).await?;
1075        let form = reqwest::multipart::Form::new()
1076            .part("file", file_part)
1077            .text("purpose", request.purpose.to_string());
1078        Ok(form)
1079    }
1080}
1081
1082impl AsyncTryFrom<AddUploadPartRequest> for reqwest::multipart::Form {
1083    type Error = OpenAIError;
1084
1085    async fn try_from(request: AddUploadPartRequest) -> Result<Self, Self::Error> {
1086        let file_part = create_file_part(request.data).await?;
1087        let form = reqwest::multipart::Form::new().part("data", file_part);
1088        Ok(form)
1089    }
1090}
1091
1092// end: types to multipart form
1093
1094// Responses API impls are now in responses/impls.rs