CLAUDE.md
This file provides guidance to Claude Code when working with code in this repository.
Development Commands
Core Development Tasks
- Install dependencies:
make install - Run all checks:
make all(format, lint, typecheck, test) - Run tests:
make test - Build docs:
make docsormake docs-serve
Single Test Commands
- Run specific test:
uv run pytest tests/test_agent.py::test_function_name -v - Run test file:
uv run pytest tests/test_agent.py -v - Run with debug:
uv run pytest tests/test_agent.py -v -s
Project Architecture
Core Components
Base Middleware (src/pydantic_ai_middleware/base.py)
AgentMiddleware[DepsT]- Abstract base class for all middleware- Lifecycle hooks:
before_run,after_run,before_model_request,before_tool_call,on_tool_error,after_tool_call,on_error tool_names: set[str] | None- Filter which tools a middleware handlestimeout: float | None- Per-middleware timeout for all hooks
Middleware Agent (src/pydantic_ai_middleware/agent.py)
MiddlewareAgent- Wraps an agent and applies middleware- Delegates to wrapped agent while intercepting lifecycle events
Middleware Toolset (src/pydantic_ai_middleware/toolset.py)
MiddlewareToolset- Wraps a toolset to intercept tool calls- Applies
before_tool_call,on_tool_error, andafter_tool_callmiddleware hooks permission_handler- Callback for handling ASK permission decisions
Decorators (src/pydantic_ai_middleware/decorators.py)
@before_run,@after_run,@on_tool_error, etc. - Create middleware from functions@before_tool_call(tools={"send_email"})- Decorator with tool name filtering_FunctionMiddleware- Internal class that wraps functions
Exceptions (src/pydantic_ai_middleware/exceptions.py)
MiddlewareError- Base exceptionInputBlocked- Block input processingToolBlocked- Block tool executionOutputBlocked- Block outputMiddlewareTimeout- Hook exceeded timeout
Permissions (src/pydantic_ai_middleware/permissions.py)
ToolDecision- Enum: ALLOW, DENY, ASKToolPermissionResult- Structured result from before_tool_callPermissionHandler- Callback type for ASK decisions
Key Design Patterns
Middleware Chain
# Middleware executes in order for before_*, reverse for after_*
agent = MiddlewareAgent(
agent=base_agent,
middleware=[mw1, mw2, mw3], # before: 1->2->3, after: 3->2->1
)
Type-Safe Dependencies
class MyMiddleware(AgentMiddleware[MyDeps]):
async def before_run(self, prompt, deps: MyDeps | None):
# deps is properly typed
...
Testing Strategy
- Unit tests:
tests/directory - Test model: Use
TestModelfrom pydantic-ai for deterministic testing - Coverage: 100% required
- pytest-asyncio: Auto mode enabled
Key Configuration Files
pyproject.toml: Project configurationMakefile: Development automation.pre-commit-config.yaml: Pre-commit hooksmkdocs.yml: Documentation configuration
Coverage
Every pull request MUST have 100% coverage. Check with make test.