1#[derive(Clone, Debug, PartialEq)]
2pub struct ToolResult {
3 output: Box<crate::ToolCallOutput>,
4}
5
6impl ToolResult {
7 pub fn from_output(output: crate::ToolCallOutput) -> Self {
8 Self {
9 output: Box::new(output),
10 }
11 }
12
13 pub fn ok(result: serde_json::Value) -> Self {
14 Self::from_output(crate::ToolCallOutput::success(result))
15 }
16
17 pub fn err(result: serde_json::Value) -> Self {
18 let message = result
19 .as_str()
20 .map(ToOwned::to_owned)
21 .unwrap_or_else(|| result.to_string());
22 Self::from_output(crate::ToolCallOutput::failure(crate::ToolFailure {
23 class: crate::ToolFailureClass::Execution,
24 code: "tool_error".to_string(),
25 message,
26 source: crate::ToolFailureSource::Tool,
27 retry: crate::ToolRetryDisposition::Never,
28 raw: Some(crate::ToolValue::from(result)),
29 }))
30 }
31
32 pub fn err_fmt(msg: impl std::fmt::Display) -> Self {
33 Self::err(serde_json::json!(msg.to_string()))
34 }
35
36 pub fn failure(failure: crate::ToolFailure) -> Self {
37 Self::from_output(crate::ToolCallOutput::failure(failure))
38 }
39
40 pub fn retryable_failure(
41 class: crate::ToolFailureClass,
42 code: impl Into<String>,
43 message: impl Into<String>,
44 after_ms: Option<u64>,
45 ) -> Self {
46 Self::failure(crate::ToolFailure::safe_retry(
47 class, code, message, after_ms,
48 ))
49 }
50
51 pub fn cancelled(message: impl Into<String>) -> Self {
52 Self::from_output(crate::ToolCallOutput::cancelled(
53 crate::ToolCancellation::runtime(message),
54 ))
55 }
56
57 pub fn cancelled_with_raw(message: impl Into<String>, raw: serde_json::Value) -> Self {
58 let mut cancellation = crate::ToolCancellation::runtime(message);
59 cancellation.raw = Some(crate::ToolValue::from(raw));
60 Self::from_output(crate::ToolCallOutput::cancelled(cancellation))
61 }
62
63 pub fn with_control(mut self, control: crate::ToolControl) -> Self {
64 self.output.as_mut().control = Some(control);
65 self
66 }
67
68 pub fn is_success(&self) -> bool {
69 self.output.is_success()
70 }
71
72 pub fn value_for_projection(&self) -> serde_json::Value {
73 match &self.output.outcome {
74 crate::ToolCallOutcome::Success(value) => value.to_json_value(),
75 crate::ToolCallOutcome::Failure(failure) => failure
76 .raw
77 .as_ref()
78 .map(crate::ToolValue::to_json_value)
79 .unwrap_or_else(|| failure.to_json_value()),
80 crate::ToolCallOutcome::Cancelled(cancellation) => cancellation
81 .raw
82 .as_ref()
83 .map(crate::ToolValue::to_json_value)
84 .unwrap_or_else(|| cancellation.to_json_value()),
85 }
86 }
87
88 pub fn as_output(&self) -> &crate::ToolCallOutput {
89 self.output.as_ref()
90 }
91
92 pub fn into_output(self) -> crate::ToolCallOutput {
93 *self.output
94 }
95}
96
97impl<T, E> From<Result<T, E>> for ToolResult
98where
99 T: serde::Serialize,
100 E: std::fmt::Display,
101{
102 fn from(result: Result<T, E>) -> Self {
103 match result {
104 Ok(value) => match serde_json::to_value(value) {
105 Ok(value) => Self::ok(value),
106 Err(err) => Self::err_fmt(format_args!("Failed to serialize tool result: {err}")),
107 },
108 Err(err) => Self::err_fmt(err),
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use serde::ser::{Error as _, Serializer};
116
117 use super::*;
118
119 #[test]
120 fn tool_result_from_result_serializes_success_values() {
121 let result: ToolResult = Result::<_, std::io::Error>::Ok(vec!["alpha", "beta"]).into();
122 assert!(result.is_success());
123 assert_eq!(
124 result.value_for_projection(),
125 serde_json::json!(["alpha", "beta"])
126 );
127 }
128
129 #[test]
130 fn tool_result_from_result_formats_errors() {
131 let result: ToolResult =
132 Result::<serde_json::Value, _>::Err(std::io::Error::other("nope")).into();
133 assert!(!result.is_success());
134 assert_eq!(result.value_for_projection(), serde_json::json!("nope"));
135 assert_eq!(
136 result.as_output().value_for_projection()["message"],
137 serde_json::json!("nope")
138 );
139 }
140
141 #[test]
142 fn tool_result_from_result_reports_serialize_failures() {
143 struct BrokenValue;
144
145 impl serde::Serialize for BrokenValue {
146 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
147 where
148 S: Serializer,
149 {
150 Err(S::Error::custom("boom"))
151 }
152 }
153
154 let result: ToolResult = Result::<BrokenValue, std::io::Error>::Ok(BrokenValue).into();
155 assert!(!result.is_success());
156 assert_eq!(
157 result.value_for_projection(),
158 serde_json::json!("Failed to serialize tool result: boom")
159 );
160 }
161}