use anyhow::Result;
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;
use crate::core::skill::SkillManager;
use crate::core::tool::ToolRegistry;
use crate::domain::tool::ToolCallContext;
pub mod framework_tools;
pub use framework_tools::{FrameworkToolExecutor, ToolEnvironment};
#[async_trait]
pub trait ToolExecutor: Send + Sync {
async fn execute(
&self,
tool_id: &str,
params: Value,
context: &ToolCallContext,
) -> Result<Value>;
fn can_execute(&self, tool_id: &str) -> bool;
fn can_execute_with_skills(&self, _tool_id: &str, _skills: &[String]) -> bool {
true }
fn supported_tools(&self) -> Vec<String> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct ToolResult {
pub success: bool,
pub data: Value,
pub error: Option<String>,
}
impl ToolResult {
pub fn success(data: Value) -> Self {
Self {
success: true,
data,
error: None,
}
}
pub fn error(msg: impl Into<String>) -> Self {
Self {
success: false,
data: Value::Null,
error: Some(msg.into()),
}
}
}
pub struct ToolExecutorRegistry {
executors: Vec<Box<dyn ToolExecutor>>,
skill_manager: Arc<SkillManager>,
}
impl ToolExecutorRegistry {
pub fn new(skill_manager: Arc<SkillManager>) -> Self {
Self {
executors: Vec::new(),
skill_manager,
}
}
pub fn with_default_skill_manager(tool_registry: Arc<ToolRegistry>) -> Self {
let skill_manager = Arc::new(SkillManager::new_with_tool_registry(tool_registry));
Self::new(skill_manager)
}
pub fn register(&mut self, executor: Box<dyn ToolExecutor>) {
self.executors.push(executor);
}
pub fn find_executor(&self, tool_id: &str) -> Option<&dyn ToolExecutor> {
self.executors
.iter()
.find(|e| e.can_execute(tool_id))
.map(|e| e.as_ref())
}
fn find_executor_with_skills(
&self,
tool_id: &str,
skills: &[String],
) -> Option<&dyn ToolExecutor> {
self.executors
.iter()
.find(|e| e.can_execute(tool_id) && e.can_execute_with_skills(tool_id, skills))
.map(|e| e.as_ref())
}
pub async fn execute(
&self,
tool_id: &str,
params: Value,
context: &ToolCallContext,
) -> Result<ToolResult> {
match self.find_executor(tool_id) {
Some(executor) => {
let result = executor.execute(tool_id, params, context).await?;
Ok(ToolResult::success(result))
}
None => Ok(ToolResult::error(format!(
"No executor found for tool: {}",
tool_id
))),
}
}
pub async fn execute_with_skills(
&self,
tool_id: &str,
params: Value,
context: &ToolCallContext,
caller_skills: &[String],
) -> Result<ToolResult> {
if !self.skill_manager.can_call_tool(tool_id, caller_skills) {
return Ok(ToolResult::error(format!(
"Insufficient skills to execute tool: {}",
tool_id
)));
}
match self.find_executor_with_skills(tool_id, caller_skills) {
Some(executor) => {
let result = executor.execute(tool_id, params, context).await?;
Ok(ToolResult::success(result))
}
None => Ok(ToolResult::error(format!(
"No executor found for tool: {}",
tool_id
))),
}
}
pub fn can_execute(&self, tool_id: &str) -> bool {
self.find_executor(tool_id).is_some()
}
pub fn can_execute_with_skills(&self, tool_id: &str, skills: &[String]) -> bool {
self.find_executor_with_skills(tool_id, skills).is_some()
&& self.skill_manager.can_call_tool(tool_id, skills)
}
pub fn list_supported_tools(&self) -> Vec<String> {
self.executors
.iter()
.flat_map(|e| e.supported_tools())
.collect()
}
}
type ToolHandlerFn = dyn Fn(Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Value>> + Send>>
+ Send
+ Sync;
pub struct FnToolExecutor {
tool_id: String,
handler: Box<ToolHandlerFn>,
}
impl FnToolExecutor {
pub fn new<F, Fut>(tool_id: impl Into<String>, handler: F) -> Self
where
F: Fn(Value) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Value>> + Send + 'static,
{
Self {
tool_id: tool_id.into(),
handler: Box::new(move |v| Box::pin(handler(v))),
}
}
}
#[async_trait]
impl ToolExecutor for FnToolExecutor {
async fn execute(
&self,
tool_id: &str,
params: Value,
_context: &ToolCallContext,
) -> Result<Value> {
if tool_id != self.tool_id {
return Err(anyhow::anyhow!(
"Tool ID mismatch: {} != {}",
tool_id,
self.tool_id
));
}
(self.handler)(params).await
}
fn can_execute(&self, tool_id: &str) -> bool {
tool_id == self.tool_id
}
fn supported_tools(&self) -> Vec<String> {
vec![self.tool_id.clone()]
}
}
#[derive(Debug, Clone)]
pub struct ToolContext {
pub caller_id: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub metadata: std::collections::HashMap<String, String>,
}
impl ToolContext {
pub fn new(caller_id: impl Into<String>) -> Self {
Self {
caller_id: caller_id.into(),
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
}