use super::Tool;
use async_trait::async_trait;
use serde_json::Value;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type ToolFn =
Arc<dyn Fn(Value) -> Pin<Box<dyn Future<Output = anyhow::Result<Value>> + Send>> + Send + Sync>;
pub struct FunctionTool {
name: String,
description: String,
parameters: Value,
func: ToolFn,
}
impl FunctionTool {
pub fn new<F, Fut>(
name: impl Into<String>,
description: impl Into<String>,
parameters: Value,
func: F,
) -> Self
where
F: Fn(Value) -> Fut + Send + Sync + 'static,
Fut: Future<Output = anyhow::Result<Value>> + Send + 'static,
{
let func = Arc::new(move |args: Value| {
let fut = func(args);
Box::pin(fut) as Pin<Box<dyn Future<Output = anyhow::Result<Value>> + Send>>
});
Self {
name: name.into(),
description: description.into(),
parameters,
func,
}
}
pub fn simple<F, Fut>(name: impl Into<String>, description: impl Into<String>, func: F) -> Self
where
F: Fn(Value) -> Fut + Send + Sync + 'static,
Fut: Future<Output = anyhow::Result<Value>> + Send + 'static,
{
Self::new(
name,
description,
serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
func,
)
}
}
#[async_trait]
impl Tool for FunctionTool {
fn name(&self) -> &str {
&self.name
}
fn description(&self) -> &str {
&self.description
}
fn parameters_schema(&self) -> Value {
self.parameters.clone()
}
async fn execute(&self, args: Value) -> anyhow::Result<Value> {
(self.func)(args).await
}
}
#[macro_export]
macro_rules! tool {
($name:expr, $desc:expr, |$args:ident| $body:expr) => {
FunctionTool::new(
$name,
$desc,
serde_json::json!({"type": "object", "properties": {}}),
|$args| async move { $body },
)
};
}