pub trait AgentHook<M>: WasmCompatSend + WasmCompatSyncwhere
M: CompletionModel,{
// Provided methods
fn on_event(
&self,
ctx: &HookContext,
event: StepEvent<'_, M>,
) -> impl Future<Output = Flow> + WasmCompatSend { ... }
fn observes(&self, kind: StepEventKind) -> bool { ... }
}Expand description
A per-run hook that observes and steers an agent run.
Implement on_event and match on the StepEvent
variants you care about; every other event falls through to the default
Flow::Continue. Hooks must be cheap to share (Clone is not required —
hooks are held behind an Arc once registered).
§Example
use rig_core::agent::{AgentHook, Flow, HookContext, StepEvent};
use rig_core::completion::CompletionModel;
#[derive(Clone)]
struct Logger;
impl<M: CompletionModel> AgentHook<M> for Logger {
async fn on_event(&self, ctx: &HookContext, event: StepEvent<'_, M>) -> Flow {
if let StepEvent::ToolCall { tool_name, args, .. } = event {
println!("[run {}] calling {tool_name}({args})", ctx.run_id());
}
Flow::cont()
}
}Provided Methods§
Sourcefn on_event(
&self,
ctx: &HookContext,
event: StepEvent<'_, M>,
) -> impl Future<Output = Flow> + WasmCompatSend
fn on_event( &self, ctx: &HookContext, event: StepEvent<'_, M>, ) -> impl Future<Output = Flow> + WasmCompatSend
Called at every observable point of the agent run (subject to
observes). Receives the run-scoped HookContext and
the StepEvent. The default implementation is a no-op: it ignores every
event and returns Flow::Continue. It does not narrow
observes (which defaults to true), so a hook that
takes this default is still dispatched every event — override observes
to skip the high-frequency delta events. (The () no-op hook overrides
observes to false, so the runner skips dispatching those delta events
to it; it still receives, and returns Flow::Continue for, every other
event.)
Sourcefn observes(&self, kind: StepEventKind) -> bool
fn observes(&self, kind: StepEventKind) -> bool
Whether this hook observes events of the given StepEventKind.
This is a performance hint for the high-frequency streaming
TextDelta /
ToolCallDelta events, which otherwise
cost one boxed future per delta. The runner skips building and
dispatching a delta event only when no hook in the stack observes it
(interest is OR-combined across the stack), so a hook may still be
invoked for a delta a sibling observes — on_event must therefore stay
total (return Flow::Continue for events it ignores) rather than
assume it is only called for observed kinds.
Control flow is never changed by observes: the shared, steering
events (ToolCall,
InvalidToolCall, …) fire identically
regardless of this method, so run() and stream() stay in lock-step.
The default observes everything.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl<M> AgentHook<M> for ()where
M: CompletionModel,
The no-op hook: observes nothing, never alters control flow.
impl<M> AgentHook<M> for ()where
M: CompletionModel,
The no-op hook: observes nothing, never alters control flow.
Source§fn observes(&self, _kind: StepEventKind) -> bool
fn observes(&self, _kind: StepEventKind) -> bool
Observe nothing, so the runner skips building/dispatching the
high-frequency streaming delta events (TextDelta / ToolCallDelta,
the only events gated on observes) for a () hook.