Skip to main content

agentkit_reporting/
policy.rs

1//! Configurable failure policies for reporters.
2//!
3//! The [`FailurePolicy`] enum controls what happens when a reporter encounters
4//! an error. Wrap any [`FallibleObserver`] in a [`PolicyReporter`] to get a
5//! [`LoopObserver`] that applies the chosen policy automatically.
6
7use crate::ReportError;
8use agentkit_loop::{AgentEvent, LoopObserver};
9
10/// Policy that determines how reporter errors are handled.
11///
12/// Reporter failures are non-fatal by default — a broken log writer shouldn't
13/// crash the agent. Hosts can configure stricter behaviour by choosing a
14/// different policy.
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub enum FailurePolicy {
17    /// Silently discard errors.
18    #[default]
19    Ignore,
20    /// Log errors to stderr via `eprintln!`.
21    Log,
22    /// Collect errors for later inspection via
23    /// [`PolicyReporter::take_errors`].
24    Accumulate,
25    /// Panic on the first error.
26    FailFast,
27}
28
29/// A reporter whose event handling can fail.
30///
31/// Implement this trait for reporters that perform I/O or other fallible
32/// operations. Wrap the implementation in [`PolicyReporter`] to obtain a
33/// [`LoopObserver`] with configurable error handling.
34pub trait FallibleObserver: Send {
35    /// Process an event, returning an error if something goes wrong.
36    fn try_handle_event(&mut self, event: &AgentEvent) -> Result<(), ReportError>;
37}
38
39/// Adapter that wraps a [`FallibleObserver`] and applies a [`FailurePolicy`].
40///
41/// This turns any fallible reporter into a [`LoopObserver`] suitable for
42/// passing to the agent loop.
43///
44/// # Example
45///
46/// ```rust
47/// use agentkit_reporting::{ChannelReporter, FailurePolicy, PolicyReporter};
48///
49/// let (reporter, rx) = ChannelReporter::pair();
50/// let reporter = PolicyReporter::new(reporter, FailurePolicy::Log);
51/// // `reporter` now implements `LoopObserver` and logs send failures to stderr.
52/// ```
53pub struct PolicyReporter<T> {
54    inner: T,
55    policy: FailurePolicy,
56    errors: Vec<ReportError>,
57}
58
59impl<T: FallibleObserver> PolicyReporter<T> {
60    /// Creates a new `PolicyReporter` wrapping the given observer with the
61    /// specified failure policy.
62    pub fn new(inner: T, policy: FailurePolicy) -> Self {
63        Self {
64            inner,
65            policy,
66            errors: Vec::new(),
67        }
68    }
69
70    /// Returns a reference to the inner observer.
71    pub fn inner(&self) -> &T {
72        &self.inner
73    }
74
75    /// Returns a mutable reference to the inner observer.
76    pub fn inner_mut(&mut self) -> &mut T {
77        &mut self.inner
78    }
79
80    /// Returns the configured failure policy.
81    pub fn policy(&self) -> FailurePolicy {
82        self.policy
83    }
84
85    /// Drains and returns all accumulated errors.
86    ///
87    /// Only meaningful when the policy is [`FailurePolicy::Accumulate`].
88    /// Subsequent calls return an empty `Vec` until new errors occur.
89    pub fn take_errors(&mut self) -> Vec<ReportError> {
90        std::mem::take(&mut self.errors)
91    }
92}
93
94impl<T: FallibleObserver> LoopObserver for PolicyReporter<T> {
95    fn handle_event(&mut self, event: AgentEvent) {
96        if let Err(e) = self.inner.try_handle_event(&event) {
97            match self.policy {
98                FailurePolicy::Ignore => {}
99                FailurePolicy::Log => {
100                    eprintln!("reporter error: {e}");
101                }
102                FailurePolicy::Accumulate => {
103                    self.errors.push(e);
104                }
105                FailurePolicy::FailFast => {
106                    panic!("reporter error: {e}");
107                }
108            }
109        }
110    }
111}