Skip to main content

lellm_graph/compiler/
inline_pass.rs

1//! InlinePass — Subgraph 内联优化 pass。
2
3use super::context::CompilerContext;
4use super::pass::CompilerPass;
5use crate::Graph;
6use crate::MergeStrategy;
7use crate::node::NodeKind;
8use crate::state::workflow_state::WorkflowState;
9
10/// Subgraph 内联优化 pass。
11///
12/// 自动识别 SubgraphNode,评估是否值得内联,
13/// 如果值得则展开 Subgraph,合并到外层 Graph。
14///
15/// # 设计理念
16///
17/// - 像 LLVM 的 function inlining
18/// - 用户不应该手动调用 `builder.merge()`
19/// - 应该由 `compile()` 自动决定
20///
21/// # 评估标准
22///
23/// 1. 图大小 < 阈值
24/// 2. 没有外部依赖
25/// 3. StateLens 是纯投影
26///
27/// # 当前状态
28///
29/// ⚠️ **骨架实现** — 目前仅识别 Subgraph 节点并收集统计信息,
30/// 不执行实际的内联展开。`run()` 始终返回 `false`。
31/// 完整的内联逻辑需要处理:
32/// - StateLens 的类型擦除与节点重映射
33/// - 边重定向(外层 → 内层入口 / 内层出口 → 外层)
34/// - NodeId 命名空间隔离
35pub struct InlinePass;
36
37impl InlinePass {
38    /// 创建新的 InlinePass。
39    pub fn new() -> Self {
40        Self
41    }
42}
43
44impl Default for InlinePass {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50impl<S: WorkflowState, M: MergeStrategy<S>> CompilerPass<S, M> for InlinePass {
51    fn name(&self) -> &str {
52        "inline"
53    }
54
55    fn run(&self, graph: &mut Graph<S, M>, ctx: &mut CompilerContext<S>) -> bool {
56        // 1. 统计节点数
57        ctx.stats.total_nodes_before = graph.nodes.len();
58
59        // 2. 识别所有 Subgraph 节点
60        let subgraph_nodes: Vec<String> = graph
61            .nodes
62            .iter()
63            .filter(|(_, kind)| matches!(kind, NodeKind::Subgraph(_)))
64            .map(|(name, _)| name.clone())
65            .collect();
66
67        ctx.stats.subgraph_count = subgraph_nodes.len();
68
69        if ctx.debug {
70            tracing::debug!(
71                subgraph_count = subgraph_nodes.len(),
72                "InlinePass: found subgraph nodes"
73            );
74        }
75
76        // 3. 对每个 Subgraph 评估是否值得内联
77        //
78        // ⚠️ TODO: 实现内联逻辑。当前为骨架,始终跳过。
79        //    需要实现:
80        //    a) 评估标准:图大小、外部依赖、Lens 类型
81        //    b) 展开 Subgraph 节点到外层 Graph
82        //    c) 重映射 NodeId 和边
83        //    d) 更新 ctx.stats.inlined_count
84        let mut modified = false;
85        for node_name in subgraph_nodes {
86            if let Some(NodeKind::Subgraph(_subgraph)) = graph.nodes.get(&node_name) {
87                // 暂时跳过,不内联
88                ctx.stats.not_inlined_count += 1;
89                if ctx.debug {
90                    tracing::debug!(
91                        node = %node_name,
92                        "InlinePass: skipping subgraph (inlining not yet implemented)"
93                    );
94                }
95            }
96            // 内联实现后,此处应修改 modified = true;
97            let _ = &mut modified;
98        }
99
100        // 4. 更新统计信息
101        ctx.stats.total_nodes_after = graph.nodes.len();
102
103        modified
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::{GraphBuilder, NodeKind, State, StateMerge, TaskNode};
111
112    #[test]
113    fn test_inline_pass_no_subgraphs() {
114        let mut builder = GraphBuilder::<State, StateMerge>::new("test");
115        builder.start("a");
116        builder.node("a", NodeKind::Task(TaskNode::new("a", |_| Ok(()))));
117        builder.end("a");
118        let mut graph = builder.build().unwrap();
119
120        let mut ctx = CompilerContext::new();
121        let pass = InlinePass::new();
122
123        let modified = pass.run(&mut graph, &mut ctx);
124
125        assert!(!modified);
126        assert_eq!(ctx.stats.subgraph_count, 0);
127    }
128}