What this snippet demonstrates
AFK provides two distinct hook/middleware systems that operate at different layers:- Tool hooks and middleware — Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline.
- LLM middleware — Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer.
Tool pre-hooks
A pre-hook runs before the main tool function executes. It receives the tool’s arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution.Pre-hook execution flow
The pre-hook receives validated arguments and must return a dictionary compatible with the main tool’sargs_model. If the returned dictionary fails validation against the tool’s model, the tool call fails with a ToolValidationError.
Tool post-hooks
A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment.{"output": <tool_output>, "tool_name": "<tool_name>"}. The post-hook must return a dictionary with the same shape.
Tool-level middleware
Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives acall_next function and the tool arguments, and can modify behavior before, after, or around execution.
Attaching middleware to a tool
call_next to pass control to the next middleware (or the actual tool function if it is the last one).
Registry-level middleware
Registry-level middleware applies to every tool in aToolRegistry, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly.
LLM client middleware
LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations:LLM middleware protocols
| Protocol | Operation | Signature |
|---|---|---|
LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse |
LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse |
LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] |
call_next (the next middleware or transport in the chain) and the request object. It can modify the request before calling call_next, modify the response after, or short-circuit entirely by returning a response without calling call_next.
When to use each layer
| Layer | Scope | Use for |
|---|---|---|
| Tool pre-hook | Single tool, before execution | Input sanitization, argument enrichment, validation |
| Tool post-hook | Single tool, after execution | Output sanitization, redaction, annotation |
| Tool middleware | Single tool, wraps execution | Timing, retries, caching, error handling |
| Registry middleware | All tools in registry | Audit logging, rate limiting, policy enforcement |
| LLM middleware | All LLM calls through client | Request metadata, response logging, tracing |
What to read next
- Tools — Full tool system architecture, the 6-step execution pipeline, and design guidelines.
- Tool Call Lifecycle — Detailed lifecycle of a tool call from LLM proposal to result delivery.
- LLMs Overview — Builder workflow, runtime profiles, and provider selection.