rig/completion/
message.rs

1use std::{convert::Infallible, str::FromStr};
2
3use crate::OneOrMany;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use super::CompletionError;
8
9// ================================================================
10// Message models
11// ================================================================
12
13/// A useful trait to help convert `rig::completion::Message` to your own message type.
14///
15/// Particularly useful if you don't want to create a free-standing function as
16/// when trying to use `TryFrom<T>`, you would normally run into the orphan rule as Vec is
17/// technically considered a foreign type (it's owned by stdlib).
18pub trait ConvertMessage: Sized + Send + Sync {
19    type Error: std::error::Error + Send;
20
21    fn convert_from_message(message: Message) -> Result<Vec<Self>, Self::Error>;
22}
23
24/// A message represents a run of input (user) and output (assistant).
25/// Each message type (based on it's `role`) can contain a atleast one bit of content such as text,
26///  images, audio, documents, or tool related information. While each message type can contain
27///  multiple content, most often, you'll only see one content type per message
28///  (an image w/ a description, etc).
29///
30/// Each provider is responsible with converting the generic message into it's provider specific
31///  type using `From` or `TryFrom` traits. Since not every provider supports every feature, the
32///  conversion can be lossy (providing an image might be discarded for a non-image supporting
33///  provider) though the message being converted back and forth should always be the same.
34#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
35#[serde(tag = "role", rename_all = "lowercase")]
36pub enum Message {
37    /// User message containing one or more content types defined by `UserContent`.
38    User { content: OneOrMany<UserContent> },
39
40    /// Assistant message containing one or more content types defined by `AssistantContent`.
41    Assistant {
42        id: Option<String>,
43        content: OneOrMany<AssistantContent>,
44    },
45}
46
47/// Describes the content of a message, which can be text, a tool result, an image, audio, or
48///  a document. Dependent on provider supporting the content type. Multimedia content is generally
49///  base64 (defined by it's format) encoded but additionally supports urls (for some providers).
50#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
51#[serde(tag = "type", rename_all = "lowercase")]
52pub enum UserContent {
53    Text(Text),
54    ToolResult(ToolResult),
55    Image(Image),
56    Audio(Audio),
57    Video(Video),
58    Document(Document),
59}
60
61/// Describes responses from a provider which is either text or a tool call.
62#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
63#[serde(untagged)]
64pub enum AssistantContent {
65    Text(Text),
66    ToolCall(ToolCall),
67    Reasoning(Reasoning),
68}
69
70#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
71#[non_exhaustive]
72pub struct Reasoning {
73    pub id: Option<String>,
74    pub reasoning: Vec<String>,
75}
76
77impl Reasoning {
78    /// Create a new reasoning item from a single item
79    pub fn new(input: &str) -> Self {
80        Self {
81            id: None,
82            reasoning: vec![input.to_string()],
83        }
84    }
85
86    pub fn optional_id(mut self, id: Option<String>) -> Self {
87        self.id = id;
88        self
89    }
90    pub fn with_id(mut self, id: String) -> Self {
91        self.id = Some(id);
92        self
93    }
94
95    pub fn multi(input: Vec<String>) -> Self {
96        Self {
97            id: None,
98            reasoning: input,
99        }
100    }
101}
102
103/// Tool result content containing information about a tool call and it's resulting content.
104#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
105pub struct ToolResult {
106    pub id: String,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub call_id: Option<String>,
109    pub content: OneOrMany<ToolResultContent>,
110}
111
112/// Describes the content of a tool result, which can be text or an image.
113#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
114#[serde(tag = "type", rename_all = "lowercase")]
115pub enum ToolResultContent {
116    Text(Text),
117    Image(Image),
118}
119
120/// Describes a tool call with an id and function to call, generally produced by a provider.
121#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
122pub struct ToolCall {
123    pub id: String,
124    pub call_id: Option<String>,
125    pub function: ToolFunction,
126}
127
128/// Describes a tool function to call with a name and arguments, generally produced by a provider.
129#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
130pub struct ToolFunction {
131    pub name: String,
132    pub arguments: serde_json::Value,
133}
134
135// ================================================================
136// Base content models
137// ================================================================
138
139/// Basic text content.
140#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
141pub struct Text {
142    pub text: String,
143}
144
145impl Text {
146    pub fn text(&self) -> &str {
147        &self.text
148    }
149}
150
151impl std::fmt::Display for Text {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        let Self { text } = self;
154        write!(f, "{text}")
155    }
156}
157
158/// Image content containing image data and metadata about it.
159#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
160pub struct Image {
161    pub data: DocumentSourceKind,
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub media_type: Option<ImageMediaType>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub detail: Option<ImageDetail>,
166    #[serde(flatten, skip_serializing_if = "Option::is_none")]
167    pub additional_params: Option<serde_json::Value>,
168}
169
170impl Image {
171    pub fn try_into_url(self) -> Result<String, MessageError> {
172        match self.data {
173            DocumentSourceKind::Url(url) => Ok(url),
174            DocumentSourceKind::Base64(data) => {
175                let Some(media_type) = self.media_type else {
176                    return Err(MessageError::ConversionError(
177                        "A media type is required to create a valid base64-encoded image URL"
178                            .to_string(),
179                    ));
180                };
181
182                Ok(format!(
183                    "data:image/{ty};base64,{data}",
184                    ty = media_type.to_mime_type()
185                ))
186            }
187            unknown => Err(MessageError::ConversionError(format!(
188                "Tried to convert unknown type to a URL: {unknown:?}"
189            ))),
190        }
191    }
192}
193
194/// The kind of image source (to be used).
195#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
196#[serde(tag = "type", content = "value", rename_all = "camelCase")]
197#[non_exhaustive]
198pub enum DocumentSourceKind {
199    /// A file URL/URI.
200    Url(String),
201    /// A base-64 encoded string.
202    Base64(String),
203    /// Raw bytes
204    Raw(Vec<u8>),
205    /// A string (or a string literal).
206    String(String),
207    #[default]
208    /// An unknown file source (there's nothing there).
209    Unknown,
210}
211
212impl DocumentSourceKind {
213    pub fn url(url: &str) -> Self {
214        Self::Url(url.to_string())
215    }
216
217    pub fn base64(base64_string: &str) -> Self {
218        Self::Base64(base64_string.to_string())
219    }
220
221    pub fn raw(bytes: impl Into<Vec<u8>>) -> Self {
222        Self::Raw(bytes.into())
223    }
224
225    pub fn string(input: &str) -> Self {
226        Self::String(input.into())
227    }
228
229    pub fn unknown() -> Self {
230        Self::Unknown
231    }
232
233    pub fn try_into_inner(self) -> Option<String> {
234        match self {
235            Self::Url(s) | Self::Base64(s) => Some(s),
236            _ => None,
237        }
238    }
239}
240
241impl std::fmt::Display for DocumentSourceKind {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        match self {
244            Self::Url(string) => write!(f, "{string}"),
245            Self::Base64(string) => write!(f, "{string}"),
246            Self::String(string) => write!(f, "{string}"),
247            Self::Raw(_) => write!(f, "<binary data>"),
248            Self::Unknown => write!(f, "<unknown>"),
249        }
250    }
251}
252
253/// Audio content containing audio data and metadata about it.
254#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
255pub struct Audio {
256    pub data: DocumentSourceKind,
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub media_type: Option<AudioMediaType>,
259    #[serde(flatten, skip_serializing_if = "Option::is_none")]
260    pub additional_params: Option<serde_json::Value>,
261}
262
263/// Video content containing video data and metadata about it.
264#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
265pub struct Video {
266    pub data: DocumentSourceKind,
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub media_type: Option<VideoMediaType>,
269    #[serde(flatten, skip_serializing_if = "Option::is_none")]
270    pub additional_params: Option<serde_json::Value>,
271}
272
273/// Document content containing document data and metadata about it.
274#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
275pub struct Document {
276    pub data: DocumentSourceKind,
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub media_type: Option<DocumentMediaType>,
279    #[serde(flatten, skip_serializing_if = "Option::is_none")]
280    pub additional_params: Option<serde_json::Value>,
281}
282
283/// Describes the format of the content, which can be base64 or string.
284#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
285#[serde(rename_all = "lowercase")]
286pub enum ContentFormat {
287    #[default]
288    Base64,
289    String,
290    Url,
291}
292
293/// Helper enum that tracks the media type of the content.
294#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
295pub enum MediaType {
296    Image(ImageMediaType),
297    Audio(AudioMediaType),
298    Document(DocumentMediaType),
299    Video(VideoMediaType),
300}
301
302/// Describes the image media type of the content. Not every provider supports every media type.
303/// Convertible to and from MIME type strings.
304#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
305#[serde(rename_all = "lowercase")]
306pub enum ImageMediaType {
307    JPEG,
308    PNG,
309    GIF,
310    WEBP,
311    HEIC,
312    HEIF,
313    SVG,
314}
315
316/// Describes the document media type of the content. Not every provider supports every media type.
317/// Includes also programming languages as document types for providers who support code running.
318/// Convertible to and from MIME type strings.
319#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
320#[serde(rename_all = "lowercase")]
321pub enum DocumentMediaType {
322    PDF,
323    TXT,
324    RTF,
325    HTML,
326    CSS,
327    MARKDOWN,
328    CSV,
329    XML,
330    Javascript,
331    Python,
332}
333
334impl DocumentMediaType {
335    pub fn is_code(&self) -> bool {
336        matches!(self, Self::Javascript | Self::Python)
337    }
338}
339
340/// Describes the audio media type of the content. Not every provider supports every media type.
341/// Convertible to and from MIME type strings.
342#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
343#[serde(rename_all = "lowercase")]
344pub enum AudioMediaType {
345    WAV,
346    MP3,
347    AIFF,
348    AAC,
349    OGG,
350    FLAC,
351}
352
353/// Describes the video media type of the content. Not every provider supports every media type.
354/// Convertible to and from MIME type strings.
355#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
356#[serde(rename_all = "lowercase")]
357pub enum VideoMediaType {
358    AVI,
359    MP4,
360    MPEG,
361}
362
363/// Describes the detail of the image content, which can be low, high, or auto (open-ai specific).
364#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
365#[serde(rename_all = "lowercase")]
366pub enum ImageDetail {
367    Low,
368    High,
369    #[default]
370    Auto,
371}
372
373// ================================================================
374// Impl. for message models
375// ================================================================
376
377impl Message {
378    /// This helper method is primarily used to extract the first string prompt from a `Message`.
379    /// Since `Message` might have more than just text content, we need to find the first text.
380    pub(crate) fn rag_text(&self) -> Option<String> {
381        match self {
382            Message::User { content } => {
383                for item in content.iter() {
384                    if let UserContent::Text(Text { text }) = item {
385                        return Some(text.clone());
386                    }
387                }
388                None
389            }
390            _ => None,
391        }
392    }
393
394    /// Helper constructor to make creating user messages easier.
395    pub fn user(text: impl Into<String>) -> Self {
396        Message::User {
397            content: OneOrMany::one(UserContent::text(text)),
398        }
399    }
400
401    /// Helper constructor to make creating assistant messages easier.
402    pub fn assistant(text: impl Into<String>) -> Self {
403        Message::Assistant {
404            id: None,
405            content: OneOrMany::one(AssistantContent::text(text)),
406        }
407    }
408
409    /// Helper constructor to make creating assistant messages easier.
410    pub fn assistant_with_id(id: String, text: impl Into<String>) -> Self {
411        Message::Assistant {
412            id: Some(id),
413            content: OneOrMany::one(AssistantContent::text(text)),
414        }
415    }
416
417    /// Helper constructor to make creating tool result messages easier.
418    pub fn tool_result(id: impl Into<String>, content: impl Into<String>) -> Self {
419        Message::User {
420            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
421                id: id.into(),
422                call_id: None,
423                content: OneOrMany::one(ToolResultContent::text(content)),
424            })),
425        }
426    }
427
428    pub fn tool_result_with_call_id(
429        id: impl Into<String>,
430        call_id: Option<String>,
431        content: impl Into<String>,
432    ) -> Self {
433        Message::User {
434            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
435                id: id.into(),
436                call_id,
437                content: OneOrMany::one(ToolResultContent::text(content)),
438            })),
439        }
440    }
441}
442
443impl UserContent {
444    /// Helper constructor to make creating user text content easier.
445    pub fn text(text: impl Into<String>) -> Self {
446        UserContent::Text(text.into().into())
447    }
448
449    /// Helper constructor to make creating user image content easier.
450    pub fn image_base64(
451        data: impl Into<String>,
452        media_type: Option<ImageMediaType>,
453        detail: Option<ImageDetail>,
454    ) -> Self {
455        UserContent::Image(Image {
456            data: DocumentSourceKind::Base64(data.into()),
457            media_type,
458            detail,
459            additional_params: None,
460        })
461    }
462
463    /// Helper constructor to make creating user image content from raw unencoded bytes easier.
464    pub fn image_raw(
465        data: impl Into<Vec<u8>>,
466        media_type: Option<ImageMediaType>,
467        detail: Option<ImageDetail>,
468    ) -> Self {
469        UserContent::Image(Image {
470            data: DocumentSourceKind::Raw(data.into()),
471            media_type,
472            detail,
473            ..Default::default()
474        })
475    }
476
477    /// Helper constructor to make creating user image content easier.
478    pub fn image_url(
479        url: impl Into<String>,
480        media_type: Option<ImageMediaType>,
481        detail: Option<ImageDetail>,
482    ) -> Self {
483        UserContent::Image(Image {
484            data: DocumentSourceKind::Url(url.into()),
485            media_type,
486            detail,
487            additional_params: None,
488        })
489    }
490
491    /// Helper constructor to make creating user audio content easier.
492    pub fn audio(data: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
493        UserContent::Audio(Audio {
494            data: DocumentSourceKind::Base64(data.into()),
495            media_type,
496            additional_params: None,
497        })
498    }
499
500    /// Helper constructor to make creating user audio content from raw unencoded bytes easier.
501    pub fn audio_raw(data: impl Into<Vec<u8>>, media_type: Option<AudioMediaType>) -> Self {
502        UserContent::Audio(Audio {
503            data: DocumentSourceKind::Raw(data.into()),
504            media_type,
505            ..Default::default()
506        })
507    }
508
509    /// Helper to create an audio resource from a URL
510    pub fn audio_url(url: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
511        UserContent::Audio(Audio {
512            data: DocumentSourceKind::Url(url.into()),
513            media_type,
514            ..Default::default()
515        })
516    }
517
518    /// Helper constructor to make creating user document content easier.
519    /// This creates a document that assumes the data being passed in is a raw string.
520    pub fn document(data: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
521        let data: String = data.into();
522        UserContent::Document(Document {
523            data: DocumentSourceKind::string(&data),
524            media_type,
525            additional_params: None,
526        })
527    }
528
529    /// Helper to create a document from raw unencoded bytes
530    pub fn document_raw(data: impl Into<Vec<u8>>, media_type: Option<DocumentMediaType>) -> Self {
531        UserContent::Document(Document {
532            data: DocumentSourceKind::Raw(data.into()),
533            media_type,
534            ..Default::default()
535        })
536    }
537
538    /// Helper to create a document from a URL
539    pub fn document_url(url: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
540        UserContent::Document(Document {
541            data: DocumentSourceKind::Url(url.into()),
542            media_type,
543            ..Default::default()
544        })
545    }
546
547    /// Helper constructor to make creating user tool result content easier.
548    pub fn tool_result(id: impl Into<String>, content: OneOrMany<ToolResultContent>) -> Self {
549        UserContent::ToolResult(ToolResult {
550            id: id.into(),
551            call_id: None,
552            content,
553        })
554    }
555
556    /// Helper constructor to make creating user tool result content easier.
557    pub fn tool_result_with_call_id(
558        id: impl Into<String>,
559        call_id: String,
560        content: OneOrMany<ToolResultContent>,
561    ) -> Self {
562        UserContent::ToolResult(ToolResult {
563            id: id.into(),
564            call_id: Some(call_id),
565            content,
566        })
567    }
568}
569
570impl AssistantContent {
571    /// Helper constructor to make creating assistant text content easier.
572    pub fn text(text: impl Into<String>) -> Self {
573        AssistantContent::Text(text.into().into())
574    }
575
576    /// Helper constructor to make creating assistant tool call content easier.
577    pub fn tool_call(
578        id: impl Into<String>,
579        name: impl Into<String>,
580        arguments: serde_json::Value,
581    ) -> Self {
582        AssistantContent::ToolCall(ToolCall {
583            id: id.into(),
584            call_id: None,
585            function: ToolFunction {
586                name: name.into(),
587                arguments,
588            },
589        })
590    }
591
592    pub fn tool_call_with_call_id(
593        id: impl Into<String>,
594        call_id: String,
595        name: impl Into<String>,
596        arguments: serde_json::Value,
597    ) -> Self {
598        AssistantContent::ToolCall(ToolCall {
599            id: id.into(),
600            call_id: Some(call_id),
601            function: ToolFunction {
602                name: name.into(),
603                arguments,
604            },
605        })
606    }
607}
608
609impl ToolResultContent {
610    /// Helper constructor to make creating tool result text content easier.
611    pub fn text(text: impl Into<String>) -> Self {
612        ToolResultContent::Text(text.into().into())
613    }
614
615    /// Helper constructor to make tool result images from a base64-encoded string.
616    pub fn image_base64(
617        data: impl Into<String>,
618        media_type: Option<ImageMediaType>,
619        detail: Option<ImageDetail>,
620    ) -> Self {
621        ToolResultContent::Image(Image {
622            data: DocumentSourceKind::Base64(data.into()),
623            media_type,
624            detail,
625            additional_params: None,
626        })
627    }
628
629    /// Helper constructor to make tool result images from a base64-encoded string.
630    pub fn image_raw(
631        data: impl Into<Vec<u8>>,
632        media_type: Option<ImageMediaType>,
633        detail: Option<ImageDetail>,
634    ) -> Self {
635        ToolResultContent::Image(Image {
636            data: DocumentSourceKind::Raw(data.into()),
637            media_type,
638            detail,
639            ..Default::default()
640        })
641    }
642
643    /// Helper constructor to make tool result images from a URL.
644    pub fn image_url(
645        url: impl Into<String>,
646        media_type: Option<ImageMediaType>,
647        detail: Option<ImageDetail>,
648    ) -> Self {
649        ToolResultContent::Image(Image {
650            data: DocumentSourceKind::Url(url.into()),
651            media_type,
652            detail,
653            additional_params: None,
654        })
655    }
656}
657
658/// Trait for converting between MIME types and media types.
659pub trait MimeType {
660    fn from_mime_type(mime_type: &str) -> Option<Self>
661    where
662        Self: Sized;
663    fn to_mime_type(&self) -> &'static str;
664}
665
666impl MimeType for MediaType {
667    fn from_mime_type(mime_type: &str) -> Option<Self> {
668        ImageMediaType::from_mime_type(mime_type)
669            .map(MediaType::Image)
670            .or_else(|| {
671                DocumentMediaType::from_mime_type(mime_type)
672                    .map(MediaType::Document)
673                    .or_else(|| AudioMediaType::from_mime_type(mime_type).map(MediaType::Audio))
674            })
675    }
676
677    fn to_mime_type(&self) -> &'static str {
678        match self {
679            MediaType::Image(media_type) => media_type.to_mime_type(),
680            MediaType::Audio(media_type) => media_type.to_mime_type(),
681            MediaType::Document(media_type) => media_type.to_mime_type(),
682            MediaType::Video(media_type) => media_type.to_mime_type(),
683        }
684    }
685}
686
687impl MimeType for ImageMediaType {
688    fn from_mime_type(mime_type: &str) -> Option<Self> {
689        match mime_type {
690            "image/jpeg" => Some(ImageMediaType::JPEG),
691            "image/png" => Some(ImageMediaType::PNG),
692            "image/gif" => Some(ImageMediaType::GIF),
693            "image/webp" => Some(ImageMediaType::WEBP),
694            "image/heic" => Some(ImageMediaType::HEIC),
695            "image/heif" => Some(ImageMediaType::HEIF),
696            "image/svg+xml" => Some(ImageMediaType::SVG),
697            _ => None,
698        }
699    }
700
701    fn to_mime_type(&self) -> &'static str {
702        match self {
703            ImageMediaType::JPEG => "image/jpeg",
704            ImageMediaType::PNG => "image/png",
705            ImageMediaType::GIF => "image/gif",
706            ImageMediaType::WEBP => "image/webp",
707            ImageMediaType::HEIC => "image/heic",
708            ImageMediaType::HEIF => "image/heif",
709            ImageMediaType::SVG => "image/svg+xml",
710        }
711    }
712}
713
714impl MimeType for DocumentMediaType {
715    fn from_mime_type(mime_type: &str) -> Option<Self> {
716        match mime_type {
717            "application/pdf" => Some(DocumentMediaType::PDF),
718            "text/plain" => Some(DocumentMediaType::TXT),
719            "text/rtf" => Some(DocumentMediaType::RTF),
720            "text/html" => Some(DocumentMediaType::HTML),
721            "text/css" => Some(DocumentMediaType::CSS),
722            "text/md" | "text/markdown" => Some(DocumentMediaType::MARKDOWN),
723            "text/csv" => Some(DocumentMediaType::CSV),
724            "text/xml" => Some(DocumentMediaType::XML),
725            "application/x-javascript" | "text/x-javascript" => Some(DocumentMediaType::Javascript),
726            "application/x-python" | "text/x-python" => Some(DocumentMediaType::Python),
727            _ => None,
728        }
729    }
730
731    fn to_mime_type(&self) -> &'static str {
732        match self {
733            DocumentMediaType::PDF => "application/pdf",
734            DocumentMediaType::TXT => "text/plain",
735            DocumentMediaType::RTF => "text/rtf",
736            DocumentMediaType::HTML => "text/html",
737            DocumentMediaType::CSS => "text/css",
738            DocumentMediaType::MARKDOWN => "text/markdown",
739            DocumentMediaType::CSV => "text/csv",
740            DocumentMediaType::XML => "text/xml",
741            DocumentMediaType::Javascript => "application/x-javascript",
742            DocumentMediaType::Python => "application/x-python",
743        }
744    }
745}
746
747impl MimeType for AudioMediaType {
748    fn from_mime_type(mime_type: &str) -> Option<Self> {
749        match mime_type {
750            "audio/wav" => Some(AudioMediaType::WAV),
751            "audio/mp3" => Some(AudioMediaType::MP3),
752            "audio/aiff" => Some(AudioMediaType::AIFF),
753            "audio/aac" => Some(AudioMediaType::AAC),
754            "audio/ogg" => Some(AudioMediaType::OGG),
755            "audio/flac" => Some(AudioMediaType::FLAC),
756            _ => None,
757        }
758    }
759
760    fn to_mime_type(&self) -> &'static str {
761        match self {
762            AudioMediaType::WAV => "audio/wav",
763            AudioMediaType::MP3 => "audio/mp3",
764            AudioMediaType::AIFF => "audio/aiff",
765            AudioMediaType::AAC => "audio/aac",
766            AudioMediaType::OGG => "audio/ogg",
767            AudioMediaType::FLAC => "audio/flac",
768        }
769    }
770}
771
772impl MimeType for VideoMediaType {
773    fn from_mime_type(mime_type: &str) -> Option<Self>
774    where
775        Self: Sized,
776    {
777        match mime_type {
778            "video/avi" => Some(VideoMediaType::AVI),
779            "video/mp4" => Some(VideoMediaType::MP4),
780            "video/mpeg" => Some(VideoMediaType::MPEG),
781            &_ => None,
782        }
783    }
784
785    fn to_mime_type(&self) -> &'static str {
786        match self {
787            VideoMediaType::AVI => "video/avi",
788            VideoMediaType::MP4 => "video/mp4",
789            VideoMediaType::MPEG => "video/mpeg",
790        }
791    }
792}
793
794impl std::str::FromStr for ImageDetail {
795    type Err = ();
796
797    fn from_str(s: &str) -> Result<Self, Self::Err> {
798        match s.to_lowercase().as_str() {
799            "low" => Ok(ImageDetail::Low),
800            "high" => Ok(ImageDetail::High),
801            "auto" => Ok(ImageDetail::Auto),
802            _ => Err(()),
803        }
804    }
805}
806
807// ================================================================
808// FromStr, From<String>, and From<&str> impls
809// ================================================================
810
811impl From<String> for Text {
812    fn from(text: String) -> Self {
813        Text { text }
814    }
815}
816
817impl From<&String> for Text {
818    fn from(text: &String) -> Self {
819        text.to_owned().into()
820    }
821}
822
823impl From<&str> for Text {
824    fn from(text: &str) -> Self {
825        text.to_owned().into()
826    }
827}
828
829impl FromStr for Text {
830    type Err = Infallible;
831
832    fn from_str(s: &str) -> Result<Self, Self::Err> {
833        Ok(s.into())
834    }
835}
836
837impl From<String> for Message {
838    fn from(text: String) -> Self {
839        Message::User {
840            content: OneOrMany::one(UserContent::Text(text.into())),
841        }
842    }
843}
844
845impl From<&str> for Message {
846    fn from(text: &str) -> Self {
847        Message::User {
848            content: OneOrMany::one(UserContent::Text(text.into())),
849        }
850    }
851}
852
853impl From<&String> for Message {
854    fn from(text: &String) -> Self {
855        Message::User {
856            content: OneOrMany::one(UserContent::Text(text.into())),
857        }
858    }
859}
860
861impl From<Text> for Message {
862    fn from(text: Text) -> Self {
863        Message::User {
864            content: OneOrMany::one(UserContent::Text(text)),
865        }
866    }
867}
868
869impl From<Image> for Message {
870    fn from(image: Image) -> Self {
871        Message::User {
872            content: OneOrMany::one(UserContent::Image(image)),
873        }
874    }
875}
876
877impl From<Audio> for Message {
878    fn from(audio: Audio) -> Self {
879        Message::User {
880            content: OneOrMany::one(UserContent::Audio(audio)),
881        }
882    }
883}
884
885impl From<Document> for Message {
886    fn from(document: Document) -> Self {
887        Message::User {
888            content: OneOrMany::one(UserContent::Document(document)),
889        }
890    }
891}
892
893impl From<String> for ToolResultContent {
894    fn from(text: String) -> Self {
895        ToolResultContent::text(text)
896    }
897}
898
899impl From<String> for AssistantContent {
900    fn from(text: String) -> Self {
901        AssistantContent::text(text)
902    }
903}
904
905impl From<String> for UserContent {
906    fn from(text: String) -> Self {
907        UserContent::text(text)
908    }
909}
910
911impl From<AssistantContent> for Message {
912    fn from(content: AssistantContent) -> Self {
913        Message::Assistant {
914            id: None,
915            content: OneOrMany::one(content),
916        }
917    }
918}
919
920impl From<UserContent> for Message {
921    fn from(content: UserContent) -> Self {
922        Message::User {
923            content: OneOrMany::one(content),
924        }
925    }
926}
927
928impl From<OneOrMany<AssistantContent>> for Message {
929    fn from(content: OneOrMany<AssistantContent>) -> Self {
930        Message::Assistant { id: None, content }
931    }
932}
933
934impl From<OneOrMany<UserContent>> for Message {
935    fn from(content: OneOrMany<UserContent>) -> Self {
936        Message::User { content }
937    }
938}
939
940impl From<ToolCall> for Message {
941    fn from(tool_call: ToolCall) -> Self {
942        Message::Assistant {
943            id: None,
944            content: OneOrMany::one(AssistantContent::ToolCall(tool_call)),
945        }
946    }
947}
948
949impl From<ToolResult> for Message {
950    fn from(tool_result: ToolResult) -> Self {
951        Message::User {
952            content: OneOrMany::one(UserContent::ToolResult(tool_result)),
953        }
954    }
955}
956
957impl From<ToolResultContent> for Message {
958    fn from(tool_result_content: ToolResultContent) -> Self {
959        Message::User {
960            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
961                id: String::new(),
962                call_id: None,
963                content: OneOrMany::one(tool_result_content),
964            })),
965        }
966    }
967}
968
969#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
970#[serde(rename_all = "snake_case")]
971pub enum ToolChoice {
972    #[default]
973    Auto,
974    None,
975    Required,
976    Specific {
977        function_names: Vec<String>,
978    },
979}
980
981// ================================================================
982// Error types
983// ================================================================
984
985/// Error type to represent issues with converting messages to and from specific provider messages.
986#[derive(Debug, Error)]
987pub enum MessageError {
988    #[error("Message conversion error: {0}")]
989    ConversionError(String),
990}
991
992impl From<MessageError> for CompletionError {
993    fn from(error: MessageError) -> Self {
994        CompletionError::RequestError(error.into())
995    }
996}