1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9#[serde(untagged)]
10pub enum UserInput {
11 Text(String),
13 Parts(Vec<ContentPart>),
15}
16
17impl From<String> for UserInput {
18 fn from(value: String) -> Self {
19 UserInput::Text(value)
20 }
21}
22
23impl From<&str> for UserInput {
24 fn from(value: &str) -> Self {
25 UserInput::Text(value.to_string())
26 }
27}
28
29impl From<Vec<ContentPart>> for UserInput {
30 fn from(value: Vec<ContentPart>) -> Self {
31 UserInput::Parts(value)
32 }
33}
34
35impl From<String> for ContentPart {
36 fn from(value: String) -> Self {
37 ContentPart::Text(TextPart { text: value })
38 }
39}
40
41impl From<&str> for ContentPart {
42 fn from(value: &str) -> Self {
43 ContentPart::Text(TextPart { text: value.to_string() })
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53#[serde(tag = "type", rename_all = "snake_case")]
54#[non_exhaustive]
55pub enum ContentPart {
56 Text(TextPart),
58 Think(ThinkPart),
60 #[serde(rename = "image_url")]
62 ImageUrl(ImageUrlPart),
63 #[serde(rename = "audio_url")]
65 AudioUrl(AudioUrlPart),
66 #[serde(rename = "video_url")]
68 VideoUrl(VideoUrlPart),
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73pub struct TextPart {
74 pub text: String,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80pub struct ThinkPart {
81 pub think: String,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub encrypted: Option<String>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
90pub struct ImageUrlPart {
91 pub image_url: MediaUrl,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
97pub struct AudioUrlPart {
98 pub audio_url: MediaUrl,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
104pub struct VideoUrlPart {
105 pub video_url: MediaUrl,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
111pub struct MediaUrl {
112 pub url: String,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub id: Option<String>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
128pub struct DisplayBlock {
129 #[serde(rename = "type")]
131 pub block_type: DisplayBlockType,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub text: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub path: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub old_text: Option<String>,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub new_text: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none", default)]
147 pub is_summary: Option<bool>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub items: Option<Vec<TodoDisplayItem>>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub language: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub command: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub data: Option<serde_json::Value>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
164#[serde(rename_all = "snake_case")]
165#[non_exhaustive]
166pub enum DisplayBlockType {
167 Brief,
169 Diff,
171 Todo,
173 Shell,
175 #[serde(rename = "unknown")]
177 Unknown,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
182pub struct TodoDisplayItem {
183 pub title: String,
185 pub status: TodoStatus,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
191#[serde(rename_all = "snake_case")]
192#[non_exhaustive]
193pub enum TodoStatus {
194 Pending,
196 InProgress,
198 Done,
200}
201
202impl DisplayBlock {
207 pub fn brief(text: impl Into<String>) -> Self {
209 Self {
210 block_type: DisplayBlockType::Brief,
211 text: Some(text.into()),
212 path: None,
213 old_text: None,
214 new_text: None,
215 is_summary: None,
216 items: None,
217 language: None,
218 command: None,
219 data: None,
220 }
221 }
222
223 pub fn diff(
225 path: impl Into<String>,
226 old_text: impl Into<String>,
227 new_text: impl Into<String>,
228 ) -> Self {
229 Self {
230 block_type: DisplayBlockType::Diff,
231 text: None,
232 path: Some(path.into()),
233 old_text: Some(old_text.into()),
234 new_text: Some(new_text.into()),
235 is_summary: None,
236 items: None,
237 language: None,
238 command: None,
239 data: None,
240 }
241 }
242
243 pub fn todo(items: Vec<TodoDisplayItem>) -> Self {
245 Self {
246 block_type: DisplayBlockType::Todo,
247 text: None,
248 path: None,
249 old_text: None,
250 new_text: None,
251 is_summary: None,
252 items: Some(items),
253 language: None,
254 command: None,
255 data: None,
256 }
257 }
258
259 pub fn shell(command: impl Into<String>, language: impl Into<String>) -> Self {
261 Self {
262 block_type: DisplayBlockType::Shell,
263 text: None,
264 path: None,
265 old_text: None,
266 new_text: None,
267 is_summary: None,
268 items: None,
269 language: Some(language.into()),
270 command: Some(command.into()),
271 data: None,
272 }
273 }
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
282pub struct ToolReturnValue {
283 pub is_error: bool,
285 pub output: ToolOutput,
287 pub message: String,
289 pub display: Vec<DisplayBlock>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub extras: Option<serde_json::Value>,
294}
295
296impl ToolReturnValue {
297 pub fn new(message: impl Into<String>) -> Self {
299 Self {
300 is_error: false,
301 output: ToolOutput::Text(String::new()),
302 message: message.into(),
303 display: vec![],
304 extras: None,
305 }
306 }
307
308 pub fn with_error(mut self) -> Self {
310 self.is_error = true;
311 self
312 }
313
314 pub fn with_output(mut self, output: impl Into<ToolOutput>) -> Self {
316 self.output = output.into();
317 self
318 }
319
320 pub fn with_display(mut self, block: DisplayBlock) -> Self {
322 self.display.push(block);
323 self
324 }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
329#[serde(untagged)]
330pub enum ToolOutput {
331 Text(String),
333 Parts(Vec<ContentPart>),
335}
336
337impl From<String> for ToolOutput {
338 fn from(value: String) -> Self {
339 ToolOutput::Text(value)
340 }
341}
342
343impl From<&str> for ToolOutput {
344 fn from(value: &str) -> Self {
345 ToolOutput::Text(value.to_string())
346 }
347}
348
349impl From<Vec<ContentPart>> for ToolOutput {
350 fn from(value: Vec<ContentPart>) -> Self {
351 ToolOutput::Parts(value)
352 }
353}