ceylon_core/agent.rs
1use crate::error::Result;
2use crate::message::Message;
3use crate::request_queue::RequestQueue;
4use async_trait::async_trait;
5use std::sync::Arc;
6
7/// The core trait that defines an autonomous agent.
8///
9/// Agents are the primary unit of computation in Ceylon. Each agent:
10/// - Has a unique name for identification and message routing
11/// - Receives lifecycle callbacks (`on_start`, `on_stop`)
12/// - Processes messages via `on_message` or `on_generic_message`
13/// - Optionally exposes tools via `tool_invoker`
14///
15/// # Implementing an Agent
16///
17/// ```rust,no_run
18/// use ceylon_core::{Agent, AgentContext, Message};
19/// use ceylon_core::error::Result;
20/// use async_trait::async_trait;
21///
22/// struct CounterAgent {
23/// count: u32,
24/// }
25///
26/// #[async_trait]
27/// impl Agent for CounterAgent {
28/// fn name(&self) -> String {
29/// "counter".to_string()
30/// }
31///
32/// async fn on_start(&mut self, _ctx: &mut AgentContext) -> Result<()> {
33/// println!("Counter starting with count: {}", self.count);
34/// Ok(())
35/// }
36///
37/// async fn on_message(&mut self, msg: Message, ctx: &mut AgentContext) -> Result<()> {
38/// self.count += 1;
39/// if let Some(id) = msg.correlation_id() {
40/// ctx.report_result(&id, format!("Count: {}", self.count));
41/// }
42/// Ok(())
43/// }
44/// }
45/// ```
46#[async_trait]
47pub trait Agent: Send + Sync {
48 /// Returns the unique name of this agent.
49 ///
50 /// This name is used for message routing and identification within a mesh.
51 fn name(&self) -> String;
52
53 /// Called when the agent starts.
54 ///
55 /// Use this for initialization logic. The mesh is running but other agents
56 /// may not have started yet.
57 async fn on_start(&mut self, _ctx: &mut AgentContext) -> Result<()> {
58 Ok(())
59 }
60
61 /// Called when a binary message is received.
62 ///
63 /// # Arguments
64 /// * `msg` - The received message with binary payload
65 /// * `ctx` - The agent context for mesh interaction
66 async fn on_message(&mut self, _msg: Message, _ctx: &mut AgentContext) -> Result<()> {
67 Ok(())
68 }
69
70 /// Handle a generic string message and return a response.
71 ///
72 /// Default implementation echoes the content back.
73 async fn on_generic_message(
74 &mut self,
75 msg: crate::message::GenericMessage,
76 _ctx: &mut AgentContext,
77 ) -> Result<crate::message::GenericResponse> {
78 Ok(crate::message::GenericResponse::new(msg.content))
79 }
80
81 /// Called when the agent is stopping.
82 ///
83 /// Use this for cleanup logic.
84 async fn on_stop(&mut self, _ctx: &mut AgentContext) -> Result<()> {
85 Ok(())
86 }
87
88 /// Get the tool invoker for this agent (if it has actions).
89 ///
90 /// Return `Some` if this agent exposes tools that can be called by LLMs.
91 fn tool_invoker(&self) -> Option<&crate::action::ToolInvoker> {
92 None
93 }
94
95 /// Get mutable tool invoker for dynamic tool registration.
96 fn tool_invoker_mut(&mut self) -> Option<&mut crate::action::ToolInvoker> {
97 None
98 }
99}
100
101/// Runtime context provided to agents during lifecycle and message handling.
102///
103/// The context gives agents access to mesh information and utilities
104/// for interacting with the request/response system.
105///
106/// # Example
107///
108/// ```rust,no_run
109/// # use ceylon_core::{Agent, AgentContext, Message};
110/// # use ceylon_core::error::Result;
111/// # use async_trait::async_trait;
112/// # struct MyAgent;
113/// # #[async_trait] impl Agent for MyAgent {
114/// # fn name(&self) -> String { "my".into() }
115/// async fn on_message(&mut self, msg: Message, ctx: &mut AgentContext) -> Result<()> {
116/// println!("Running in mesh: {}", ctx.mesh_name);
117/// if let Some(id) = msg.correlation_id() {
118/// ctx.report_result(&id, "Done!".to_string());
119/// }
120/// Ok(())
121/// }
122/// # }
123/// ```
124pub struct AgentContext {
125 /// The name of the mesh this agent is running in.
126 pub mesh_name: String,
127 request_queue: Option<Arc<RequestQueue>>,
128}
129
130impl AgentContext {
131 /// Create a new agent context.
132 ///
133 /// # Arguments
134 /// * `mesh_name` - Name of the mesh
135 /// * `request_queue` - Optional request queue for request/response pattern
136 pub fn new(mesh_name: String, request_queue: Option<Arc<RequestQueue>>) -> Self {
137 Self {
138 mesh_name,
139 request_queue,
140 }
141 }
142
143 /// Report a result for a pending request.
144 ///
145 /// Call this to send a response back to the requester when handling
146 /// a message with a correlation ID.
147 pub fn report_result(&self, request_id: &str, response: String) {
148 if let Some(queue) = &self.request_queue {
149 queue.complete(request_id, response);
150 }
151 }
152
153 /// Check if this context has a request queue available.
154 pub fn has_request_queue(&self) -> bool {
155 self.request_queue.is_some()
156 }
157}