use anyhow::Result;
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;
use crate::core::capability::CapabilityRegistry;
use crate::core::skill::SkillManager;
use crate::core::tool::ToolRegistry;
use crate::domain::capability::CapabilityCallContext;
#[async_trait]
pub trait CapabilityExecutor: Send + Sync {
async fn execute(
&self,
capability_id: &str,
params: Value,
context: &CapabilityCallContext,
) -> Result<Value>;
fn can_execute(&self, capability_id: &str) -> bool;
fn can_execute_with_skills(&self, _capability_id: &str, _skills: &[String]) -> bool {
true }
fn supported_capabilities(&self) -> Vec<String> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct CapabilityResult {
pub success: bool,
pub data: Value,
pub error: Option<String>,
}
impl CapabilityResult {
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 CapabilityExecutorRegistry {
executors: Vec<Box<dyn CapabilityExecutor>>,
skill_manager: Arc<SkillManager>,
}
impl CapabilityExecutorRegistry {
pub fn new(skill_manager: Arc<SkillManager>) -> Self {
Self {
executors: Vec::new(),
skill_manager,
}
}
pub fn with_default_skill_manager(
tool_registry: Arc<ToolRegistry>,
capability_registry: Arc<CapabilityRegistry>,
) -> Self {
let skill_manager = Arc::new(SkillManager::new_with_registries(
tool_registry,
capability_registry,
));
Self::new(skill_manager)
}
pub fn register(&mut self, executor: Box<dyn CapabilityExecutor>) {
self.executors.push(executor);
}
pub fn find_executor(&self, capability_id: &str) -> Option<&dyn CapabilityExecutor> {
self.executors
.iter()
.find(|e| e.can_execute(capability_id))
.map(|e| e.as_ref())
}
fn find_executor_with_skills(
&self,
capability_id: &str,
skills: &[String],
) -> Option<&dyn CapabilityExecutor> {
self.executors
.iter()
.find(|e| {
e.can_execute(capability_id) && e.can_execute_with_skills(capability_id, skills)
})
.map(|e| e.as_ref())
}
pub async fn execute(
&self,
capability_id: &str,
params: Value,
context: &CapabilityCallContext,
) -> Result<CapabilityResult> {
match self.find_executor(capability_id) {
Some(executor) => {
let result = executor.execute(capability_id, params, context).await?;
Ok(CapabilityResult::success(result))
}
None => Ok(CapabilityResult::error(format!(
"No executor found for capability: {}",
capability_id
))),
}
}
pub async fn execute_with_skills(
&self,
capability_id: &str,
params: Value,
context: &CapabilityCallContext,
caller_skills: &[String],
) -> Result<CapabilityResult> {
if !self
.skill_manager
.can_call_capability(capability_id, caller_skills)
{
return Ok(CapabilityResult::error(format!(
"Insufficient skills to execute capability: {}",
capability_id
)));
}
match self.find_executor_with_skills(capability_id, caller_skills) {
Some(executor) => {
let result = executor.execute(capability_id, params, context).await?;
Ok(CapabilityResult::success(result))
}
None => Ok(CapabilityResult::error(format!(
"No executor found for capability: {}",
capability_id
))),
}
}
pub fn can_execute(&self, capability_id: &str) -> bool {
self.find_executor(capability_id).is_some()
}
pub fn can_execute_with_skills(&self, capability_id: &str, skills: &[String]) -> bool {
self.find_executor_with_skills(capability_id, skills)
.is_some()
&& self
.skill_manager
.can_call_capability(capability_id, skills)
}
pub fn list_supported_capabilities(&self) -> Vec<String> {
self.executors
.iter()
.flat_map(|e| e.supported_capabilities())
.collect()
}
}
type CapabilityHandlerFn = dyn Fn(Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Value>> + Send>>
+ Send
+ Sync;
pub struct FnCapabilityExecutor {
capability_id: String,
handler: Box<CapabilityHandlerFn>,
}
impl FnCapabilityExecutor {
pub fn new<F, Fut>(capability_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 {
capability_id: capability_id.into(),
handler: Box::new(move |v| Box::pin(handler(v))),
}
}
}
#[async_trait]
impl CapabilityExecutor for FnCapabilityExecutor {
async fn execute(
&self,
capability_id: &str,
params: Value,
_context: &CapabilityCallContext,
) -> Result<Value> {
if capability_id != self.capability_id {
return Err(anyhow::anyhow!(
"Capability ID mismatch: {} != {}",
capability_id,
self.capability_id
));
}
(self.handler)(params).await
}
fn can_execute(&self, capability_id: &str) -> bool {
capability_id == self.capability_id
}
fn supported_capabilities(&self) -> Vec<String> {
vec![self.capability_id.clone()]
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct CapabilityContext {
pub caller_id: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub metadata: std::collections::HashMap<String, String>,
}
impl CapabilityContext {
#[allow(dead_code)]
pub fn new(caller_id: impl Into<String>) -> Self {
Self {
caller_id: caller_id.into(),
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
#[allow(dead_code)]
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
}