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 pub fn from_tool_output(output: impl Into<String>) -> OneOrMany<ToolResultContent> {
749 let output_str = output.into();
750
751 if let Ok(json) = serde_json::from_str::<serde_json::Value>(&output_str) {
752 if json.get("response").is_some() || json.get("parts").is_some() {
753 let mut results: Vec<ToolResultContent> = Vec::new();
754
755 if let Some(response) = json.get("response") {
756 results.push(ToolResultContent::Text(Text {
757 text: response.to_string(),
758 }));
759 }
760
761 if let Some(parts) = json.get("parts").and_then(|p| p.as_array()) {
762 for part in parts {
763 let is_image = part
764 .get("type")
765 .and_then(|t| t.as_str())
766 .is_some_and(|t| t == "image");
767
768 if !is_image {
769 continue;
770 }
771
772 if let (Some(data), Some(mime_type)) = (
773 part.get("data").and_then(|v| v.as_str()),
774 part.get("mimeType").and_then(|v| v.as_str()),
775 ) {
776 let data_kind =
777 if data.starts_with("http://") || data.starts_with("https://") {
778 DocumentSourceKind::Url(data.to_string())
779 } else {
780 DocumentSourceKind::Base64(data.to_string())
781 };
782
783 results.push(ToolResultContent::Image(Image {
784 data: data_kind,
785 media_type: ImageMediaType::from_mime_type(mime_type),
786 detail: None,
787 additional_params: None,
788 }));
789 }
790 }
791 }
792
793 if !results.is_empty() {
794 return OneOrMany::many(results).unwrap_or_else(|_| {
795 OneOrMany::one(ToolResultContent::Text(output_str.into()))
796 });
797 }
798 }
799
800 let is_image = json
801 .get("type")
802 .and_then(|v| v.as_str())
803 .is_some_and(|t| t == "image");
804
805 if is_image
806 && let (Some(data), Some(mime_type)) = (
807 json.get("data").and_then(|v| v.as_str()),
808 json.get("mimeType").and_then(|v| v.as_str()),
809 )
810 {
811 let data_kind = if data.starts_with("http://") || data.starts_with("https://") {
812 DocumentSourceKind::Url(data.to_string())
813 } else {
814 DocumentSourceKind::Base64(data.to_string())
815 };
816
817 return OneOrMany::one(ToolResultContent::Image(Image {
818 data: data_kind,
819 media_type: ImageMediaType::from_mime_type(mime_type),
820 detail: None,
821 additional_params: None,
822 }));
823 }
824 }
825
826 OneOrMany::one(ToolResultContent::Text(output_str.into()))
827 }
828}
829
830pub trait MimeType {
832 fn from_mime_type(mime_type: &str) -> Option<Self>
833 where
834 Self: Sized;
835 fn to_mime_type(&self) -> &'static str;
836}
837
838impl MimeType for MediaType {
839 fn from_mime_type(mime_type: &str) -> Option<Self> {
840 ImageMediaType::from_mime_type(mime_type)
841 .map(MediaType::Image)
842 .or_else(|| {
843 DocumentMediaType::from_mime_type(mime_type)
844 .map(MediaType::Document)
845 .or_else(|| {
846 AudioMediaType::from_mime_type(mime_type)
847 .map(MediaType::Audio)
848 .or_else(|| {
849 VideoMediaType::from_mime_type(mime_type).map(MediaType::Video)
850 })
851 })
852 })
853 }
854
855 fn to_mime_type(&self) -> &'static str {
856 match self {
857 MediaType::Image(media_type) => media_type.to_mime_type(),
858 MediaType::Audio(media_type) => media_type.to_mime_type(),
859 MediaType::Document(media_type) => media_type.to_mime_type(),
860 MediaType::Video(media_type) => media_type.to_mime_type(),
861 }
862 }
863}
864
865impl MimeType for ImageMediaType {
866 fn from_mime_type(mime_type: &str) -> Option<Self> {
867 match mime_type {
868 "image/jpeg" => Some(ImageMediaType::JPEG),
869 "image/png" => Some(ImageMediaType::PNG),
870 "image/gif" => Some(ImageMediaType::GIF),
871 "image/webp" => Some(ImageMediaType::WEBP),
872 "image/heic" => Some(ImageMediaType::HEIC),
873 "image/heif" => Some(ImageMediaType::HEIF),
874 "image/svg+xml" => Some(ImageMediaType::SVG),
875 _ => None,
876 }
877 }
878
879 fn to_mime_type(&self) -> &'static str {
880 match self {
881 ImageMediaType::JPEG => "image/jpeg",
882 ImageMediaType::PNG => "image/png",
883 ImageMediaType::GIF => "image/gif",
884 ImageMediaType::WEBP => "image/webp",
885 ImageMediaType::HEIC => "image/heic",
886 ImageMediaType::HEIF => "image/heif",
887 ImageMediaType::SVG => "image/svg+xml",
888 }
889 }
890}
891
892impl MimeType for DocumentMediaType {
893 fn from_mime_type(mime_type: &str) -> Option<Self> {
894 match mime_type {
895 "application/pdf" => Some(DocumentMediaType::PDF),
896 "text/plain" => Some(DocumentMediaType::TXT),
897 "text/rtf" => Some(DocumentMediaType::RTF),
898 "text/html" => Some(DocumentMediaType::HTML),
899 "text/css" => Some(DocumentMediaType::CSS),
900 "text/md" | "text/markdown" => Some(DocumentMediaType::MARKDOWN),
901 "text/csv" => Some(DocumentMediaType::CSV),
902 "text/xml" => Some(DocumentMediaType::XML),
903 "application/x-javascript" | "text/x-javascript" => Some(DocumentMediaType::Javascript),
904 "application/x-python" | "text/x-python" => Some(DocumentMediaType::Python),
905 _ => None,
906 }
907 }
908
909 fn to_mime_type(&self) -> &'static str {
910 match self {
911 DocumentMediaType::PDF => "application/pdf",
912 DocumentMediaType::TXT => "text/plain",
913 DocumentMediaType::RTF => "text/rtf",
914 DocumentMediaType::HTML => "text/html",
915 DocumentMediaType::CSS => "text/css",
916 DocumentMediaType::MARKDOWN => "text/markdown",
917 DocumentMediaType::CSV => "text/csv",
918 DocumentMediaType::XML => "text/xml",
919 DocumentMediaType::Javascript => "application/x-javascript",
920 DocumentMediaType::Python => "application/x-python",
921 }
922 }
923}
924
925impl MimeType for AudioMediaType {
926 fn from_mime_type(mime_type: &str) -> Option<Self> {
927 match mime_type {
928 "audio/wav" => Some(AudioMediaType::WAV),
929 "audio/mp3" => Some(AudioMediaType::MP3),
930 "audio/aiff" => Some(AudioMediaType::AIFF),
931 "audio/aac" => Some(AudioMediaType::AAC),
932 "audio/ogg" => Some(AudioMediaType::OGG),
933 "audio/flac" => Some(AudioMediaType::FLAC),
934 _ => None,
935 }
936 }
937
938 fn to_mime_type(&self) -> &'static str {
939 match self {
940 AudioMediaType::WAV => "audio/wav",
941 AudioMediaType::MP3 => "audio/mp3",
942 AudioMediaType::AIFF => "audio/aiff",
943 AudioMediaType::AAC => "audio/aac",
944 AudioMediaType::OGG => "audio/ogg",
945 AudioMediaType::FLAC => "audio/flac",
946 }
947 }
948}
949
950impl MimeType for VideoMediaType {
951 fn from_mime_type(mime_type: &str) -> Option<Self>
952 where
953 Self: Sized,
954 {
955 match mime_type {
956 "video/avi" => Some(VideoMediaType::AVI),
957 "video/mp4" => Some(VideoMediaType::MP4),
958 "video/mpeg" => Some(VideoMediaType::MPEG),
959 &_ => None,
960 }
961 }
962
963 fn to_mime_type(&self) -> &'static str {
964 match self {
965 VideoMediaType::AVI => "video/avi",
966 VideoMediaType::MP4 => "video/mp4",
967 VideoMediaType::MPEG => "video/mpeg",
968 }
969 }
970}
971
972impl std::str::FromStr for ImageDetail {
973 type Err = ();
974
975 fn from_str(s: &str) -> Result<Self, Self::Err> {
976 match s.to_lowercase().as_str() {
977 "low" => Ok(ImageDetail::Low),
978 "high" => Ok(ImageDetail::High),
979 "auto" => Ok(ImageDetail::Auto),
980 _ => Err(()),
981 }
982 }
983}
984
985impl From<String> for Text {
990 fn from(text: String) -> Self {
991 Text { text }
992 }
993}
994
995impl From<&String> for Text {
996 fn from(text: &String) -> Self {
997 text.to_owned().into()
998 }
999}
1000
1001impl From<&str> for Text {
1002 fn from(text: &str) -> Self {
1003 text.to_owned().into()
1004 }
1005}
1006
1007impl FromStr for Text {
1008 type Err = Infallible;
1009
1010 fn from_str(s: &str) -> Result<Self, Self::Err> {
1011 Ok(s.into())
1012 }
1013}
1014
1015impl From<String> for Message {
1016 fn from(text: String) -> Self {
1017 Message::User {
1018 content: OneOrMany::one(UserContent::Text(text.into())),
1019 }
1020 }
1021}
1022
1023impl From<&str> for Message {
1024 fn from(text: &str) -> Self {
1025 Message::User {
1026 content: OneOrMany::one(UserContent::Text(text.into())),
1027 }
1028 }
1029}
1030
1031impl From<&String> for Message {
1032 fn from(text: &String) -> Self {
1033 Message::User {
1034 content: OneOrMany::one(UserContent::Text(text.into())),
1035 }
1036 }
1037}
1038
1039impl From<Text> for Message {
1040 fn from(text: Text) -> Self {
1041 Message::User {
1042 content: OneOrMany::one(UserContent::Text(text)),
1043 }
1044 }
1045}
1046
1047impl From<Image> for Message {
1048 fn from(image: Image) -> Self {
1049 Message::User {
1050 content: OneOrMany::one(UserContent::Image(image)),
1051 }
1052 }
1053}
1054
1055impl From<Audio> for Message {
1056 fn from(audio: Audio) -> Self {
1057 Message::User {
1058 content: OneOrMany::one(UserContent::Audio(audio)),
1059 }
1060 }
1061}
1062
1063impl From<Document> for Message {
1064 fn from(document: Document) -> Self {
1065 Message::User {
1066 content: OneOrMany::one(UserContent::Document(document)),
1067 }
1068 }
1069}
1070
1071impl From<String> for ToolResultContent {
1072 fn from(text: String) -> Self {
1073 ToolResultContent::text(text)
1074 }
1075}
1076
1077impl From<String> for AssistantContent {
1078 fn from(text: String) -> Self {
1079 AssistantContent::text(text)
1080 }
1081}
1082
1083impl From<String> for UserContent {
1084 fn from(text: String) -> Self {
1085 UserContent::text(text)
1086 }
1087}
1088
1089impl From<AssistantContent> for Message {
1090 fn from(content: AssistantContent) -> Self {
1091 Message::Assistant {
1092 id: None,
1093 content: OneOrMany::one(content),
1094 }
1095 }
1096}
1097
1098impl From<UserContent> for Message {
1099 fn from(content: UserContent) -> Self {
1100 Message::User {
1101 content: OneOrMany::one(content),
1102 }
1103 }
1104}
1105
1106impl From<OneOrMany<AssistantContent>> for Message {
1107 fn from(content: OneOrMany<AssistantContent>) -> Self {
1108 Message::Assistant { id: None, content }
1109 }
1110}
1111
1112impl From<OneOrMany<UserContent>> for Message {
1113 fn from(content: OneOrMany<UserContent>) -> Self {
1114 Message::User { content }
1115 }
1116}
1117
1118impl From<ToolCall> for Message {
1119 fn from(tool_call: ToolCall) -> Self {
1120 Message::Assistant {
1121 id: None,
1122 content: OneOrMany::one(AssistantContent::ToolCall(tool_call)),
1123 }
1124 }
1125}
1126
1127impl From<ToolResult> for Message {
1128 fn from(tool_result: ToolResult) -> Self {
1129 Message::User {
1130 content: OneOrMany::one(UserContent::ToolResult(tool_result)),
1131 }
1132 }
1133}
1134
1135impl From<ToolResultContent> for Message {
1136 fn from(tool_result_content: ToolResultContent) -> Self {
1137 Message::User {
1138 content: OneOrMany::one(UserContent::ToolResult(ToolResult {
1139 id: String::new(),
1140 call_id: None,
1141 content: OneOrMany::one(tool_result_content),
1142 })),
1143 }
1144 }
1145}
1146
1147#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
1148#[serde(rename_all = "snake_case")]
1149pub enum ToolChoice {
1150 #[default]
1151 Auto,
1152 None,
1153 Required,
1154 Specific {
1155 function_names: Vec<String>,
1156 },
1157}
1158
1159#[derive(Debug, Error)]
1165pub enum MessageError {
1166 #[error("Message conversion error: {0}")]
1167 ConversionError(String),
1168}
1169
1170impl From<MessageError> for CompletionError {
1171 fn from(error: MessageError) -> Self {
1172 CompletionError::RequestError(error.into())
1173 }
1174}