rhema-core 0.1.0

A small, embeddable, generic step engine for one exchange with a model
Documentation
//! The streaming seam: events emitted during a step and the sink that
//! receives them.
//!
//! Ownership of events: the [`crate::ModelProvider`] emits streaming deltas
//! (`ThinkingDelta`, `TextDelta`, `ToolCallStart`, `ToolCallArgsDelta`)
//! while it streams; the [`crate::Agent`] emits the lifecycle events
//! (`StepStart`, `ToolResult`, `StepEnd`). A provider that announces a tool
//! call while streaming it is not echoed by the engine — the engine only
//! emits `ToolCallStart` for calls the provider did not announce.

/// One streaming or lifecycle event of a step.
#[derive(Debug, Clone, PartialEq)]
pub enum Event<'a> {
    /// The step begins; `iteration` counts the steps this agent has already
    /// completed.
    StepStart {
        /// Zero-based index of this step within the agent.
        iteration: u32,
    },
    /// A piece of the model's reasoning trace, in stream order. Ephemeral
    /// by design: never appended to the session history — sinks render it
    /// live (or drop it), and it is gone when the step ends.
    ThinkingDelta(&'a str),
    /// A piece of assistant text, in stream order.
    TextDelta(&'a str),
    /// A tool call begins streaming; args follow as deltas.
    ToolCallStart {
        /// The call's correlation id.
        id: &'a str,
        /// The tool's name.
        name: &'a str,
    },
    /// A piece of a tool call's JSON arguments, in stream order. The id
    /// names the call the piece belongs to: the wire formats let a server
    /// interleave the fragments of parallel calls, so sinks must not
    /// assume the call that started last is the one still streaming.
    ToolCallArgsDelta {
        /// The streaming call's correlation id.
        id: &'a str,
        /// A piece of the call's JSON arguments.
        delta: &'a str,
    },
    /// A chunk of a running tool's human-facing output — a command's
    /// completed lines — in stream order. Ephemeral like
    /// [`Event::ThinkingDelta`]: never appended to the session history;
    /// sinks render it live, and the full text arrives in the call's
    /// `ToolResult`.
    ToolOutputDelta {
        /// The running call's correlation id.
        id: &'a str,
        /// A chunk of the tool's output; complete lines, newline
        /// included.
        chunk: &'a str,
    },
    /// A tool result was appended to the session history.
    ToolResult {
        /// The answered call's id.
        id: &'a str,
        /// Result payload.
        content: &'a str,
        /// Whether the call failed.
        is_error: bool,
        /// The process exit code of command-like tools; `None` otherwise.
        code: Option<i32>,
    },
    /// The step ends.
    StepEnd {
        /// Token accounting for the model call.
        usage: crate::output::Usage,
        /// Why the model stopped.
        stop_reason: &'a crate::output::StopReason,
    },
}

/// Receives a step's events. Drivers implement this to stream into a UI, a
/// log, or a channel; a simple collector is a `Vec` behind a closure.
///
/// `Send` because the sink reference crosses await points inside provider
/// implementations, which must produce `Send` futures.
pub trait EventSink: Send {
    /// Handle one event. Called synchronously, in stream order.
    fn emit(&mut self, event: Event<'_>);
}