1use std::{convert::Infallible, str::FromStr};
2
3use crate::OneOrMany;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use super::CompletionError;
8
9pub 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#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
35#[serde(tag = "role", rename_all = "lowercase")]
36pub enum Message {
37 User { content: OneOrMany<UserContent> },
39
40 Assistant {
42 id: Option<String>,
43 content: OneOrMany<AssistantContent>,
44 },
45}
46
47#[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#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
63#[serde(untagged)]
64pub enum AssistantContent {
65 Text(Text),
66 ToolCall(ToolCall),
67 Reasoning(Reasoning),
68 Image(Image),
69}
70
71#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
72#[non_exhaustive]
73pub struct Reasoning {
74 pub id: Option<String>,
75 pub reasoning: Vec<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
86 pub signature: Option<String>,
87}
88
89impl Reasoning {
90 pub fn new(input: &str) -> Self {
92 Self {
93 id: None,
94 reasoning: vec![input.to_string()],
95 signature: None,
96 }
97 }
98
99 pub fn optional_id(mut self, id: Option<String>) -> Self {
100 self.id = id;
101 self
102 }
103 pub fn with_id(mut self, id: String) -> Self {
104 self.id = Some(id);
105 self
106 }
107
108 pub fn with_signature(mut self, signature: Option<String>) -> Self {
109 self.signature = signature;
110 self
111 }
112
113 pub fn multi(input: Vec<String>) -> Self {
114 Self {
115 id: None,
116 reasoning: input,
117 signature: None,
118 }
119 }
120}
121
122#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
124pub struct ToolResult {
125 pub id: String,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub call_id: Option<String>,
128 pub content: OneOrMany<ToolResultContent>,
129}
130
131#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
133#[serde(tag = "type", rename_all = "lowercase")]
134pub enum ToolResultContent {
135 Text(Text),
136 Image(Image),
137}
138
139#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
141pub struct ToolCall {
142 pub id: String,
143 pub call_id: Option<String>,
144 pub function: ToolFunction,
145}
146
147#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
149pub struct ToolFunction {
150 pub name: String,
151 pub arguments: serde_json::Value,
152}
153
154#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
160pub struct Text {
161 pub text: String,
162}
163
164impl Text {
165 pub fn text(&self) -> &str {
166 &self.text
167 }
168}
169
170impl std::fmt::Display for Text {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 let Self { text } = self;
173 write!(f, "{text}")
174 }
175}
176
177#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
179pub struct Image {
180 pub data: DocumentSourceKind,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub media_type: Option<ImageMediaType>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub detail: Option<ImageDetail>,
185 #[serde(flatten, skip_serializing_if = "Option::is_none")]
186 pub additional_params: Option<serde_json::Value>,
187}
188
189impl Image {
190 pub fn try_into_url(self) -> Result<String, MessageError> {
191 match self.data {
192 DocumentSourceKind::Url(url) => Ok(url),
193 DocumentSourceKind::Base64(data) => {
194 let Some(media_type) = self.media_type else {
195 return Err(MessageError::ConversionError(
196 "A media type is required to create a valid base64-encoded image URL"
197 .to_string(),
198 ));
199 };
200
201 Ok(format!(
202 "data:image/{ty};base64,{data}",
203 ty = media_type.to_mime_type()
204 ))
205 }
206 unknown => Err(MessageError::ConversionError(format!(
207 "Tried to convert unknown type to a URL: {unknown:?}"
208 ))),
209 }
210 }
211}
212
213#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
215#[serde(tag = "type", content = "value", rename_all = "camelCase")]
216#[non_exhaustive]
217pub enum DocumentSourceKind {
218 Url(String),
220 Base64(String),
222 Raw(Vec<u8>),
224 String(String),
226 #[default]
227 Unknown,
229}
230
231impl DocumentSourceKind {
232 pub fn url(url: &str) -> Self {
233 Self::Url(url.to_string())
234 }
235
236 pub fn base64(base64_string: &str) -> Self {
237 Self::Base64(base64_string.to_string())
238 }
239
240 pub fn raw(bytes: impl Into<Vec<u8>>) -> Self {
241 Self::Raw(bytes.into())
242 }
243
244 pub fn string(input: &str) -> Self {
245 Self::String(input.into())
246 }
247
248 pub fn unknown() -> Self {
249 Self::Unknown
250 }
251
252 pub fn try_into_inner(self) -> Option<String> {
253 match self {
254 Self::Url(s) | Self::Base64(s) => Some(s),
255 _ => None,
256 }
257 }
258}
259
260impl std::fmt::Display for DocumentSourceKind {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 match self {
263 Self::Url(string) => write!(f, "{string}"),
264 Self::Base64(string) => write!(f, "{string}"),
265 Self::String(string) => write!(f, "{string}"),
266 Self::Raw(_) => write!(f, "<binary data>"),
267 Self::Unknown => write!(f, "<unknown>"),
268 }
269 }
270}
271
272#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
274pub struct Audio {
275 pub data: DocumentSourceKind,
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub media_type: Option<AudioMediaType>,
278 #[serde(flatten, skip_serializing_if = "Option::is_none")]
279 pub additional_params: Option<serde_json::Value>,
280}
281
282#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
284pub struct Video {
285 pub data: DocumentSourceKind,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub media_type: Option<VideoMediaType>,
288 #[serde(flatten, skip_serializing_if = "Option::is_none")]
289 pub additional_params: Option<serde_json::Value>,
290}
291
292#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
294pub struct Document {
295 pub data: DocumentSourceKind,
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub media_type: Option<DocumentMediaType>,
298 #[serde(flatten, skip_serializing_if = "Option::is_none")]
299 pub additional_params: Option<serde_json::Value>,
300}
301
302#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
304#[serde(rename_all = "lowercase")]
305pub enum ContentFormat {
306 #[default]
307 Base64,
308 String,
309 Url,
310}
311
312#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
314pub enum MediaType {
315 Image(ImageMediaType),
316 Audio(AudioMediaType),
317 Document(DocumentMediaType),
318 Video(VideoMediaType),
319}
320
321#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
324#[serde(rename_all = "lowercase")]
325pub enum ImageMediaType {
326 JPEG,
327 PNG,
328 GIF,
329 WEBP,
330 HEIC,
331 HEIF,
332 SVG,
333}
334
335#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
339#[serde(rename_all = "lowercase")]
340pub enum DocumentMediaType {
341 PDF,
342 TXT,
343 RTF,
344 HTML,
345 CSS,
346 MARKDOWN,
347 CSV,
348 XML,
349 Javascript,
350 Python,
351}
352
353impl DocumentMediaType {
354 pub fn is_code(&self) -> bool {
355 matches!(self, Self::Javascript | Self::Python)
356 }
357}
358
359#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
362#[serde(rename_all = "lowercase")]
363pub enum AudioMediaType {
364 WAV,
365 MP3,
366 AIFF,
367 AAC,
368 OGG,
369 FLAC,
370}
371
372#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
375#[serde(rename_all = "lowercase")]
376pub enum VideoMediaType {
377 AVI,
378 MP4,
379 MPEG,
380}
381
382#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
384#[serde(rename_all = "lowercase")]
385pub enum ImageDetail {
386 Low,
387 High,
388 #[default]
389 Auto,
390}
391
392impl Message {
397 pub(crate) fn rag_text(&self) -> Option<String> {
400 match self {
401 Message::User { content } => {
402 for item in content.iter() {
403 if let UserContent::Text(Text { text }) = item {
404 return Some(text.clone());
405 }
406 }
407 None
408 }
409 _ => None,
410 }
411 }
412
413 pub fn user(text: impl Into<String>) -> Self {
415 Message::User {
416 content: OneOrMany::one(UserContent::text(text)),
417 }
418 }
419
420 pub fn assistant(text: impl Into<String>) -> Self {
422 Message::Assistant {
423 id: None,
424 content: OneOrMany::one(AssistantContent::text(text)),
425 }
426 }
427
428 pub fn assistant_with_id(id: String, text: impl Into<String>) -> Self {
430 Message::Assistant {
431 id: Some(id),
432 content: OneOrMany::one(AssistantContent::text(text)),
433 }
434 }
435
436 pub fn tool_result(id: impl Into<String>, content: impl Into<String>) -> Self {
438 Message::User {
439 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
440 id: id.into(),
441 call_id: None,
442 content: OneOrMany::one(ToolResultContent::text(content)),
443 })),
444 }
445 }
446
447 pub fn tool_result_with_call_id(
448 id: impl Into<String>,
449 call_id: Option<String>,
450 content: impl Into<String>,
451 ) -> Self {
452 Message::User {
453 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
454 id: id.into(),
455 call_id,
456 content: OneOrMany::one(ToolResultContent::text(content)),
457 })),
458 }
459 }
460}
461
462impl UserContent {
463 pub fn text(text: impl Into<String>) -> Self {
465 UserContent::Text(text.into().into())
466 }
467
468 pub fn image_base64(
470 data: impl Into<String>,
471 media_type: Option<ImageMediaType>,
472 detail: Option<ImageDetail>,
473 ) -> Self {
474 UserContent::Image(Image {
475 data: DocumentSourceKind::Base64(data.into()),
476 media_type,
477 detail,
478 additional_params: None,
479 })
480 }
481
482 pub fn image_raw(
484 data: impl Into<Vec<u8>>,
485 media_type: Option<ImageMediaType>,
486 detail: Option<ImageDetail>,
487 ) -> Self {
488 UserContent::Image(Image {
489 data: DocumentSourceKind::Raw(data.into()),
490 media_type,
491 detail,
492 ..Default::default()
493 })
494 }
495
496 pub fn image_url(
498 url: impl Into<String>,
499 media_type: Option<ImageMediaType>,
500 detail: Option<ImageDetail>,
501 ) -> Self {
502 UserContent::Image(Image {
503 data: DocumentSourceKind::Url(url.into()),
504 media_type,
505 detail,
506 additional_params: None,
507 })
508 }
509
510 pub fn audio(data: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
512 UserContent::Audio(Audio {
513 data: DocumentSourceKind::Base64(data.into()),
514 media_type,
515 additional_params: None,
516 })
517 }
518
519 pub fn audio_raw(data: impl Into<Vec<u8>>, media_type: Option<AudioMediaType>) -> Self {
521 UserContent::Audio(Audio {
522 data: DocumentSourceKind::Raw(data.into()),
523 media_type,
524 ..Default::default()
525 })
526 }
527
528 pub fn audio_url(url: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
530 UserContent::Audio(Audio {
531 data: DocumentSourceKind::Url(url.into()),
532 media_type,
533 ..Default::default()
534 })
535 }
536
537 pub fn document(data: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
540 let data: String = data.into();
541 UserContent::Document(Document {
542 data: DocumentSourceKind::string(&data),
543 media_type,
544 additional_params: None,
545 })
546 }
547
548 pub fn document_raw(data: impl Into<Vec<u8>>, media_type: Option<DocumentMediaType>) -> Self {
550 UserContent::Document(Document {
551 data: DocumentSourceKind::Raw(data.into()),
552 media_type,
553 ..Default::default()
554 })
555 }
556
557 pub fn document_url(url: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
559 UserContent::Document(Document {
560 data: DocumentSourceKind::Url(url.into()),
561 media_type,
562 ..Default::default()
563 })
564 }
565
566 pub fn tool_result(id: impl Into<String>, content: OneOrMany<ToolResultContent>) -> Self {
568 UserContent::ToolResult(ToolResult {
569 id: id.into(),
570 call_id: None,
571 content,
572 })
573 }
574
575 pub fn tool_result_with_call_id(
577 id: impl Into<String>,
578 call_id: String,
579 content: OneOrMany<ToolResultContent>,
580 ) -> Self {
581 UserContent::ToolResult(ToolResult {
582 id: id.into(),
583 call_id: Some(call_id),
584 content,
585 })
586 }
587}
588
589impl AssistantContent {
590 pub fn text(text: impl Into<String>) -> Self {
592 AssistantContent::Text(text.into().into())
593 }
594
595 pub fn image_base64(
597 data: impl Into<String>,
598 media_type: Option<ImageMediaType>,
599 detail: Option<ImageDetail>,
600 ) -> Self {
601 AssistantContent::Image(Image {
602 data: DocumentSourceKind::Base64(data.into()),
603 media_type,
604 detail,
605 additional_params: None,
606 })
607 }
608
609 pub fn tool_call(
611 id: impl Into<String>,
612 name: impl Into<String>,
613 arguments: serde_json::Value,
614 ) -> Self {
615 AssistantContent::ToolCall(ToolCall {
616 id: id.into(),
617 call_id: None,
618 function: ToolFunction {
619 name: name.into(),
620 arguments,
621 },
622 })
623 }
624
625 pub fn tool_call_with_call_id(
626 id: impl Into<String>,
627 call_id: String,
628 name: impl Into<String>,
629 arguments: serde_json::Value,
630 ) -> Self {
631 AssistantContent::ToolCall(ToolCall {
632 id: id.into(),
633 call_id: Some(call_id),
634 function: ToolFunction {
635 name: name.into(),
636 arguments,
637 },
638 })
639 }
640
641 pub fn reasoning(reasoning: impl AsRef<str>) -> Self {
642 AssistantContent::Reasoning(Reasoning::new(reasoning.as_ref()))
643 }
644}
645
646impl ToolResultContent {
647 pub fn text(text: impl Into<String>) -> Self {
649 ToolResultContent::Text(text.into().into())
650 }
651
652 pub fn image_base64(
654 data: impl Into<String>,
655 media_type: Option<ImageMediaType>,
656 detail: Option<ImageDetail>,
657 ) -> Self {
658 ToolResultContent::Image(Image {
659 data: DocumentSourceKind::Base64(data.into()),
660 media_type,
661 detail,
662 additional_params: None,
663 })
664 }
665
666 pub fn image_raw(
668 data: impl Into<Vec<u8>>,
669 media_type: Option<ImageMediaType>,
670 detail: Option<ImageDetail>,
671 ) -> Self {
672 ToolResultContent::Image(Image {
673 data: DocumentSourceKind::Raw(data.into()),
674 media_type,
675 detail,
676 ..Default::default()
677 })
678 }
679
680 pub fn image_url(
682 url: impl Into<String>,
683 media_type: Option<ImageMediaType>,
684 detail: Option<ImageDetail>,
685 ) -> Self {
686 ToolResultContent::Image(Image {
687 data: DocumentSourceKind::Url(url.into()),
688 media_type,
689 detail,
690 additional_params: None,
691 })
692 }
693}
694
695pub trait MimeType {
697 fn from_mime_type(mime_type: &str) -> Option<Self>
698 where
699 Self: Sized;
700 fn to_mime_type(&self) -> &'static str;
701}
702
703impl MimeType for MediaType {
704 fn from_mime_type(mime_type: &str) -> Option<Self> {
705 ImageMediaType::from_mime_type(mime_type)
706 .map(MediaType::Image)
707 .or_else(|| {
708 DocumentMediaType::from_mime_type(mime_type)
709 .map(MediaType::Document)
710 .or_else(|| {
711 AudioMediaType::from_mime_type(mime_type)
712 .map(MediaType::Audio)
713 .or_else(|| {
714 VideoMediaType::from_mime_type(mime_type).map(MediaType::Video)
715 })
716 })
717 })
718 }
719
720 fn to_mime_type(&self) -> &'static str {
721 match self {
722 MediaType::Image(media_type) => media_type.to_mime_type(),
723 MediaType::Audio(media_type) => media_type.to_mime_type(),
724 MediaType::Document(media_type) => media_type.to_mime_type(),
725 MediaType::Video(media_type) => media_type.to_mime_type(),
726 }
727 }
728}
729
730impl MimeType for ImageMediaType {
731 fn from_mime_type(mime_type: &str) -> Option<Self> {
732 match mime_type {
733 "image/jpeg" => Some(ImageMediaType::JPEG),
734 "image/png" => Some(ImageMediaType::PNG),
735 "image/gif" => Some(ImageMediaType::GIF),
736 "image/webp" => Some(ImageMediaType::WEBP),
737 "image/heic" => Some(ImageMediaType::HEIC),
738 "image/heif" => Some(ImageMediaType::HEIF),
739 "image/svg+xml" => Some(ImageMediaType::SVG),
740 _ => None,
741 }
742 }
743
744 fn to_mime_type(&self) -> &'static str {
745 match self {
746 ImageMediaType::JPEG => "image/jpeg",
747 ImageMediaType::PNG => "image/png",
748 ImageMediaType::GIF => "image/gif",
749 ImageMediaType::WEBP => "image/webp",
750 ImageMediaType::HEIC => "image/heic",
751 ImageMediaType::HEIF => "image/heif",
752 ImageMediaType::SVG => "image/svg+xml",
753 }
754 }
755}
756
757impl MimeType for DocumentMediaType {
758 fn from_mime_type(mime_type: &str) -> Option<Self> {
759 match mime_type {
760 "application/pdf" => Some(DocumentMediaType::PDF),
761 "text/plain" => Some(DocumentMediaType::TXT),
762 "text/rtf" => Some(DocumentMediaType::RTF),
763 "text/html" => Some(DocumentMediaType::HTML),
764 "text/css" => Some(DocumentMediaType::CSS),
765 "text/md" | "text/markdown" => Some(DocumentMediaType::MARKDOWN),
766 "text/csv" => Some(DocumentMediaType::CSV),
767 "text/xml" => Some(DocumentMediaType::XML),
768 "application/x-javascript" | "text/x-javascript" => Some(DocumentMediaType::Javascript),
769 "application/x-python" | "text/x-python" => Some(DocumentMediaType::Python),
770 _ => None,
771 }
772 }
773
774 fn to_mime_type(&self) -> &'static str {
775 match self {
776 DocumentMediaType::PDF => "application/pdf",
777 DocumentMediaType::TXT => "text/plain",
778 DocumentMediaType::RTF => "text/rtf",
779 DocumentMediaType::HTML => "text/html",
780 DocumentMediaType::CSS => "text/css",
781 DocumentMediaType::MARKDOWN => "text/markdown",
782 DocumentMediaType::CSV => "text/csv",
783 DocumentMediaType::XML => "text/xml",
784 DocumentMediaType::Javascript => "application/x-javascript",
785 DocumentMediaType::Python => "application/x-python",
786 }
787 }
788}
789
790impl MimeType for AudioMediaType {
791 fn from_mime_type(mime_type: &str) -> Option<Self> {
792 match mime_type {
793 "audio/wav" => Some(AudioMediaType::WAV),
794 "audio/mp3" => Some(AudioMediaType::MP3),
795 "audio/aiff" => Some(AudioMediaType::AIFF),
796 "audio/aac" => Some(AudioMediaType::AAC),
797 "audio/ogg" => Some(AudioMediaType::OGG),
798 "audio/flac" => Some(AudioMediaType::FLAC),
799 _ => None,
800 }
801 }
802
803 fn to_mime_type(&self) -> &'static str {
804 match self {
805 AudioMediaType::WAV => "audio/wav",
806 AudioMediaType::MP3 => "audio/mp3",
807 AudioMediaType::AIFF => "audio/aiff",
808 AudioMediaType::AAC => "audio/aac",
809 AudioMediaType::OGG => "audio/ogg",
810 AudioMediaType::FLAC => "audio/flac",
811 }
812 }
813}
814
815impl MimeType for VideoMediaType {
816 fn from_mime_type(mime_type: &str) -> Option<Self>
817 where
818 Self: Sized,
819 {
820 match mime_type {
821 "video/avi" => Some(VideoMediaType::AVI),
822 "video/mp4" => Some(VideoMediaType::MP4),
823 "video/mpeg" => Some(VideoMediaType::MPEG),
824 &_ => None,
825 }
826 }
827
828 fn to_mime_type(&self) -> &'static str {
829 match self {
830 VideoMediaType::AVI => "video/avi",
831 VideoMediaType::MP4 => "video/mp4",
832 VideoMediaType::MPEG => "video/mpeg",
833 }
834 }
835}
836
837impl std::str::FromStr for ImageDetail {
838 type Err = ();
839
840 fn from_str(s: &str) -> Result<Self, Self::Err> {
841 match s.to_lowercase().as_str() {
842 "low" => Ok(ImageDetail::Low),
843 "high" => Ok(ImageDetail::High),
844 "auto" => Ok(ImageDetail::Auto),
845 _ => Err(()),
846 }
847 }
848}
849
850impl From<String> for Text {
855 fn from(text: String) -> Self {
856 Text { text }
857 }
858}
859
860impl From<&String> for Text {
861 fn from(text: &String) -> Self {
862 text.to_owned().into()
863 }
864}
865
866impl From<&str> for Text {
867 fn from(text: &str) -> Self {
868 text.to_owned().into()
869 }
870}
871
872impl FromStr for Text {
873 type Err = Infallible;
874
875 fn from_str(s: &str) -> Result<Self, Self::Err> {
876 Ok(s.into())
877 }
878}
879
880impl From<String> for Message {
881 fn from(text: String) -> Self {
882 Message::User {
883 content: OneOrMany::one(UserContent::Text(text.into())),
884 }
885 }
886}
887
888impl From<&str> for Message {
889 fn from(text: &str) -> Self {
890 Message::User {
891 content: OneOrMany::one(UserContent::Text(text.into())),
892 }
893 }
894}
895
896impl From<&String> for Message {
897 fn from(text: &String) -> Self {
898 Message::User {
899 content: OneOrMany::one(UserContent::Text(text.into())),
900 }
901 }
902}
903
904impl From<Text> for Message {
905 fn from(text: Text) -> Self {
906 Message::User {
907 content: OneOrMany::one(UserContent::Text(text)),
908 }
909 }
910}
911
912impl From<Image> for Message {
913 fn from(image: Image) -> Self {
914 Message::User {
915 content: OneOrMany::one(UserContent::Image(image)),
916 }
917 }
918}
919
920impl From<Audio> for Message {
921 fn from(audio: Audio) -> Self {
922 Message::User {
923 content: OneOrMany::one(UserContent::Audio(audio)),
924 }
925 }
926}
927
928impl From<Document> for Message {
929 fn from(document: Document) -> Self {
930 Message::User {
931 content: OneOrMany::one(UserContent::Document(document)),
932 }
933 }
934}
935
936impl From<String> for ToolResultContent {
937 fn from(text: String) -> Self {
938 ToolResultContent::text(text)
939 }
940}
941
942impl From<String> for AssistantContent {
943 fn from(text: String) -> Self {
944 AssistantContent::text(text)
945 }
946}
947
948impl From<String> for UserContent {
949 fn from(text: String) -> Self {
950 UserContent::text(text)
951 }
952}
953
954impl From<AssistantContent> for Message {
955 fn from(content: AssistantContent) -> Self {
956 Message::Assistant {
957 id: None,
958 content: OneOrMany::one(content),
959 }
960 }
961}
962
963impl From<UserContent> for Message {
964 fn from(content: UserContent) -> Self {
965 Message::User {
966 content: OneOrMany::one(content),
967 }
968 }
969}
970
971impl From<OneOrMany<AssistantContent>> for Message {
972 fn from(content: OneOrMany<AssistantContent>) -> Self {
973 Message::Assistant { id: None, content }
974 }
975}
976
977impl From<OneOrMany<UserContent>> for Message {
978 fn from(content: OneOrMany<UserContent>) -> Self {
979 Message::User { content }
980 }
981}
982
983impl From<ToolCall> for Message {
984 fn from(tool_call: ToolCall) -> Self {
985 Message::Assistant {
986 id: None,
987 content: OneOrMany::one(AssistantContent::ToolCall(tool_call)),
988 }
989 }
990}
991
992impl From<ToolResult> for Message {
993 fn from(tool_result: ToolResult) -> Self {
994 Message::User {
995 content: OneOrMany::one(UserContent::ToolResult(tool_result)),
996 }
997 }
998}
999
1000impl From<ToolResultContent> for Message {
1001 fn from(tool_result_content: ToolResultContent) -> Self {
1002 Message::User {
1003 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
1004 id: String::new(),
1005 call_id: None,
1006 content: OneOrMany::one(tool_result_content),
1007 })),
1008 }
1009 }
1010}
1011
1012#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
1013#[serde(rename_all = "snake_case")]
1014pub enum ToolChoice {
1015 #[default]
1016 Auto,
1017 None,
1018 Required,
1019 Specific {
1020 function_names: Vec<String>,
1021 },
1022}
1023
1024#[derive(Debug, Error)]
1030pub enum MessageError {
1031 #[error("Message conversion error: {0}")]
1032 ConversionError(String),
1033}
1034
1035impl From<MessageError> for CompletionError {
1036 fn from(error: MessageError) -> Self {
1037 CompletionError::RequestError(error.into())
1038 }
1039}