use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use serde_json::Value;
use super::{ToolError, ToolOutput};
use crate::provider::ToolDefinition;
pub trait ToolHandler<Ctx = ()>: Send + Sync {
fn definition(&self) -> ToolDefinition;
fn execute<'a>(
&'a self,
input: Value,
ctx: &'a Ctx,
) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + 'a>>;
}
pub struct FnToolHandler<Ctx, F> {
pub(crate) definition: ToolDefinition,
pub(crate) handler: F,
pub(crate) _ctx: PhantomData<fn(&Ctx)>,
}
impl<Ctx, F> std::fmt::Debug for FnToolHandler<Ctx, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnToolHandler")
.field("name", &self.definition.name)
.finish_non_exhaustive()
}
}
impl<Ctx, F, Fut, O> ToolHandler<Ctx> for FnToolHandler<Ctx, F>
where
Ctx: Send + Sync + 'static,
F: for<'c> Fn(Value, &'c Ctx) -> Fut + Send + Sync,
Fut: Future<Output = Result<O, ToolError>> + Send + 'static,
O: Into<ToolOutput> + Send + 'static,
{
fn definition(&self) -> ToolDefinition {
self.definition.clone()
}
fn execute<'a>(
&'a self,
input: Value,
ctx: &'a Ctx,
) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + 'a>> {
let fut = (self.handler)(input, ctx);
Box::pin(async move { fut.await.map(Into::into) })
}
}
pub struct NoCtxToolHandler<F> {
pub(crate) definition: ToolDefinition,
pub(crate) handler: F,
}
impl<F> std::fmt::Debug for NoCtxToolHandler<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NoCtxToolHandler")
.field("name", &self.definition.name)
.finish_non_exhaustive()
}
}
impl<F, Fut, O> ToolHandler<()> for NoCtxToolHandler<F>
where
F: Fn(Value) -> Fut + Send + Sync,
Fut: Future<Output = Result<O, ToolError>> + Send + 'static,
O: Into<ToolOutput> + Send + 'static,
{
fn definition(&self) -> ToolDefinition {
self.definition.clone()
}
fn execute<'a>(
&'a self,
input: Value,
_ctx: &'a (),
) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + 'a>> {
let fut = (self.handler)(input);
Box::pin(async move { fut.await.map(Into::into) })
}
}