mojentic/agents/
base_async_agent.rs

1//! Base trait for asynchronous agents.
2//!
3//! This module defines the core `BaseAsyncAgent` trait that all async agents
4//! must implement. Agents receive events asynchronously and can produce new
5//! events in response.
6
7use crate::event::Event;
8use crate::Result;
9use async_trait::async_trait;
10
11/// Base trait for all asynchronous agents in the system.
12///
13/// Agents process events and optionally emit new events. This trait defines
14/// the core interface that all agents must implement.
15///
16/// # Examples
17///
18/// ```
19/// use mojentic::agents::BaseAsyncAgent;
20/// use mojentic::event::Event;
21/// use mojentic::Result;
22/// use async_trait::async_trait;
23///
24/// struct MyAgent;
25///
26/// #[async_trait]
27/// impl BaseAsyncAgent for MyAgent {
28///     async fn receive_event_async(&self, event: Box<dyn Event>) -> Result<Vec<Box<dyn Event>>> {
29///         // Process the event and return new events
30///         Ok(vec![])
31///     }
32/// }
33/// ```
34#[async_trait]
35pub trait BaseAsyncAgent: Send + Sync {
36    /// Process an event asynchronously and return resulting events.
37    ///
38    /// This method is called when an event is routed to this agent. The agent
39    /// can perform any async work needed and return zero or more new events
40    /// to be processed.
41    ///
42    /// # Arguments
43    ///
44    /// * `event` - The event to process
45    ///
46    /// # Returns
47    ///
48    /// A vector of new events to be dispatched, or an error if processing failed.
49    async fn receive_event_async(&self, event: Box<dyn Event>) -> Result<Vec<Box<dyn Event>>>;
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::event::Event;
56    use serde::{Deserialize, Serialize};
57    use std::any::Any;
58
59    #[derive(Debug, Clone, Serialize, Deserialize)]
60    struct TestEvent {
61        source: String,
62        correlation_id: Option<String>,
63        data: String,
64    }
65
66    impl Event for TestEvent {
67        fn source(&self) -> &str {
68            &self.source
69        }
70
71        fn correlation_id(&self) -> Option<&str> {
72            self.correlation_id.as_deref()
73        }
74
75        fn set_correlation_id(&mut self, id: String) {
76            self.correlation_id = Some(id);
77        }
78
79        fn as_any(&self) -> &dyn Any {
80            self
81        }
82
83        fn clone_box(&self) -> Box<dyn Event> {
84            Box::new(self.clone())
85        }
86    }
87
88    struct SimpleAgent;
89
90    #[async_trait]
91    impl BaseAsyncAgent for SimpleAgent {
92        async fn receive_event_async(&self, _event: Box<dyn Event>) -> Result<Vec<Box<dyn Event>>> {
93            Ok(vec![])
94        }
95    }
96
97    struct EchoAgent;
98
99    #[async_trait]
100    impl BaseAsyncAgent for EchoAgent {
101        async fn receive_event_async(&self, event: Box<dyn Event>) -> Result<Vec<Box<dyn Event>>> {
102            // Echo back a new event
103            let new_event = TestEvent {
104                source: "EchoAgent".to_string(),
105                correlation_id: event.correlation_id().map(|s| s.to_string()),
106                data: "echoed".to_string(),
107            };
108            Ok(vec![Box::new(new_event)])
109        }
110    }
111
112    #[tokio::test]
113    async fn test_simple_agent_returns_empty() {
114        let agent = SimpleAgent;
115        let event = Box::new(TestEvent {
116            source: "Test".to_string(),
117            correlation_id: None,
118            data: "test".to_string(),
119        }) as Box<dyn Event>;
120
121        let result = agent.receive_event_async(event).await.unwrap();
122        assert_eq!(result.len(), 0);
123    }
124
125    #[tokio::test]
126    async fn test_echo_agent_returns_event() {
127        let agent = EchoAgent;
128        let event = Box::new(TestEvent {
129            source: "Test".to_string(),
130            correlation_id: Some("test-123".to_string()),
131            data: "original".to_string(),
132        }) as Box<dyn Event>;
133
134        let result = agent.receive_event_async(event).await.unwrap();
135        assert_eq!(result.len(), 1);
136
137        let returned_event = result[0].as_any().downcast_ref::<TestEvent>().unwrap();
138        assert_eq!(returned_event.source(), "EchoAgent");
139        assert_eq!(returned_event.correlation_id(), Some("test-123"));
140        assert_eq!(returned_event.data, "echoed");
141    }
142
143    #[tokio::test]
144    async fn test_agent_preserves_correlation_id() {
145        let agent = EchoAgent;
146        let event = Box::new(TestEvent {
147            source: "Test".to_string(),
148            correlation_id: Some("preserve-456".to_string()),
149            data: "test".to_string(),
150        }) as Box<dyn Event>;
151
152        let result = agent.receive_event_async(event).await.unwrap();
153        assert_eq!(result.len(), 1);
154
155        let correlation = result[0].correlation_id();
156        assert_eq!(correlation, Some("preserve-456"));
157    }
158}