1use std::sync::Arc;
7
8use tokio::sync::broadcast;
9
10use crate::agent_session::AgentSession;
11use crate::confirmation::ToolConfirmation;
12use crate::events::EventActions;
13use crate::middleware::MiddlewareChain;
14use crate::run_config::RunConfig;
15
16#[derive(Debug, Clone)]
20pub enum AgentEvent {
21 Session(rs_genai::session::SessionEvent),
23 AgentStarted {
26 name: String,
28 },
29 AgentCompleted {
31 name: String,
33 },
34 ToolCallStarted {
36 name: String,
38 args: serde_json::Value,
40 },
41 ToolCallCompleted {
43 name: String,
45 result: serde_json::Value,
47 duration: std::time::Duration,
49 },
50 ToolCallFailed {
52 name: String,
54 error: String,
56 },
57 StreamingToolYield {
59 name: String,
61 value: serde_json::Value,
63 },
64 AgentTransfer {
66 from: String,
68 to: String,
70 },
71 StateChanged {
73 key: String,
75 },
76 LoopIteration {
78 iteration: u32,
80 },
81 Timeout,
83 RouteSelected {
85 agent_name: String,
87 },
88 FallbackActivated {
90 agent_name: String,
92 },
93}
94
95pub struct InvocationContext {
100 pub agent_session: AgentSession,
103
104 pub event_tx: broadcast::Sender<AgentEvent>,
106
107 pub middleware: MiddlewareChain,
109
110 pub run_config: RunConfig,
112
113 pub session_id: Option<String>,
115
116 pub artifact_service: Option<Arc<dyn crate::artifacts::ArtifactService>>,
118 pub memory_service: Option<Arc<dyn crate::memory::MemoryService>>,
120 pub session_service: Option<Arc<dyn crate::session::SessionService>>,
122}
123
124impl InvocationContext {
125 pub fn new(agent_session: AgentSession) -> Self {
127 let (event_tx, _) = broadcast::channel(256);
128 Self {
129 agent_session,
130 event_tx,
131 middleware: MiddlewareChain::new(),
132 run_config: RunConfig::default(),
133 session_id: None,
134 artifact_service: None,
135 memory_service: None,
136 session_service: None,
137 }
138 }
139
140 pub fn with_middleware(agent_session: AgentSession, middleware: MiddlewareChain) -> Self {
142 let (event_tx, _) = broadcast::channel(256);
143 Self {
144 agent_session,
145 event_tx,
146 middleware,
147 run_config: RunConfig::default(),
148 session_id: None,
149 artifact_service: None,
150 memory_service: None,
151 session_service: None,
152 }
153 }
154
155 pub fn emit(&self, event: AgentEvent) {
157 let _ = self.event_tx.send(event);
158 }
159
160 pub fn subscribe(&self) -> broadcast::Receiver<AgentEvent> {
162 self.event_tx.subscribe()
163 }
164
165 pub fn state(&self) -> &crate::state::State {
167 self.agent_session.state()
168 }
169
170 pub fn with_artifact_service(
172 mut self,
173 service: Arc<dyn crate::artifacts::ArtifactService>,
174 ) -> Self {
175 self.artifact_service = Some(service);
176 self
177 }
178
179 pub fn with_memory_service(mut self, service: Arc<dyn crate::memory::MemoryService>) -> Self {
181 self.memory_service = Some(service);
182 self
183 }
184
185 pub fn with_session_service(
187 mut self,
188 service: Arc<dyn crate::session::SessionService>,
189 ) -> Self {
190 self.session_service = Some(service);
191 self
192 }
193}
194
195pub struct CallbackContext<'a> {
200 ctx: &'a InvocationContext,
201 pub event_actions: EventActions,
203}
204
205impl<'a> CallbackContext<'a> {
206 pub fn new(ctx: &'a InvocationContext) -> Self {
208 Self {
209 ctx,
210 event_actions: EventActions::default(),
211 }
212 }
213
214 pub fn state(&self) -> &crate::state::State {
216 self.ctx.state()
217 }
218
219 pub fn session_id(&self) -> Option<&str> {
221 self.ctx.session_id.as_deref()
222 }
223
224 pub fn invocation_context(&self) -> &InvocationContext {
226 self.ctx
227 }
228}
229
230pub struct ToolContext<'a> {
234 pub callback: CallbackContext<'a>,
236 pub function_call_id: Option<String>,
238 pub confirmation: Option<ToolConfirmation>,
240}
241
242impl<'a> ToolContext<'a> {
243 pub fn new(ctx: &'a InvocationContext, function_call_id: Option<String>) -> Self {
245 Self {
246 callback: CallbackContext::new(ctx),
247 function_call_id,
248 confirmation: None,
249 }
250 }
251
252 pub fn state(&self) -> &crate::state::State {
254 self.callback.state()
255 }
256
257 pub fn with_confirmation(mut self, confirmation: ToolConfirmation) -> Self {
259 self.confirmation = Some(confirmation);
260 self
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
269 fn agent_event_is_send_and_clone() {
270 fn assert_send_clone<T: Send + Clone>() {}
271 assert_send_clone::<AgentEvent>();
272 }
273
274 #[test]
275 fn invocation_context_has_default_run_config() {
276 use std::sync::Arc;
277 use tokio::sync::broadcast;
278
279 let (evt_tx, _) = broadcast::channel(16);
280 let writer: Arc<dyn rs_genai::session::SessionWriter> =
281 Arc::new(crate::test_helpers::MockWriter);
282 let session = crate::agent_session::AgentSession::from_writer(writer, evt_tx);
283 let ctx = InvocationContext::new(session);
284
285 assert_eq!(ctx.run_config.max_llm_calls, 500);
286 assert!(ctx.session_id.is_none());
287 }
288
289 #[test]
290 fn callback_context_state_access() {
291 use std::sync::Arc;
292 use tokio::sync::broadcast;
293
294 let (evt_tx, _) = broadcast::channel(16);
295 let writer: Arc<dyn rs_genai::session::SessionWriter> =
296 Arc::new(crate::test_helpers::MockWriter);
297 let session = crate::agent_session::AgentSession::from_writer(writer, evt_tx);
298 let ctx = InvocationContext::new(session);
299 ctx.state().set("key", "value");
300
301 let cb_ctx = CallbackContext::new(&ctx);
302 assert_eq!(
303 cb_ctx.state().get::<String>("key"),
304 Some("value".to_string())
305 );
306 }
307
308 #[test]
309 fn tool_context_wraps_callback_context() {
310 use std::sync::Arc;
311 use tokio::sync::broadcast;
312
313 let (evt_tx, _) = broadcast::channel(16);
314 let writer: Arc<dyn rs_genai::session::SessionWriter> =
315 Arc::new(crate::test_helpers::MockWriter);
316 let session = crate::agent_session::AgentSession::from_writer(writer, evt_tx);
317 let ctx = InvocationContext::new(session);
318 ctx.state().set("x", 42);
319
320 let tool_ctx = ToolContext::new(&ctx, Some("call-1".to_string()));
321 assert_eq!(tool_ctx.state().get::<i32>("x"), Some(42));
322 assert_eq!(tool_ctx.function_call_id.as_deref(), Some("call-1"));
323 assert!(tool_ctx.confirmation.is_none());
324 }
325
326 #[test]
327 fn tool_context_with_confirmation() {
328 use std::sync::Arc;
329 use tokio::sync::broadcast;
330
331 let (evt_tx, _) = broadcast::channel(16);
332 let writer: Arc<dyn rs_genai::session::SessionWriter> =
333 Arc::new(crate::test_helpers::MockWriter);
334 let session = crate::agent_session::AgentSession::from_writer(writer, evt_tx);
335 let ctx = InvocationContext::new(session);
336
337 let tool_ctx =
338 ToolContext::new(&ctx, None).with_confirmation(ToolConfirmation::confirmed());
339 assert!(tool_ctx.confirmation.as_ref().unwrap().confirmed);
340 }
341}