1use crate::compiled::CompiledGraph;
5use crate::edge::{ConditionalEdge, GraphEdge};
6use crate::errors::{GraphError, GraphResult};
7use crate::node::{AsyncFn, AsyncNode, GraphNode, SyncNode};
8use crate::state::{Reducer, ReplaceReducer, StateSchema, StateUpdate};
9use crate::subgraph::SubgraphNode;
10use std::collections::HashMap;
11use std::sync::Arc;
12
13pub const START: &str = "__start__";
15pub const END: &str = "__end__";
17
18pub struct StateGraph<S: StateSchema> {
23 nodes: HashMap<String, Arc<dyn GraphNode<S>>>,
24 edges: Vec<GraphEdge>,
25 entry_point: Option<String>,
26 reducers: HashMap<String, Arc<dyn Reducer<S>>>,
27 default_reducer: Arc<dyn Reducer<S>>,
28 conditional_routers: HashMap<String, Arc<dyn ConditionalEdge<S>>>,
29}
30
31impl<S: StateSchema + 'static> StateGraph<S> {
32 pub fn new() -> Self {
34 Self {
35 nodes: HashMap::new(),
36 edges: Vec::new(),
37 entry_point: None,
38 reducers: HashMap::new(),
39 default_reducer: Arc::new(ReplaceReducer),
40 conditional_routers: HashMap::new(),
41 }
42 }
43
44 pub fn add_node<N: GraphNode<S> + 'static>(&mut self, node: N) -> &mut Self {
46 let name = node.name().to_string();
47 self.nodes.insert(name, Arc::new(node));
48 self
49 }
50
51 pub fn add_node_fn<F>(&mut self, name: impl Into<String>, func: F) -> &mut Self
53 where
54 F: Fn(&S) -> Result<StateUpdate<S>, GraphError> + Send + Sync + 'static,
55 {
56 let node = SyncNode::new(name, func);
57 let node_name = node.name().to_string();
58 self.nodes.insert(node_name, Arc::new(node));
59 self
60 }
61
62 pub fn add_async_node<F>(&mut self, name: impl Into<String>, func: F) -> &mut Self
64 where
65 F: AsyncFn<S> + 'static,
66 {
67 let node = AsyncNode::new(name, func);
68 let node_name = node.name().to_string();
69 self.nodes.insert(node_name, Arc::new(node));
70 self
71 }
72
73 pub fn add_subgraph<SubS: StateSchema + 'static>(
75 &mut self,
76 name: impl Into<String>,
77 subgraph: CompiledGraph<SubS>,
78 input_mapper: impl Fn(&S) -> SubS + Send + Sync + 'static,
79 output_mapper: impl Fn(&SubS, &mut S) + Send + Sync + 'static,
80 ) -> &mut Self {
81 let node = SubgraphNode::new(name, subgraph, input_mapper, output_mapper);
82 let node_name = node.name().to_string();
83 self.nodes.insert(node_name, Arc::new(node));
84 self
85 }
86
87 pub fn add_subgraph_same_state(
89 &mut self,
90 name: impl Into<String>,
91 subgraph: CompiledGraph<S>,
92 ) -> &mut Self {
93 let node: SubgraphNode<S, S> = SubgraphNode::same_state(name, subgraph);
94 let node_name = node.name().to_string();
95 self.nodes.insert(node_name, Arc::new(node));
96 self
97 }
98
99 pub fn add_edge(&mut self, source: impl Into<String>, target: impl Into<String>) -> &mut Self {
101 let edge = GraphEdge::fixed(source, target);
102 self.edges.push(edge);
103 self
104 }
105
106 pub fn add_conditional_edges(
108 &mut self,
109 source: impl Into<String>,
110 router_name: impl Into<String>,
111 targets: HashMap<String, String>,
112 default: Option<String>,
113 ) -> &mut Self {
114 let edge = GraphEdge::conditional(source, router_name, targets, default);
115 self.edges.push(edge);
116 self
117 }
118
119 pub fn add_fan_out(&mut self, source: impl Into<String>, targets: Vec<String>) -> &mut Self {
121 let edge = GraphEdge::fan_out(source, targets);
122 self.edges.push(edge);
123 self
124 }
125
126 pub fn add_fan_in(&mut self, sources: Vec<String>, target: impl Into<String>) -> &mut Self {
128 let edge = GraphEdge::fan_in(sources, target);
129 self.edges.push(edge);
130 self
131 }
132
133 pub fn set_conditional_router<R: ConditionalEdge<S> + 'static>(
135 &mut self,
136 name: impl Into<String>,
137 router: R,
138 ) -> &mut Self {
139 self.conditional_routers
140 .insert(name.into(), Arc::new(router));
141 self
142 }
143
144 pub fn set_entry_point(&mut self, node: impl Into<String>) -> &mut Self {
146 self.entry_point = Some(node.into());
147 self
148 }
149
150 pub fn set_reducer(
152 &mut self,
153 field: impl Into<String>,
154 reducer: Arc<dyn Reducer<S>>,
155 ) -> &mut Self {
156 self.reducers.insert(field.into(), reducer);
157 self
158 }
159
160 pub fn compile(&self) -> GraphResult<CompiledGraph<S>> {
162 if self.nodes.is_empty() {
163 return Err(GraphError::ValidationError(
164 "Graph has no nodes".to_string(),
165 ));
166 }
167
168 let entry = self
169 .entry_point
170 .clone()
171 .or_else(|| self.find_first_node_after_start())
172 .ok_or_else(|| GraphError::ValidationError("No entry point defined".to_string()))?;
173
174 if !self.nodes.contains_key(&entry) && entry != START {
175 return Err(GraphError::ValidationError(format!(
176 "Entry point '{}' not found",
177 entry
178 )));
179 }
180
181 let mut compiled = CompiledGraph::new(
182 self.nodes.clone(),
183 self.edges.clone(),
184 entry,
185 self.default_reducer.clone(),
186 );
187
188 for (name, router) in &self.conditional_routers {
189 compiled.add_router(name.clone(), router.clone());
190 }
191
192 compiled.validate()?;
193 Ok(compiled)
194 }
195
196 fn find_first_node_after_start(&self) -> Option<String> {
197 for edge in &self.edges {
198 if edge.source() == START {
199 if let Some(target) = edge.fixed_target() {
200 return Some(target.to_string());
201 }
202 }
203 }
204 None
205 }
206}
207
208impl<S: StateSchema + 'static> Default for StateGraph<S> {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214pub struct GraphBuilder<S: StateSchema> {
216 graph: StateGraph<S>,
217}
218
219impl<S: StateSchema + 'static> GraphBuilder<S> {
220 pub fn new() -> Self {
222 Self {
223 graph: StateGraph::new(),
224 }
225 }
226
227 pub fn add_node<N: GraphNode<S> + 'static>(mut self, node: N) -> Self {
229 self.graph.add_node(node);
230 self
231 }
232
233 pub fn add_node_fn<F>(mut self, name: impl Into<String>, func: F) -> Self
235 where
236 F: Fn(&S) -> Result<StateUpdate<S>, GraphError> + Send + Sync + 'static,
237 {
238 self.graph.add_node_fn(name, func);
239 self
240 }
241
242 pub fn add_async_node<F>(mut self, name: impl Into<String>, func: F) -> Self
244 where
245 F: AsyncFn<S> + 'static,
246 {
247 self.graph.add_async_node(name, func);
248 self
249 }
250
251 pub fn add_subgraph<SubS: StateSchema + 'static>(
253 mut self,
254 name: impl Into<String>,
255 subgraph: CompiledGraph<SubS>,
256 input_mapper: impl Fn(&S) -> SubS + Send + Sync + 'static,
257 output_mapper: impl Fn(&SubS, &mut S) + Send + Sync + 'static,
258 ) -> Self {
259 self.graph
260 .add_subgraph(name, subgraph, input_mapper, output_mapper);
261 self
262 }
263
264 pub fn add_subgraph_same_state(
266 mut self,
267 name: impl Into<String>,
268 subgraph: CompiledGraph<S>,
269 ) -> Self {
270 self.graph.add_subgraph_same_state(name, subgraph);
271 self
272 }
273
274 pub fn add_edge(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
276 self.graph.add_edge(source, target);
277 self
278 }
279
280 pub fn add_conditional_edges(
282 mut self,
283 source: impl Into<String>,
284 router_name: impl Into<String>,
285 targets: HashMap<String, String>,
286 default: Option<String>,
287 ) -> Self {
288 self.graph
289 .add_conditional_edges(source, router_name, targets, default);
290 self
291 }
292
293 pub fn add_fan_out(mut self, source: impl Into<String>, targets: Vec<String>) -> Self {
295 self.graph.add_fan_out(source, targets);
296 self
297 }
298
299 pub fn add_fan_in(mut self, sources: Vec<String>, target: impl Into<String>) -> Self {
301 self.graph.add_fan_in(sources, target);
302 self
303 }
304
305 pub fn set_entry_point(mut self, node: impl Into<String>) -> Self {
307 self.graph.set_entry_point(node);
308 self
309 }
310
311 pub fn compile(self) -> GraphResult<CompiledGraph<S>> {
313 self.graph.compile()
314 }
315
316 pub fn build(self) -> StateGraph<S> {
318 self.graph
319 }
320}
321
322impl<S: StateSchema + 'static> Default for GraphBuilder<S> {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::state::AgentState;
332
333 #[test]
334 fn test_graph_creation() {
335 let graph: StateGraph<AgentState> = StateGraph::new();
336 assert!(graph.nodes.is_empty());
337 assert!(graph.edges.is_empty());
338 }
339
340 #[test]
341 fn test_add_node_fn() {
342 let mut graph: StateGraph<AgentState> = StateGraph::new();
343 graph.add_node_fn("test_node", |state: &AgentState| {
344 Ok(StateUpdate::full(state.clone()))
345 });
346 assert_eq!(graph.nodes.len(), 1);
347 }
348
349 #[test]
350 fn test_add_edge() {
351 let mut graph: StateGraph<AgentState> = StateGraph::new();
352 graph.add_edge(START, "node1");
353 graph.add_edge("node1", END);
354 assert_eq!(graph.edges.len(), 2);
355 }
356
357 #[test]
358 fn test_compile_empty_graph() {
359 let graph: StateGraph<AgentState> = StateGraph::new();
360 let result = graph.compile();
361 assert!(result.is_err());
362 }
363
364 #[test]
365 fn test_builder_pattern() {
366 let compiled = GraphBuilder::<AgentState>::new()
367 .add_node_fn("process", |state| Ok(StateUpdate::full(state.clone())))
368 .add_edge(START, "process")
369 .add_edge("process", END)
370 .compile();
371 assert!(compiled.is_ok());
372 }
373}