Skip to main content

lash_core/
tool_result.rs

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}