feat: 新增技能编排引擎和工作流构建器组件
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor: 统一Hands系统常量到单个源文件 refactor: 更新Hands中文名称和描述 fix: 修复技能市场在连接状态变化时重新加载 fix: 修复身份变更提案的错误处理逻辑 docs: 更新多个功能文档的验证状态和实现位置 docs: 更新Hands系统文档 test: 添加测试文件验证工作区路径
This commit is contained in:
@@ -9,6 +9,7 @@ mod export;
|
||||
mod http;
|
||||
mod skill;
|
||||
mod hand;
|
||||
mod orchestration;
|
||||
|
||||
pub use llm::*;
|
||||
pub use parallel::*;
|
||||
@@ -17,6 +18,7 @@ pub use export::*;
|
||||
pub use http::*;
|
||||
pub use skill::*;
|
||||
pub use hand::*;
|
||||
pub use orchestration::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -57,6 +59,9 @@ pub enum ActionError {
|
||||
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
|
||||
#[error("Orchestration error: {0}")]
|
||||
Orchestration(String),
|
||||
}
|
||||
|
||||
/// Action registry - holds references to all action executors
|
||||
@@ -70,6 +75,9 @@ pub struct ActionRegistry {
|
||||
/// Hand registry (injected from kernel)
|
||||
hand_registry: Option<Arc<dyn HandActionDriver>>,
|
||||
|
||||
/// Orchestration driver (injected from kernel)
|
||||
orchestration_driver: Option<Arc<dyn OrchestrationActionDriver>>,
|
||||
|
||||
/// Template directory
|
||||
template_dir: Option<std::path::PathBuf>,
|
||||
}
|
||||
@@ -81,6 +89,7 @@ impl ActionRegistry {
|
||||
llm_driver: None,
|
||||
skill_registry: None,
|
||||
hand_registry: None,
|
||||
orchestration_driver: None,
|
||||
template_dir: None,
|
||||
}
|
||||
}
|
||||
@@ -103,6 +112,12 @@ impl ActionRegistry {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set orchestration driver
|
||||
pub fn with_orchestration_driver(mut self, driver: Arc<dyn OrchestrationActionDriver>) -> Self {
|
||||
self.orchestration_driver = Some(driver);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set template directory
|
||||
pub fn with_template_dir(mut self, dir: std::path::PathBuf) -> Self {
|
||||
self.template_dir = Some(dir);
|
||||
@@ -166,6 +181,22 @@ impl ActionRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a skill orchestration
|
||||
pub async fn execute_orchestration(
|
||||
&self,
|
||||
graph_id: Option<&str>,
|
||||
graph: Option<&Value>,
|
||||
input: HashMap<String, Value>,
|
||||
) -> Result<Value, ActionError> {
|
||||
if let Some(driver) = &self.orchestration_driver {
|
||||
driver.execute(graph_id, graph, input)
|
||||
.await
|
||||
.map_err(ActionError::Orchestration)
|
||||
} else {
|
||||
Err(ActionError::Orchestration("Orchestration driver not configured".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Render classroom
|
||||
pub async fn render_classroom(&self, data: &Value) -> Result<Value, ActionError> {
|
||||
// This will integrate with the classroom renderer
|
||||
@@ -377,3 +408,14 @@ pub trait HandActionDriver: Send + Sync {
|
||||
params: HashMap<String, Value>,
|
||||
) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
/// Orchestration action driver trait
|
||||
#[async_trait]
|
||||
pub trait OrchestrationActionDriver: Send + Sync {
|
||||
async fn execute(
|
||||
&self,
|
||||
graph_id: Option<&str>,
|
||||
graph: Option<&Value>,
|
||||
input: HashMap<String, Value>,
|
||||
) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
61
crates/zclaw-pipeline/src/actions/orchestration.rs
Normal file
61
crates/zclaw-pipeline/src/actions/orchestration.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
//! Skill orchestration action
|
||||
//!
|
||||
//! Executes skill graphs (DAGs) with data passing and parallel execution.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use serde_json::Value;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::OrchestrationActionDriver;
|
||||
|
||||
/// Orchestration driver that uses the skill orchestration engine
|
||||
pub struct SkillOrchestrationDriver {
|
||||
/// Skill registry for executing skills
|
||||
skill_registry: Arc<zclaw_skills::SkillRegistry>,
|
||||
}
|
||||
|
||||
impl SkillOrchestrationDriver {
|
||||
/// Create a new orchestration driver
|
||||
pub fn new(skill_registry: Arc<zclaw_skills::SkillRegistry>) -> Self {
|
||||
Self { skill_registry }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl OrchestrationActionDriver for SkillOrchestrationDriver {
|
||||
async fn execute(
|
||||
&self,
|
||||
graph_id: Option<&str>,
|
||||
graph: Option<&Value>,
|
||||
input: HashMap<String, Value>,
|
||||
) -> Result<Value, String> {
|
||||
use zclaw_skills::orchestration::{SkillGraph, DefaultExecutor, SkillGraphExecutor};
|
||||
|
||||
// Load or parse the graph
|
||||
let skill_graph = if let Some(graph_value) = graph {
|
||||
// Parse inline graph definition
|
||||
serde_json::from_value::<SkillGraph>(graph_value.clone())
|
||||
.map_err(|e| format!("Failed to parse graph: {}", e))?
|
||||
} else if let Some(id) = graph_id {
|
||||
// Load graph from registry (TODO: implement graph storage)
|
||||
return Err(format!("Graph loading by ID not yet implemented: {}", id));
|
||||
} else {
|
||||
return Err("Either graph_id or graph must be provided".to_string());
|
||||
};
|
||||
|
||||
// Create executor
|
||||
let executor = DefaultExecutor::new(self.skill_registry.clone());
|
||||
|
||||
// Create skill context with default values
|
||||
let context = zclaw_skills::SkillContext::default();
|
||||
|
||||
// Execute the graph
|
||||
let result = executor.execute(&skill_graph, input, &context)
|
||||
.await
|
||||
.map_err(|e| format!("Orchestration execution failed: {}", e))?;
|
||||
|
||||
// Return the output
|
||||
Ok(result.output)
|
||||
}
|
||||
}
|
||||
@@ -281,6 +281,16 @@ impl PipelineExecutor {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(*ms)).await;
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
Action::SkillOrchestration { graph_id, graph, input } => {
|
||||
let resolved_input = context.resolve_map(input)?;
|
||||
self.action_registry.execute_orchestration(
|
||||
graph_id.as_deref(),
|
||||
graph.as_ref(),
|
||||
resolved_input,
|
||||
).await
|
||||
.map_err(|e| ExecuteError::Action(e.to_string()))
|
||||
}
|
||||
}
|
||||
}.boxed()
|
||||
}
|
||||
|
||||
@@ -326,6 +326,19 @@ pub enum Action {
|
||||
/// Duration in milliseconds
|
||||
ms: u64,
|
||||
},
|
||||
|
||||
/// Skill orchestration - execute multiple skills in a DAG
|
||||
SkillOrchestration {
|
||||
/// Graph ID (reference to a pre-defined graph) or inline definition
|
||||
graph_id: Option<String>,
|
||||
|
||||
/// Inline graph definition (alternative to graph_id)
|
||||
graph: Option<serde_json::Value>,
|
||||
|
||||
/// Input variables
|
||||
#[serde(default)]
|
||||
input: HashMap<String, String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_http_method() -> String {
|
||||
|
||||
Reference in New Issue
Block a user