Skip to main content

lc_core/tools/
structured.rs

1// src/core/tools/structured.rs
2//! Structured tool wrapper.
3//!
4//! Wraps generic Tool as BaseTool with string interface.
5
6use super::{BaseTool, Tool, ToolError};
7use async_trait::async_trait;
8use serde_json::Value;
9
10/// Structured tool wrapper.
11///
12/// Wraps a Tool trait implementation as BaseTool,
13/// automatically handling JSON input parsing and output serialization.
14pub struct StructuredTool<T: Tool> {
15    /// Inner tool instance.
16    inner: T,
17    /// Tool name.
18    name: String,
19    /// Tool description.
20    description: String,
21    /// JSON Schema.
22    schema: Option<Value>,
23}
24
25impl<T: Tool> StructuredTool<T> {
26    /// Creates a structured tool.
27    ///
28    /// # Arguments
29    /// * `tool` - Inner tool instance.
30    /// * `name` - Tool name (optional, defaults to "tool").
31    /// * `description` - Tool description (optional, defaults to "A tool").
32    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    /// Parses JSON string to tool input type.
47    fn parse_input(&self, input: String) -> Result<T::Input, ToolError> {
48        // Parse as JSON
49        let json: Value = serde_json::from_str(&input)
50            .map_err(|e| ToolError::InvalidInput(format!("JSON parse failed: {}", e)))?;
51
52        // Convert to target type
53        serde_json::from_value(json)
54            .map_err(|e| ToolError::InvalidInput(format!("Input format mismatch: {}", e)))
55    }
56
57    /// Serializes output to JSON string.
58    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        // Parse input
76        let parsed_input = self.parse_input(input)?;
77
78        // Execute tool
79        let output = self.inner.invoke(parsed_input).await?;
80
81        // Serialize output
82        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}