claude_agent/types/tool/
output.rs1use 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}