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)]
71pub struct Reasoning {
72    pub id: Option<String>,
73    pub reasoning: Vec<String>,
74}
75
76impl Reasoning {
77    /// Create a new reasoning item from a single item
78    pub fn new(input: &str) -> Self {
79        Self {
80            id: None,
81            reasoning: vec![input.to_string()],
82        }
83    }
84
85    pub fn multi(input: Vec<String>) -> Self {
86        Self {
87            id: None,
88            reasoning: input,
89        }
90    }
91
92    pub fn optional_id(mut self, id: Option<String>) -> Self {
93        self.id = id;
94        self
95    }
96
97    pub fn with_id(mut self, id: String) -> Self {
98        self.id = Some(id);
99        self
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
170/// The kind of image source (to be used).
171#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
172#[serde(tag = "type", rename_all = "camelCase")]
173#[non_exhaustive]
174pub enum DocumentSourceKind {
175    /// A file URL/URI.
176    Url(String),
177    /// A base-64 encoded string.
178    Base64(String),
179    #[default]
180    /// An unknown file source (there's nothing there).
181    Unknown,
182}
183
184impl DocumentSourceKind {
185    pub fn url(url: &str) -> Self {
186        Self::Url(url.to_string())
187    }
188
189    pub fn base64(base64_string: &str) -> Self {
190        Self::Base64(base64_string.to_string())
191    }
192
193    pub fn unknown() -> Self {
194        Self::Unknown
195    }
196}
197
198impl std::fmt::Display for DocumentSourceKind {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        match self {
201            Self::Url(string) => write!(f, "{string}"),
202            Self::Base64(string) => write!(f, "{string}"),
203            Self::Unknown => write!(f, "<unknown>"),
204        }
205    }
206}
207
208/// Audio content containing audio data and metadata about it.
209#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
210pub struct Audio {
211    pub data: String,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub format: Option<ContentFormat>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub media_type: Option<AudioMediaType>,
216    #[serde(flatten, skip_serializing_if = "Option::is_none")]
217    pub additional_params: Option<serde_json::Value>,
218}
219
220/// Video content containing video data and metadata about it.
221#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
222pub struct Video {
223    pub data: String,
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub format: Option<ContentFormat>,
226    #[serde(skip_serializing_if = "Option::is_none")]
227    pub media_type: Option<VideoMediaType>,
228    #[serde(flatten, skip_serializing_if = "Option::is_none")]
229    pub additional_params: Option<serde_json::Value>,
230}
231
232/// Document content containing document data and metadata about it.
233#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
234pub struct Document {
235    pub data: String,
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub format: Option<ContentFormat>,
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub media_type: Option<DocumentMediaType>,
240    #[serde(flatten, skip_serializing_if = "Option::is_none")]
241    pub additional_params: Option<serde_json::Value>,
242}
243
244/// Describes the format of the content, which can be base64 or string.
245#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
246#[serde(rename_all = "lowercase")]
247pub enum ContentFormat {
248    #[default]
249    Base64,
250    String,
251}
252
253/// Helper enum that tracks the media type of the content.
254#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
255pub enum MediaType {
256    Image(ImageMediaType),
257    Audio(AudioMediaType),
258    Document(DocumentMediaType),
259    Video(VideoMediaType),
260}
261
262/// Describes the image media type of the content. Not every provider supports every media type.
263/// Convertible to and from MIME type strings.
264#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
265#[serde(rename_all = "lowercase")]
266pub enum ImageMediaType {
267    JPEG,
268    PNG,
269    GIF,
270    WEBP,
271    HEIC,
272    HEIF,
273    SVG,
274}
275
276/// Describes the document media type of the content. Not every provider supports every media type.
277/// Includes also programming languages as document types for providers who support code running.
278/// Convertible to and from MIME type strings.
279#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
280#[serde(rename_all = "lowercase")]
281pub enum DocumentMediaType {
282    PDF,
283    TXT,
284    RTF,
285    HTML,
286    CSS,
287    MARKDOWN,
288    CSV,
289    XML,
290    Javascript,
291    Python,
292}
293
294/// Describes the audio media type of the content. Not every provider supports every media type.
295/// Convertible to and from MIME type strings.
296#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
297#[serde(rename_all = "lowercase")]
298pub enum AudioMediaType {
299    WAV,
300    MP3,
301    AIFF,
302    AAC,
303    OGG,
304    FLAC,
305}
306
307/// Describes the video media type of the content. Not every provider supports every media type.
308/// Convertible to and from MIME type strings.
309#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
310#[serde(rename_all = "lowercase")]
311pub enum VideoMediaType {
312    AVI,
313    MP4,
314    MPEG,
315}
316
317/// Describes the detail of the image content, which can be low, high, or auto (open-ai specific).
318#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
319#[serde(rename_all = "lowercase")]
320pub enum ImageDetail {
321    Low,
322    High,
323    #[default]
324    Auto,
325}
326
327// ================================================================
328// Impl. for message models
329// ================================================================
330
331impl Message {
332    /// This helper method is primarily used to extract the first string prompt from a `Message`.
333    /// Since `Message` might have more than just text content, we need to find the first text.
334    pub(crate) fn rag_text(&self) -> Option<String> {
335        match self {
336            Message::User { content } => {
337                for item in content.iter() {
338                    if let UserContent::Text(Text { text }) = item {
339                        return Some(text.clone());
340                    }
341                }
342                None
343            }
344            _ => None,
345        }
346    }
347
348    /// Helper constructor to make creating user messages easier.
349    pub fn user(text: impl Into<String>) -> Self {
350        Message::User {
351            content: OneOrMany::one(UserContent::text(text)),
352        }
353    }
354
355    /// Helper constructor to make creating assistant messages easier.
356    pub fn assistant(text: impl Into<String>) -> Self {
357        Message::Assistant {
358            id: None,
359            content: OneOrMany::one(AssistantContent::text(text)),
360        }
361    }
362
363    /// Helper constructor to make creating assistant messages easier.
364    pub fn assistant_with_id(id: String, text: impl Into<String>) -> Self {
365        Message::Assistant {
366            id: Some(id),
367            content: OneOrMany::one(AssistantContent::text(text)),
368        }
369    }
370
371    /// Helper constructor to make creating tool result messages easier.
372    pub fn tool_result(id: impl Into<String>, content: impl Into<String>) -> Self {
373        Message::User {
374            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
375                id: id.into(),
376                call_id: None,
377                content: OneOrMany::one(ToolResultContent::text(content)),
378            })),
379        }
380    }
381
382    pub fn tool_result_with_call_id(
383        id: impl Into<String>,
384        call_id: Option<String>,
385        content: impl Into<String>,
386    ) -> Self {
387        Message::User {
388            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
389                id: id.into(),
390                call_id,
391                content: OneOrMany::one(ToolResultContent::text(content)),
392            })),
393        }
394    }
395}
396
397impl UserContent {
398    /// Helper constructor to make creating user text content easier.
399    pub fn text(text: impl Into<String>) -> Self {
400        UserContent::Text(text.into().into())
401    }
402
403    /// Helper constructor to make creating user image content easier.
404    pub fn image_base64(
405        data: impl Into<String>,
406        media_type: Option<ImageMediaType>,
407        detail: Option<ImageDetail>,
408    ) -> Self {
409        UserContent::Image(Image {
410            data: DocumentSourceKind::Base64(data.into()),
411            media_type,
412            detail,
413            additional_params: None,
414        })
415    }
416
417    /// Helper constructor to make creating user image content easier.
418    pub fn image_url(
419        url: impl Into<String>,
420        media_type: Option<ImageMediaType>,
421        detail: Option<ImageDetail>,
422    ) -> Self {
423        UserContent::Image(Image {
424            data: DocumentSourceKind::Url(url.into()),
425            media_type,
426            detail,
427            additional_params: None,
428        })
429    }
430
431    /// Helper constructor to make creating user audio content easier.
432    pub fn audio(
433        data: impl Into<String>,
434        format: Option<ContentFormat>,
435        media_type: Option<AudioMediaType>,
436    ) -> Self {
437        UserContent::Audio(Audio {
438            data: data.into(),
439            format,
440            media_type,
441            additional_params: None,
442        })
443    }
444
445    /// Helper constructor to make creating user document content easier.
446    pub fn document(
447        data: impl Into<String>,
448        format: Option<ContentFormat>,
449        media_type: Option<DocumentMediaType>,
450    ) -> Self {
451        UserContent::Document(Document {
452            data: data.into(),
453            format,
454            media_type,
455            additional_params: None,
456        })
457    }
458
459    /// Helper constructor to make creating user tool result content easier.
460    pub fn tool_result(id: impl Into<String>, content: OneOrMany<ToolResultContent>) -> Self {
461        UserContent::ToolResult(ToolResult {
462            id: id.into(),
463            call_id: None,
464            content,
465        })
466    }
467
468    /// Helper constructor to make creating user tool result content easier.
469    pub fn tool_result_with_call_id(
470        id: impl Into<String>,
471        call_id: String,
472        content: OneOrMany<ToolResultContent>,
473    ) -> Self {
474        UserContent::ToolResult(ToolResult {
475            id: id.into(),
476            call_id: Some(call_id),
477            content,
478        })
479    }
480}
481
482impl AssistantContent {
483    /// Helper constructor to make creating assistant text content easier.
484    pub fn text(text: impl Into<String>) -> Self {
485        AssistantContent::Text(text.into().into())
486    }
487
488    /// Helper constructor to make creating assistant tool call content easier.
489    pub fn tool_call(
490        id: impl Into<String>,
491        name: impl Into<String>,
492        arguments: serde_json::Value,
493    ) -> Self {
494        AssistantContent::ToolCall(ToolCall {
495            id: id.into(),
496            call_id: None,
497            function: ToolFunction {
498                name: name.into(),
499                arguments,
500            },
501        })
502    }
503
504    pub fn tool_call_with_call_id(
505        id: impl Into<String>,
506        call_id: String,
507        name: impl Into<String>,
508        arguments: serde_json::Value,
509    ) -> Self {
510        AssistantContent::ToolCall(ToolCall {
511            id: id.into(),
512            call_id: Some(call_id),
513            function: ToolFunction {
514                name: name.into(),
515                arguments,
516            },
517        })
518    }
519}
520
521impl ToolResultContent {
522    /// Helper constructor to make creating tool result text content easier.
523    pub fn text(text: impl Into<String>) -> Self {
524        ToolResultContent::Text(text.into().into())
525    }
526
527    /// Helper constructor to make tool result images from a base64-encoded string.
528    pub fn image_base64(
529        data: impl Into<String>,
530        media_type: Option<ImageMediaType>,
531        detail: Option<ImageDetail>,
532    ) -> Self {
533        ToolResultContent::Image(Image {
534            data: DocumentSourceKind::Base64(data.into()),
535            media_type,
536            detail,
537            additional_params: None,
538        })
539    }
540
541    /// Helper constructor to make tool result images from a URL.
542    pub fn image_url(
543        url: impl Into<String>,
544        media_type: Option<ImageMediaType>,
545        detail: Option<ImageDetail>,
546    ) -> Self {
547        ToolResultContent::Image(Image {
548            data: DocumentSourceKind::Url(url.into()),
549            media_type,
550            detail,
551            additional_params: None,
552        })
553    }
554}
555
556/// Trait for converting between MIME types and media types.
557pub trait MimeType {
558    fn from_mime_type(mime_type: &str) -> Option<Self>
559    where
560        Self: Sized;
561    fn to_mime_type(&self) -> &'static str;
562}
563
564impl MimeType for MediaType {
565    fn from_mime_type(mime_type: &str) -> Option<Self> {
566        ImageMediaType::from_mime_type(mime_type)
567            .map(MediaType::Image)
568            .or_else(|| {
569                DocumentMediaType::from_mime_type(mime_type)
570                    .map(MediaType::Document)
571                    .or_else(|| AudioMediaType::from_mime_type(mime_type).map(MediaType::Audio))
572            })
573    }
574
575    fn to_mime_type(&self) -> &'static str {
576        match self {
577            MediaType::Image(media_type) => media_type.to_mime_type(),
578            MediaType::Audio(media_type) => media_type.to_mime_type(),
579            MediaType::Document(media_type) => media_type.to_mime_type(),
580            MediaType::Video(media_type) => media_type.to_mime_type(),
581        }
582    }
583}
584
585impl MimeType for ImageMediaType {
586    fn from_mime_type(mime_type: &str) -> Option<Self> {
587        match mime_type {
588            "image/jpeg" => Some(ImageMediaType::JPEG),
589            "image/png" => Some(ImageMediaType::PNG),
590            "image/gif" => Some(ImageMediaType::GIF),
591            "image/webp" => Some(ImageMediaType::WEBP),
592            "image/heic" => Some(ImageMediaType::HEIC),
593            "image/heif" => Some(ImageMediaType::HEIF),
594            "image/svg+xml" => Some(ImageMediaType::SVG),
595            _ => None,
596        }
597    }
598
599    fn to_mime_type(&self) -> &'static str {
600        match self {
601            ImageMediaType::JPEG => "image/jpeg",
602            ImageMediaType::PNG => "image/png",
603            ImageMediaType::GIF => "image/gif",
604            ImageMediaType::WEBP => "image/webp",
605            ImageMediaType::HEIC => "image/heic",
606            ImageMediaType::HEIF => "image/heif",
607            ImageMediaType::SVG => "image/svg+xml",
608        }
609    }
610}
611
612impl MimeType for DocumentMediaType {
613    fn from_mime_type(mime_type: &str) -> Option<Self> {
614        match mime_type {
615            "application/pdf" => Some(DocumentMediaType::PDF),
616            "text/plain" => Some(DocumentMediaType::TXT),
617            "text/rtf" => Some(DocumentMediaType::RTF),
618            "text/html" => Some(DocumentMediaType::HTML),
619            "text/css" => Some(DocumentMediaType::CSS),
620            "text/md" | "text/markdown" => Some(DocumentMediaType::MARKDOWN),
621            "text/csv" => Some(DocumentMediaType::CSV),
622            "text/xml" => Some(DocumentMediaType::XML),
623            "application/x-javascript" | "text/x-javascript" => Some(DocumentMediaType::Javascript),
624            "application/x-python" | "text/x-python" => Some(DocumentMediaType::Python),
625            _ => None,
626        }
627    }
628
629    fn to_mime_type(&self) -> &'static str {
630        match self {
631            DocumentMediaType::PDF => "application/pdf",
632            DocumentMediaType::TXT => "text/plain",
633            DocumentMediaType::RTF => "text/rtf",
634            DocumentMediaType::HTML => "text/html",
635            DocumentMediaType::CSS => "text/css",
636            DocumentMediaType::MARKDOWN => "text/markdown",
637            DocumentMediaType::CSV => "text/csv",
638            DocumentMediaType::XML => "text/xml",
639            DocumentMediaType::Javascript => "application/x-javascript",
640            DocumentMediaType::Python => "application/x-python",
641        }
642    }
643}
644
645impl MimeType for AudioMediaType {
646    fn from_mime_type(mime_type: &str) -> Option<Self> {
647        match mime_type {
648            "audio/wav" => Some(AudioMediaType::WAV),
649            "audio/mp3" => Some(AudioMediaType::MP3),
650            "audio/aiff" => Some(AudioMediaType::AIFF),
651            "audio/aac" => Some(AudioMediaType::AAC),
652            "audio/ogg" => Some(AudioMediaType::OGG),
653            "audio/flac" => Some(AudioMediaType::FLAC),
654            _ => None,
655        }
656    }
657
658    fn to_mime_type(&self) -> &'static str {
659        match self {
660            AudioMediaType::WAV => "audio/wav",
661            AudioMediaType::MP3 => "audio/mp3",
662            AudioMediaType::AIFF => "audio/aiff",
663            AudioMediaType::AAC => "audio/aac",
664            AudioMediaType::OGG => "audio/ogg",
665            AudioMediaType::FLAC => "audio/flac",
666        }
667    }
668}
669
670impl MimeType for VideoMediaType {
671    fn from_mime_type(mime_type: &str) -> Option<Self>
672    where
673        Self: Sized,
674    {
675        match mime_type {
676            "video/avi" => Some(VideoMediaType::AVI),
677            "video/mp4" => Some(VideoMediaType::MP4),
678            "video/mpeg" => Some(VideoMediaType::MPEG),
679            &_ => None,
680        }
681    }
682
683    fn to_mime_type(&self) -> &'static str {
684        match self {
685            VideoMediaType::AVI => "video/avi",
686            VideoMediaType::MP4 => "video/mp4",
687            VideoMediaType::MPEG => "video/mpeg",
688        }
689    }
690}
691
692impl std::str::FromStr for ImageDetail {
693    type Err = ();
694
695    fn from_str(s: &str) -> Result<Self, Self::Err> {
696        match s.to_lowercase().as_str() {
697            "low" => Ok(ImageDetail::Low),
698            "high" => Ok(ImageDetail::High),
699            "auto" => Ok(ImageDetail::Auto),
700            _ => Err(()),
701        }
702    }
703}
704
705// ================================================================
706// FromStr, From<String>, and From<&str> impls
707// ================================================================
708
709impl From<String> for Text {
710    fn from(text: String) -> Self {
711        Text { text }
712    }
713}
714
715impl From<&String> for Text {
716    fn from(text: &String) -> Self {
717        text.to_owned().into()
718    }
719}
720
721impl From<&str> for Text {
722    fn from(text: &str) -> Self {
723        text.to_owned().into()
724    }
725}
726
727impl FromStr for Text {
728    type Err = Infallible;
729
730    fn from_str(s: &str) -> Result<Self, Self::Err> {
731        Ok(s.into())
732    }
733}
734
735impl From<String> for Message {
736    fn from(text: String) -> Self {
737        Message::User {
738            content: OneOrMany::one(UserContent::Text(text.into())),
739        }
740    }
741}
742
743impl From<&str> for Message {
744    fn from(text: &str) -> Self {
745        Message::User {
746            content: OneOrMany::one(UserContent::Text(text.into())),
747        }
748    }
749}
750
751impl From<&String> for Message {
752    fn from(text: &String) -> Self {
753        Message::User {
754            content: OneOrMany::one(UserContent::Text(text.into())),
755        }
756    }
757}
758
759impl From<Text> for Message {
760    fn from(text: Text) -> Self {
761        Message::User {
762            content: OneOrMany::one(UserContent::Text(text)),
763        }
764    }
765}
766
767impl From<Image> for Message {
768    fn from(image: Image) -> Self {
769        Message::User {
770            content: OneOrMany::one(UserContent::Image(image)),
771        }
772    }
773}
774
775impl From<Audio> for Message {
776    fn from(audio: Audio) -> Self {
777        Message::User {
778            content: OneOrMany::one(UserContent::Audio(audio)),
779        }
780    }
781}
782
783impl From<Document> for Message {
784    fn from(document: Document) -> Self {
785        Message::User {
786            content: OneOrMany::one(UserContent::Document(document)),
787        }
788    }
789}
790
791impl From<String> for ToolResultContent {
792    fn from(text: String) -> Self {
793        ToolResultContent::text(text)
794    }
795}
796
797impl From<String> for AssistantContent {
798    fn from(text: String) -> Self {
799        AssistantContent::text(text)
800    }
801}
802
803impl From<String> for UserContent {
804    fn from(text: String) -> Self {
805        UserContent::text(text)
806    }
807}
808
809impl From<AssistantContent> for Message {
810    fn from(content: AssistantContent) -> Self {
811        Message::Assistant {
812            id: None,
813            content: OneOrMany::one(content),
814        }
815    }
816}
817
818impl From<UserContent> for Message {
819    fn from(content: UserContent) -> Self {
820        Message::User {
821            content: OneOrMany::one(content),
822        }
823    }
824}
825
826impl From<OneOrMany<AssistantContent>> for Message {
827    fn from(content: OneOrMany<AssistantContent>) -> Self {
828        Message::Assistant { id: None, content }
829    }
830}
831
832impl From<OneOrMany<UserContent>> for Message {
833    fn from(content: OneOrMany<UserContent>) -> Self {
834        Message::User { content }
835    }
836}
837
838impl From<ToolCall> for Message {
839    fn from(tool_call: ToolCall) -> Self {
840        Message::Assistant {
841            id: None,
842            content: OneOrMany::one(AssistantContent::ToolCall(tool_call)),
843        }
844    }
845}
846
847impl From<ToolResult> for Message {
848    fn from(tool_result: ToolResult) -> Self {
849        Message::User {
850            content: OneOrMany::one(UserContent::ToolResult(tool_result)),
851        }
852    }
853}
854
855impl From<ToolResultContent> for Message {
856    fn from(tool_result_content: ToolResultContent) -> Self {
857        Message::User {
858            content: OneOrMany::one(UserContent::ToolResult(ToolResult {
859                id: String::new(),
860                call_id: None,
861                content: OneOrMany::one(tool_result_content),
862            })),
863        }
864    }
865}
866
867// ================================================================
868// Error types
869// ================================================================
870
871/// Error type to represent issues with converting messages to and from specific provider messages.
872#[derive(Debug, Error)]
873pub enum MessageError {
874    #[error("Message conversion error: {0}")]
875    ConversionError(String),
876}
877
878impl From<MessageError> for CompletionError {
879    fn from(error: MessageError) -> Self {
880        CompletionError::RequestError(error.into())
881    }
882}