agentkit_reporting/channel.rs
1//! Channel-based reporter adapter.
2//!
3//! [`ChannelReporter`] forwards events to another thread or task via a
4//! [`std::sync::mpsc::Sender`]. This keeps the observer contract synchronous
5//! while allowing expensive event processing to happen off the driver's hot
6//! path.
7
8use std::sync::mpsc::{self, Receiver, Sender};
9
10use agentkit_loop::{AgentEvent, LoopObserver};
11
12use crate::ReportError;
13use crate::policy::FallibleObserver;
14
15/// Reporter adapter that forwards events over a channel.
16///
17/// Use this when you need expensive or async event processing without
18/// blocking the agent loop. The receiving end can live on a dedicated
19/// thread or async task.
20///
21/// # Example
22///
23/// ```rust
24/// use agentkit_reporting::ChannelReporter;
25///
26/// let (reporter, rx) = ChannelReporter::pair();
27///
28/// // Spawn a consumer on another thread.
29/// std::thread::spawn(move || {
30/// while let Ok(event) = rx.recv() {
31/// println!("{event:?}");
32/// }
33/// });
34///
35/// // `reporter` implements `LoopObserver` — hand it to the agent loop.
36/// ```
37pub struct ChannelReporter {
38 sender: Sender<AgentEvent>,
39}
40
41impl ChannelReporter {
42 /// Creates a `ChannelReporter` from an existing sender.
43 pub fn new(sender: Sender<AgentEvent>) -> Self {
44 Self { sender }
45 }
46
47 /// Creates a `ChannelReporter` together with the receiving end of the
48 /// channel.
49 ///
50 /// This is a convenience wrapper around [`std::sync::mpsc::channel`].
51 pub fn pair() -> (Self, Receiver<AgentEvent>) {
52 let (sender, receiver) = mpsc::channel();
53 (Self { sender }, receiver)
54 }
55}
56
57impl LoopObserver for ChannelReporter {
58 fn handle_event(&mut self, event: AgentEvent) {
59 // Silently drop if the receiver is gone — reporters are non-fatal.
60 let _ = self.sender.send(event);
61 }
62}
63
64impl FallibleObserver for ChannelReporter {
65 fn try_handle_event(&mut self, event: &AgentEvent) -> Result<(), ReportError> {
66 self.sender
67 .send(event.clone())
68 .map_err(|_| ReportError::ChannelSend)
69 }
70}