Skip to main content

enact_core/tool/
function.rs

1//! Function tool - wrap any async function as a tool
2
3use super::Tool;
4use async_trait::async_trait;
5use serde_json::Value;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10/// Type alias for async tool functions
11pub type ToolFn =
12    Arc<dyn Fn(Value) -> Pin<Box<dyn Future<Output = anyhow::Result<Value>> + Send>> + Send + Sync>;
13
14/// FunctionTool - wraps an async closure as a tool
15pub struct FunctionTool {
16    name: String,
17    description: String,
18    parameters: Value,
19    func: ToolFn,
20}
21
22impl FunctionTool {
23    /// Create a new function tool
24    pub fn new<F, Fut>(
25        name: impl Into<String>,
26        description: impl Into<String>,
27        parameters: Value,
28        func: F,
29    ) -> Self
30    where
31        F: Fn(Value) -> Fut + Send + Sync + 'static,
32        Fut: Future<Output = anyhow::Result<Value>> + Send + 'static,
33    {
34        let func = Arc::new(move |args: Value| {
35            let fut = func(args);
36            Box::pin(fut) as Pin<Box<dyn Future<Output = anyhow::Result<Value>> + Send>>
37        });
38
39        Self {
40            name: name.into(),
41            description: description.into(),
42            parameters,
43            func,
44        }
45    }
46
47    /// Create a simple tool with no parameters
48    pub fn simple<F, Fut>(name: impl Into<String>, description: impl Into<String>, func: F) -> Self
49    where
50        F: Fn(Value) -> Fut + Send + Sync + 'static,
51        Fut: Future<Output = anyhow::Result<Value>> + Send + 'static,
52    {
53        Self::new(
54            name,
55            description,
56            serde_json::json!({
57                "type": "object",
58                "properties": {},
59                "required": []
60            }),
61            func,
62        )
63    }
64}
65
66#[async_trait]
67impl Tool for FunctionTool {
68    fn name(&self) -> &str {
69        &self.name
70    }
71
72    fn description(&self) -> &str {
73        &self.description
74    }
75
76    fn parameters_schema(&self) -> Value {
77        self.parameters.clone()
78    }
79
80    async fn execute(&self, args: Value) -> anyhow::Result<Value> {
81        (self.func)(args).await
82    }
83}
84
85/// Macro to create a FunctionTool more easily
86#[macro_export]
87macro_rules! tool {
88    ($name:expr, $desc:expr, |$args:ident| $body:expr) => {
89        FunctionTool::new(
90            $name,
91            $desc,
92            serde_json::json!({"type": "object", "properties": {}}),
93            |$args| async move { $body },
94        )
95    };
96}