use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use serde_json::Value;
use anyhow::Result;
#[derive(Debug, Clone)]
pub struct ToolOutput {
pub content: String,
pub metadata: Option<Value>,
}
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn execute(&self, args: Value) -> Result<ToolOutput>;
fn parameters(&self) -> Value;
fn clone_box(&self) -> Box<dyn Tool>;
}
impl Clone for Box<dyn Tool> {
fn clone(&self) -> Box<dyn Tool> {
self.clone_box()
}
}
#[derive(Clone, Default)]
pub struct ToolRegistry {
tools: Arc<HashMap<String, Box<dyn Tool>>>,
}
impl ToolRegistry {
pub fn new() -> Self {
Self {
tools: Arc::new(HashMap::new()),
}
}
pub fn register(&mut self, tool: Box<dyn Tool>) {
let mut tools = (*self.tools).clone();
tools.insert(tool.name().to_string(), tool);
self.tools = Arc::new(tools);
}
pub fn get(&self, name: &str) -> Option<&Box<dyn Tool>> {
self.tools.get(name)
}
pub fn list_tools(&self) -> Vec<&Box<dyn Tool>> {
self.tools.values().collect()
}
}
#[derive(Clone)]
pub struct CalculatorTool;
#[async_trait]
impl Tool for CalculatorTool {
fn name(&self) -> &str {
"calculator"
}
fn description(&self) -> &str {
"Perform basic arithmetic operations"
}
fn parameters(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to evaluate (e.g., '2 + 2')"
}
},
"required": ["expression"]
})
}
async fn execute(&self, args: Value) -> Result<ToolOutput> {
let expression = args["expression"].as_str()
.ok_or_else(|| anyhow::anyhow!("Missing 'expression' argument"))?;
let result = if expression.contains('+') {
let parts: Vec<&str> = expression.split('+').collect();
let a: f64 = parts[0].trim().parse()?;
let b: f64 = parts[1].trim().parse()?;
a + b
} else {
0.0
};
Ok(ToolOutput {
content: result.to_string(),
metadata: None,
})
}
fn clone_box(&self) -> Box<dyn Tool> {
Box::new(self.clone())
}
}