Files
DevFlow/crates/df-workflow/src/dag.rs
绝尘 98393b4908 新增: 初始化 DevFlow 项目仓库
Tauri 2 + Vue 3 + Vite 6 桌面应用,Rust workspace 含 13 个 crate
(df-ai / df-storage / df-workflow / df-core / df-execute 等)。
核心能力:AI 聊天 agentic 循环(工具调用+人工审批)、工作流引擎、
任务/想法/项目/阶段管理、可追溯性,及配套前端组件。
2026-06-12 01:31:05 +08:00

142 lines
3.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! DAG有向无环图定义与拓扑排序
use std::collections::{HashMap, HashSet, VecDeque};
use df_core::error::Result;
use df_core::types::NodeId;
use crate::node::Node;
/// 图的边:从 source 到 target
#[derive(Debug, Clone)]
pub struct Edge {
pub source: NodeId,
pub target: NodeId,
/// 边的条件表达式(可选)
pub condition: Option<String>,
}
/// DAG 结构
pub struct Dag {
/// 节点集合
pub nodes: HashMap<NodeId, Box<dyn Node>>,
/// 边集合
pub edges: Vec<Edge>,
}
impl Dag {
/// 创建空 DAG
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
edges: Vec::new(),
}
}
/// 添加节点
pub fn add_node(&mut self, id: NodeId, node: Box<dyn Node>) {
self.nodes.insert(id, node);
}
/// 添加边
pub fn add_edge(&mut self, source: NodeId, target: NodeId) {
self.edges.push(Edge {
source,
target,
condition: None,
});
}
/// 添加带条件的边
pub fn add_edge_with_condition(
&mut self,
source: NodeId,
target: NodeId,
condition: String,
) {
self.edges.push(Edge {
source,
target,
condition: Some(condition),
});
}
/// 获取指定节点的所有上游节点 ID
pub fn predecessors(&self, node_id: &NodeId) -> Vec<NodeId> {
self.edges
.iter()
.filter(|e| &e.target == node_id)
.map(|e| e.source.clone())
.collect()
}
/// 获取指定节点的所有下游节点 ID
pub fn successors(&self, node_id: &NodeId) -> Vec<NodeId> {
self.edges
.iter()
.filter(|e| &e.source == node_id)
.map(|e| e.target.clone())
.collect()
}
/// 拓扑排序 — 返回按执行顺序排列的节点 ID 层级
///
/// 返回 Vec<Vec<NodeId>>,每层内的节点可以并行执行
pub fn topological_layers(&self) -> Result<Vec<Vec<NodeId>>> {
let node_ids: HashSet<NodeId> = self.nodes.keys().cloned().collect();
let mut in_degree: HashMap<NodeId, usize> = HashMap::new();
// 初始化入度
for id in &node_ids {
in_degree.insert(id.clone(), 0);
}
for edge in &self.edges {
if node_ids.contains(&edge.source) && node_ids.contains(&edge.target) {
*in_degree.entry(edge.target.clone()).or_insert(0) += 1;
}
}
// BFS 分层
let mut layers = Vec::new();
let mut queue: VecDeque<NodeId> = in_degree
.iter()
.filter(|(_, &deg)| deg == 0)
.map(|(id, _)| id.clone())
.collect();
let mut processed = 0usize;
while !queue.is_empty() {
let mut layer: Vec<NodeId> = Vec::new();
let layer_size = queue.len();
for _ in 0..layer_size {
if let Some(id) = queue.pop_front() {
layer.push(id.clone());
for succ in self.successors(&id) {
let deg = in_degree.get_mut(&succ).unwrap();
*deg -= 1;
if *deg == 0 {
queue.push_back(succ);
}
}
processed += 1;
}
}
layers.push(layer);
}
if processed != node_ids.len() {
return Err(df_core::error::Error::Workflow(
"DAG 中存在环,无法进行拓扑排序".to_string(),
));
}
Ok(layers)
}
}
impl Default for Dag {
fn default() -> Self {
Self::new()
}
}