Metadata-Version: 2.5
Name: djangorestframework-pydantic-ai
Version: 0.36.3
Summary: Expose djangorestframework-services services and selectors as a Pydantic-AI toolset.
Project-URL: Homepage, https://github.com/Artui/djangorestframework-pydantic-ai
Project-URL: Repository, https://github.com/Artui/djangorestframework-pydantic-ai
Project-URL: Issues, https://github.com/Artui/djangorestframework-pydantic-ai/issues
Author-email: Artur Veres <artur8118@gmail.com>
License: MIT
License-File: LICENSE
Keywords: agent,ai,django,drf,llm,pydantic-ai,rest-framework,tools
Classifier: Development Status :: 7 - Inactive
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: django-service-specs>=0.10.0
Requires-Dist: django>=4.2
Requires-Dist: djangorestframework-services>=0.58.0
Requires-Dist: djangorestframework>=3.14
Requires-Dist: pydantic-ai-slim<3,>=2.16
Provides-Extra: filter
Requires-Dist: django-filter>=23; extra == 'filter'
Description-Content-Type: text/markdown

> **This package has moved.** `djangorestframework-pydantic-ai` is no longer
> developed; 0.36.x is its final line, and 0.36.3 only fixes the test doubles
> in `rest_framework_pydantic_ai.testing` for pydantic-ai 2.60 and later.
> Development continues as
> [`django-spec-agents`](https://pypi.org/project/django-spec-agents/), where this
> package is now `django_spec_agents.spec_tools`
> (`pip install "django-spec-agents[specs]"`). See
> [Moving to django-spec-agents](https://artui.github.io/djangorestframework-pydantic-ai/moving-to-django-spec-agents/)
> for what each name became, and the
> [new documentation](https://artui.github.io/django-spec-agents/).

# djangorestframework-pydantic-ai

[![CI](https://github.com/Artui/djangorestframework-pydantic-ai/workflows/tests/badge.svg)](https://github.com/Artui/djangorestframework-pydantic-ai/actions/workflows/tests.yml)
[![PyPI](https://img.shields.io/pypi/v/djangorestframework-pydantic-ai.svg)](https://pypi.org/project/djangorestframework-pydantic-ai/)
[![Python versions](https://img.shields.io/pypi/pyversions/djangorestframework-pydantic-ai.svg)](https://pypi.org/project/djangorestframework-pydantic-ai/)
[![Django versions](https://img.shields.io/pypi/djversions/djangorestframework-pydantic-ai.svg)](https://pypi.org/project/djangorestframework-pydantic-ai/)
[![Docs](https://img.shields.io/badge/docs-artui.github.io-blue.svg)](https://artui.github.io/djangorestframework-pydantic-ai/)
[![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Artui/djangorestframework-pydantic-ai/gh-pages/coverage.json)](https://github.com/Artui/djangorestframework-pydantic-ai/actions/workflows/tests.yml)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![License](https://img.shields.io/pypi/l/djangorestframework-pydantic-ai.svg)](https://github.com/Artui/djangorestframework-pydantic-ai/blob/main/LICENSE)

Expose [`djangorestframework-services`](https://github.com/Artui/djangorestframework-services)
services and selectors as a [Pydantic-AI](https://ai.pydantic.dev) 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.

## Install

```bash
pip install djangorestframework-pydantic-ai
```

It 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]"`).

## Quickstart

```python
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, and return **one page** —
`{"items": [...], "page": 1, "totalPages": N, "hasNext": bool}` — so a model
reading a collection can tell whether it saw all of it.

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
`ToolFailed`, which the model sees as a failed call rather than a successful one
carrying prose, and a call an `Affordance` refuses names the rule as well as the
reason (`The books are closed. (code: books_closed)`); a denied permission raises `PermissionDenied`, which aborts
the run under a plain `pydantic_ai.Agent`. A host that installs a tool-failure
policy — `django-pydantic-agent`'s `build_agent` does by default — turns that
denial into a failed-tool result instead, so the run continues; the call is
still denied either way. Unexpected arguments are **rejected by default** — pass
`unknown_arguments=UnknownArguments.IGNORE` to `SpecToolset` to drop them instead.

A tool whose `Affordance` condition is unmet right now, such as posting to a closed
period, is left out of that step's tool list, and the step's instructions name it
with its reason so the model can tell the user why. Permissions do not filter the
list: a denied tool is still offered, and the denial comes on the call.

Every line of the instructions block is a field of `AgentConventions`, the
heading of the unavailable list included, and so are a handle field's fallback
description, the sentence scoping a paged tool's read-shaping parameters, and the
missing-argument retry. `SpecToolset(specs, conventions=AgentConventions(pagination=...))`
rewords one line and leaves the rest as they are. The toolset still decides when
each line is said, and `None` drops a line. Argument descriptions, such as those
of `limit` and `page`, and the toolset's other retries stay fixed.

An argument the model must not choose, such as the project a run is scoped to,
is the server's to supply: `SpecToolset(registry, tool_server_arguments={"list_vendors":
ServerArguments(values={"project_pk": "7"})})` binds it on every call to that
tool, takes it out of the tool's schema, and drops whatever the model sends
under it. `ServerArguments.withheld` keeps a name from the model with no value
in its place, so its default answers.

See the [documentation](https://artui.github.io/djangorestframework-pydantic-ai/)
for the full reference.

## License

MIT
