Skip to main content

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 usage(mut self, usage: Usage) -> Self {
182        self.inner_usage = Some(usage);
183        self
184    }
185
186    pub fn model(mut self, model: impl Into<String>) -> Self {
187        self.inner_model = Some(model.into());
188        self
189    }
190
191    pub fn 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_non_retryable(&self) -> bool {
202        matches!(
203            self.output,
204            ToolOutput::Error(
205                ToolError::PermissionDenied { .. }
206                    | ToolError::SecurityViolation { .. }
207                    | ToolError::UnknownTool { .. }
208            )
209        )
210    }
211
212    pub fn text(&self) -> String {
213        self.output.text()
214    }
215
216    pub fn error_message(&self) -> String {
217        self.output.error_message()
218    }
219
220    pub fn as_error(&self) -> Option<&ToolError> {
221        self.output.as_error()
222    }
223
224    pub fn permission_denied(tool: impl Into<String>, reason: impl Into<String>) -> Self {
225        Self {
226            output: ToolOutput::permission_denied(tool, reason),
227            inner_usage: None,
228            inner_model: None,
229        }
230    }
231
232    pub fn unknown_tool(name: impl Into<String>) -> Self {
233        Self {
234            output: ToolOutput::tool_error(ToolError::unknown_tool(name)),
235            inner_usage: None,
236            inner_model: None,
237        }
238    }
239
240    pub fn timeout(timeout_ms: u64) -> Self {
241        Self {
242            output: ToolOutput::timeout(timeout_ms),
243            inner_usage: None,
244            inner_model: None,
245        }
246    }
247
248    pub fn security_error(message: impl Into<String>) -> Self {
249        Self {
250            output: ToolOutput::security_error(message),
251            inner_usage: None,
252            inner_model: None,
253        }
254    }
255}
256
257impl From<ToolOutput> for ToolResult {
258    fn from(output: ToolOutput) -> Self {
259        Self {
260            output,
261            inner_usage: None,
262            inner_model: None,
263        }
264    }
265}
266
267impl From<String> for ToolResult {
268    fn from(s: String) -> Self {
269        Self::success(s)
270    }
271}
272
273impl From<&str> for ToolResult {
274    fn from(s: &str) -> Self {
275        Self::success(s)
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    #[test]
284    fn test_tool_output_from_result() {
285        let ok: Result<&str, &str> = Ok("success");
286        let output: ToolOutput = ok.into();
287        assert!(!output.is_error());
288
289        let err: Result<&str, &str> = Err("failed");
290        let output: ToolOutput = err.into();
291        assert!(output.is_error());
292    }
293}