Expose djangorestframework-services
services and selectors as a Pydantic-AI toolset, so a
plain pydantic_ai.Agent can call them as tools — no MCP server and no AG-UI
bridge in the path.
Every tool call routes through drf-services' transport-neutral surface
(dispatch_spec plus its off-HTTP helpers), so a tool call runs the same input
validation, the same permission_classes — both class-level has_permission
and object-level has_object_permission on the resolved row — and the same
serializer rendering your DRF views apply, just without the HTTP hop.
pip install djangorestframework-pydantic-aiIt depends only on djangorestframework-services and pydantic-ai-slim. A model
provider is pulled in separately, the usual Pydantic-AI way (e.g.
pip install "pydantic-ai-slim[anthropic]").
from pydantic_ai import Agent
from rest_framework_pydantic_ai import AgentDeps, SpecToolset
toolset = SpecToolset({
"list_orders": orders_selector_spec, # SelectorSpec -> read-only tool
"create_order": create_order_spec, # ServiceSpec -> mutation tool
})
agent = Agent("anthropic:claude-opus-4-8", deps_type=AgentDeps, toolsets=[toolset])
result = await agent.run(
"create an order of 3 widgets",
deps=AgentDeps(user=request.user),
)The agent acts as deps.user: each call builds an off-HTTP request/view context,
enforces the spec's permission_classes (class- and object-level),
dispatches the spec, and renders the result through the spec's serializer. List
selectors gain page / limit / order tool args.
Failures map onto the model loop so the agent self-corrects: invalid input, a bad
page / limit / order value, and unexpected (hallucinated) arguments come
back as ModelRetry; a business error or missing row becomes a readable
{"error": ...}; a denied permission raises PermissionDenied and aborts the
run. Unexpected arguments are rejected by default — pass unknown_arguments=
to SpecToolset (IGNORE / PASSTHROUGH) to change that.
See the documentation for the full reference.
MIT