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