1use crate::error::ProviderError;
2use serde::{Deserialize, Serialize};
3use std::{convert::Infallible, str::FromStr};
4use thiserror::Error;
5
6#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
14#[serde(tag = "role", rename_all = "lowercase")]
15pub enum Message {
16 System { content: String },
18
19 User { content: Vec<UserContent> },
21
22 Assistant(AssistantMessage),
25}
26
27pub mod citation;
28mod identity;
29mod native;
30
31pub use citation::{Citation, DocumentRange, Source, SourceLocation, Span};
32
33pub use identity::{CallId, EmptyCallId, EmptyToolName, LocalCallId, ProviderCallId, ToolName};
34pub(crate) use native::Canonical;
35pub use native::{Api, Blob, Fingerprint, Native, Opaque, Origin, StopReason};
36
37#[non_exhaustive]
44#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
45pub struct AssistantMessage {
46 pub content: Vec<AssistantContent>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub origin: Option<Origin>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub stop: Option<StopReason>,
54}
55
56impl AssistantMessage {
57 pub fn new(content: Vec<AssistantContent>) -> Self {
59 Self {
60 content,
61 ..Self::default()
62 }
63 }
64
65 pub fn rolled_back(origin: Option<Origin>, content: Vec<AssistantContent>) -> Self {
69 Self {
70 content,
71 origin,
72 stop: Some(StopReason::ToolUse),
73 }
74 }
75
76 pub fn aborted(
79 origin: Option<Origin>,
80 content: Vec<AssistantContent>,
81 reason: impl Into<String>,
82 ) -> Self {
83 Self {
84 content,
85 origin,
86 stop: Some(StopReason::Aborted(reason.into())),
87 }
88 }
89
90 pub fn with_content(mut self, content: Vec<AssistantContent>) -> Self {
92 self.content = content;
93 self
94 }
95
96 pub fn with_origin(mut self, origin: impl Into<Option<Origin>>) -> Self {
98 self.origin = origin.into();
99 self
100 }
101
102 pub fn with_stop(mut self, stop: impl Into<Option<StopReason>>) -> Self {
104 self.stop = stop.into();
105 self
106 }
107
108 pub fn tool_calls(&self) -> impl Iterator<Item = &ToolCall> {
110 self.content.iter().filter_map(|part| match part {
111 AssistantContent::ToolCall(call) => Some(call),
112 _ => None,
113 })
114 }
115
116 pub fn text(&self) -> String {
118 self.content
119 .iter()
120 .filter_map(|part| match part {
121 AssistantContent::Text(text) => Some(text.text.as_str()),
122 _ => None,
123 })
124 .collect()
125 }
126
127 pub fn is_empty_turn(&self) -> bool {
131 self.content.iter().all(AssistantContent::is_blank)
132 }
133}
134
135pub fn turn_delivered_no_answer(choice: &[AssistantContent]) -> bool {
139 !choice.iter().any(AssistantContent::is_answer)
140}
141
142pub fn turn_failure(
152 choice: &[AssistantContent],
153 stop: Option<&StopReason>,
154 finish: Option<&crate::completion::FinishReason>,
155) -> Option<String> {
156 let failed = stop.filter(|stop| stop.is_failure());
157 let calls = choice
158 .iter()
159 .any(|block| matches!(block, AssistantContent::ToolCall(_)));
160 if let Some(StopReason::Error(reason) | StopReason::Aborted(reason)) = failed
161 && calls
162 {
163 return Some(format!(
164 "the turn failed, so none of its tool calls ran: {reason}"
165 ));
166 }
167 let answered = !turn_delivered_no_answer(choice);
168 if !answered && let Some(finish) = finish.filter(|finish| finish.truncated_output()) {
169 return Some(finish.no_answer_message());
170 }
171 match failed {
172 Some(StopReason::Error(reason)) if answered => {
173 Some(format!("the provider failed the turn: {reason}"))
174 }
175 Some(StopReason::Error(reason)) => Some(format!(
176 "the provider failed the turn without an answer: {reason}"
177 )),
178 _ => None,
179 }
180}
181
182#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
185#[serde(tag = "type", rename_all = "lowercase")]
186pub enum UserContent {
187 Text(Text),
189 ToolResult(ToolResult),
191 Image(Image),
193 Audio(Audio),
195 Video(Video),
197 Document(Document),
199}
200
201#[non_exhaustive]
204#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
205#[serde(tag = "type", rename_all = "lowercase")]
206pub enum AssistantContent {
207 Text(Text),
209 ToolCall(ToolCall),
211 Reasoning(Reasoning),
213 Image(Image),
215 Opaque(Opaque),
217}
218
219impl AssistantContent {
220 pub fn canonical(&self) -> Self {
223 match self {
224 Self::Text(text) => Self::Text(Text {
225 text: text.text.clone(),
226 citations: text.citations.clone(),
227 native: None,
228 }),
229 Self::ToolCall(call) => {
230 Self::ToolCall(ToolCall::new(call.id.clone(), call.function.clone()))
231 }
232 Self::Reasoning(reasoning) => Self::Reasoning(Reasoning {
233 text: reasoning.text.clone(),
234 redacted: reasoning.redacted,
235 native: None,
236 }),
237 Self::Image(image) => Self::Image(Image {
238 data: image.data.clone(),
239 media_type: image.media_type.clone(),
240 detail: image.detail.clone(),
241 native: None,
242 }),
243 Self::Opaque(opaque) => Self::Opaque(opaque.clone()),
244 }
245 }
246
247 pub fn into_canonical(mut self) -> Self {
249 if let Some(native) = self.native_slot() {
250 *native = None;
251 }
252 self
253 }
254
255 pub fn is_answer(&self) -> bool {
258 match self {
259 Self::Text(text) => !text.text.trim().is_empty(),
260 Self::ToolCall(_) | Self::Image(_) => true,
261 Self::Reasoning(_) | Self::Opaque(_) => false,
262 }
263 }
264
265 pub fn is_blank(&self) -> bool {
271 match self {
272 Self::Text(Text { text, .. }) => text.trim().is_empty() && self.native_item().is_none(),
273 Self::Reasoning(Reasoning { text, redacted, .. }) => {
274 (*redacted || text.trim().is_empty()) && self.native_item().is_none()
275 }
276 Self::Opaque(opaque) => !opaque.replay,
277 Self::ToolCall(_) | Self::Image(_) => false,
278 }
279 }
280
281 pub fn fingerprint(&self) -> Fingerprint {
287 match self {
292 Self::Text(text) => Fingerprint::of_canonical(&("v1", "text", &text.text)),
293 Self::Reasoning(reasoning) => {
294 Fingerprint::of_canonical(&("v1", "reasoning", &reasoning.text, reasoning.redacted))
295 }
296 Self::ToolCall(call) => {
297 let id = match &call.id {
298 CallId::Provider(id) => id.as_str(),
299 CallId::Local(_) => "~local",
300 };
301 Fingerprint::of_canonical(&(
302 "v1",
303 "call",
304 id,
305 call.function.name.as_str(),
306 native::Canonical(&call.function.arguments),
307 &call.function.invalid_arguments,
308 ))
309 }
310 Self::Image(image) => Fingerprint::of_canonical(&(
311 "v1",
312 "image",
313 &image.media_type,
314 &image.detail,
315 &image.data,
316 )),
317 Self::Opaque(_) => Fingerprint::of_canonical(&("v1", "opaque")),
318 }
319 }
320
321 fn native_slot(&mut self) -> Option<&mut Option<Native>> {
322 match self {
323 Self::Text(text) => Some(&mut text.native),
324 Self::ToolCall(call) => Some(&mut call.native),
325 Self::Reasoning(reasoning) => Some(&mut reasoning.native),
326 Self::Image(image) => Some(&mut image.native),
327 Self::Opaque(_) => None,
328 }
329 }
330
331 pub fn with_native(mut self, item: serde_json::Value) -> Self {
334 let fingerprint = self.fingerprint();
335 if let Some(native) = self.native_slot() {
336 *native = Some(Native { item, fingerprint });
337 }
338 self
339 }
340
341 pub(crate) fn native(&self) -> Option<(&serde_json::Value, bool)> {
344 let native = match self {
345 Self::Text(text) => text.native.as_ref(),
346 Self::ToolCall(call) => call.native.as_ref(),
347 Self::Reasoning(reasoning) => reasoning.native.as_ref(),
348 Self::Image(image) => image.native.as_ref(),
349 Self::Opaque(_) => None,
350 }?;
351 Some((&native.item, native.fingerprint == self.fingerprint()))
352 }
353
354 pub(crate) fn stale_item(&self) -> Option<&serde_json::Value> {
357 self.native()
358 .and_then(|(item, current)| (!current).then_some(item))
359 }
360
361 pub fn native_item(&self) -> Option<&serde_json::Value> {
364 self.native()
365 .and_then(|(item, current)| current.then_some(item))
366 }
367}
368
369#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
373pub struct Reasoning {
374 pub text: String,
376 #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
378 pub redacted: bool,
379 #[serde(
381 default,
382 skip_serializing_if = "Option::is_none",
383 deserialize_with = "native::lenient"
384 )]
385 pub native: Option<Native>,
386}
387
388impl Reasoning {
389 pub fn new(text: impl Into<String>) -> Self {
391 Self {
392 text: text.into(),
393 ..Self::default()
394 }
395 }
396}
397
398#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
403pub struct ToolResult {
404 pub call: CallId,
406 pub name: ToolName,
409 pub content: Vec<ToolResultContent>,
411 #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
414 pub is_error: bool,
415}
416
417#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
419#[serde(tag = "type", rename_all = "lowercase")]
420pub enum ToolResultContent {
421 Text(Text),
423 Image(Image),
425 Json {
427 value: serde_json::Value,
429 },
430}
431
432impl ToolResultContent {
433 pub fn as_text(&self) -> Option<&str> {
435 match self {
436 Self::Text(text) => Some(&text.text),
437 Self::Image(_) | Self::Json { .. } => None,
438 }
439 }
440
441 pub fn as_json(&self) -> Option<&serde_json::Value> {
443 match self {
444 Self::Json { value } => Some(value),
445 Self::Text(_) | Self::Image(_) => None,
446 }
447 }
448
449 pub fn deserialize_json<T>(&self) -> Result<T, serde_json::Error>
465 where
466 T: serde::de::DeserializeOwned,
467 {
468 match self {
469 Self::Json { value } => T::deserialize(value),
470 Self::Text(text) => serde_json::from_str(&text.text),
471 Self::Image(_) => Err(<serde_json::Error as serde::de::Error>::custom(
472 "an image tool result is not JSON",
473 )),
474 }
475 }
476}
477
478#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
480pub struct ToolCall {
481 pub id: CallId,
484 pub function: ToolFunction,
486 #[serde(
488 default,
489 skip_serializing_if = "Option::is_none",
490 deserialize_with = "native::lenient"
491 )]
492 pub native: Option<Native>,
493}
494
495impl ToolCall {
496 pub fn new(id: CallId, function: ToolFunction) -> Self {
498 Self {
499 id,
500 function,
501 native: None,
502 }
503 }
504
505 pub fn from_wire(wire_id: impl Into<String>, function: ToolFunction) -> Self {
508 Self::new(CallId::from_wire(wire_id), function)
509 }
510
511 pub fn result(&self, content: Vec<ToolResultContent>) -> ToolResult {
513 ToolResult {
514 call: self.id.clone(),
515 name: self.function.name.clone(),
516 content,
517 is_error: false,
518 }
519 }
520
521 pub fn answer(&self, result: &crate::tool::ToolResult) -> ToolResult {
524 ToolResult {
525 is_error: !result.is_success(),
526 ..self.result(result.output().clone().into_content())
527 }
528 }
529}
530
531#[derive(Clone, Debug, Serialize, PartialEq)]
553pub struct ToolFunction {
554 pub name: ToolName,
556 pub arguments: serde_json::Map<String, serde_json::Value>,
558 #[serde(default, skip_serializing_if = "Option::is_none")]
561 pub invalid_arguments: Option<String>,
562}
563
564impl ToolFunction {
565 pub fn new(name: ToolName, arguments: serde_json::Value) -> Self {
569 use serde_json::Value;
570 let (arguments, invalid_arguments) = match arguments {
571 Value::Object(arguments) => (arguments, None),
572 Value::Null => (serde_json::Map::new(), None),
573 Value::String(text) => {
574 return Self::parse(name, &text);
575 }
576 other => (serde_json::Map::new(), Some(other.to_string())),
577 };
578 Self {
579 name,
580 arguments,
581 invalid_arguments,
582 }
583 }
584
585 pub fn parse(name: ToolName, text: &str) -> Self {
590 use serde_json::Value;
591 let parsed = crate::json_utils::parse_tool_arguments(text);
592 let (arguments, invalid) = match parsed {
593 Ok(Value::Object(arguments)) => (arguments, false),
594 Ok(Value::Null) => (serde_json::Map::new(), false),
595 Ok(Value::String(inner)) => match serde_json::from_str(&inner) {
596 Ok(Value::Object(arguments)) => (arguments, false),
597 _ => (serde_json::Map::new(), true),
598 },
599 Ok(_) => (serde_json::Map::new(), true),
600 Err(_) => (
601 crate::json_utils::parse_partial_object(text).unwrap_or_default(),
602 true,
603 ),
604 };
605 Self {
606 name,
607 arguments,
608 invalid_arguments: invalid.then(|| text.to_owned()),
609 }
610 }
611
612 pub fn arguments_value(&self) -> serde_json::Value {
614 serde_json::Value::Object(self.arguments.clone())
615 }
616
617 pub fn arguments_json(&self) -> String {
620 serde_json::to_string(&self.arguments).unwrap_or_default()
621 }
622
623 pub fn raw_arguments(&self) -> String {
626 self.invalid_arguments
627 .clone()
628 .unwrap_or_else(|| self.arguments_json())
629 }
630}
631
632impl<'de> Deserialize<'de> for ToolFunction {
633 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
637 #[derive(Deserialize)]
638 struct Repr {
639 name: ToolName,
640 #[serde(default)]
641 arguments: serde_json::Value,
642 #[serde(default)]
643 invalid_arguments: Option<String>,
644 }
645 let Repr {
646 name,
647 arguments,
648 invalid_arguments,
649 } = Repr::deserialize(deserializer)?;
650 let mut function = Self::new(name, arguments);
651 if invalid_arguments.is_some() {
652 function.invalid_arguments = invalid_arguments;
653 }
654 Ok(function)
655 }
656}
657
658#[non_exhaustive]
662#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
663#[serde(from = "TextRepr")]
664pub struct Text {
665 pub text: String,
667 #[serde(default, skip_serializing_if = "Option::is_none")]
670 citations: Option<citation::Citations>,
671 #[serde(
673 default,
674 skip_serializing_if = "Option::is_none",
675 deserialize_with = "native::lenient"
676 )]
677 pub native: Option<Native>,
678}
679
680#[derive(Deserialize)]
683struct TextRepr {
684 text: String,
685 #[serde(default, deserialize_with = "citation::lenient")]
686 citations: Option<citation::Citations>,
687 #[serde(default, deserialize_with = "native::lenient")]
688 native: Option<Native>,
689}
690
691impl From<TextRepr> for Text {
692 fn from(repr: TextRepr) -> Self {
693 let TextRepr {
694 text,
695 citations,
696 native,
697 } = repr;
698 Self {
699 text,
700 citations,
701 native,
702 }
703 .checked()
704 }
705}
706
707impl Text {
708 pub fn new(text: impl Into<String>) -> Self {
710 Self {
711 text: text.into(),
712 citations: None,
713 native: None,
714 }
715 }
716
717 pub fn text(&self) -> &str {
719 &self.text
720 }
721}
722
723impl std::fmt::Display for Text {
724 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
725 let Self { text, .. } = self;
726 write!(f, "{text}")
727 }
728}
729
730#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
733pub struct Image {
734 pub data: DocumentSourceKind,
736 #[serde(skip_serializing_if = "Option::is_none")]
738 pub media_type: Option<ImageMediaType>,
739 #[serde(skip_serializing_if = "Option::is_none")]
741 pub detail: Option<ImageDetail>,
742 #[serde(
744 default,
745 skip_serializing_if = "Option::is_none",
746 deserialize_with = "native::lenient"
747 )]
748 pub native: Option<Native>,
749}
750
751#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
753#[serde(tag = "type", content = "value", rename_all = "camelCase")]
754pub enum DocumentSourceKind {
755 Url(String),
757 #[serde(alias = "string")]
759 Base64(String),
760 FileId(String),
762 Raw(Vec<u8>),
764 #[default]
765 Unknown,
767}
768
769impl DocumentSourceKind {
770 pub fn url(url: impl Into<String>) -> Self {
772 Self::Url(url.into())
773 }
774
775 pub fn base64(base64_string: impl Into<String>) -> Self {
777 Self::Base64(base64_string.into())
778 }
779
780 pub fn file_id(file_id: impl Into<String>) -> Self {
782 Self::FileId(file_id.into())
783 }
784}
785
786impl std::fmt::Display for DocumentSourceKind {
787 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
788 match self {
789 Self::Url(string) => write!(f, "{string}"),
790 Self::Base64(string) => write!(f, "{string}"),
791 Self::FileId(string) => write!(f, "{string}"),
792 Self::Raw(_) => write!(f, "<binary data>"),
793 Self::Unknown => write!(f, "<unknown>"),
794 }
795 }
796}
797
798#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
800pub struct Audio {
801 pub data: DocumentSourceKind,
803 #[serde(skip_serializing_if = "Option::is_none")]
805 pub media_type: Option<AudioMediaType>,
806}
807
808#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
810pub struct Video {
811 pub data: DocumentSourceKind,
813 #[serde(skip_serializing_if = "Option::is_none")]
815 pub media_type: Option<VideoMediaType>,
816 #[serde(default, skip_serializing_if = "Option::is_none")]
819 pub additional_params: Option<serde_json::Value>,
820}
821
822#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
824#[serde(tag = "type", content = "value", rename_all = "camelCase")]
825pub enum DocumentData {
826 #[serde(alias = "string")]
828 Text(String),
829 #[serde(untagged)]
831 File(DocumentSourceKind),
832}
833
834impl Default for DocumentData {
835 fn default() -> Self {
836 Self::File(DocumentSourceKind::Unknown)
837 }
838}
839
840impl From<DocumentSourceKind> for DocumentData {
841 fn from(source: DocumentSourceKind) -> Self {
842 Self::File(source)
843 }
844}
845
846#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
848pub struct Document {
849 pub data: DocumentData,
851 #[serde(skip_serializing_if = "Option::is_none")]
853 pub media_type: Option<DocumentMediaType>,
854 #[serde(default, skip_serializing_if = "Option::is_none")]
857 pub additional_params: Option<serde_json::Value>,
858}
859
860#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
862pub enum MediaType {
863 Image(ImageMediaType),
864 Audio(AudioMediaType),
865 Document(DocumentMediaType),
866 Video(VideoMediaType),
867}
868
869#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
872#[serde(rename_all = "lowercase")]
873pub enum ImageMediaType {
874 JPEG,
875 PNG,
876 GIF,
877 WEBP,
878 HEIC,
879 HEIF,
880 SVG,
881}
882
883#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
887#[serde(rename_all = "lowercase")]
888pub enum DocumentMediaType {
889 PDF,
890 TXT,
891 RTF,
892 HTML,
893 CSS,
894 MARKDOWN,
895 CSV,
896 XML,
897 Javascript,
898 Python,
899}
900
901impl ImageMediaType {
902 pub fn sniff(bytes: &[u8]) -> Option<Self> {
913 if bytes.starts_with(b"\x89PNG\r\n\x1a\n") {
914 Some(Self::PNG)
915 } else if bytes.starts_with(b"\xff\xd8\xff") {
916 Some(Self::JPEG)
917 } else if bytes.starts_with(b"GIF87a") || bytes.starts_with(b"GIF89a") {
918 Some(Self::GIF)
919 } else if bytes.starts_with(b"RIFF") && bytes.get(8..12) == Some(b"WEBP".as_slice()) {
920 Some(Self::WEBP)
921 } else {
922 None
923 }
924 }
925
926 pub fn extension(&self) -> &'static str {
928 match self {
929 Self::JPEG => "jpg",
930 Self::PNG => "png",
931 Self::GIF => "gif",
932 Self::WEBP => "webp",
933 Self::HEIC => "heic",
934 Self::HEIF => "heif",
935 Self::SVG => "svg",
936 }
937 }
938}
939
940#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
943#[serde(rename_all = "lowercase")]
944pub enum AudioMediaType {
945 WAV,
946 MP3,
947 AIFF,
948 AAC,
949 OGG,
950 FLAC,
951 M4A,
952 PCM16,
953 PCM24,
954}
955
956#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
959#[serde(rename_all = "lowercase")]
960pub enum VideoMediaType {
961 AVI,
962 MP4,
963 MPEG,
964 MOV,
965 WEBM,
966}
967
968#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
970#[serde(rename_all = "lowercase")]
971pub enum ImageDetail {
972 Low,
973 High,
974 #[default]
975 Auto,
976}
977
978impl Message {
979 pub fn final_answer(&self) -> Option<String> {
990 let Message::Assistant(reply) = self else {
991 return None;
992 };
993 let mut parts = Vec::new();
994 for item in reply.content.iter() {
995 match item {
996 AssistantContent::Text(text) => parts.push(text.text.as_str()),
997 AssistantContent::ToolCall(_) => return None,
998 _ => {}
999 }
1000 }
1001 let text = parts.join("\n\n");
1002 let text = text.trim();
1003 (!text.is_empty()).then(|| text.to_owned())
1004 }
1005
1006 pub fn rag_text(&self) -> Option<String> {
1008 match self {
1009 Message::User { content } => {
1010 for item in content.iter() {
1011 if let UserContent::Text(Text { text, .. }) = item {
1012 return Some(text.clone());
1013 }
1014 }
1015 None
1016 }
1017 Message::System { .. } => None,
1018 _ => None,
1019 }
1020 }
1021
1022 pub fn system(text: impl Into<String>) -> Self {
1024 Message::System {
1025 content: text.into(),
1026 }
1027 }
1028
1029 pub fn user(text: impl Into<String>) -> Self {
1031 Message::User {
1032 content: vec![UserContent::text(text)],
1033 }
1034 }
1035
1036 pub fn assistant(text: impl Into<String>) -> Self {
1038 Message::Assistant(AssistantMessage::new(vec![AssistantContent::text(text)]))
1039 }
1040
1041 pub fn tool_result(call: CallId, name: ToolName, content: impl Into<String>) -> Self {
1045 Message::User {
1046 content: vec![UserContent::tool_result(
1047 call,
1048 name,
1049 vec![ToolResultContent::text(content)],
1050 )],
1051 }
1052 }
1053
1054 pub fn tool_results(results: Vec<ToolResult>) -> Self {
1056 Message::User {
1057 content: results.into_iter().map(UserContent::ToolResult).collect(),
1058 }
1059 }
1060
1061 pub fn images(&self) -> impl Iterator<Item = &Image> {
1064 let (user, turn): (&[UserContent], &[AssistantContent]) = match self {
1065 Message::User { content } => (content, &[]),
1066 Message::Assistant(turn) => (&[], &turn.content),
1067 Message::System { .. } => (&[], &[]),
1068 };
1069 let user = user.iter().flat_map(|item| {
1070 let (image, results): (_, &[ToolResultContent]) = match item {
1071 UserContent::Image(image) => (Some(image), &[]),
1072 UserContent::ToolResult(result) => (None, &result.content),
1073 _ => (None, &[]),
1074 };
1075 let results = results.iter().filter_map(|part| match part {
1076 ToolResultContent::Image(image) => Some(image),
1077 _ => None,
1078 });
1079 image.into_iter().chain(results)
1080 });
1081 let turn = turn.iter().filter_map(|block| match block {
1082 AssistantContent::Image(image) => Some(image),
1083 _ => None,
1084 });
1085 user.chain(turn)
1086 }
1087
1088 pub fn images_mut(&mut self) -> impl Iterator<Item = &mut Image> {
1090 let (user, turn): (&mut [UserContent], &mut [AssistantContent]) = match self {
1091 Message::User { content } => (content, Default::default()),
1092 Message::Assistant(turn) => (Default::default(), &mut turn.content),
1093 Message::System { .. } => Default::default(),
1094 };
1095 let user = user.iter_mut().flat_map(|item| {
1096 let (image, results): (_, &mut [ToolResultContent]) = match item {
1097 UserContent::Image(image) => (Some(image), Default::default()),
1098 UserContent::ToolResult(result) => (None, &mut result.content),
1099 _ => Default::default(),
1100 };
1101 let results = results.iter_mut().filter_map(|part| match part {
1102 ToolResultContent::Image(image) => Some(image),
1103 _ => None,
1104 });
1105 image.into_iter().chain(results)
1106 });
1107 let turn = turn.iter_mut().filter_map(|block| match block {
1108 AssistantContent::Image(image) => Some(image),
1109 _ => None,
1110 });
1111 user.chain(turn)
1112 }
1113}
1114
1115macro_rules! media_ctors {
1117 () => {};
1118 (
1119 $(#[$meta:meta])* $name:ident => Image($kind:ident: $data:ty);
1120 $($rest:tt)*
1121 ) => {
1122 $(#[$meta])*
1123 pub fn $name(
1124 data: impl Into<$data>,
1125 media_type: Option<ImageMediaType>,
1126 detail: Option<ImageDetail>,
1127 ) -> Self {
1128 Self::Image(Image {
1129 data: DocumentSourceKind::$kind(data.into()),
1130 media_type,
1131 detail,
1132 native: None,
1133 })
1134 }
1135 media_ctors! { $($rest)* }
1136 };
1137 (
1138 $(#[$meta:meta])* $name:ident => $variant:ident(params $mt:ty, $kind:path: $data:ty);
1139 $($rest:tt)*
1140 ) => {
1141 $(#[$meta])*
1142 pub fn $name(data: impl Into<$data>, media_type: Option<$mt>) -> Self {
1143 Self::$variant($variant {
1144 data: $kind(data.into()).into(),
1145 media_type,
1146 additional_params: None,
1147 })
1148 }
1149 media_ctors! { $($rest)* }
1150 };
1151 (
1152 $(#[$meta:meta])* $name:ident => $variant:ident($mt:ty, $kind:ident: $data:ty);
1153 $($rest:tt)*
1154 ) => {
1155 $(#[$meta])*
1156 pub fn $name(data: impl Into<$data>, media_type: Option<$mt>) -> Self {
1157 Self::$variant($variant {
1158 data: DocumentSourceKind::$kind(data.into()),
1159 media_type,
1160 })
1161 }
1162 media_ctors! { $($rest)* }
1163 };
1164}
1165
1166impl UserContent {
1167 pub fn text(text: impl Into<String>) -> Self {
1169 UserContent::Text(text.into().into())
1170 }
1171
1172 media_ctors! {
1173 image_base64 => Image(Base64: String);
1175 image_raw => Image(Raw: Vec<u8>);
1177 image_url => Image(Url: String);
1179 audio_base64 => Audio(AudioMediaType, Base64: String);
1181 audio_raw => Audio(AudioMediaType, Raw: Vec<u8>);
1183 audio_url => Audio(AudioMediaType, Url: String);
1185 video_base64 => Video(params VideoMediaType, DocumentSourceKind::Base64: String);
1187 video_raw => Video(params VideoMediaType, DocumentSourceKind::Raw: Vec<u8>);
1189 video_url => Video(params VideoMediaType, DocumentSourceKind::Url: String);
1191 document_base64 => Document(params DocumentMediaType, DocumentSourceKind::Base64: String);
1193 document_raw => Document(params DocumentMediaType, DocumentSourceKind::Raw: Vec<u8>);
1195 document_url => Document(params DocumentMediaType, DocumentSourceKind::Url: String);
1197 document_text => Document(params DocumentMediaType, DocumentData::Text: String);
1201 }
1202
1203 pub fn tool_result(call: CallId, name: ToolName, content: Vec<ToolResultContent>) -> Self {
1205 UserContent::ToolResult(ToolResult {
1206 call,
1207 name,
1208 content,
1209 is_error: false,
1210 })
1211 }
1212}
1213
1214impl AssistantContent {
1215 pub fn text(text: impl Into<String>) -> Self {
1217 AssistantContent::Text(text.into().into())
1218 }
1219
1220 media_ctors! {
1221 image_base64 => Image(Base64: String);
1223 }
1224
1225 pub fn tool_call(id: impl Into<String>, name: ToolName, arguments: serde_json::Value) -> Self {
1228 AssistantContent::ToolCall(ToolCall::from_wire(id, ToolFunction::new(name, arguments)))
1229 }
1230
1231 pub fn reasoning(reasoning: impl Into<String>) -> Self {
1233 AssistantContent::Reasoning(Reasoning::new(reasoning))
1234 }
1235}
1236
1237impl ToolResultContent {
1238 pub fn text(text: impl Into<String>) -> Self {
1240 ToolResultContent::Text(text.into().into())
1241 }
1242
1243 pub fn json(value: serde_json::Value) -> Self {
1245 ToolResultContent::Json { value }
1246 }
1247
1248 media_ctors! {
1249 image_base64 => Image(Base64: String);
1251 image_raw => Image(Raw: Vec<u8>);
1253 image_url => Image(Url: String);
1255 }
1256}
1257
1258pub trait MimeType {
1260 fn from_mime_type(mime_type: &str) -> Option<Self>
1261 where
1262 Self: Sized;
1263 fn to_mime_type(&self) -> &'static str;
1264}
1265
1266impl MimeType for MediaType {
1267 fn from_mime_type(mime_type: &str) -> Option<Self> {
1268 ImageMediaType::from_mime_type(mime_type)
1269 .map(MediaType::Image)
1270 .or_else(|| DocumentMediaType::from_mime_type(mime_type).map(MediaType::Document))
1271 .or_else(|| AudioMediaType::from_mime_type(mime_type).map(MediaType::Audio))
1272 .or_else(|| VideoMediaType::from_mime_type(mime_type).map(MediaType::Video))
1273 }
1274
1275 fn to_mime_type(&self) -> &'static str {
1276 match self {
1277 MediaType::Image(media_type) => media_type.to_mime_type(),
1278 MediaType::Audio(media_type) => media_type.to_mime_type(),
1279 MediaType::Document(media_type) => media_type.to_mime_type(),
1280 MediaType::Video(media_type) => media_type.to_mime_type(),
1281 }
1282 }
1283}
1284
1285macro_rules! impl_mime_type {
1290 ($ty:ident { $($variant:ident => $canonical:literal $(| $alias:literal)*),+ $(,)? }) => {
1291 impl MimeType for $ty {
1292 fn from_mime_type(mime_type: &str) -> Option<Self> {
1293 match mime_type {
1294 $($canonical $(| $alias)* => Some($ty::$variant),)+
1295 _ => None,
1296 }
1297 }
1298
1299 fn to_mime_type(&self) -> &'static str {
1300 match self {
1301 $($ty::$variant => $canonical,)+
1302 }
1303 }
1304 }
1305 };
1306}
1307
1308impl_mime_type!(ImageMediaType {
1309 JPEG => "image/jpeg",
1310 PNG => "image/png",
1311 GIF => "image/gif",
1312 WEBP => "image/webp",
1313 HEIC => "image/heic",
1314 HEIF => "image/heif",
1315 SVG => "image/svg+xml",
1316});
1317
1318impl_mime_type!(DocumentMediaType {
1319 PDF => "application/pdf",
1320 TXT => "text/plain",
1321 RTF => "text/rtf",
1322 HTML => "text/html",
1323 CSS => "text/css",
1324 MARKDOWN => "text/markdown" | "text/md",
1325 CSV => "text/csv",
1326 XML => "text/xml",
1327 Javascript => "application/x-javascript" | "text/x-javascript",
1328 Python => "application/x-python" | "text/x-python",
1329});
1330
1331impl_mime_type!(AudioMediaType {
1332 WAV => "audio/wav",
1333 MP3 => "audio/mp3",
1334 AIFF => "audio/aiff",
1335 AAC => "audio/aac",
1336 OGG => "audio/ogg",
1337 FLAC => "audio/flac",
1338 M4A => "audio/m4a",
1339 PCM16 => "audio/pcm16",
1340 PCM24 => "audio/pcm24",
1341});
1342
1343impl_mime_type!(VideoMediaType {
1344 AVI => "video/avi",
1345 MP4 => "video/mp4",
1346 MPEG => "video/mpeg",
1347 MOV => "video/mov",
1348 WEBM => "video/webm",
1349});
1350
1351impl std::str::FromStr for ImageDetail {
1352 type Err = ();
1353
1354 fn from_str(s: &str) -> Result<Self, Self::Err> {
1355 match s.to_lowercase().as_str() {
1356 "low" => Ok(ImageDetail::Low),
1357 "high" => Ok(ImageDetail::High),
1358 "auto" => Ok(ImageDetail::Auto),
1359 _ => Err(()),
1360 }
1361 }
1362}
1363
1364macro_rules! text_from {
1366 ($($src:ty),+ $(,)?) => {$(
1367 impl From<$src> for Text {
1368 fn from(text: $src) -> Self {
1369 Text::new(text)
1370 }
1371 }
1372 )+};
1373}
1374
1375text_from!(String, &String, &str);
1376
1377macro_rules! text_content_from_string {
1379 ($($ty:ident),+ $(,)?) => {$(
1380 impl From<String> for $ty {
1381 fn from(text: String) -> Self {
1382 $ty::text(text)
1383 }
1384 }
1385 )+};
1386}
1387
1388text_content_from_string!(ToolResultContent, AssistantContent, UserContent);
1389
1390macro_rules! single_content_message_from {
1393 (User { $($src:ty => $variant:ident),+ $(,)? }) => {$(
1394 impl From<$src> for Message {
1395 fn from(value: $src) -> Self {
1396 Message::User {
1397 content: vec![UserContent::$variant(value.into())],
1398 }
1399 }
1400 }
1401 )+};
1402 (Assistant { $($src:ty => $variant:ident),+ $(,)? }) => {$(
1403 impl From<$src> for Message {
1404 fn from(value: $src) -> Self {
1405 Message::Assistant(AssistantMessage::new(vec![AssistantContent::$variant(
1406 value.into(),
1407 )]))
1408 }
1409 }
1410 )+};
1411}
1412
1413single_content_message_from!(User {
1414 String => Text,
1415 &str => Text,
1416 &String => Text,
1417 Text => Text,
1418 Image => Image,
1419 Audio => Audio,
1420 Document => Document,
1421 ToolResult => ToolResult,
1422});
1423
1424single_content_message_from!(Assistant {
1425 ToolCall => ToolCall,
1426});
1427
1428impl FromStr for Text {
1429 type Err = Infallible;
1430
1431 fn from_str(s: &str) -> Result<Self, Self::Err> {
1432 Ok(s.into())
1433 }
1434}
1435
1436impl From<&Message> for Message {
1437 fn from(msg: &Message) -> Self {
1438 msg.clone()
1439 }
1440}
1441
1442impl From<std::sync::Arc<Message>> for Message {
1445 fn from(msg: std::sync::Arc<Message>) -> Self {
1446 std::sync::Arc::unwrap_or_clone(msg)
1447 }
1448}
1449
1450impl From<AssistantContent> for Message {
1451 fn from(content: AssistantContent) -> Self {
1452 Message::Assistant(AssistantMessage::new(vec![content]))
1453 }
1454}
1455
1456impl From<AssistantMessage> for Message {
1457 fn from(message: AssistantMessage) -> Self {
1458 Message::Assistant(message)
1459 }
1460}
1461
1462impl From<UserContent> for Message {
1463 fn from(content: UserContent) -> Self {
1464 Message::User {
1465 content: vec![content],
1466 }
1467 }
1468}
1469
1470impl From<Vec<AssistantContent>> for Message {
1471 fn from(content: Vec<AssistantContent>) -> Self {
1472 Message::Assistant(AssistantMessage::new(content))
1473 }
1474}
1475
1476impl From<Vec<UserContent>> for Message {
1477 fn from(content: Vec<UserContent>) -> Self {
1478 Message::User { content }
1479 }
1480}
1481
1482#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
1484#[serde(rename_all = "snake_case")]
1485pub enum ToolChoice {
1486 #[default]
1488 Auto,
1489 None,
1491 Required,
1493 Specific {
1495 function_names: Vec<ToolName>,
1497 },
1498}
1499
1500#[derive(Debug, Error)]
1502pub enum MessageError {
1503 #[error("Message conversion error: {0}")]
1504 ConversionError(String),
1505}
1506
1507impl From<MessageError> for ProviderError {
1508 fn from(error: MessageError) -> Self {
1509 ProviderError::request(error)
1510 }
1511}
1512
1513#[cfg(test)]
1514mod tests;