Skip to content

Reference

The public API is two symbols, both importable from the package root.

SpecToolset

SpecToolset

Bases: ExternalToolset[Any]

Exposes drf-services specs as a Pydantic-AI toolset.

Build it from a name -> spec mapping and hand it to an Agent::

toolset = SpecToolset({
    "list_orders": orders_selector_spec,   # SelectorSpec -> read-only tool
    "create_order": create_order_spec,     # ServiceSpec  -> mutation tool
})
agent = Agent(model, deps_type=AgentDeps, toolsets=[toolset])

Each key becomes one tool: the description is the spec's selector/service docstring, the parameter schema comes from spec_to_json_schema (with a list selector's page / limit / order args merged in), and the readOnlyHint annotation is derived from the spec kind (selectors read, services mutate).

get_user overrides how the acting identity is read off the run context; it defaults to ctx.deps.user.

unknown_arguments controls what happens to tool args outside a spec's declared input set — a hallucinated key the model invented. It defaults to :attr:~rest_framework_services.UnknownArguments.REJECT, which surfaces the unexpected key as a :class:pydantic_ai.ModelRetry so the model self-corrects; specs whose declared set is open (a filter_set or **kwargs selector) are unaffected. Pass IGNORE to silently drop them or PASSTHROUGH to forward them to the callable.

get_tools async

get_tools(ctx: RunContext[Any]) -> dict[str, ToolsetTool[Any]]

Re-stamp the base tools kind="function" so the run loop calls us.

ExternalToolset marks every tool kind="external", which Pydantic-AI defers — it yields the call back to the caller and ends the run, never invoking call_tool. This toolset executes specs in-process, so the tools must run like ordinary function tools.

AgentDeps

AgentDeps dataclass

Dependencies a Pydantic-AI agent passes to a :class:SpecToolset.

Carries the acting user so the toolset can run each spec under the same off-HTTP context and permission checks a DRF view would apply. Pass an instance as deps when running the agent::

agent = Agent(model, deps_type=AgentDeps, toolsets=[toolset])
await agent.run("create an order for …", deps=AgentDeps(user=request.user))

SpecToolset reads ctx.deps.user by default. A project that threads identity differently — a richer principal, a lookup keyed off a token — can keep its own deps type and hand SpecToolset a get_user extractor instead of using this class.