Skip to main content

matrixcode_core/workflow/
mod.rs

1//! Workflow Module
2//!
3//! 工作流引擎核心框架,提供基于 YAML 定义的 DAG 工作流管理。
4//!
5//! # 模块结构
6//!
7//! - `def`: 工作流定义结构(WorkflowDef, NodeDef, EdgeDef 等)
8//! - `parser`: YAML 解析器
9//! - `context`: 运行时状态管理
10//! - `template`: 模板渲染引擎({{var}} 替换)
11//! - `rule_engine`: 验证规则引擎
12//! - `engine`: 状态机引擎
13//! - `executors`: 节点执行器(AI、工具、条件、验证)
14//!
15//! # 快速开始
16//!
17//! ```rust,ignore
18//! use matrixcode::workflow::{parse_workflow, WorkflowEngine, WorkflowContext};
19//!
20//! // 解析工作流定义
21//! let yaml = r#"
22//! id: hello-workflow
23//! name: Hello Workflow
24//! nodes:
25//!   - id: start
26//!     type: start
27//!     name: Start
28//!   - id: greet
29//!     type: task
30//!     name: Greet
31//!     task: greet
32//!   - id: end
33//!     type: end
34//!     name: End
35//! edges:
36//!   - from: start
37//!     to: greet
38//!   - from: greet
39//!     to: end
40//! "#;
41//!
42//! let workflow = parse_workflow(yaml)?;
43//!
44//! // 创建引擎并运行
45//! let engine = WorkflowEngine::new(workflow)?;
46//! let context = engine.run(HashMap::new()).await?;
47//!
48//! println!("Workflow status: {:?}", context.status);
49//! ```
50
51pub mod def;
52pub mod parser;
53pub mod context;
54pub mod template;
55pub mod rule_engine;
56pub mod engine;
57pub mod executors;
58pub mod persistence;
59pub mod registry;
60
61// 重导出公共接口
62pub use def::{
63    WorkflowDef,
64    NodeDef,
65    EdgeDef,
66    NodeType,
67    FailureStrategy,
68    InputDef,
69    OutputDef,
70    BranchDef,
71    ParallelBranchDef,
72};
73
74pub use parser::{
75    parse_workflow,
76    parse_workflow_from_file,
77    to_yaml,
78};
79
80pub use context::{
81    WorkflowContext,
82    WorkflowStatus,
83    NodeStatus,
84    NodeExecution,
85};
86
87pub use template::{
88    TemplateRenderer,
89    render as render_template,
90};
91
92pub use rule_engine::{
93    Rule,
94    RuleEngine,
95    ValidationResult,
96    evaluate_expression,
97};
98
99pub use engine::{
100    WorkflowEngine,
101    WorkflowEvent,
102    EventListener,
103    TaskExecutor,
104    DefaultTaskExecutor,
105};
106
107pub use persistence::{
108    WorkflowPersistence,
109};
110
111pub use registry::{
112    WorkflowRegistry,
113    WorkflowInfo,
114    WorkflowSource,
115};
116
117pub use executors::{
118    NodeExecutor,
119    AiExecutor,
120    AiExecutorConfig,
121    ToolExecutor,
122    ToolExecutorConfig,
123    ProxyExecutor,
124    ConditionExecutor,
125    ValidateExecutor,
126    ValidateExecutorConfig,
127    CompositeExecutor,
128    CompositeMode,
129    ExecutorFactory,
130};
131
132#[cfg(test)]
133mod integration_tests {
134    use super::*;
135    use std::collections::HashMap;
136
137    #[test]
138    fn test_parse_and_validate_workflow() {
139        let yaml = r#"
140id: integration-test
141name: Integration Test Workflow
142version: "1.0.0"
143description: A test workflow for integration testing
144inputs:
145  - name: user_name
146    type: string
147    required: true
148  - name: count
149    type: number
150    required: false
151    default: 1
152outputs:
153  - name: result
154    value: "{{output}}"
155variables:
156  greeting: "Hello"
157nodes:
158  - id: start
159    type: start
160    name: Start
161  - id: process
162    type: task
163    name: Process
164    task: process_task
165    params:
166      name: "{{user_name}}"
167      count: "{{count}}"
168    on_failure:
169      type: retry
170      max_attempts: 3
171      interval_ms: 1000
172    timeout_ms: 30000
173  - id: end
174    type: end
175    name: End
176edges:
177  - from: start
178    to: process
179  - from: process
180    to: end
181"#;
182        let workflow = parse_workflow(yaml).unwrap();
183        assert_eq!(workflow.id, "integration-test");
184        assert_eq!(workflow.nodes.len(), 3);
185        assert_eq!(workflow.edges.len(), 2);
186        assert!(workflow.validate().is_ok());
187    }
188
189    #[tokio::test]
190    async fn test_workflow_execution() {
191        let workflow_def = WorkflowDef {
192            id: "test-exec".to_string(),
193            name: "Test Execution".to_string(),
194            version: "1.0.0".to_string(),
195            description: None,
196            inputs: vec![],
197            outputs: vec![],
198            nodes: vec![
199                NodeDef {
200                    id: "start".to_string(),
201                    node_type: NodeType::Start,
202                    name: "Start".to_string(),
203                    description: None,
204                    task: None,
205                    params: HashMap::new(),
206                    on_failure: FailureStrategy::Abort,
207                    timeout_ms: None,
208                    branches: None,
209                    parallel_branches: None,
210                    workflow: None,
211                    wait_ms: None,
212                    approvers: None,
213                },
214                NodeDef {
215                    id: "end".to_string(),
216                    node_type: NodeType::End,
217                    name: "End".to_string(),
218                    description: None,
219                    task: None,
220                    params: HashMap::new(),
221                    on_failure: FailureStrategy::Abort,
222                    timeout_ms: None,
223                    branches: None,
224                    parallel_branches: None,
225                    workflow: None,
226                    wait_ms: None,
227                    approvers: None,
228                },
229            ],
230            edges: vec![EdgeDef {
231                id: "e1".to_string(),
232                from: "start".to_string(),
233                to: "end".to_string(),
234                condition: None,
235                label: None,
236            }],
237            variables: HashMap::new(),
238            default_failure_strategy: FailureStrategy::Abort,
239            timeout_ms: None,
240        };
241
242        let engine = WorkflowEngine::new(workflow_def).unwrap();
243        let context = engine.run(HashMap::new()).await.unwrap();
244
245        assert_eq!(context.status, WorkflowStatus::Completed);
246    }
247
248    #[test]
249    fn test_template_rendering() {
250        let mut vars = HashMap::new();
251        vars.insert("name".to_string(), serde_json::json!("World"));
252        vars.insert("count".to_string(), serde_json::json!(42));
253
254        let result = render_template("Hello, {{name}}! Count: {{count}}", &vars).unwrap();
255        assert_eq!(result, "Hello, World! Count: 42");
256    }
257
258    #[test]
259    fn test_rule_validation() {
260        let mut engine = RuleEngine::new();
261        let mut context = HashMap::new();
262        context.insert("status".to_string(), serde_json::json!("success"));
263        context.insert("count".to_string(), serde_json::json!(10));
264
265        let rule = Rule::All {
266            rules: vec![
267                Rule::Equals {
268                    field: "status".to_string(),
269                    value: serde_json::json!("success"),
270                },
271                Rule::GreaterThan {
272                    field: "count".to_string(),
273                    value: 5.0,
274                },
275            ],
276        };
277
278        let result = engine.validate(&rule, &context).unwrap();
279        assert!(result.passed);
280    }
281}