claude_agent/types/tool/
output.rs

1//! Tool execution input and output types.
2
3use serde::{Deserialize, Serialize};
4
5use super::error::ToolError;
6use crate::types::response::Usage;
7
8#[derive(Debug, Clone)]
9pub struct ToolInput {
10    pub id: String,
11    pub name: String,
12    pub input: serde_json::Value,
13}
14
15#[derive(Debug, Clone)]
16pub enum ToolOutput {
17    Success(String),
18    SuccessBlocks(Vec<ToolOutputBlock>),
19    Error(ToolError),
20    Empty,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "type", rename_all = "snake_case")]
25pub enum ToolOutputBlock {
26    Text {
27        text: String,
28    },
29    Image {
30        data: String,
31        media_type: String,
32    },
33    #[serde(rename = "search_result")]
34    SearchResult(crate::types::search::SearchResultBlock),
35}
36
37impl ToolOutput {
38    pub fn success(content: impl Into<String>) -> Self {
39        Self::Success(content.into())
40    }
41
42    pub fn error(message: impl Into<String>) -> Self {
43        Self::Error(ToolError::execution_failed(message))
44    }
45
46    pub fn tool_error(error: ToolError) -> Self {
47        Self::Error(error)
48    }
49
50    pub fn permission_denied(tool: impl Into<String>, permission: impl Into<String>) -> Self {
51        Self::Error(ToolError::permission_denied(tool, permission))
52    }
53
54    pub fn not_found(path: impl Into<String>) -> Self {
55        Self::Error(ToolError::not_found(path))
56    }
57
58    pub fn invalid_input(message: impl Into<String>) -> Self {
59        Self::Error(ToolError::invalid_input(message))
60    }
61
62    pub fn timeout(timeout_ms: u64) -> Self {
63        Self::Error(ToolError::timeout(timeout_ms))
64    }
65
66    pub fn security_error(message: impl Into<String>) -> Self {
67        Self::Error(ToolError::security_violation(message))
68    }
69
70    pub fn empty() -> Self {
71        Self::Empty
72    }
73
74    pub fn search_results(results: Vec<crate::types::search::SearchResultBlock>) -> Self {
75        Self::SuccessBlocks(
76            results
77                .into_iter()
78                .map(ToolOutputBlock::SearchResult)
79                .collect(),
80        )
81    }
82
83    pub fn is_error(&self) -> bool {
84        matches!(self, Self::Error(_))
85    }
86
87    pub fn as_error(&self) -> Option<&ToolError> {
88        match self {
89            Self::Error(e) => Some(e),
90            _ => None,
91        }
92    }
93
94    pub fn error_message(&self) -> String {
95        match self {
96            Self::Error(e) => e.to_string(),
97            _ => String::new(),
98        }
99    }
100
101    pub fn text(&self) -> String {
102        match self {
103            Self::Success(content) => content.clone(),
104            Self::SuccessBlocks(blocks) => blocks
105                .iter()
106                .filter_map(|b| match b {
107                    ToolOutputBlock::Text { text } => Some(text.as_str()),
108                    _ => None,
109                })
110                .collect::<Vec<_>>()
111                .join("\n"),
112            Self::Error(e) => e.to_string(),
113            Self::Empty => String::new(),
114        }
115    }
116}
117
118impl From<String> for ToolOutput {
119    fn from(s: String) -> Self {
120        Self::Success(s)
121    }
122}
123
124impl From<&str> for ToolOutput {
125    fn from(s: &str) -> Self {
126        Self::Success(s.to_string())
127    }
128}
129
130impl<T, E> From<Result<T, E>> for ToolOutput
131where
132    T: Into<String>,
133    E: std::fmt::Display,
134{
135    fn from(result: Result<T, E>) -> Self {
136        match result {
137            Ok(content) => Self::Success(content.into()),
138            Err(e) => Self::error(e.to_string()),
139        }
140    }
141}
142
143impl From<ToolError> for ToolOutput {
144    fn from(error: ToolError) -> Self {
145        Self::Error(error)
146    }
147}
148
149#[derive(Debug, Clone)]
150pub struct ToolResult {
151    pub output: ToolOutput,
152    pub inner_usage: Option<Usage>,
153    pub inner_model: Option<String>,
154}
155
156impl ToolResult {
157    pub fn success(content: impl Into<String>) -> Self {
158        Self {
159            output: ToolOutput::success(content),
160            inner_usage: None,
161            inner_model: None,
162        }
163    }
164
165    pub fn error(message: impl Into<String>) -> Self {
166        Self {
167            output: ToolOutput::error(message),
168            inner_usage: None,
169            inner_model: None,
170        }
171    }
172
173    pub fn empty() -> Self {
174        Self {
175            output: ToolOutput::Empty,
176            inner_usage: None,
177            inner_model: None,
178        }
179    }
180
181    pub fn with_usage(mut self, usage: Usage) -> Self {
182        self.inner_usage = Some(usage);
183        self
184    }
185
186    pub fn with_model(mut self, model: impl Into<String>) -> Self {
187        self.inner_model = Some(model.into());
188        self
189    }
190
191    pub fn with_inner_call(mut self, usage: Usage, model: impl Into<String>) -> Self {
192        self.inner_usage = Some(usage);
193        self.inner_model = Some(model.into());
194        self
195    }
196
197    pub fn is_error(&self) -> bool {
198        self.output.is_error()
199    }
200
201    pub fn is_permission_denied(&self) -> bool {
202        matches!(
203            self.output,
204            ToolOutput::Error(ToolError::PermissionDenied { .. })
205        )
206    }
207
208    pub fn is_non_retryable(&self) -> bool {
209        matches!(
210            self.output,
211            ToolOutput::Error(
212                ToolError::PermissionDenied { .. }
213                    | ToolError::SecurityViolation { .. }
214                    | ToolError::UnknownTool { .. }
215            )
216        )
217    }
218
219    pub fn text(&self) -> String {
220        self.output.text()
221    }
222
223    pub fn error_message(&self) -> String {
224        self.output.error_message()
225    }
226
227    pub fn as_error(&self) -> Option<&ToolError> {
228        self.output.as_error()
229    }
230
231    pub fn permission_denied(tool: impl Into<String>, reason: impl Into<String>) -> Self {
232        Self {
233            output: ToolOutput::permission_denied(tool, reason),
234            inner_usage: None,
235            inner_model: None,
236        }
237    }
238
239    pub fn unknown_tool(name: impl Into<String>) -> Self {
240        Self {
241            output: ToolOutput::tool_error(ToolError::unknown_tool(name)),
242            inner_usage: None,
243            inner_model: None,
244        }
245    }
246
247    pub fn timeout(timeout_ms: u64) -> Self {
248        Self {
249            output: ToolOutput::timeout(timeout_ms),
250            inner_usage: None,
251            inner_model: None,
252        }
253    }
254
255    pub fn security_error(message: impl Into<String>) -> Self {
256        Self {
257            output: ToolOutput::security_error(message),
258            inner_usage: None,
259            inner_model: None,
260        }
261    }
262}
263
264impl From<ToolOutput> for ToolResult {
265    fn from(output: ToolOutput) -> Self {
266        Self {
267            output,
268            inner_usage: None,
269            inner_model: None,
270        }
271    }
272}
273
274impl From<String> for ToolResult {
275    fn from(s: String) -> Self {
276        Self::success(s)
277    }
278}
279
280impl From<&str> for ToolResult {
281    fn from(s: &str) -> Self {
282        Self::success(s)
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289
290    #[test]
291    fn test_tool_output_from_result() {
292        let ok: Result<&str, &str> = Ok("success");
293        let output: ToolOutput = ok.into();
294        assert!(!output.is_error());
295
296        let err: Result<&str, &str> = Err("failed");
297        let output: ToolOutput = err.into();
298        assert!(output.is_error());
299    }
300}