Skip to main content

autoagents_core/tool/
mod.rs

1use autoagents_llm::chat::{FunctionTool, Tool};
2pub use autoagents_protocol::ToolCallResult;
3use schemars::JsonSchema;
4use serde::{Serialize, de::DeserializeOwned};
5use serde_json::Value;
6use std::fmt::Debug;
7use std::sync::Arc;
8mod runtime;
9use async_trait::async_trait;
10pub use runtime::ToolRuntime;
11
12#[cfg(feature = "wasmtime")]
13pub use runtime::{WasmRuntime, WasmRuntimeError};
14
15#[derive(Debug, thiserror::Error)]
16pub enum ToolCallError {
17    #[error("Runtime Error {0}")]
18    RuntimeError(#[from] Box<dyn std::error::Error + Sync + Send>),
19
20    #[error("Serde Error {0}")]
21    SerdeError(#[from] serde_json::Error),
22}
23
24pub trait ToolT: Send + Sync + Debug + ToolRuntime {
25    /// The name of the tool.
26    fn name(&self) -> &str;
27    /// A description explaining the tool’s purpose.
28    fn description(&self) -> &str;
29    /// Return a description of the expected arguments.
30    fn args_schema(&self) -> Value;
31    /// Return a description of the tool output when available.
32    fn output_schema(&self) -> Option<Value> {
33        None
34    }
35}
36
37/// Marker trait for input types used by `#[derive(ToolInput)]` macros.
38pub trait ToolInputT {
39    fn io_schema() -> &'static str;
40}
41
42/// Parsed schema hook generated by `#[derive(ToolInput)]` for use with `#[tool]`.
43///
44/// If the compiler reports that `ToolInputSchema` is not implemented, add
45/// `#[derive(ToolInput)]` to the struct referenced by `#[tool(..., input = YourArgs)]`.
46#[doc(hidden)]
47pub trait ToolInputSchema: ToolInputT {
48    fn io_schema_value() -> &'static Value;
49}
50
51/// Marker trait for output types used by typed tool bindings.
52pub trait ToolOutputT: Serialize + DeserializeOwned + Send + Sync + JsonSchema {
53    fn io_schema() -> Value {
54        let schema = schemars::r#gen::SchemaSettings::draft07()
55            .into_generator()
56            .into_root_schema_for::<Self>();
57        serde_json::to_value(schema.schema).unwrap_or(Value::Null)
58    }
59}
60
61impl<T> ToolOutputT for T where T: Serialize + DeserializeOwned + Send + Sync + JsonSchema {}
62
63/// A wrapper that allows Arc<dyn ToolT> to be used as Box<dyn ToolT>
64/// This is useful for sharing tools across multiple agents without cloning
65/// Wrapper around `Arc<dyn ToolT>` that presents a `Box<dyn ToolT>`-like API.
66/// Useful when sharing tool instances across multiple agents without cloning.
67#[derive(Debug)]
68pub struct SharedTool {
69    inner: Arc<dyn ToolT>,
70}
71
72impl SharedTool {
73    /// Create a new SharedTool from an Arc<dyn ToolT>
74    pub fn new(tool: Arc<dyn ToolT>) -> Self {
75        Self { inner: tool }
76    }
77}
78
79#[async_trait]
80impl ToolRuntime for SharedTool {
81    async fn execute(&self, args: Value) -> Result<Value, ToolCallError> {
82        self.inner.execute(args).await
83    }
84}
85
86impl ToolT for SharedTool {
87    fn name(&self) -> &str {
88        self.inner.name()
89    }
90
91    fn description(&self) -> &str {
92        self.inner.description()
93    }
94
95    fn args_schema(&self) -> Value {
96        self.inner.args_schema()
97    }
98
99    fn output_schema(&self) -> Option<Value> {
100        self.inner.output_schema()
101    }
102}
103
104/// Helper function to convert Vec<Arc<dyn ToolT>> to Vec<Box<dyn ToolT>>
105/// This is useful when implementing AgentDeriveT::tools() with shared tools
106/// Convert a vector of `Arc<dyn ToolT>` into boxed trait objects for use in
107/// agent definitions.
108pub fn shared_tools_to_boxes(tools: &[Arc<dyn ToolT>]) -> Vec<Box<dyn ToolT>> {
109    tools
110        .iter()
111        .map(|t| Box::new(SharedTool::new(Arc::clone(t))) as Box<dyn ToolT>)
112        .collect()
113}
114
115/// Convert a ToolT trait object to an LLM Tool
116#[allow(clippy::borrowed_box)]
117pub fn to_llm_tool(tool: &Box<dyn ToolT>) -> Tool {
118    Tool {
119        tool_type: "function".to_string(),
120        function: FunctionTool {
121            name: tool.name().to_string(),
122            description: tool.description().to_string(),
123            parameters: tool.args_schema(),
124        },
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use autoagents_llm::chat::Tool;
132    use schemars::JsonSchema;
133    use serde::{Deserialize, Serialize};
134    use serde_json::json;
135    use std::sync::Arc;
136
137    #[derive(Debug, Serialize, Deserialize)]
138    struct TestInput {
139        name: String,
140        value: i32,
141    }
142
143    #[derive(Debug, Serialize, Deserialize, JsonSchema)]
144    struct TestOutput {
145        processed_name: String,
146        doubled_value: i32,
147    }
148
149    impl ToolInputT for TestInput {
150        fn io_schema() -> &'static str {
151            r#"{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"integer"}},"required":["name","value"]}"#
152        }
153    }
154
155    #[derive(Debug)]
156    struct MockTool {
157        name: &'static str,
158        description: &'static str,
159        should_fail: bool,
160    }
161
162    impl MockTool {
163        fn new(name: &'static str, description: &'static str) -> Self {
164            Self {
165                name,
166                description,
167                should_fail: false,
168            }
169        }
170
171        fn with_failure(name: &'static str, description: &'static str) -> Self {
172            Self {
173                name,
174                description,
175                should_fail: true,
176            }
177        }
178    }
179
180    impl ToolT for MockTool {
181        fn name(&self) -> &'static str {
182            self.name
183        }
184
185        fn description(&self) -> &'static str {
186            self.description
187        }
188
189        fn args_schema(&self) -> Value {
190            json!({
191                "type": "object",
192                "properties": {
193                    "name": {"type": "string"},
194                    "value": {"type": "integer"}
195                },
196                "required": ["name", "value"]
197            })
198        }
199    }
200
201    #[async_trait]
202    impl ToolRuntime for MockTool {
203        async fn execute(
204            &self,
205            args: serde_json::Value,
206        ) -> Result<serde_json::Value, ToolCallError> {
207            if self.should_fail {
208                return Err(ToolCallError::RuntimeError(
209                    "Mock tool failure".to_string().into(),
210                ));
211            }
212
213            let input: TestInput = serde_json::from_value(args)?;
214            Ok(json!({
215                "processed_name": input.name,
216                "doubled_value": input.value * 2
217            }))
218        }
219    }
220
221    #[test]
222    fn test_tool_call_error_runtime_error() {
223        let error = ToolCallError::RuntimeError("Runtime error".to_string().into());
224        assert_eq!(error.to_string(), "Runtime Error Runtime error");
225    }
226
227    #[test]
228    fn test_tool_call_error_serde_error() {
229        let json_error = serde_json::from_str::<Value>("invalid json").unwrap_err();
230        let error = ToolCallError::SerdeError(json_error);
231        assert!(error.to_string().contains("Serde Error"));
232    }
233
234    #[test]
235    fn test_tool_call_error_debug() {
236        let error = ToolCallError::RuntimeError("Debug test".to_string().into());
237        let debug_str = format!("{error:?}");
238        assert!(debug_str.contains("RuntimeError"));
239    }
240
241    #[test]
242    fn test_tool_call_error_from_serde() {
243        let json_error = serde_json::from_str::<Value>("invalid json").unwrap_err();
244        let error: ToolCallError = json_error.into();
245        assert!(matches!(error, ToolCallError::SerdeError(_)));
246    }
247
248    #[test]
249    fn test_tool_call_error_from_box_error() {
250        let box_error: Box<dyn std::error::Error + Send + Sync> = "Test error".into();
251        let error: ToolCallError = box_error.into();
252        assert!(matches!(error, ToolCallError::RuntimeError(_)));
253    }
254
255    #[test]
256    fn test_mock_tool_creation() {
257        let tool = MockTool::new("test_tool", "A test tool");
258        assert_eq!(tool.name(), "test_tool");
259        assert_eq!(tool.description(), "A test tool");
260        assert!(!tool.should_fail);
261    }
262
263    #[test]
264    fn test_mock_tool_with_failure() {
265        let tool = MockTool::with_failure("failing_tool", "A failing tool");
266        assert_eq!(tool.name(), "failing_tool");
267        assert_eq!(tool.description(), "A failing tool");
268        assert!(tool.should_fail);
269    }
270
271    #[test]
272    fn test_mock_tool_args_schema() {
273        let tool = MockTool::new("schema_tool", "Schema test");
274        let schema = tool.args_schema();
275
276        assert_eq!(schema["type"], "object");
277        assert!(schema["properties"].is_object());
278        assert!(schema["properties"]["name"].is_object());
279        assert!(schema["properties"]["value"].is_object());
280        assert_eq!(schema["properties"]["name"]["type"], "string");
281        assert_eq!(schema["properties"]["value"]["type"], "integer");
282    }
283
284    #[tokio::test]
285    async fn test_mock_tool_run_success() {
286        let tool = MockTool::new("success_tool", "Success test");
287        let input = json!({
288            "name": "test",
289            "value": 42
290        });
291
292        let result = tool.execute(input).await;
293        assert!(result.is_ok());
294
295        let output = result.unwrap();
296        assert_eq!(output["processed_name"], "test");
297        assert_eq!(output["doubled_value"], 84);
298    }
299
300    #[tokio::test]
301    async fn test_mock_tool_run_failure() {
302        let tool = MockTool::with_failure("failure_tool", "Failure test");
303        let input = json!({
304            "name": "test",
305            "value": 42
306        });
307
308        let result = tool.execute(input).await;
309        assert!(result.is_err());
310        assert!(
311            result
312                .unwrap_err()
313                .to_string()
314                .contains("Mock tool failure")
315        );
316    }
317
318    #[tokio::test]
319    async fn test_mock_tool_run_invalid_input() {
320        let tool = MockTool::new("invalid_input_tool", "Invalid input test");
321        let input = json!({
322            "invalid_field": "test"
323        });
324
325        let result = tool.execute(input).await;
326        assert!(result.is_err());
327        assert!(matches!(result.unwrap_err(), ToolCallError::SerdeError(_)));
328    }
329
330    #[tokio::test]
331    async fn test_mock_tool_run_with_extra_fields() {
332        let tool = MockTool::new("extra_fields_tool", "Extra fields test");
333        let input = json!({
334            "name": "test",
335            "value": 42,
336            "extra_field": "ignored"
337        });
338
339        let result = tool.execute(input).await;
340        assert!(result.is_ok());
341
342        let output = result.unwrap();
343        assert_eq!(output["processed_name"], "test");
344        assert_eq!(output["doubled_value"], 84);
345    }
346
347    #[test]
348    fn test_mock_tool_debug() {
349        let tool = MockTool::new("debug_tool", "Debug test");
350        let debug_str = format!("{tool:?}");
351        assert!(debug_str.contains("MockTool"));
352        assert!(debug_str.contains("debug_tool"));
353    }
354
355    #[test]
356    fn test_tool_input_trait() {
357        let schema = TestInput::io_schema();
358        assert!(schema.contains("object"));
359        assert!(schema.contains("name"));
360        assert!(schema.contains("value"));
361        assert!(schema.contains("string"));
362        assert!(schema.contains("integer"));
363    }
364
365    #[test]
366    fn test_tool_output_trait() {
367        let schema = TestOutput::io_schema();
368        assert_eq!(schema["type"], "object");
369        assert_eq!(schema["properties"]["processed_name"]["type"], "string");
370        assert_eq!(schema["properties"]["doubled_value"]["type"], "integer");
371        let required = schema["required"]
372            .as_array()
373            .expect("required fields should be serialized as an array");
374        assert_eq!(required.len(), 2);
375        assert!(required.iter().any(|value| value == "processed_name"));
376        assert!(required.iter().any(|value| value == "doubled_value"));
377    }
378
379    #[test]
380    fn test_test_input_serialization() {
381        let input = TestInput {
382            name: "test".to_string(),
383            value: 42,
384        };
385        let serialized = serde_json::to_string(&input).unwrap();
386        assert!(serialized.contains("test"));
387        assert!(serialized.contains("42"));
388    }
389
390    #[test]
391    fn test_test_input_deserialization() {
392        let json = r#"{"name":"test","value":42}"#;
393        let input: TestInput = serde_json::from_str(json).unwrap();
394        assert_eq!(input.name, "test");
395        assert_eq!(input.value, 42);
396    }
397
398    #[test]
399    fn test_test_input_debug() {
400        let input = TestInput {
401            name: "debug".to_string(),
402            value: 123,
403        };
404        let debug_str = format!("{input:?}");
405        assert!(debug_str.contains("TestInput"));
406        assert!(debug_str.contains("debug"));
407        assert!(debug_str.contains("123"));
408    }
409
410    #[test]
411    fn test_boxed_tool_to_tool_conversion() {
412        let mock_tool = MockTool::new("convert_tool", "Conversion test");
413        let boxed_tool: Box<dyn ToolT> = Box::new(mock_tool);
414
415        let tool: Tool = to_llm_tool(&boxed_tool);
416        assert_eq!(tool.tool_type, "function");
417        assert_eq!(tool.function.name, "convert_tool");
418        assert_eq!(tool.function.description, "Conversion test");
419        assert_eq!(tool.function.parameters["type"], "object");
420    }
421
422    #[tokio::test]
423    async fn test_shared_tool_delegates_execution_and_metadata() {
424        let shared = SharedTool::new(Arc::new(MockTool::new("shared_tool", "Shared tool")));
425
426        assert_eq!(shared.name(), "shared_tool");
427        assert_eq!(shared.description(), "Shared tool");
428
429        let result = shared
430            .execute(json!({
431                "name": "shared",
432                "value": 21
433            }))
434            .await
435            .expect("shared tool executes");
436
437        assert_eq!(result["processed_name"], "shared");
438        assert_eq!(result["doubled_value"], 42);
439    }
440
441    #[test]
442    fn test_shared_tools_to_boxes_preserves_tool_metadata() {
443        let tools: Vec<Arc<dyn ToolT>> = vec![
444            Arc::new(MockTool::new("tool1", "First tool")),
445            Arc::new(MockTool::new("tool2", "Second tool")),
446        ];
447
448        let boxed_tools = shared_tools_to_boxes(&tools);
449
450        assert_eq!(boxed_tools.len(), 2);
451        assert_eq!(boxed_tools[0].name(), "tool1");
452        assert_eq!(boxed_tools[1].description(), "Second tool");
453    }
454
455    #[test]
456    fn test_tool_conversion_preserves_schema() {
457        let mock_tool = MockTool::new("schema_tool", "Schema preservation test");
458        let boxed_tool: Box<dyn ToolT> = Box::new(mock_tool);
459
460        let tool: Tool = to_llm_tool(&boxed_tool);
461        let schema = &tool.function.parameters;
462
463        assert_eq!(schema["type"], "object");
464        assert_eq!(schema["properties"]["name"]["type"], "string");
465        assert_eq!(schema["properties"]["value"]["type"], "integer");
466        let required = schema["required"]
467            .as_array()
468            .expect("required fields should be serialized as an array");
469        assert_eq!(required.len(), 2);
470        assert!(required.iter().any(|value| value == "name"));
471        assert!(required.iter().any(|value| value == "value"));
472    }
473
474    #[test]
475    fn test_tool_trait_object_usage() {
476        let tools: Vec<Box<dyn ToolT>> = vec![
477            Box::new(MockTool::new("tool1", "First tool")),
478            Box::new(MockTool::new("tool2", "Second tool")),
479            Box::new(MockTool::with_failure("tool3", "Third tool")),
480        ];
481
482        for tool in &tools {
483            assert!(!tool.name().is_empty());
484            assert!(!tool.description().is_empty());
485            assert!(tool.args_schema().is_object());
486        }
487    }
488
489    #[tokio::test]
490    async fn test_tool_run_with_different_inputs() {
491        let tool = MockTool::new("varied_input_tool", "Varied input test");
492
493        let inputs = vec![
494            json!({"name": "test1", "value": 1}),
495            json!({"name": "test2", "value": -5}),
496            json!({"name": "", "value": 0}),
497            json!({"name": "long_name_test", "value": 999999}),
498        ];
499
500        for input in inputs {
501            let result = tool.execute(input.clone()).await;
502            assert!(result.is_ok());
503
504            let output = result.unwrap();
505            assert_eq!(output["processed_name"], input["name"]);
506            assert_eq!(
507                output["doubled_value"],
508                input["value"].as_i64().unwrap() * 2
509            );
510        }
511    }
512
513    #[test]
514    fn test_tool_error_chaining() {
515        let json_error = serde_json::from_str::<Value>("invalid").unwrap_err();
516        let tool_error = ToolCallError::SerdeError(json_error);
517
518        // Test error source chain
519        use std::error::Error;
520        assert!(tool_error.source().is_some());
521    }
522
523    #[test]
524    fn test_tool_with_empty_name() {
525        let tool = MockTool::new("", "Empty name test");
526        assert_eq!(tool.name(), "");
527        assert_eq!(tool.description(), "Empty name test");
528    }
529
530    #[test]
531    fn test_tool_with_empty_description() {
532        let tool = MockTool::new("empty_desc", "");
533        assert_eq!(tool.name(), "empty_desc");
534        assert_eq!(tool.description(), "");
535    }
536
537    #[test]
538    fn test_tool_schema_complex() {
539        let tool = MockTool::new("complex_tool", "Complex schema test");
540        let schema = tool.args_schema();
541
542        // Verify schema structure
543        assert!(schema.is_object());
544        assert!(schema["properties"].is_object());
545        assert!(schema["required"].is_array());
546        assert_eq!(schema["required"].as_array().unwrap().len(), 2);
547    }
548
549    #[test]
550    fn test_multiple_tool_instances() {
551        let tool1 = MockTool::new("tool1", "First instance");
552        let tool2 = MockTool::new("tool2", "Second instance");
553
554        assert_ne!(tool1.name(), tool2.name());
555        assert_ne!(tool1.description(), tool2.description());
556
557        // Both should have the same schema structure
558        assert_eq!(tool1.args_schema(), tool2.args_schema());
559    }
560
561    #[test]
562    fn test_tool_send_sync() {
563        fn assert_send_sync<T: Send + Sync>() {}
564        assert_send_sync::<MockTool>();
565    }
566
567    #[test]
568    fn test_tool_trait_object_send_sync() {
569        fn assert_send_sync<T: Send + Sync>() {}
570        assert_send_sync::<Box<dyn ToolT>>();
571    }
572
573    #[test]
574    fn test_tool_call_result_creation() {
575        let result = ToolCallResult {
576            tool_name: "test_tool".to_string(),
577            success: true,
578            arguments: json!({"param": "value"}),
579            result: json!({"output": "success"}),
580        };
581
582        assert_eq!(result.tool_name, "test_tool");
583        assert!(result.success);
584        assert_eq!(result.arguments, json!({"param": "value"}));
585        assert_eq!(result.result, json!({"output": "success"}));
586    }
587
588    #[test]
589    fn test_tool_call_result_serialization() {
590        let result = ToolCallResult {
591            tool_name: "serialize_tool".to_string(),
592            success: false,
593            arguments: json!({"input": "test"}),
594            result: json!({"error": "failed"}),
595        };
596
597        let serialized = serde_json::to_string(&result).unwrap();
598        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
599
600        assert_eq!(deserialized.tool_name, "serialize_tool");
601        assert!(!deserialized.success);
602        assert_eq!(deserialized.arguments, json!({"input": "test"}));
603        assert_eq!(deserialized.result, json!({"error": "failed"}));
604    }
605
606    #[test]
607    fn test_tool_call_result_clone() {
608        let result = ToolCallResult {
609            tool_name: "clone_tool".to_string(),
610            success: true,
611            arguments: json!({"data": [1, 2, 3]}),
612            result: json!({"processed": [2, 4, 6]}),
613        };
614
615        let cloned = result.clone();
616        assert_eq!(result.tool_name, cloned.tool_name);
617        assert_eq!(result.success, cloned.success);
618        assert_eq!(result.arguments, cloned.arguments);
619        assert_eq!(result.result, cloned.result);
620    }
621
622    #[test]
623    fn test_tool_call_result_debug() {
624        let result = ToolCallResult {
625            tool_name: "debug_tool".to_string(),
626            success: true,
627            arguments: json!({}),
628            result: json!(null),
629        };
630
631        let debug_str = format!("{result:?}");
632        assert!(debug_str.contains("ToolCallResult"));
633        assert!(debug_str.contains("debug_tool"));
634    }
635
636    #[test]
637    fn test_tool_call_result_with_null_values() {
638        let result = ToolCallResult {
639            tool_name: "null_tool".to_string(),
640            success: false,
641            arguments: json!(null),
642            result: json!(null),
643        };
644
645        let serialized = serde_json::to_string(&result).unwrap();
646        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
647
648        assert_eq!(deserialized.tool_name, "null_tool");
649        assert!(!deserialized.success);
650        assert_eq!(deserialized.arguments, json!(null));
651        assert_eq!(deserialized.result, json!(null));
652    }
653
654    #[test]
655    fn test_tool_call_result_with_complex_data() {
656        let complex_args = json!({
657            "nested": {
658                "array": [1, 2, {"key": "value"}],
659                "string": "test",
660                "number": 42.5
661            }
662        });
663
664        let complex_result = json!({
665            "status": "completed",
666            "data": {
667                "items": ["a", "b", "c"],
668                "count": 3
669            }
670        });
671
672        let result = ToolCallResult {
673            tool_name: "complex_tool".to_string(),
674            success: true,
675            arguments: complex_args.clone(),
676            result: complex_result.clone(),
677        };
678
679        let serialized = serde_json::to_string(&result).unwrap();
680        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
681
682        assert_eq!(deserialized.arguments, complex_args);
683        assert_eq!(deserialized.result, complex_result);
684    }
685
686    #[test]
687    fn test_tool_call_result_empty_tool_name() {
688        let result = ToolCallResult {
689            tool_name: String::default(),
690            success: true,
691            arguments: json!({}),
692            result: json!({}),
693        };
694
695        assert!(result.tool_name.is_empty());
696        assert!(result.success);
697    }
698
699    #[test]
700    fn test_tool_call_result_large_data() {
701        let large_string = "x".repeat(10000);
702        let result = ToolCallResult {
703            tool_name: "large_tool".to_string(),
704            success: true,
705            arguments: json!({"large_param": large_string}),
706            result: json!({"processed": true}),
707        };
708
709        let serialized = serde_json::to_string(&result).unwrap();
710        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
711
712        assert_eq!(deserialized.tool_name, "large_tool");
713        assert!(deserialized.success);
714        assert!(
715            deserialized.arguments["large_param"]
716                .as_str()
717                .unwrap()
718                .len()
719                == 10000
720        );
721    }
722
723    #[test]
724    fn test_tool_call_result_equality() {
725        let result1 = ToolCallResult {
726            tool_name: "equal_tool".to_string(),
727            success: true,
728            arguments: json!({"param": "value"}),
729            result: json!({"output": "result"}),
730        };
731
732        let result2 = ToolCallResult {
733            tool_name: "equal_tool".to_string(),
734            success: true,
735            arguments: json!({"param": "value"}),
736            result: json!({"output": "result"}),
737        };
738
739        let result3 = ToolCallResult {
740            tool_name: "different_tool".to_string(),
741            success: true,
742            arguments: json!({"param": "value"}),
743            result: json!({"output": "result"}),
744        };
745
746        // Test equality through serialization since ToolCallResult doesn't implement PartialEq
747        let serialized1 = serde_json::to_string(&result1).unwrap();
748        let serialized2 = serde_json::to_string(&result2).unwrap();
749        let serialized3 = serde_json::to_string(&result3).unwrap();
750
751        assert_eq!(serialized1, serialized2);
752        assert_ne!(serialized1, serialized3);
753    }
754
755    #[test]
756    fn test_tool_call_result_with_unicode() {
757        let result = ToolCallResult {
758            tool_name: "unicode_tool".to_string(),
759            success: true,
760            arguments: json!({"message": "Hello δΈ–η•Œ! 🌍"}),
761            result: json!({"response": "Processed: Hello δΈ–η•Œ! 🌍"}),
762        };
763
764        let serialized = serde_json::to_string(&result).unwrap();
765        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
766
767        assert_eq!(deserialized.arguments["message"], "Hello δΈ–η•Œ! 🌍");
768        assert_eq!(deserialized.result["response"], "Processed: Hello δΈ–η•Œ! 🌍");
769    }
770
771    #[test]
772    fn test_tool_call_result_with_arrays() {
773        let result = ToolCallResult {
774            tool_name: "array_tool".to_string(),
775            success: true,
776            arguments: json!({"numbers": [1, 2, 3, 4, 5]}),
777            result: json!({"sum": 15, "count": 5}),
778        };
779
780        let serialized = serde_json::to_string(&result).unwrap();
781        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
782
783        assert_eq!(deserialized.arguments["numbers"], json!([1, 2, 3, 4, 5]));
784        assert_eq!(deserialized.result["sum"], 15);
785        assert_eq!(deserialized.result["count"], 5);
786    }
787
788    #[test]
789    fn test_tool_call_result_boolean_values() {
790        let result = ToolCallResult {
791            tool_name: "bool_tool".to_string(),
792            success: false,
793            arguments: json!({"enabled": true, "debug": false}),
794            result: json!({"valid": false, "error": true}),
795        };
796
797        let serialized = serde_json::to_string(&result).unwrap();
798        let deserialized: ToolCallResult = serde_json::from_str(&serialized).unwrap();
799
800        assert!(!deserialized.success);
801        assert_eq!(deserialized.arguments["enabled"], true);
802        assert_eq!(deserialized.arguments["debug"], false);
803        assert_eq!(deserialized.result["valid"], false);
804        assert_eq!(deserialized.result["error"], true);
805    }
806}