use std::future::Future;
use std::pin::Pin;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::id::{SessionId, TurnId};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DeferrablePolicy {
#[default]
Never,
Deferred,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ToolPolicy {
pub deferrable: DeferrablePolicy,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolOutput {
pub content: String,
pub is_error: bool,
}
impl ToolOutput {
pub fn text(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: false,
}
}
pub fn error(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: true,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ToolContext {
pub session_id: SessionId,
pub turn_id: TurnId,
pub extensions: crate::extensions::Extensions,
}
#[async_trait]
pub trait Tool: Send + Sync {
fn definition(&self) -> ToolDefinition;
async fn execute(&self, arguments: serde_json::Value, context: &ToolContext) -> ToolOutput;
fn policy(&self) -> ToolPolicy {
ToolPolicy::default()
}
}
type BoxedToolFuture = Pin<Box<dyn Future<Output = ToolOutput> + Send>>;
type BoxedToolFn = Box<dyn Fn(serde_json::Value) -> BoxedToolFuture + Send + Sync>;
pub struct FnTool {
definition: ToolDefinition,
handler: BoxedToolFn,
}
impl FnTool {
pub fn new<F, Fut>(
name: impl Into<String>,
description: impl Into<String>,
parameters: serde_json::Value,
handler: F,
) -> Self
where
F: Fn(serde_json::Value) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ToolOutput> + Send + 'static,
{
Self {
definition: ToolDefinition {
name: name.into(),
description: description.into(),
parameters,
},
handler: Box::new(move |args| Box::pin(handler(args))),
}
}
}
#[async_trait]
impl Tool for FnTool {
fn definition(&self) -> ToolDefinition {
self.definition.clone()
}
async fn execute(&self, arguments: serde_json::Value, _context: &ToolContext) -> ToolOutput {
(self.handler)(arguments).await
}
}