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