dynamo_async_openai/types/
impls.rs

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