ri_agent_graph/
builder.rs1#![allow(deprecated)] use crate::checkpoint_store::CheckpointStore;
4use crate::checkpointer::CheckpointSaver;
5use crate::edge::EdgeType;
6use crate::error::{AgentGraphError, Result};
7use crate::event_sink::EventSink;
8use crate::executor::Executor;
9use crate::graph::AgentGraph;
10use crate::graph::{END, START};
11use crate::interrupt::InterruptConfig;
12use crate::node::Node;
13use crate::reducer::Reducer;
14use crate::retry::RetryPolicy;
15use crate::router::RoutingFunction;
16use std::collections::HashMap;
17use std::sync::Arc;
18
19pub struct AgentGraphBuilder {
21 pub(crate) nodes: HashMap<String, Arc<dyn Node>>,
22 pub(crate) edges: HashMap<String, Vec<EdgeType>>,
23 pub(crate) max_iterations: usize,
24 pub(crate) enable_cycle_detection: bool,
25 pub(crate) retry_policies: HashMap<String, RetryPolicy>,
26 pub(crate) interrupt_config: Option<InterruptConfig>,
27 pub(crate) checkpointer: Option<Arc<dyn CheckpointSaver>>,
28 pub(crate) checkpoint_store: Option<Arc<dyn CheckpointStore>>,
29 pub(crate) reducers: Vec<(String, Arc<dyn Reducer>)>,
30 pub(crate) graph_name: Option<String>,
31 pub(crate) event_sink: Option<Arc<dyn EventSink>>,
32 pub(crate) executor: Option<Arc<dyn Executor>>,
33}
34
35impl AgentGraphBuilder {
36 pub(crate) fn new() -> Self {
37 Self {
38 nodes: HashMap::new(),
39 edges: HashMap::new(),
40 max_iterations: 100,
41 enable_cycle_detection: true,
42 retry_policies: HashMap::new(),
43 interrupt_config: None,
44 checkpointer: None,
45 checkpoint_store: None,
46 reducers: Vec::new(),
47 graph_name: None,
48 event_sink: None,
49 executor: None,
50 }
51 }
52
53 pub fn with_name(mut self, name: impl Into<String>) -> Self {
55 self.graph_name = Some(name.into());
56 self
57 }
58
59 pub fn add_node(mut self, name: impl Into<String>, node: Box<dyn Node>) -> Self {
61 self.nodes.insert(name.into(), Arc::from(node));
62 self
63 }
64
65 pub fn add_node_with_retry(
67 mut self,
68 name: impl Into<String>,
69 node: Box<dyn Node>,
70 retry: RetryPolicy,
71 ) -> Self {
72 let name = name.into();
73 self.nodes.insert(name.clone(), Arc::from(node));
74 self.retry_policies.insert(name, retry);
75 self
76 }
77
78 pub fn add_subgraph(mut self, name: impl Into<String>, subgraph: AgentGraph) -> Self {
80 self.nodes.insert(name.into(), Arc::new(subgraph));
81 self
82 }
83
84 pub fn add_edge(mut self, from: impl Into<String>, to: impl Into<String>) -> Self {
87 let from = from.into();
88 let to = to.into();
89 self.edges
90 .entry(from)
91 .or_default()
92 .push(EdgeType::Normal(to));
93 self
94 }
95
96 pub fn add_conditional_edge(
98 mut self,
99 from: impl Into<String>,
100 router: Box<dyn RoutingFunction>,
101 ) -> Self {
102 let from = from.into();
103 self.edges
104 .entry(from)
105 .or_default()
106 .push(EdgeType::Conditional(router));
107 self
108 }
109
110 pub fn set_entry_point(self, node: impl Into<String>) -> Self {
112 self.add_edge(START, node)
113 }
114
115 pub fn set_finish_point(self, node: impl Into<String>) -> Self {
117 self.add_edge(node, END)
118 }
119
120 pub fn with_max_iterations(mut self, max: usize) -> Self {
122 self.max_iterations = max;
123 self
124 }
125
126 pub fn with_cycle_detection(mut self, enable: bool) -> Self {
128 self.enable_cycle_detection = enable;
129 self
130 }
131
132 pub fn with_reducer(mut self, key: impl Into<String>, reducer: impl Reducer + 'static) -> Self {
134 self.reducers.push((key.into(), Arc::new(reducer)));
135 self
136 }
137
138 pub fn with_interrupt_before(mut self, nodes: Vec<String>) -> Self {
140 let cfg = self
141 .interrupt_config
142 .get_or_insert_with(InterruptConfig::new);
143 cfg.interrupt_before.extend(nodes);
144 self
145 }
146
147 pub fn with_interrupt_after(mut self, nodes: Vec<String>) -> Self {
149 let cfg = self
150 .interrupt_config
151 .get_or_insert_with(InterruptConfig::new);
152 cfg.interrupt_after.extend(nodes);
153 self
154 }
155
156 pub fn with_checkpointer(mut self, checkpointer: impl CheckpointSaver + 'static) -> Self {
158 self.checkpointer = Some(Arc::new(checkpointer));
159 self
160 }
161
162 pub fn with_checkpoint_store(mut self, store: Arc<dyn CheckpointStore>) -> Self {
164 self.checkpoint_store = Some(store);
165 self
166 }
167
168 pub fn with_event_sink(mut self, sink: Arc<dyn EventSink>) -> Self {
170 self.event_sink = Some(sink);
171 self
172 }
173
174 pub fn with_executor(mut self, executor: Arc<dyn Executor>) -> Self {
176 self.executor = Some(executor);
177 self
178 }
179
180 pub fn build(self) -> Result<AgentGraph> {
182 for (from, edge_list) in &self.edges {
183 for edge in edge_list {
184 if let EdgeType::Normal(to) = edge {
185 if to != END && !self.nodes.contains_key(to) && to != START {
186 return Err(AgentGraphError::NodeNotFound(format!(
187 "Edge from '{}' points to non-existent node '{}'",
188 from, to
189 )));
190 }
191 }
192 }
193 }
194
195 Ok(AgentGraph {
196 nodes: self.nodes,
197 edges: self.edges,
198 max_iterations: self.max_iterations,
199 enable_cycle_detection: self.enable_cycle_detection,
200 retry_policies: self.retry_policies,
201 interrupt_config: self.interrupt_config,
202 checkpointer: self.checkpointer,
203 checkpoint_store: self.checkpoint_store,
204 reducers: self.reducers,
205 graph_name: self.graph_name,
206 event_sink: self.event_sink,
207 executor: self.executor,
208 })
209 }
210}