molo 0.2.4

A lightweight Rust agent framework
Documentation
//! Application-level event set for [`ReActAgent`](crate::agent::ReActAgent).
//!
//! These are ReAct's own event types, not framework-level events: other
//! Agent implementations (planning / retrieval / sub-agent orchestration)
//! define their own event types and simply implement
//! [`AgentEvent`](crate::agent::AgentEvent) — the event set is up to each
//! Agent; the framework does not prescribe a unified set.
//! ReAct's event set is **closed**, expressed as a single enum
//! [`ReActEvent`] — consumers do one downcast followed by an exhaustive
//! match, rather than downcasting per type. Once an
//! [`EventChannel`](crate::event_channel::EventChannel) is attached,
//! ReActAgent publishes these events on both the streaming and non-streaming
//! run paths.
//!
//! # Examples
//!
//! Attach a broadcast channel and subscribe to the sequence of event names
//! for one run:
//!
//! ```
//! # #[tokio::main]
//! # async fn main() -> Result<(), molo::AgentError> {
//! use molo::agent::AgentEvent;
//! use molo::event_channel::{BroadcastEventChannel, EventChannel, EventReceiver};
//! use molo::provider::{FakeProvider, FakeReply};
//! use molo::{react_agent, Agent};
//!
//! let channel = BroadcastEventChannel::new(64);
//! let mut rx = channel.subscribe();
//! let mut agent = react_agent!(FakeProvider::new([FakeReply::Text("Hello".into())]))
//!     .with_event_channel(channel);
//!
//! agent.run("hi").await?;
//! drop(agent); // channel closed: once the subscriber drains the remaining events, recv returns None
//! while let Some(event) = rx.recv().await {
//!     println!("{}", event.name());
//! }
//! # Ok(())
//! # }
//! ```

use super::{AgentEvent, RunSummary};
use crate::AgentError;
use crate::tool::RegistryError;

/// A single event from ReActAgent (application level; the event name is
/// provided per variant via [`AgentEvent::name`]).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReActEvent {
    /// The run has started (the user input is already recorded). Marks the
    /// start of a run segment in a long-lived channel.
    RunStarted {
        /// Observability identifier for this run: every trace span carries a
        /// `run.id` attribute with the same value, which correlates the event
        /// stream with observability data; subsequent events in the same
        /// segment implicitly belong to this run.
        run_id: String,
        /// The user input for this run.
        input: String,
    },
    /// An increment of the model's reply text (same content as
    /// [`MessageChunk::Delta`](super::MessageChunk::Delta) — pushed to
    /// pipeline-only subscribers so they need not pull the stream).
    Delta {
        /// Text increment.
        text: String,
    },
    /// An increment of the model's reasoning. Note that
    /// [`MessageChunk`](crate::agent::MessageChunk) has no Reasoning variant
    /// (see its docs); pipeline events are each Agent's own choice, and ReAct
    /// chooses to push reasoning.
    Reasoning {
        /// Reasoning increment.
        text: String,
    },
    /// A tool has started executing (the loop received the model's tool call
    /// request and is about to execute it).
    ToolStarted {
        /// The original id of this call, paired with
        /// [`ToolCompleted`](ReActEvent::ToolCompleted).
        id: String,
        /// The tool name.
        name: String,
        /// The arguments JSON generated by the model.
        arguments: String,
    },
    /// A tool has finished executing; `result` is the return value of
    /// [`ToolRegistry::call`](crate::tool::ToolRegistry::call) — Ok = result
    /// text, Err = failure (Display is the error text fed back to the model).
    ToolCompleted {
        /// The original id of this call, paired with
        /// [`ToolStarted`](ReActEvent::ToolStarted).
        id: String,
        /// The tool name.
        name: String,
        /// The execution result (Ok/Err status is classified by the registry,
        /// see [`RegistryError`](crate::tool::RegistryError)).
        result: Result<String, RegistryError>,
    },
    /// The run has ended (published on success, cancellation, and error) —
    /// the end of a run segment in a long-lived channel, carrying the
    /// execution summary for this run (also accumulated on the non-streaming
    /// path).
    RunEnded {
        /// Execution summary for this run (same semantics as the streaming
        /// `Done` chunk).
        summary: RunSummary,
        /// None = normal end; Some = cancelled / errored (cancellation is
        /// [`AgentError::Cancelled`](crate::AgentError::Cancelled)).
        error: Option<AgentError>,
    },
}

impl AgentEvent for ReActEvent {
    fn name(&self) -> &'static str {
        match self {
            ReActEvent::RunStarted { .. } => "run.started",
            ReActEvent::Delta { .. } => "delta",
            ReActEvent::Reasoning { .. } => "reasoning",
            ReActEvent::ToolStarted { .. } => "tool.started",
            ReActEvent::ToolCompleted { .. } => "tool.completed",
            ReActEvent::RunEnded { .. } => "run.ended",
        }
    }
}