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 pub signature: Option<String>,
155 pub additional_params: Option<serde_json::Value>,
157}
158
159impl ToolCall {
160 pub fn new(id: String, function: ToolFunction) -> Self {
161 Self {
162 id,
163 call_id: None,
164 function,
165 signature: None,
166 additional_params: None,
167 }
168 }
169
170 pub fn with_call_id(mut self, call_id: String) -> Self {
171 self.call_id = Some(call_id);
172 self
173 }
174
175 pub fn with_signature(mut self, signature: Option<String>) -> Self {
176 self.signature = signature;
177 self
178 }
179
180 pub fn with_additional_params(mut self, additional_params: Option<serde_json::Value>) -> Self {
181 self.additional_params = additional_params;
182 self
183 }
184}
185
186#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
188pub struct ToolFunction {
189 pub name: String,
190 pub arguments: serde_json::Value,
191}
192
193impl ToolFunction {
194 pub fn new(name: String, arguments: serde_json::Value) -> Self {
195 Self { name, arguments }
196 }
197}
198
199#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
205pub struct Text {
206 pub text: String,
207}
208
209impl Text {
210 pub fn text(&self) -> &str {
211 &self.text
212 }
213}
214
215impl std::fmt::Display for Text {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 let Self { text } = self;
218 write!(f, "{text}")
219 }
220}
221
222#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
224pub struct Image {
225 pub data: DocumentSourceKind,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub media_type: Option<ImageMediaType>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub detail: Option<ImageDetail>,
230 #[serde(flatten, skip_serializing_if = "Option::is_none")]
231 pub additional_params: Option<serde_json::Value>,
232}
233
234impl Image {
235 pub fn try_into_url(self) -> Result<String, MessageError> {
236 match self.data {
237 DocumentSourceKind::Url(url) => Ok(url),
238 DocumentSourceKind::Base64(data) => {
239 let Some(media_type) = self.media_type else {
240 return Err(MessageError::ConversionError(
241 "A media type is required to create a valid base64-encoded image URL"
242 .to_string(),
243 ));
244 };
245
246 Ok(format!(
247 "data:image/{ty};base64,{data}",
248 ty = media_type.to_mime_type()
249 ))
250 }
251 unknown => Err(MessageError::ConversionError(format!(
252 "Tried to convert unknown type to a URL: {unknown:?}"
253 ))),
254 }
255 }
256}
257
258#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
260#[serde(tag = "type", content = "value", rename_all = "camelCase")]
261#[non_exhaustive]
262pub enum DocumentSourceKind {
263 Url(String),
265 Base64(String),
267 Raw(Vec<u8>),
269 String(String),
271 #[default]
272 Unknown,
274}
275
276impl DocumentSourceKind {
277 pub fn url(url: &str) -> Self {
278 Self::Url(url.to_string())
279 }
280
281 pub fn base64(base64_string: &str) -> Self {
282 Self::Base64(base64_string.to_string())
283 }
284
285 pub fn raw(bytes: impl Into<Vec<u8>>) -> Self {
286 Self::Raw(bytes.into())
287 }
288
289 pub fn string(input: &str) -> Self {
290 Self::String(input.into())
291 }
292
293 pub fn unknown() -> Self {
294 Self::Unknown
295 }
296
297 pub fn try_into_inner(self) -> Option<String> {
298 match self {
299 Self::Url(s) | Self::Base64(s) => Some(s),
300 _ => None,
301 }
302 }
303}
304
305impl std::fmt::Display for DocumentSourceKind {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 Self::Url(string) => write!(f, "{string}"),
309 Self::Base64(string) => write!(f, "{string}"),
310 Self::String(string) => write!(f, "{string}"),
311 Self::Raw(_) => write!(f, "<binary data>"),
312 Self::Unknown => write!(f, "<unknown>"),
313 }
314 }
315}
316
317#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
319pub struct Audio {
320 pub data: DocumentSourceKind,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub media_type: Option<AudioMediaType>,
323 #[serde(flatten, skip_serializing_if = "Option::is_none")]
324 pub additional_params: Option<serde_json::Value>,
325}
326
327#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
329pub struct Video {
330 pub data: DocumentSourceKind,
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub media_type: Option<VideoMediaType>,
333 #[serde(flatten, skip_serializing_if = "Option::is_none")]
334 pub additional_params: Option<serde_json::Value>,
335}
336
337#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
339pub struct Document {
340 pub data: DocumentSourceKind,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub media_type: Option<DocumentMediaType>,
343 #[serde(flatten, skip_serializing_if = "Option::is_none")]
344 pub additional_params: Option<serde_json::Value>,
345}
346
347#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
349#[serde(rename_all = "lowercase")]
350pub enum ContentFormat {
351 #[default]
352 Base64,
353 String,
354 Url,
355}
356
357#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
359pub enum MediaType {
360 Image(ImageMediaType),
361 Audio(AudioMediaType),
362 Document(DocumentMediaType),
363 Video(VideoMediaType),
364}
365
366#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
369#[serde(rename_all = "lowercase")]
370pub enum ImageMediaType {
371 JPEG,
372 PNG,
373 GIF,
374 WEBP,
375 HEIC,
376 HEIF,
377 SVG,
378}
379
380#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
384#[serde(rename_all = "lowercase")]
385pub enum DocumentMediaType {
386 PDF,
387 TXT,
388 RTF,
389 HTML,
390 CSS,
391 MARKDOWN,
392 CSV,
393 XML,
394 Javascript,
395 Python,
396}
397
398impl DocumentMediaType {
399 pub fn is_code(&self) -> bool {
400 matches!(self, Self::Javascript | Self::Python)
401 }
402}
403
404#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
407#[serde(rename_all = "lowercase")]
408pub enum AudioMediaType {
409 WAV,
410 MP3,
411 AIFF,
412 AAC,
413 OGG,
414 FLAC,
415}
416
417#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
420#[serde(rename_all = "lowercase")]
421pub enum VideoMediaType {
422 AVI,
423 MP4,
424 MPEG,
425}
426
427#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
429#[serde(rename_all = "lowercase")]
430pub enum ImageDetail {
431 Low,
432 High,
433 #[default]
434 Auto,
435}
436
437impl Message {
442 pub(crate) fn rag_text(&self) -> Option<String> {
445 match self {
446 Message::User { content } => {
447 for item in content.iter() {
448 if let UserContent::Text(Text { text }) = item {
449 return Some(text.clone());
450 }
451 }
452 None
453 }
454 _ => None,
455 }
456 }
457
458 pub fn user(text: impl Into<String>) -> Self {
460 Message::User {
461 content: OneOrMany::one(UserContent::text(text)),
462 }
463 }
464
465 pub fn assistant(text: impl Into<String>) -> Self {
467 Message::Assistant {
468 id: None,
469 content: OneOrMany::one(AssistantContent::text(text)),
470 }
471 }
472
473 pub fn assistant_with_id(id: String, text: impl Into<String>) -> Self {
475 Message::Assistant {
476 id: Some(id),
477 content: OneOrMany::one(AssistantContent::text(text)),
478 }
479 }
480
481 pub fn tool_result(id: impl Into<String>, content: impl Into<String>) -> Self {
483 Message::User {
484 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
485 id: id.into(),
486 call_id: None,
487 content: OneOrMany::one(ToolResultContent::text(content)),
488 })),
489 }
490 }
491
492 pub fn tool_result_with_call_id(
493 id: impl Into<String>,
494 call_id: Option<String>,
495 content: impl Into<String>,
496 ) -> Self {
497 Message::User {
498 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
499 id: id.into(),
500 call_id,
501 content: OneOrMany::one(ToolResultContent::text(content)),
502 })),
503 }
504 }
505}
506
507impl UserContent {
508 pub fn text(text: impl Into<String>) -> Self {
510 UserContent::Text(text.into().into())
511 }
512
513 pub fn image_base64(
515 data: impl Into<String>,
516 media_type: Option<ImageMediaType>,
517 detail: Option<ImageDetail>,
518 ) -> Self {
519 UserContent::Image(Image {
520 data: DocumentSourceKind::Base64(data.into()),
521 media_type,
522 detail,
523 additional_params: None,
524 })
525 }
526
527 pub fn image_raw(
529 data: impl Into<Vec<u8>>,
530 media_type: Option<ImageMediaType>,
531 detail: Option<ImageDetail>,
532 ) -> Self {
533 UserContent::Image(Image {
534 data: DocumentSourceKind::Raw(data.into()),
535 media_type,
536 detail,
537 ..Default::default()
538 })
539 }
540
541 pub fn image_url(
543 url: impl Into<String>,
544 media_type: Option<ImageMediaType>,
545 detail: Option<ImageDetail>,
546 ) -> Self {
547 UserContent::Image(Image {
548 data: DocumentSourceKind::Url(url.into()),
549 media_type,
550 detail,
551 additional_params: None,
552 })
553 }
554
555 pub fn audio(data: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
557 UserContent::Audio(Audio {
558 data: DocumentSourceKind::Base64(data.into()),
559 media_type,
560 additional_params: None,
561 })
562 }
563
564 pub fn audio_raw(data: impl Into<Vec<u8>>, media_type: Option<AudioMediaType>) -> Self {
566 UserContent::Audio(Audio {
567 data: DocumentSourceKind::Raw(data.into()),
568 media_type,
569 ..Default::default()
570 })
571 }
572
573 pub fn audio_url(url: impl Into<String>, media_type: Option<AudioMediaType>) -> Self {
575 UserContent::Audio(Audio {
576 data: DocumentSourceKind::Url(url.into()),
577 media_type,
578 ..Default::default()
579 })
580 }
581
582 pub fn document(data: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
585 let data: String = data.into();
586 UserContent::Document(Document {
587 data: DocumentSourceKind::string(&data),
588 media_type,
589 additional_params: None,
590 })
591 }
592
593 pub fn document_raw(data: impl Into<Vec<u8>>, media_type: Option<DocumentMediaType>) -> Self {
595 UserContent::Document(Document {
596 data: DocumentSourceKind::Raw(data.into()),
597 media_type,
598 ..Default::default()
599 })
600 }
601
602 pub fn document_url(url: impl Into<String>, media_type: Option<DocumentMediaType>) -> Self {
604 UserContent::Document(Document {
605 data: DocumentSourceKind::Url(url.into()),
606 media_type,
607 ..Default::default()
608 })
609 }
610
611 pub fn tool_result(id: impl Into<String>, content: OneOrMany<ToolResultContent>) -> Self {
613 UserContent::ToolResult(ToolResult {
614 id: id.into(),
615 call_id: None,
616 content,
617 })
618 }
619
620 pub fn tool_result_with_call_id(
622 id: impl Into<String>,
623 call_id: String,
624 content: OneOrMany<ToolResultContent>,
625 ) -> Self {
626 UserContent::ToolResult(ToolResult {
627 id: id.into(),
628 call_id: Some(call_id),
629 content,
630 })
631 }
632}
633
634impl AssistantContent {
635 pub fn text(text: impl Into<String>) -> Self {
637 AssistantContent::Text(text.into().into())
638 }
639
640 pub fn image_base64(
642 data: impl Into<String>,
643 media_type: Option<ImageMediaType>,
644 detail: Option<ImageDetail>,
645 ) -> Self {
646 AssistantContent::Image(Image {
647 data: DocumentSourceKind::Base64(data.into()),
648 media_type,
649 detail,
650 additional_params: None,
651 })
652 }
653
654 pub fn tool_call(
656 id: impl Into<String>,
657 name: impl Into<String>,
658 arguments: serde_json::Value,
659 ) -> Self {
660 AssistantContent::ToolCall(ToolCall::new(
661 id.into(),
662 ToolFunction {
663 name: name.into(),
664 arguments,
665 },
666 ))
667 }
668
669 pub fn tool_call_with_call_id(
670 id: impl Into<String>,
671 call_id: String,
672 name: impl Into<String>,
673 arguments: serde_json::Value,
674 ) -> Self {
675 AssistantContent::ToolCall(
676 ToolCall::new(
677 id.into(),
678 ToolFunction {
679 name: name.into(),
680 arguments,
681 },
682 )
683 .with_call_id(call_id),
684 )
685 }
686
687 pub fn reasoning(reasoning: impl AsRef<str>) -> Self {
688 AssistantContent::Reasoning(Reasoning::new(reasoning.as_ref()))
689 }
690}
691
692impl ToolResultContent {
693 pub fn text(text: impl Into<String>) -> Self {
695 ToolResultContent::Text(text.into().into())
696 }
697
698 pub fn image_base64(
700 data: impl Into<String>,
701 media_type: Option<ImageMediaType>,
702 detail: Option<ImageDetail>,
703 ) -> Self {
704 ToolResultContent::Image(Image {
705 data: DocumentSourceKind::Base64(data.into()),
706 media_type,
707 detail,
708 additional_params: None,
709 })
710 }
711
712 pub fn image_raw(
714 data: impl Into<Vec<u8>>,
715 media_type: Option<ImageMediaType>,
716 detail: Option<ImageDetail>,
717 ) -> Self {
718 ToolResultContent::Image(Image {
719 data: DocumentSourceKind::Raw(data.into()),
720 media_type,
721 detail,
722 ..Default::default()
723 })
724 }
725
726 pub fn image_url(
728 url: impl Into<String>,
729 media_type: Option<ImageMediaType>,
730 detail: Option<ImageDetail>,
731 ) -> Self {
732 ToolResultContent::Image(Image {
733 data: DocumentSourceKind::Url(url.into()),
734 media_type,
735 detail,
736 additional_params: None,
737 })
738 }
739}
740
741pub trait MimeType {
743 fn from_mime_type(mime_type: &str) -> Option<Self>
744 where
745 Self: Sized;
746 fn to_mime_type(&self) -> &'static str;
747}
748
749impl MimeType for MediaType {
750 fn from_mime_type(mime_type: &str) -> Option<Self> {
751 ImageMediaType::from_mime_type(mime_type)
752 .map(MediaType::Image)
753 .or_else(|| {
754 DocumentMediaType::from_mime_type(mime_type)
755 .map(MediaType::Document)
756 .or_else(|| {
757 AudioMediaType::from_mime_type(mime_type)
758 .map(MediaType::Audio)
759 .or_else(|| {
760 VideoMediaType::from_mime_type(mime_type).map(MediaType::Video)
761 })
762 })
763 })
764 }
765
766 fn to_mime_type(&self) -> &'static str {
767 match self {
768 MediaType::Image(media_type) => media_type.to_mime_type(),
769 MediaType::Audio(media_type) => media_type.to_mime_type(),
770 MediaType::Document(media_type) => media_type.to_mime_type(),
771 MediaType::Video(media_type) => media_type.to_mime_type(),
772 }
773 }
774}
775
776impl MimeType for ImageMediaType {
777 fn from_mime_type(mime_type: &str) -> Option<Self> {
778 match mime_type {
779 "image/jpeg" => Some(ImageMediaType::JPEG),
780 "image/png" => Some(ImageMediaType::PNG),
781 "image/gif" => Some(ImageMediaType::GIF),
782 "image/webp" => Some(ImageMediaType::WEBP),
783 "image/heic" => Some(ImageMediaType::HEIC),
784 "image/heif" => Some(ImageMediaType::HEIF),
785 "image/svg+xml" => Some(ImageMediaType::SVG),
786 _ => None,
787 }
788 }
789
790 fn to_mime_type(&self) -> &'static str {
791 match self {
792 ImageMediaType::JPEG => "image/jpeg",
793 ImageMediaType::PNG => "image/png",
794 ImageMediaType::GIF => "image/gif",
795 ImageMediaType::WEBP => "image/webp",
796 ImageMediaType::HEIC => "image/heic",
797 ImageMediaType::HEIF => "image/heif",
798 ImageMediaType::SVG => "image/svg+xml",
799 }
800 }
801}
802
803impl MimeType for DocumentMediaType {
804 fn from_mime_type(mime_type: &str) -> Option<Self> {
805 match mime_type {
806 "application/pdf" => Some(DocumentMediaType::PDF),
807 "text/plain" => Some(DocumentMediaType::TXT),
808 "text/rtf" => Some(DocumentMediaType::RTF),
809 "text/html" => Some(DocumentMediaType::HTML),
810 "text/css" => Some(DocumentMediaType::CSS),
811 "text/md" | "text/markdown" => Some(DocumentMediaType::MARKDOWN),
812 "text/csv" => Some(DocumentMediaType::CSV),
813 "text/xml" => Some(DocumentMediaType::XML),
814 "application/x-javascript" | "text/x-javascript" => Some(DocumentMediaType::Javascript),
815 "application/x-python" | "text/x-python" => Some(DocumentMediaType::Python),
816 _ => None,
817 }
818 }
819
820 fn to_mime_type(&self) -> &'static str {
821 match self {
822 DocumentMediaType::PDF => "application/pdf",
823 DocumentMediaType::TXT => "text/plain",
824 DocumentMediaType::RTF => "text/rtf",
825 DocumentMediaType::HTML => "text/html",
826 DocumentMediaType::CSS => "text/css",
827 DocumentMediaType::MARKDOWN => "text/markdown",
828 DocumentMediaType::CSV => "text/csv",
829 DocumentMediaType::XML => "text/xml",
830 DocumentMediaType::Javascript => "application/x-javascript",
831 DocumentMediaType::Python => "application/x-python",
832 }
833 }
834}
835
836impl MimeType for AudioMediaType {
837 fn from_mime_type(mime_type: &str) -> Option<Self> {
838 match mime_type {
839 "audio/wav" => Some(AudioMediaType::WAV),
840 "audio/mp3" => Some(AudioMediaType::MP3),
841 "audio/aiff" => Some(AudioMediaType::AIFF),
842 "audio/aac" => Some(AudioMediaType::AAC),
843 "audio/ogg" => Some(AudioMediaType::OGG),
844 "audio/flac" => Some(AudioMediaType::FLAC),
845 _ => None,
846 }
847 }
848
849 fn to_mime_type(&self) -> &'static str {
850 match self {
851 AudioMediaType::WAV => "audio/wav",
852 AudioMediaType::MP3 => "audio/mp3",
853 AudioMediaType::AIFF => "audio/aiff",
854 AudioMediaType::AAC => "audio/aac",
855 AudioMediaType::OGG => "audio/ogg",
856 AudioMediaType::FLAC => "audio/flac",
857 }
858 }
859}
860
861impl MimeType for VideoMediaType {
862 fn from_mime_type(mime_type: &str) -> Option<Self>
863 where
864 Self: Sized,
865 {
866 match mime_type {
867 "video/avi" => Some(VideoMediaType::AVI),
868 "video/mp4" => Some(VideoMediaType::MP4),
869 "video/mpeg" => Some(VideoMediaType::MPEG),
870 &_ => None,
871 }
872 }
873
874 fn to_mime_type(&self) -> &'static str {
875 match self {
876 VideoMediaType::AVI => "video/avi",
877 VideoMediaType::MP4 => "video/mp4",
878 VideoMediaType::MPEG => "video/mpeg",
879 }
880 }
881}
882
883impl std::str::FromStr for ImageDetail {
884 type Err = ();
885
886 fn from_str(s: &str) -> Result<Self, Self::Err> {
887 match s.to_lowercase().as_str() {
888 "low" => Ok(ImageDetail::Low),
889 "high" => Ok(ImageDetail::High),
890 "auto" => Ok(ImageDetail::Auto),
891 _ => Err(()),
892 }
893 }
894}
895
896impl From<String> for Text {
901 fn from(text: String) -> Self {
902 Text { text }
903 }
904}
905
906impl From<&String> for Text {
907 fn from(text: &String) -> Self {
908 text.to_owned().into()
909 }
910}
911
912impl From<&str> for Text {
913 fn from(text: &str) -> Self {
914 text.to_owned().into()
915 }
916}
917
918impl FromStr for Text {
919 type Err = Infallible;
920
921 fn from_str(s: &str) -> Result<Self, Self::Err> {
922 Ok(s.into())
923 }
924}
925
926impl From<String> for Message {
927 fn from(text: String) -> Self {
928 Message::User {
929 content: OneOrMany::one(UserContent::Text(text.into())),
930 }
931 }
932}
933
934impl From<&str> for Message {
935 fn from(text: &str) -> Self {
936 Message::User {
937 content: OneOrMany::one(UserContent::Text(text.into())),
938 }
939 }
940}
941
942impl From<&String> for Message {
943 fn from(text: &String) -> Self {
944 Message::User {
945 content: OneOrMany::one(UserContent::Text(text.into())),
946 }
947 }
948}
949
950impl From<Text> for Message {
951 fn from(text: Text) -> Self {
952 Message::User {
953 content: OneOrMany::one(UserContent::Text(text)),
954 }
955 }
956}
957
958impl From<Image> for Message {
959 fn from(image: Image) -> Self {
960 Message::User {
961 content: OneOrMany::one(UserContent::Image(image)),
962 }
963 }
964}
965
966impl From<Audio> for Message {
967 fn from(audio: Audio) -> Self {
968 Message::User {
969 content: OneOrMany::one(UserContent::Audio(audio)),
970 }
971 }
972}
973
974impl From<Document> for Message {
975 fn from(document: Document) -> Self {
976 Message::User {
977 content: OneOrMany::one(UserContent::Document(document)),
978 }
979 }
980}
981
982impl From<String> for ToolResultContent {
983 fn from(text: String) -> Self {
984 ToolResultContent::text(text)
985 }
986}
987
988impl From<String> for AssistantContent {
989 fn from(text: String) -> Self {
990 AssistantContent::text(text)
991 }
992}
993
994impl From<String> for UserContent {
995 fn from(text: String) -> Self {
996 UserContent::text(text)
997 }
998}
999
1000impl From<AssistantContent> for Message {
1001 fn from(content: AssistantContent) -> Self {
1002 Message::Assistant {
1003 id: None,
1004 content: OneOrMany::one(content),
1005 }
1006 }
1007}
1008
1009impl From<UserContent> for Message {
1010 fn from(content: UserContent) -> Self {
1011 Message::User {
1012 content: OneOrMany::one(content),
1013 }
1014 }
1015}
1016
1017impl From<OneOrMany<AssistantContent>> for Message {
1018 fn from(content: OneOrMany<AssistantContent>) -> Self {
1019 Message::Assistant { id: None, content }
1020 }
1021}
1022
1023impl From<OneOrMany<UserContent>> for Message {
1024 fn from(content: OneOrMany<UserContent>) -> Self {
1025 Message::User { content }
1026 }
1027}
1028
1029impl From<ToolCall> for Message {
1030 fn from(tool_call: ToolCall) -> Self {
1031 Message::Assistant {
1032 id: None,
1033 content: OneOrMany::one(AssistantContent::ToolCall(tool_call)),
1034 }
1035 }
1036}
1037
1038impl From<ToolResult> for Message {
1039 fn from(tool_result: ToolResult) -> Self {
1040 Message::User {
1041 content: OneOrMany::one(UserContent::ToolResult(tool_result)),
1042 }
1043 }
1044}
1045
1046impl From<ToolResultContent> for Message {
1047 fn from(tool_result_content: ToolResultContent) -> Self {
1048 Message::User {
1049 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
1050 id: String::new(),
1051 call_id: None,
1052 content: OneOrMany::one(tool_result_content),
1053 })),
1054 }
1055 }
1056}
1057
1058#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
1059#[serde(rename_all = "snake_case")]
1060pub enum ToolChoice {
1061 #[default]
1062 Auto,
1063 None,
1064 Required,
1065 Specific {
1066 function_names: Vec<String>,
1067 },
1068}
1069
1070#[derive(Debug, Error)]
1076pub enum MessageError {
1077 #[error("Message conversion error: {0}")]
1078 ConversionError(String),
1079}
1080
1081impl From<MessageError> for CompletionError {
1082 fn from(error: MessageError) -> Self {
1083 CompletionError::RequestError(error.into())
1084 }
1085}