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