1use crate::errors::GraphError;
8use crate::state::{StateSchema, StateUpdate};
9use async_trait::async_trait;
10use std::future::Future;
11use std::marker::PhantomData;
12use std::pin::Pin;
13
14#[async_trait]
19pub trait GraphNode<S: StateSchema>: Send + Sync + 'static {
20 async fn execute(
29 &self,
30 state: &S,
31 config: Option<NodeConfig>,
32 ) -> Result<StateUpdate<S>, GraphError>;
33
34 fn name(&self) -> &str;
36}
37
38#[derive(Debug, Clone, Default)]
40pub struct NodeConfig {
41 pub recursion_limit: usize,
43
44 pub metadata: std::collections::HashMap<String, serde_json::Value>,
46
47 pub debug: bool,
49}
50
51pub type NodeResult<S> = Result<StateUpdate<S>, GraphError>;
53
54pub type AsyncNodeFn<S> =
56 Box<dyn Fn(&S) -> Pin<Box<dyn Future<Output = NodeResult<S>> + Send>> + Send + Sync>;
57
58pub trait AsyncFn<S: StateSchema>: Send + Sync {
60 fn call(&self, state: &S) -> Pin<Box<dyn Future<Output = NodeResult<S>> + Send>>;
62}
63
64impl<S: StateSchema, F, Fut> AsyncFn<S> for F
65where
66 F: Fn(&S) -> Fut + Send + Sync + 'static,
67 Fut: Future<Output = NodeResult<S>> + Send + 'static,
68{
69 fn call(&self, state: &S) -> Pin<Box<dyn Future<Output = NodeResult<S>> + Send>> {
70 Box::pin((self)(state))
71 }
72}
73
74pub struct AsyncNode<S: StateSchema, F: AsyncFn<S>> {
76 name: String,
77 func: F,
78 _marker: PhantomData<S>,
79}
80
81impl<S: StateSchema, F: AsyncFn<S>> AsyncNode<S, F> {
82 pub fn new(name: impl Into<String>, func: F) -> Self {
84 Self {
85 name: name.into(),
86 func,
87 _marker: PhantomData,
88 }
89 }
90}
91
92#[async_trait]
93impl<S: StateSchema, F: AsyncFn<S> + 'static> GraphNode<S> for AsyncNode<S, F> {
94 async fn execute(&self, state: &S, _config: Option<NodeConfig>) -> NodeResult<S> {
95 self.func.call(state).await
96 }
97
98 fn name(&self) -> &str {
99 &self.name
100 }
101}
102
103pub struct FunctionNode<S: StateSchema, F> {
107 name: String,
108 func: F,
109 _marker: PhantomData<S>,
110}
111
112impl<S: StateSchema, F> FunctionNode<S, F>
113where
114 F: Fn(&S) -> Pin<Box<dyn Future<Output = Result<StateUpdate<S>, GraphError>> + Send>>
115 + Send
116 + Sync,
117{
118 pub fn new(name: impl Into<String>, func: F) -> Self {
120 Self {
121 name: name.into(),
122 func,
123 _marker: PhantomData,
124 }
125 }
126}
127
128#[async_trait]
129impl<S: StateSchema, F: 'static> GraphNode<S> for FunctionNode<S, F>
130where
131 F: Fn(&S) -> Pin<Box<dyn Future<Output = Result<StateUpdate<S>, GraphError>> + Send>>
132 + Send
133 + Sync,
134{
135 async fn execute(
136 &self,
137 state: &S,
138 _config: Option<NodeConfig>,
139 ) -> Result<StateUpdate<S>, GraphError> {
140 (self.func)(state).await
141 }
142
143 fn name(&self) -> &str {
144 &self.name
145 }
146}
147
148pub struct SyncNode<S: StateSchema, F> {
152 name: String,
153 func: F,
154 _marker: PhantomData<S>,
155}
156
157impl<S: StateSchema, F> SyncNode<S, F>
158where
159 F: Fn(&S) -> Result<StateUpdate<S>, GraphError> + Send + Sync,
160{
161 pub fn new(name: impl Into<String>, func: F) -> Self {
163 Self {
164 name: name.into(),
165 func,
166 _marker: PhantomData,
167 }
168 }
169}
170
171#[async_trait]
172impl<S: StateSchema, F: 'static> GraphNode<S> for SyncNode<S, F>
173where
174 F: Fn(&S) -> Result<StateUpdate<S>, GraphError> + Send + Sync,
175{
176 async fn execute(
177 &self,
178 state: &S,
179 _config: Option<NodeConfig>,
180 ) -> Result<StateUpdate<S>, GraphError> {
181 (self.func)(state)
182 }
183
184 fn name(&self) -> &str {
185 &self.name
186 }
187}
188
189pub struct SentinelNode {
191 name: String,
192}
193
194impl SentinelNode {
195 pub fn start() -> Self {
197 Self {
198 name: crate::START.to_string(),
199 }
200 }
201
202 pub fn end() -> Self {
204 Self {
205 name: crate::END.to_string(),
206 }
207 }
208
209 pub fn custom(name: impl Into<String>) -> Self {
211 Self { name: name.into() }
212 }
213}
214
215#[async_trait]
216impl<S: StateSchema> GraphNode<S> for SentinelNode {
217 async fn execute(
218 &self,
219 _state: &S,
220 _config: Option<NodeConfig>,
221 ) -> Result<StateUpdate<S>, GraphError> {
222 Ok(StateUpdate::unchanged())
224 }
225
226 fn name(&self) -> &str {
227 &self.name
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234 use crate::state::AgentState;
235
236 #[tokio::test]
237 async fn test_sync_node() {
238 let node = SyncNode::new("test", |state: &AgentState| {
239 Ok(StateUpdate::full(AgentState::new(state.input.clone())))
240 });
241
242 let state = AgentState::new("Hello".to_string());
243 let result = node.execute(&state, None).await;
244 assert!(result.is_ok());
245 }
246
247 #[test]
248 fn test_sentinel_nodes() {
249 let start: SentinelNode = SentinelNode::start();
250 assert_eq!(GraphNode::<AgentState>::name(&start), crate::START);
251
252 let end: SentinelNode = SentinelNode::end();
253 assert_eq!(GraphNode::<AgentState>::name(&end), crate::END);
254 }
255}