use oxi_agent::{AgentTool, AgentToolResult, ToolContext, ToolError};
use serde_json::Value;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type ToolHandler =
Arc<dyn Fn(Value, &ToolContext) -> Result<AgentToolResult, ToolError> + Send + Sync>;
pub type AsyncToolHandler = Arc<
dyn Fn(
Value,
&ToolContext,
) -> Pin<Box<dyn Future<Output = Result<AgentToolResult, ToolError>> + Send>>
+ Send
+ Sync,
>;
pub struct ClosureTool {
name: String,
description: String,
schema: Value,
handler: AsyncToolHandler,
}
impl ClosureTool {
pub fn new_sync(
name: impl Into<String>,
description: impl Into<String>,
schema: Value,
handler: impl Fn(Value, &ToolContext) -> Result<AgentToolResult, ToolError>
+ Send
+ Sync
+ 'static,
) -> Self {
#[allow(clippy::type_complexity)]
let handler_arc: Arc<
dyn Fn(Value, &ToolContext) -> Result<AgentToolResult, ToolError> + Send + Sync,
> = Arc::new(handler);
Self {
name: name.into(),
description: description.into(),
schema,
handler: Arc::new(move |params, ctx| {
let result = handler_arc(params, ctx);
Box::pin(async move { result })
}),
}
}
pub fn new_async(
name: impl Into<String>,
description: impl Into<String>,
schema: Value,
handler: impl Fn(
Value,
&ToolContext,
)
-> Pin<Box<dyn Future<Output = Result<AgentToolResult, ToolError>> + Send>>
+ Send
+ Sync
+ 'static,
) -> Self {
Self {
name: name.into(),
description: description.into(),
schema,
handler: Arc::new(handler),
}
}
}
impl AgentTool for ClosureTool {
fn name(&self) -> &str {
&self.name
}
fn label(&self) -> &str {
&self.name
}
fn description(&self) -> &str {
&self.description
}
fn parameters_schema(&self) -> Value {
self.schema.clone()
}
fn execute<'a>(
&'a self,
_tool_call_id: &'a str,
params: Value,
_signal: Option<tokio::sync::oneshot::Receiver<()>>,
ctx: &'a ToolContext,
) -> Pin<Box<dyn Future<Output = Result<AgentToolResult, ToolError>> + Send + 'a>> {
(self.handler)(params, ctx)
}
}