Skip to main content

lc_langgraph/compiled/
types.rs

1// crates/lc-langgraph/src/compiled/types.rs
2//! Public types for graph execution results and streaming events
3
4use crate::edge::GraphEdge;
5use crate::node::GraphNode;
6use crate::state::{StateSchema, StateUpdate};
7use async_trait::async_trait;
8use serde_json::Value as JsonValue;
9use std::collections::HashMap;
10use std::sync::Arc;
11
12/// GraphInvocation - Result of graph execution
13#[derive(Debug)]
14pub struct GraphInvocation<S: StateSchema> {
15    /// The final state after execution.
16    pub final_state: S,
17    /// Execution steps recorded during the run.
18    pub steps: Vec<ExecutionStep>,
19    /// Number of recursive steps consumed during execution.
20    pub recursion_count: usize,
21}
22
23impl<S: StateSchema> GraphInvocation<S> {
24    /// Return the final state.
25    pub fn state(&self) -> &S {
26        &self.final_state
27    }
28
29    /// Return the recorded execution steps.
30    pub fn steps(&self) -> &[ExecutionStep] {
31        &self.steps
32    }
33}
34
35/// ExecutionStep - Single step in execution history
36#[derive(Debug, Clone)]
37pub enum ExecutionStep {
38    /// A regular node execution step.
39    Node {
40        /// Name of the executed node.
41        name: String,
42        /// Metadata returned by the node execution.
43        metadata: HashMap<String, JsonValue>,
44    },
45    /// A checkpoint was saved during execution.
46    Checkpoint {
47        /// The saved checkpoint id.
48        id: String,
49        /// The next node scheduled to run after the checkpoint.
50        next_node: String,
51    },
52    /// A parallel branch execution step.
53    ParallelNode {
54        /// Name of the parallel branch.
55        branch: String,
56        /// Metadata returned by the branch execution.
57        metadata: HashMap<String, JsonValue>,
58    },
59}
60
61impl ExecutionStep {
62    /// Construct a `Node` execution step.
63    pub fn node(name: impl Into<String>, metadata: HashMap<String, JsonValue>) -> Self {
64        Self::Node {
65            name: name.into(),
66            metadata,
67        }
68    }
69
70    /// Construct a `Checkpoint` execution step.
71    pub fn checkpoint(id: impl Into<String>, next_node: impl Into<String>) -> Self {
72        Self::Checkpoint {
73            id: id.into(),
74            next_node: next_node.into(),
75        }
76    }
77
78    /// Construct a `ParallelNode` execution step.
79    pub fn parallel_node(branch: impl Into<String>, metadata: HashMap<String, JsonValue>) -> Self {
80        Self::ParallelNode {
81            branch: branch.into(),
82            metadata,
83        }
84    }
85}
86
87/// ParallelBranch - Result of a parallel execution branch
88#[derive(Debug, Clone)]
89pub struct ParallelBranch<S: StateSchema> {
90    /// Name of the parallel branch.
91    pub name: String,
92    /// Final state produced by the branch.
93    pub final_state: S,
94    /// Execution steps recorded in the branch.
95    pub steps: Vec<ExecutionStep>,
96}
97
98/// ParallelInvocation - Result of graph execution with parallel branches
99#[derive(Debug)]
100pub struct ParallelInvocation<S: StateSchema> {
101    /// The final merged state after execution.
102    pub final_state: S,
103    /// Execution steps recorded during the run.
104    pub steps: Vec<ExecutionStep>,
105    /// Number of recursive steps consumed during execution.
106    pub recursion_count: usize,
107    /// Captured results of each parallel branch.
108    pub parallel_branches: Vec<ParallelBranch<S>>,
109}
110
111impl<S: StateSchema> ParallelInvocation<S> {
112    /// Return the final merged state.
113    pub fn state(&self) -> &S {
114        &self.final_state
115    }
116
117    /// Return the captured parallel branch results.
118    pub fn branches(&self) -> &[ParallelBranch<S>] {
119        &self.parallel_branches
120    }
121}
122
123/// StreamEvent - Event for streaming execution
124#[derive(Debug, Clone)]
125pub enum StreamEvent<S: StateSchema> {
126    /// Execution started with the initial state.
127    Start(S),
128    /// A node was entered with the current state.
129    EnterNode(String, S),
130    /// A node completed with its state update.
131    NodeComplete(String, StateUpdate<S>),
132    /// The state was updated after applying a reducer.
133    StateUpdate(S),
134    /// Execution finished with the final state.
135    End(S),
136}
137
138impl<S: StateSchema> StreamEvent<S> {
139    /// Construct a `Start` stream event.
140    pub fn start(state: S) -> Self {
141        Self::Start(state)
142    }
143
144    /// Construct an `EnterNode` stream event.
145    pub fn enter_node(name: impl Into<String>, state: S) -> Self {
146        Self::EnterNode(name.into(), state)
147    }
148
149    /// Construct a `NodeComplete` stream event.
150    pub fn node_complete(name: impl Into<String>, update: StateUpdate<S>) -> Self {
151        Self::NodeComplete(name.into(), update)
152    }
153
154    /// Construct a `StateUpdate` stream event.
155    pub fn state_update(state: S) -> Self {
156        Self::StateUpdate(state)
157    }
158
159    /// Construct an `End` stream event.
160    pub fn end(state: S) -> Self {
161        Self::End(state)
162    }
163}
164
165/// GraphExecution - State for interrupted execution that can be resumed
166#[derive(Debug, Clone)]
167pub struct GraphExecution<S: StateSchema> {
168    /// The state at the point of interruption.
169    pub state: S,
170    /// The node where execution was interrupted.
171    pub current_node: String,
172    /// Execution steps recorded so far.
173    pub steps: Vec<ExecutionStep>,
174    /// Recursion budget consumed up to the interruption.
175    pub recursion_count: usize,
176    /// Where execution was interrupted ("node" or "after_node").
177    pub interrupted_at: String,
178}
179
180impl<S: StateSchema> GraphExecution<S> {
181    /// Create a new execution context for resuming from an interruption.
182    pub fn new(
183        state: S,
184        current_node: impl Into<String>,
185        interrupted_at: impl Into<String>,
186    ) -> Self {
187        Self {
188            state,
189            current_node: current_node.into(),
190            steps: Vec::new(),
191            recursion_count: 0,
192            interrupted_at: interrupted_at.into(),
193        }
194    }
195
196    /// Return the current state.
197    pub fn state(&self) -> &S {
198        &self.state
199    }
200
201    /// Return the interruption point.
202    pub fn interrupted_at(&self) -> &str {
203        &self.interrupted_at
204    }
205}
206
207// ── Dynamic extension types ──
208
209/// A task submitted externally for dynamic planning mid-execution.
210#[derive(Debug, Clone)]
211pub struct DynamicTask {
212    /// Unique id of the task.
213    pub id: String,
214    /// Description of the task to plan for.
215    pub description: String,
216}
217
218/// Result of dynamic planning: nodes and edges to inject into a running graph.
219pub struct DynamicInjection<S: StateSchema> {
220    /// Nodes to inject into the runtime, keyed by name.
221    pub nodes: Vec<(String, Arc<dyn GraphNode<S>>)>,
222    /// Edges to inject into the runtime.
223    pub edges: Vec<GraphEdge>,
224}
225
226/// Planner trait for converting external tasks into graph nodes/edges at runtime.
227#[async_trait]
228pub trait DynamicPlanner<S: StateSchema>: Send + Sync {
229    /// Given pending tasks and current state, produce nodes/edges to inject.
230    async fn plan(
231        &self,
232        tasks: &[DynamicTask],
233        current_state: &S,
234    ) -> Result<DynamicInjection<S>, String>;
235}