autoagents_core/runtime/
mod.rs1use crate::actor::{AnyActor, CloneableMessage, Transport};
2use async_trait::async_trait;
3use autoagents_protocol::{Event, RuntimeID};
4use ractor::ActorRef;
5use std::any::{Any, TypeId};
6use std::fmt::Debug;
7use std::sync::Arc;
8use tokio::sync::mpsc;
9use tokio::sync::mpsc::error::SendError;
10use tokio::task::JoinError;
11
12pub(crate) mod manager;
13mod single_threaded;
14use crate::actor::Topic;
15use crate::utils::BoxEventStream;
16pub use single_threaded::SingleThreadedRuntime;
17
18#[derive(Debug, Clone)]
20pub struct RuntimeConfig {
21 pub queue_size: Option<usize>,
22}
23
24impl Default for RuntimeConfig {
25 fn default() -> Self {
26 Self {
27 queue_size: Some(100),
28 }
29 }
30}
31
32#[derive(Debug, thiserror::Error)]
34pub enum RuntimeError {
35 #[error("Send Message Error: {0}")]
36 SendMessage(String),
37
38 #[error("TopicTypeMismatch")]
39 TopicTypeMismatch(String, TypeId),
40
41 #[error("Join Error: {0}")]
42 JoinError(JoinError),
43
44 #[error("Runtime operation failed: {0}")]
45 OperationFailed(String),
46
47 #[error("Event error: {0}")]
48 EventError(#[from] Box<SendError<Event>>),
49}
50
51#[async_trait]
55pub trait Runtime: Send + Sync {
56 fn id(&self) -> RuntimeID;
57
58 async fn subscribe_any(
59 &self,
60 topic_name: &str,
61 topic_type: TypeId,
62 actor: Arc<dyn AnyActor>,
63 ) -> Result<(), RuntimeError>;
64
65 async fn publish_any(
66 &self,
67 topic_name: &str,
68 topic_type: TypeId,
69 message: Arc<dyn Any + Send + Sync>,
70 ) -> Result<(), RuntimeError>;
71
72 fn tx(&self) -> mpsc::Sender<Event>;
76 async fn transport(&self) -> Arc<dyn Transport>;
77 async fn take_event_receiver(&self) -> Option<BoxEventStream<Event>>;
78 async fn subscribe_events(&self) -> BoxEventStream<Event>;
80 async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
82 async fn stop(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
84}
85
86#[async_trait]
89pub trait TypedRuntime: Runtime {
90 async fn subscribe<M>(&self, topic: &Topic<M>, actor: ActorRef<M>) -> Result<(), RuntimeError>
91 where
92 M: CloneableMessage + 'static,
93 {
94 let arc_actor = Arc::new(actor) as Arc<dyn AnyActor>;
95 self.subscribe_any(topic.name(), TypeId::of::<M>(), arc_actor)
96 .await
97 }
98
99 async fn publish<M>(&self, topic: &Topic<M>, message: M) -> Result<(), RuntimeError>
100 where
101 M: CloneableMessage + 'static,
102 {
103 let arc_msg = Arc::new(message) as Arc<dyn Any + Send + Sync>;
104 self.publish_any(topic.name(), TypeId::of::<M>(), arc_msg)
105 .await
106 }
107
108 async fn send_message<M: CloneableMessage + 'static>(
109 &self,
110 message: M,
111 addr: ActorRef<M>,
112 ) -> Result<(), RuntimeError> {
113 addr.cast(message)
114 .map_err(|e| RuntimeError::SendMessage(e.to_string()))
115 }
116}
117
118impl<T: Runtime + ?Sized> TypedRuntime for T {}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::actor::{ActorMessage, CloneableMessage, LocalTransport, Topic, Transport};
125 use async_trait::async_trait;
126 use futures::stream;
127 use tokio::sync::Mutex;
128
129 #[derive(Debug, Clone)]
130 struct TestMessage {
131 value: String,
132 }
133
134 impl ActorMessage for TestMessage {}
135 impl CloneableMessage for TestMessage {}
136
137 #[derive(Debug)]
138 struct TestRuntime {
139 published: Arc<Mutex<Vec<(String, TypeId, String)>>>,
140 tx: mpsc::Sender<Event>,
141 }
142
143 impl TestRuntime {
144 fn new() -> Self {
145 let (tx, _rx) = mpsc::channel(1);
146 Self {
147 published: Arc::new(Mutex::new(Vec::new())),
148 tx,
149 }
150 }
151 }
152
153 #[async_trait]
154 impl Runtime for TestRuntime {
155 fn id(&self) -> RuntimeID {
156 RuntimeID::new_v4()
157 }
158
159 async fn subscribe_any(
160 &self,
161 _topic_name: &str,
162 _topic_type: TypeId,
163 _actor: Arc<dyn AnyActor>,
164 ) -> Result<(), RuntimeError> {
165 Ok(())
166 }
167
168 async fn publish_any(
169 &self,
170 topic_name: &str,
171 topic_type: TypeId,
172 message: Arc<dyn Any + Send + Sync>,
173 ) -> Result<(), RuntimeError> {
174 let msg = message
175 .downcast_ref::<TestMessage>()
176 .map(|m| m.value.clone())
177 .unwrap_or_default();
178 let mut published = self.published.lock().await;
179 published.push((topic_name.to_string(), topic_type, msg));
180 Ok(())
181 }
182
183 fn tx(&self) -> mpsc::Sender<Event> {
184 self.tx.clone()
185 }
186
187 async fn transport(&self) -> Arc<dyn Transport> {
188 Arc::new(LocalTransport)
189 }
190
191 async fn take_event_receiver(&self) -> Option<BoxEventStream<Event>> {
192 None
193 }
194
195 async fn subscribe_events(&self) -> BoxEventStream<Event> {
196 Box::pin(stream::empty())
197 }
198
199 async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
200 Ok(())
201 }
202
203 async fn stop(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
204 Ok(())
205 }
206 }
207
208 #[tokio::test]
209 async fn test_typed_runtime_publish_records_message() {
210 let runtime = TestRuntime::new();
211 let topic = Topic::<TestMessage>::new("topic");
212 runtime
213 .publish(
214 &topic,
215 TestMessage {
216 value: "hello".to_string(),
217 },
218 )
219 .await
220 .unwrap();
221
222 let published = runtime.published.lock().await.clone();
223 assert_eq!(published.len(), 1);
224 assert_eq!(published[0].0, "topic");
225 assert_eq!(published[0].2, "hello");
226 }
227}