Skip to main content

adk_anthropic/types/
content_block.rs

1use serde::{Deserialize, Serialize};
2
3use crate::types::{
4    CodeExecutionResultBlock, DocumentBlock, ImageBlock, ProgrammaticToolUseBlock,
5    RedactedThinkingBlock, ServerToolUseBlock, TextBlock, ThinkingBlock, ToolResultBlock,
6    ToolUseBlock, WebSearchToolResultBlock,
7};
8
9/// A block of content in a message.
10///
11/// This enum represents the different types of content blocks that can be included
12/// in a message's content.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14#[serde(tag = "type")]
15pub enum ContentBlock {
16    /// A block of text content
17    #[serde(rename = "text")]
18    Text(TextBlock),
19
20    /// An image block
21    #[serde(rename = "image")]
22    Image(ImageBlock),
23
24    /// A block representing a tool use request
25    #[serde(rename = "tool_use")]
26    ToolUse(ToolUseBlock),
27
28    /// A block representing a server-side tool use request
29    #[serde(rename = "server_tool_use")]
30    ServerToolUse(ServerToolUseBlock),
31
32    /// A web search tool result block
33    #[serde(rename = "web_search_tool_result")]
34    WebSearchToolResult(WebSearchToolResultBlock),
35
36    /// A tool result block
37    #[serde(rename = "tool_result")]
38    ToolResult(ToolResultBlock),
39
40    /// A document block
41    #[serde(rename = "document")]
42    Document(DocumentBlock),
43
44    /// A block containing model thinking
45    #[serde(rename = "thinking")]
46    Thinking(ThinkingBlock),
47
48    /// A block containing redacted thinking data
49    #[serde(rename = "redacted_thinking")]
50    RedactedThinking(RedactedThinkingBlock),
51
52    /// A code execution result block
53    #[serde(rename = "code_execution_result")]
54    CodeExecutionResult(CodeExecutionResultBlock),
55
56    /// A programmatic tool use block from code execution
57    #[serde(rename = "programmatic_tool_use")]
58    ProgrammaticToolUse(ProgrammaticToolUseBlock),
59}
60
61impl ContentBlock {
62    /// Returns true if this block is a text block
63    pub fn is_text(&self) -> bool {
64        matches!(self, ContentBlock::Text(_))
65    }
66
67    /// Returns true if this block is an image block
68    pub fn is_image(&self) -> bool {
69        matches!(self, ContentBlock::Image(_))
70    }
71
72    /// Returns true if this block is a tool use block
73    pub fn is_tool_use(&self) -> bool {
74        matches!(self, ContentBlock::ToolUse(_))
75    }
76
77    /// Returns true if this block is a server tool use block
78    pub fn is_server_tool_use(&self) -> bool {
79        matches!(self, ContentBlock::ServerToolUse(_))
80    }
81
82    /// Returns true if this block is a web search tool result block
83    pub fn is_web_search_tool_result(&self) -> bool {
84        matches!(self, ContentBlock::WebSearchToolResult(_))
85    }
86
87    /// Returns true if this block is a tool result block
88    pub fn is_tool_result(&self) -> bool {
89        matches!(self, ContentBlock::ToolResult(_))
90    }
91
92    /// Returns true if this block is a document block
93    pub fn is_document(&self) -> bool {
94        matches!(self, ContentBlock::Document(_))
95    }
96
97    /// Returns true if this block is a thinking block
98    pub fn is_thinking(&self) -> bool {
99        matches!(self, ContentBlock::Thinking(_))
100    }
101
102    /// Returns true if this block is a redacted thinking block
103    pub fn is_redacted_thinking(&self) -> bool {
104        matches!(self, ContentBlock::RedactedThinking(_))
105    }
106
107    /// Returns a reference to the inner TextBlock if this is a Text variant,
108    /// or None otherwise.
109    pub fn as_text(&self) -> Option<&TextBlock> {
110        match self {
111            ContentBlock::Text(block) => Some(block),
112            _ => None,
113        }
114    }
115
116    /// Returns a reference to the inner ImageBlock if this is an Image variant,
117    /// or None otherwise.
118    pub fn as_image(&self) -> Option<&ImageBlock> {
119        match self {
120            ContentBlock::Image(block) => Some(block),
121            _ => None,
122        }
123    }
124
125    /// Returns a reference to the inner ToolUseBlock if this is a ToolUse variant,
126    /// or None otherwise.
127    pub fn as_tool_use(&self) -> Option<&ToolUseBlock> {
128        match self {
129            ContentBlock::ToolUse(block) => Some(block),
130            _ => None,
131        }
132    }
133
134    /// Returns a reference to the inner ServerToolUseBlock if this is a ServerToolUse variant,
135    /// or None otherwise.
136    pub fn as_server_tool_use(&self) -> Option<&ServerToolUseBlock> {
137        match self {
138            ContentBlock::ServerToolUse(block) => Some(block),
139            _ => None,
140        }
141    }
142
143    /// Returns a reference to the inner WebSearchToolResultBlock if this is a WebSearchToolResult variant,
144    /// or None otherwise.
145    pub fn as_web_search_tool_result(&self) -> Option<&WebSearchToolResultBlock> {
146        match self {
147            ContentBlock::WebSearchToolResult(block) => Some(block),
148            _ => None,
149        }
150    }
151
152    /// Returns a reference to the inner ToolResultBlock if this is a ToolResult variant,
153    /// or None otherwise.
154    pub fn as_tool_result(&self) -> Option<&ToolResultBlock> {
155        match self {
156            ContentBlock::ToolResult(block) => Some(block),
157            _ => None,
158        }
159    }
160
161    /// Returns a reference to the inner DocumentBlock if this is a Document variant,
162    /// or None otherwise.
163    pub fn as_document(&self) -> Option<&DocumentBlock> {
164        match self {
165            ContentBlock::Document(block) => Some(block),
166            _ => None,
167        }
168    }
169
170    /// Returns a reference to the inner ThinkingBlock if this is a Thinking variant,
171    /// or None otherwise.
172    pub fn as_thinking(&self) -> Option<&ThinkingBlock> {
173        match self {
174            ContentBlock::Thinking(block) => Some(block),
175            _ => None,
176        }
177    }
178
179    /// Returns a reference to the inner RedactedThinkingBlock if this is a RedactedThinking variant,
180    /// or None otherwise.
181    pub fn as_redacted_thinking(&self) -> Option<&RedactedThinkingBlock> {
182        match self {
183            ContentBlock::RedactedThinking(block) => Some(block),
184            _ => None,
185        }
186    }
187
188    /// Returns true if this block is a code execution result block
189    pub fn is_code_execution_result(&self) -> bool {
190        matches!(self, ContentBlock::CodeExecutionResult(_))
191    }
192
193    /// Returns a reference to the inner CodeExecutionResultBlock if this is a CodeExecutionResult variant,
194    /// or None otherwise.
195    pub fn as_code_execution_result(&self) -> Option<&CodeExecutionResultBlock> {
196        match self {
197            ContentBlock::CodeExecutionResult(block) => Some(block),
198            _ => None,
199        }
200    }
201
202    /// Returns true if this block is a programmatic tool use block
203    pub fn is_programmatic_tool_use(&self) -> bool {
204        matches!(self, ContentBlock::ProgrammaticToolUse(_))
205    }
206
207    /// Returns a reference to the inner ProgrammaticToolUseBlock if this is a ProgrammaticToolUse variant,
208    /// or None otherwise.
209    pub fn as_programmatic_tool_use(&self) -> Option<&ProgrammaticToolUseBlock> {
210        match self {
211            ContentBlock::ProgrammaticToolUse(block) => Some(block),
212            _ => None,
213        }
214    }
215}
216
217/// Helper methods to create ContentBlock variants
218impl From<TextBlock> for ContentBlock {
219    fn from(block: TextBlock) -> Self {
220        ContentBlock::Text(block)
221    }
222}
223
224impl From<ImageBlock> for ContentBlock {
225    fn from(block: ImageBlock) -> Self {
226        ContentBlock::Image(block)
227    }
228}
229
230impl From<ToolUseBlock> for ContentBlock {
231    fn from(block: ToolUseBlock) -> Self {
232        ContentBlock::ToolUse(block)
233    }
234}
235
236impl From<ServerToolUseBlock> for ContentBlock {
237    fn from(block: ServerToolUseBlock) -> Self {
238        ContentBlock::ServerToolUse(block)
239    }
240}
241
242impl From<WebSearchToolResultBlock> for ContentBlock {
243    fn from(block: WebSearchToolResultBlock) -> Self {
244        ContentBlock::WebSearchToolResult(block)
245    }
246}
247
248impl From<ToolResultBlock> for ContentBlock {
249    fn from(block: ToolResultBlock) -> Self {
250        ContentBlock::ToolResult(block)
251    }
252}
253
254impl From<DocumentBlock> for ContentBlock {
255    fn from(block: DocumentBlock) -> Self {
256        ContentBlock::Document(block)
257    }
258}
259
260impl From<ThinkingBlock> for ContentBlock {
261    fn from(block: ThinkingBlock) -> Self {
262        ContentBlock::Thinking(block)
263    }
264}
265
266impl From<RedactedThinkingBlock> for ContentBlock {
267    fn from(block: RedactedThinkingBlock) -> Self {
268        ContentBlock::RedactedThinking(block)
269    }
270}
271
272impl From<CodeExecutionResultBlock> for ContentBlock {
273    fn from(block: CodeExecutionResultBlock) -> Self {
274        ContentBlock::CodeExecutionResult(block)
275    }
276}
277
278impl From<ProgrammaticToolUseBlock> for ContentBlock {
279    fn from(block: ProgrammaticToolUseBlock) -> Self {
280        ContentBlock::ProgrammaticToolUse(block)
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287
288    #[test]
289    fn text_block_serialization() {
290        let text_block = TextBlock::new("This is some text content.");
291        let content_block = ContentBlock::from(text_block);
292
293        let json = serde_json::to_string(&content_block).unwrap();
294        let expected = r#"{"type":"text","text":"This is some text content."}"#;
295
296        assert_eq!(json, expected);
297    }
298
299    #[test]
300    fn tool_use_block_serialization() {
301        let input_json = serde_json::json!({
302            "query": "weather in San Francisco",
303            "limit": 5
304        });
305
306        let tool_block = ToolUseBlock::new("tool_123", "search", input_json);
307        let content_block = ContentBlock::from(tool_block);
308
309        let json = serde_json::to_string(&content_block).unwrap();
310        let actual: serde_json::Value = serde_json::from_str(&json).unwrap();
311        let expected: serde_json::Value = serde_json::json!({
312            "type": "tool_use",
313            "id": "tool_123",
314            "input": {"limit": 5, "query": "weather in San Francisco"},
315            "name": "search"
316        });
317
318        assert_eq!(actual, expected);
319    }
320
321    #[test]
322    fn server_tool_use_block_serialization() {
323        let server_block =
324            ServerToolUseBlock::new_web_search("tool_123", "weather in San Francisco");
325        let content_block = ContentBlock::from(server_block);
326
327        let json = serde_json::to_string(&content_block).unwrap();
328        let expected = r#"{"type":"server_tool_use","id":"tool_123","input":{"query":"weather in San Francisco"},"name":"web_search"}"#;
329
330        assert_eq!(json, expected);
331    }
332
333    #[test]
334    fn thinking_block_serialization() {
335        let thinking_block = ThinkingBlock::new(
336            "Let me think through this problem step by step...",
337            "abc123signature",
338        );
339        let content_block = ContentBlock::from(thinking_block);
340
341        let json = serde_json::to_string(&content_block).unwrap();
342        let expected = r#"{"type":"thinking","signature":"abc123signature","thinking":"Let me think through this problem step by step..."}"#;
343
344        assert_eq!(json, expected);
345    }
346
347    #[test]
348    fn redacted_thinking_block_serialization() {
349        let redacted_block = RedactedThinkingBlock::new("encoded-thinking-data-123");
350        let content_block = ContentBlock::from(redacted_block);
351
352        let json = serde_json::to_string(&content_block).unwrap();
353        let expected = r#"{"type":"redacted_thinking","data":"encoded-thinking-data-123"}"#;
354
355        assert_eq!(json, expected);
356    }
357
358    #[test]
359    fn deserialization() {
360        let json = r#"{"text":"This is some text content.","type":"text"}"#;
361        let content_block: ContentBlock = serde_json::from_str(json).unwrap();
362
363        assert!(content_block.is_text());
364        assert!(!content_block.is_tool_use());
365
366        if let ContentBlock::Text(block) = content_block {
367            assert_eq!(block.text, "This is some text content.");
368        } else {
369            panic!("Expected TextBlock");
370        }
371
372        let json = r#"{"id":"tool_123","input":{"query":"weather in San Francisco"},"name":"web_search","type":"server_tool_use"}"#;
373        let content_block: ContentBlock = serde_json::from_str(json).unwrap();
374
375        assert!(content_block.is_server_tool_use());
376        assert!(!content_block.is_text());
377
378        if let ContentBlock::ServerToolUse(block) = content_block {
379            assert_eq!(block.id, "tool_123");
380            assert_eq!(block.name, "web_search");
381        } else {
382            panic!("Expected ServerToolUseBlock");
383        }
384    }
385
386    #[test]
387    fn as_methods() {
388        let text_block = TextBlock::new("This is some text content.");
389        let content_block = ContentBlock::from(text_block);
390
391        assert!(content_block.as_text().is_some());
392        assert!(content_block.as_image().is_none());
393        assert!(content_block.as_tool_use().is_none());
394        assert!(content_block.as_server_tool_use().is_none());
395        assert!(content_block.as_web_search_tool_result().is_none());
396        assert!(content_block.as_tool_result().is_none());
397        assert!(content_block.as_document().is_none());
398        assert!(content_block.as_thinking().is_none());
399        assert!(content_block.as_redacted_thinking().is_none());
400
401        let text_ref = content_block.as_text().unwrap();
402        assert_eq!(text_ref.text, "This is some text content.");
403    }
404
405    #[test]
406    fn image_block_serialization() {
407        let image_source =
408            crate::types::UrlImageSource::new("https://example.com/image.jpg".to_string());
409        let image_block = ImageBlock::new_with_url(image_source);
410        let content_block = ContentBlock::from(image_block);
411
412        let json = serde_json::to_string(&content_block).unwrap();
413        let expected =
414            r#"{"type":"image","source":{"type":"url","url":"https://example.com/image.jpg"}}"#;
415
416        assert_eq!(json, expected);
417    }
418}