Skip to main content

ChatMiddleware

Trait ChatMiddleware 

Source
pub trait ChatMiddleware: Send + Sync {
    // Provided methods
    fn on_run_started<'life0, 'life1, 'life2, 'life3, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        messages: &'life2 [Message],
        config: &'life3 RunConfig,
    ) -> Pin<Box<dyn Future<Output = HookAction> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait { ... }
    fn on_chat_request<'life0, 'life1, 'life2, 'life3, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        step_id: &'life2 StepId,
        req: &'life3 mut ChatRequest,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait { ... }
    fn on_chunk<'life0, 'life1, 'async_trait>(
        &'life0 self,
        chunk: &'life1 StreamChunk,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn on_chunk_mut<'life0, 'life1, 'async_trait>(
        &'life0 self,
        chunk: &'life1 mut StreamChunk,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn on_run_finished<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        reason: &'life2 FinishReason,
        usage: &'life3 Usage,
        new_messages: &'life4 [Message],
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait { ... }
    fn on_run_error<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        err: &'life2 (dyn Error + Send + Sync),
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait { ... }
    fn on_before_tool_call<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        step_id: &'life2 StepId,
        name: &'life3 str,
        args: &'life4 Value,
    ) -> Pin<Box<dyn Future<Output = ToolDecision> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait { ... }
    fn on_before_tool_call_mut<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        step_id: &'life2 StepId,
        name: &'life3 str,
        args: &'life4 mut Value,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait { ... }
    fn on_after_tool_call<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        step_id: &'life2 StepId,
        name: &'life3 str,
        args: &'life4 Value,
        result: &'life5 ToolResultContent,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait,
             'life5: 'async_trait { ... }
    fn on_after_tool_call_mut<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
        &'life0 self,
        run_id: &'life1 RunId,
        step_id: &'life2 StepId,
        name: &'life3 str,
        args: &'life4 Value,
        result: &'life5 mut ToolResultContent,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait,
             'life5: 'async_trait { ... }
}
Expand description

Extension point invoked by the engine at every lifecycle event of a run.

Middlewares run in the registration order of crate::RunConfig::middlewares. For every hook with a _mut counterpart, the engine fires every middleware’s mutating variant first (in registration order), then every read-only variant — so transformers always run as a phase ahead of observers, and every observer sees the same fully-mutated input.

All hooks have default no-op implementations; only override the ones you need. Implementors must be Send + Sync because the engine holds them behind Arc<dyn ChatMiddleware>.

Provided Methods§

Source

fn on_run_started<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, run_id: &'life1 RunId, messages: &'life2 [Message], config: &'life3 RunConfig, ) -> Pin<Box<dyn Future<Output = HookAction> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Fired once per run before any provider call. Return HookAction::Terminate to abort early; the engine surfaces the reason as crate::FinishReason::Aborted and still fires Self::on_run_finished so observability is consistent.

Source

fn on_chat_request<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, run_id: &'life1 RunId, step_id: &'life2 StepId, req: &'life3 mut ChatRequest, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Fired once per step, after the engine has assembled the per-turn ChatRequest but before sending it. Mutate req to inject defaults, switch model parameters per-turn, or strip fields. The façade’s per-builder defaults are wired through an internal middleware that runs ahead of any user-supplied one.

Source

fn on_chunk<'life0, 'life1, 'async_trait>( &'life0 self, chunk: &'life1 StreamChunk, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Fired for every StreamChunk the engine emits, including chunks the engine itself synthesizes (RunStarted/StepStarted/StepFinished/ToolResult/ RunFinished/HistoryCompacted). For mutation, override Self::on_chunk_mut instead.

Source

fn on_chunk_mut<'life0, 'life1, 'async_trait>( &'life0 self, chunk: &'life1 mut StreamChunk, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Mutating counterpart to Self::on_chunk. Engines invoke every middleware’s on_chunk_mut (in registration order) before any on_chunk, so transformers run as a phase ahead of observers and every observer sees the same fully-mutated chunk. The mutated chunk is also what the engine itself uses to build assistant history and what the stream consumer ultimately receives.

Source

fn on_run_finished<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, run_id: &'life1 RunId, reason: &'life2 FinishReason, usage: &'life3 Usage, new_messages: &'life4 [Message], ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Fired once per run after the engine emits its StreamChunk::RunFinished. Always fires — including aborted runs and runs terminated by middleware — so observers see a consistent close. new_messages covers everything the engine added to history this run; partial tool results are preserved when the run was aborted mid-step.

Source

fn on_run_error<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, run_id: &'life1 RunId, err: &'life2 (dyn Error + Send + Sync), ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Fired when a run terminates with a transport / setup-time error from the provider (i.e. an Err returned to the caller). Aborts via HookAction::Terminate / ToolDecision::Terminate / RunConfig.cancellation / RunConfig.timeout go through Self::on_run_finished instead — they are not errors.

Source

fn on_before_tool_call<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, run_id: &'life1 RunId, step_id: &'life2 StepId, name: &'life3 str, args: &'life4 Value, ) -> Pin<Box<dyn Future<Output = ToolDecision> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Fired before the engine invokes a tool. Return ToolDecision::Skip to feed a synthesized error result back to the model without running the tool, or ToolDecision::Terminate to abort the run. This is the gating hook; for input rewriting, use Self::on_before_tool_call_mut.

Source

fn on_before_tool_call_mut<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, run_id: &'life1 RunId, step_id: &'life2 StepId, name: &'life3 str, args: &'life4 mut Value, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Mutating counterpart to Self::on_before_tool_call. Engines invoke every middleware’s on_before_tool_call_mut (in registration order) before any on_before_tool_call, so input transforms (sanitization, redaction, defaulting) run as a phase ahead of gating decisions. Gating still belongs in on_before_tool_call; this hook only rewrites args.

Source

fn on_after_tool_call<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>( &'life0 self, run_id: &'life1 RunId, step_id: &'life2 StepId, name: &'life3 str, args: &'life4 Value, result: &'life5 ToolResultContent, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, 'life5: 'async_trait,

Fired after the engine has executed a tool but before the result is yielded to the stream consumer or recorded in history. Read-only; for output rewriting use Self::on_after_tool_call_mut.

Source

fn on_after_tool_call_mut<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>( &'life0 self, run_id: &'life1 RunId, step_id: &'life2 StepId, name: &'life3 str, args: &'life4 Value, result: &'life5 mut ToolResultContent, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, 'life5: 'async_trait,

Mutating counterpart to Self::on_after_tool_call. Engines invoke every middleware’s on_after_tool_call_mut (in registration order) before any on_after_tool_call, so output transforms (PII scrubbing, truncation-with-marker) run as a phase ahead of observers. The mutated result is what the model sees on the next turn and what the engine emits in StreamChunk::ToolResult.

Implementors§