1use crate::command::NodeOutput;
2use crate::config::GraphConfig;
3use crate::error::Result;
4use crate::state::AgentState;
5use async_trait::async_trait;
6use std::future::Future;
7use std::pin::Pin;
8
9#[async_trait]
12pub trait Node: Send + Sync {
13 async fn execute(&self, state: &AgentState, config: &GraphConfig) -> Result<NodeOutput>;
15
16 fn name(&self) -> Option<&str> {
18 None
19 }
20}
21
22pub struct FnNode<F>
24where
25 F: Fn(&AgentState, &GraphConfig) -> Pin<Box<dyn Future<Output = Result<NodeOutput>> + Send>>
26 + Send
27 + Sync,
28{
29 func: F,
30 name: Option<String>,
31}
32
33impl<F> FnNode<F>
34where
35 F: Fn(&AgentState, &GraphConfig) -> Pin<Box<dyn Future<Output = Result<NodeOutput>> + Send>>
36 + Send
37 + Sync,
38{
39 pub fn new(func: F) -> Self {
40 Self { func, name: None }
41 }
42
43 pub fn with_name(mut self, name: impl Into<String>) -> Self {
44 self.name = Some(name.into());
45 self
46 }
47}
48
49#[async_trait]
50impl<F> Node for FnNode<F>
51where
52 F: Fn(&AgentState, &GraphConfig) -> Pin<Box<dyn Future<Output = Result<NodeOutput>> + Send>>
53 + Send
54 + Sync,
55{
56 async fn execute(&self, state: &AgentState, config: &GraphConfig) -> Result<NodeOutput> {
57 (self.func)(state, config).await
58 }
59
60 fn name(&self) -> Option<&str> {
61 self.name.as_deref()
62 }
63}
64
65#[macro_export]
95macro_rules! node {
96 (|$state:ident| async move $body:block) => {
98 Box::new($crate::node::FnNode::new(
99 |__state: &$crate::state::AgentState, __config: &$crate::config::GraphConfig| {
100 let $state = __state.clone();
101 let _ = __config;
102 Box::pin(async move {
103 let __result = (|| async move { $body })().await;
104 __result.map(::std::convert::Into::into)
105 })
106 },
107 ))
108 };
109 ($name:expr, |$state:ident| async move $body:block) => {
111 Box::new(
112 $crate::node::FnNode::new(
113 |__state: &$crate::state::AgentState, __config: &$crate::config::GraphConfig| {
114 let $state = __state.clone();
115 let _ = __config;
116 Box::pin(async move {
117 let __result = (|| async move { $body })().await;
118 __result.map(::std::convert::Into::into)
119 })
120 },
121 )
122 .with_name($name),
123 )
124 };
125 (|$state:ident, $config:ident| async move $body:block) => {
127 Box::new($crate::node::FnNode::new(
128 |__state: &$crate::state::AgentState, __config: &$crate::config::GraphConfig| {
129 let $state = __state.clone();
130 let $config = __config.clone();
131 Box::pin(async move {
132 let __result = (|| async move { $body })().await;
133 __result.map(::std::convert::Into::into)
134 })
135 },
136 ))
137 };
138 ($name:expr, |$state:ident, $config:ident| async move $body:block) => {
140 Box::new(
141 $crate::node::FnNode::new(
142 |__state: &$crate::state::AgentState, __config: &$crate::config::GraphConfig| {
143 let $state = __state.clone();
144 let $config = __config.clone();
145 Box::pin(async move {
146 let __result = (|| async move { $body })().await;
147 __result.map(::std::convert::Into::into)
148 })
149 },
150 )
151 .with_name($name),
152 )
153 };
154}