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§
Sourcefn 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_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.
Sourcefn 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_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.
Sourcefn 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<'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.
Sourcefn 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_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.
Sourcefn 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_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.
Sourcefn 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_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.
Sourcefn 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<'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.
Sourcefn 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_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.
Sourcefn 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<'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.
Sourcefn 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,
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.