use async_trait::async_trait;
use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
#[async_trait]
pub trait BaseTool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn run(&self, input: String) -> Result<String, ToolError>;
fn args_schema(&self) -> Option<Value> {
None
}
fn return_direct(&self) -> bool {
false
}
async fn handle_error(&self, error: ToolError) -> String {
format!("Tool '{}' execution failed: {}", self.name(), error)
}
}
#[async_trait]
pub trait Tool: Send + Sync {
type Input: DeserializeOwned + JsonSchema + Send + Sync + 'static;
type Output: Serialize + Send + Sync;
async fn invoke(&self, input: Self::Input) -> Result<Self::Output, ToolError>;
fn args_schema(&self) -> Option<Value> {
use schemars::schema_for;
serde_json::to_value(schema_for!(Self::Input)).ok()
}
}
#[derive(Debug, thiserror::Error)]
pub enum ToolError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Execution failed: {0}")]
ExecutionFailed(String),
#[error("Timeout: {0} seconds")]
Timeout(u64),
#[error("Tool not found: {0}")]
ToolNotFound(String),
}
use super::ToolDefinition;
pub fn to_tool_definition(tool: &dyn BaseTool) -> ToolDefinition {
ToolDefinition::new(tool.name(), tool.description()).with_parameters(
tool.args_schema()
.unwrap_or(serde_json::json!({"type": "object"})),
)
}