lc_core/tools/
structured.rs1use super::{BaseTool, Tool, ToolError};
7use async_trait::async_trait;
8use serde_json::Value;
9
10pub struct StructuredTool<T: Tool> {
15 inner: T,
17 name: String,
19 description: String,
21 schema: Option<Value>,
23}
24
25impl<T: Tool> StructuredTool<T> {
26 pub fn new(tool: T, name: Option<&str>, description: Option<&str>) -> Self {
33 let schema = tool.args_schema();
34 Self {
35 inner: tool,
36 name: name
37 .map(|s| s.to_string())
38 .unwrap_or_else(|| "tool".to_string()),
39 description: description
40 .map(|s| s.to_string())
41 .unwrap_or_else(|| "A tool".to_string()),
42 schema,
43 }
44 }
45
46 fn parse_input(&self, input: String) -> Result<T::Input, ToolError> {
48 let json: Value = serde_json::from_str(&input)
50 .map_err(|e| ToolError::InvalidInput(format!("JSON parse failed: {}", e)))?;
51
52 serde_json::from_value(json)
54 .map_err(|e| ToolError::InvalidInput(format!("Input format mismatch: {}", e)))
55 }
56
57 fn serialize_output(output: T::Output) -> Result<String, ToolError> {
59 serde_json::to_string(&output)
60 .map_err(|e| ToolError::ExecutionFailed(format!("Output serialization failed: {}", e)))
61 }
62}
63
64#[async_trait]
65impl<T: Tool> BaseTool for StructuredTool<T> {
66 fn name(&self) -> &str {
67 &self.name
68 }
69
70 fn description(&self) -> &str {
71 &self.description
72 }
73
74 async fn run(&self, input: String) -> Result<String, ToolError> {
75 let parsed_input = self.parse_input(input)?;
77
78 let output = self.inner.invoke(parsed_input).await?;
80
81 Self::serialize_output(output)
83 }
84
85 fn args_schema(&self) -> Option<Value> {
86 self.schema.clone()
87 }
88
89 fn return_direct(&self) -> bool {
90 false
91 }
92
93 async fn handle_error(&self, error: ToolError) -> String {
94 format!("Tool '{}' execution failed: {}", self.name, error)
95 }
96}
97
98impl<T: Tool> std::fmt::Debug for StructuredTool<T> {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("StructuredTool")
101 .field("name", &self.name)
102 .field("description", &self.description)
103 .finish()
104 }
105}