Hooks
Middleware provides two styles of hooks to intercept agent execution:Node-style hooks
Run sequentially at specific execution points.
Wrap-style hooks
Run around each model or tool call.
Node-style hooks
Run sequentially at specific execution points. Use for logging, validation, and state updates. Available hooks:before_agent- Before agent starts (once per invocation)before_model- Before each model callafter_model- After each model responseafter_agent- After agent completes (once per invocation)
- Decorator
- Class
Wrap-style hooks
Intercept execution and control when the handler is called. Use for retries, caching, and transformation. You decide if the handler is called zero times (short-circuit), once (normal flow), or multiple times (retry logic). Available hooks:wrap_model_call- Around each model callwrap_tool_call- Around each tool call
- Decorator
- Class
Create middleware
You can create middleware in two ways:Decorator-based middleware
Quick and simple for single-hook middleware. Use decorators to wrap individual functions.
Class-based middleware
More powerful for complex middleware with multiple hooks or configuration.
Decorator-based middleware
Quick and simple for single-hook middleware. Use decorators to wrap individual functions. Available decorators: Node-style:- @[
@before_agent] - Runs before agent starts (once per invocation) @before_model- Runs before each model call@after_model- Runs after each model response- @[
@after_agent] - Runs after agent completes (once per invocation)
@wrap_model_call- Wraps each model call with custom logic@wrap_tool_call- Wraps each tool call with custom logic
@dynamic_prompt- Generates dynamic system prompts
- Single hook needed
- No complex configuration
- Quick prototyping
Class-based middleware
More powerful for complex middleware with multiple hooks or configuration. Example:- Multiple hooks needed
- Complex configuration required
- Reuse across projects with init-time configuration
Custom state schema
Middleware can extend the agent’s state with custom properties.- Decorator
- Class
Execution order
When using multiple middleware, understand how they execute:Execution flow
Execution flow
Before hooks run in order:
middleware1.before_agent()middleware2.before_agent()middleware3.before_agent()
middleware1.before_model()middleware2.before_model()middleware3.before_model()
middleware1.wrap_model_call()→middleware2.wrap_model_call()→middleware3.wrap_model_call()→ model
middleware3.after_model()middleware2.after_model()middleware1.after_model()
middleware3.after_agent()middleware2.after_agent()middleware1.after_agent()
before_*hooks: First to lastafter_*hooks: Last to first (reverse)wrap_*hooks: Nested (first middleware wraps all others)
Agent jumps
To exit early from middleware, return a dictionary withjump_to:
Available jump targets:
'end': Jump to the end of the agent execution (or the firstafter_agenthook)'tools': Jump to the tools node'model': Jump to the model node (or the firstbefore_modelhook)
- Decorator
- Class
Best practices
- Keep middleware focused - each should do one thing well
- Handle errors gracefully - don’t let middleware errors crash the agent
- Use appropriate hook types:
- Node-style for sequential logic (logging, validation)
- Wrap-style for control flow (retry, fallback, caching)
- Clearly document any custom state properties
- Unit test middleware independently before integrating
- Consider execution order - place critical middleware first in the list
- Use built-in middleware when possible
Examples
Dynamic model selection
- Decorator
- Class
Tool call monitoring
- Decorator
- Class
Dynamically selecting tools
Select relevant tools at runtime to improve performance and accuracy. Benefits:- Shorter prompts - Reduce complexity by exposing only relevant tools
- Better accuracy - Models choose correctly from fewer options
- Permission control - Dynamically filter tools based on user access
- Decorator
- Class