1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use serde_json::Value;
3use std::fmt;
4
5pub(crate) fn deserialize_content_blocks<'de, D>(
7 deserializer: D,
8) -> Result<Vec<ContentBlock>, D::Error>
9where
10 D: Deserializer<'de>,
11{
12 let value: Value = Value::deserialize(deserializer)?;
13 match value {
14 Value::String(s) => Ok(vec![ContentBlock::Text(TextBlock { text: s })]),
15 Value::Array(_) => serde_json::from_value(value).map_err(serde::de::Error::custom),
16 _ => Err(serde::de::Error::custom(
17 "content must be a string or array",
18 )),
19 }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "type", rename_all = "snake_case")]
25pub enum ContentBlock {
26 Text(TextBlock),
27 Image(ImageBlock),
28 Thinking(ThinkingBlock),
29 ToolUse(ToolUseBlock),
30 ToolResult(ToolResultBlock),
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct TextBlock {
36 pub text: String,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ImageBlock {
42 pub source: ImageSource,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub enum ImageSourceType {
48 Base64,
50 Unknown(String),
52}
53
54impl ImageSourceType {
55 pub fn as_str(&self) -> &str {
56 match self {
57 Self::Base64 => "base64",
58 Self::Unknown(s) => s.as_str(),
59 }
60 }
61}
62
63impl fmt::Display for ImageSourceType {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 f.write_str(self.as_str())
66 }
67}
68
69impl From<&str> for ImageSourceType {
70 fn from(s: &str) -> Self {
71 match s {
72 "base64" => Self::Base64,
73 other => Self::Unknown(other.to_string()),
74 }
75 }
76}
77
78impl Serialize for ImageSourceType {
79 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
80 serializer.serialize_str(self.as_str())
81 }
82}
83
84impl<'de> Deserialize<'de> for ImageSourceType {
85 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
86 let s = String::deserialize(deserializer)?;
87 Ok(Self::from(s.as_str()))
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Hash)]
93pub enum MediaType {
94 Jpeg,
96 Png,
98 Gif,
100 Webp,
102 Unknown(String),
104}
105
106impl MediaType {
107 pub fn as_str(&self) -> &str {
108 match self {
109 Self::Jpeg => "image/jpeg",
110 Self::Png => "image/png",
111 Self::Gif => "image/gif",
112 Self::Webp => "image/webp",
113 Self::Unknown(s) => s.as_str(),
114 }
115 }
116}
117
118impl fmt::Display for MediaType {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str(self.as_str())
121 }
122}
123
124impl From<&str> for MediaType {
125 fn from(s: &str) -> Self {
126 match s {
127 "image/jpeg" => Self::Jpeg,
128 "image/png" => Self::Png,
129 "image/gif" => Self::Gif,
130 "image/webp" => Self::Webp,
131 other => Self::Unknown(other.to_string()),
132 }
133 }
134}
135
136impl Serialize for MediaType {
137 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138 serializer.serialize_str(self.as_str())
139 }
140}
141
142impl<'de> Deserialize<'de> for MediaType {
143 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
144 let s = String::deserialize(deserializer)?;
145 Ok(Self::from(s.as_str()))
146 }
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct ImageSource {
152 #[serde(rename = "type")]
153 pub source_type: ImageSourceType,
154 pub media_type: MediaType,
155 pub data: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ThinkingBlock {
161 pub thinking: String,
162 pub signature: String,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct ToolUseBlock {
168 pub id: String,
169 pub name: String,
170 pub input: Value,
171}
172
173impl ToolUseBlock {
174 pub fn typed_input(&self) -> Option<crate::tool_inputs::ToolInput> {
196 serde_json::from_value(self.input.clone()).ok()
197 }
198
199 pub fn try_typed_input(&self) -> Result<crate::tool_inputs::ToolInput, serde_json::Error> {
203 serde_json::from_value(self.input.clone())
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct ToolResultBlock {
210 pub tool_use_id: String,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub content: Option<ToolResultContent>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub is_error: Option<bool>,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219#[serde(untagged)]
220pub enum ToolResultContent {
221 Text(String),
222 Structured(Vec<Value>),
223}