django-ag-ui
Wire a Pydantic-AI agent into any Django project and
speak the AG-UI protocol to a browser — a streaming
agent endpoint, a typed tool registry, and the plumbing in between. No admin
specifics; that lives in the downstream
django-admin-agent, and the
browser half is
@artooi/ag-ui-web-component.
- Async AG-UI endpoint —
DjangoAGUIViewwraps Pydantic-AI'sAGUIAdapterand returns aStreamingHttpResponseof AG-UI events (SSE). Conversation state rides in each request, so there's no cross-request session store and multi-worker deployments are safe by default. - Typed tool registry — register plain callables with
@tool; JSON Schema is derived from their signatures.destructive=/category=/confirm=/summary=metadata surface asx-destructive/x-category/x-confirm/x-summaryextensions for client-side gating. - Configurable agent — the
DJANGO_AG_UIsettings cover the scalars (the model,MODEL_SETTINGS,RETRIES, an explicitAPI_KEY), and collaborators are constructor arguments onAGUIServer:toolsets=/capabilities=,provider=, and anagent_factory=escape hatch for full control of construction. - Authentication, closed by default — every route refuses anonymous
requests (
401) until you say otherwise, and aget_user(request)hook establishes the user tools, the drf-mcp bridge, and conversation ownership act as. See Security defaults. - One-object mounting — an
AGUIServer(registry, …)config object exposing a namespaced.urls, mounted theadmin.siteway withpath("agent/", server.urls). It builds the agent view and every sub-view from the registry passed once, and forwards one auth policy to all of them. - Skills — a
SkillRegistry/SkillSpeccatalog of pre-defined prompts served at<prefix>skills/(viaAGUIServer(..., skills=...)), surfaced by the web component as chips and a/-command palette. - Tool metadata catalog — a read-only
ToolsViewserved at<prefix>tools/(mounted automatically byAGUIServer), giving the web component (data-tools-url) friendly card labels for server-side tools whose schema never reaches the browser. - Rate limiting — a
throttlehook on the agent endpoint (consume(request)→Retry-Afterseconds orNone), with a cache-backedFixedWindowThrottleshipped; runs after authentication, so a limiter keys on the acting user. - Audit boundary — an
AuditLoggerProtocol (Null/Loggingshipped, pluggable by dotted path) records every server-side tool call. - Opt-in conversation persistence — a
ConversationStoreProtocol with a no-op default, a session-backed store, and an abstract model-backed base. - Thread history — the store can
listandrenamea user's threads, and aThreadsViewserved at<prefix>threads/(mounted byAGUIServerwhen a store is active) backs a chat-history drawer (owner-scoped GET list / GET messages / PATCH rename / DELETE). An opt-indjango_ag_ui.contrib.storeapp ships a ready-made durable model +DefaultConversationStore(add it toINSTALLED_APPSandmigrate); the base package still ships no model. - File uploads — an
AttachmentStoreProtocol (owner-scoped, off by default) with anAttachmentsViewserved at<prefix>attachments/(mounted byAGUIServerwhen a store is active; server-validated POST upload / owner-checked GET download / DELETE). Uploads travel as lightweight refs, and a per-requestread_attachmenttool lets the agent read the bytes server-side. The samecontrib.storeapp ships aStorage-backedDefaultAttachmentStore. - Voice input — a
TranscriptionBackendProtocol (off by default) with aTranscribeViewserved at<prefix>transcribe/(mounted byAGUIServerwhen a backend is active; multipart audio in,{"text"}out). An opt-inOpenAITranscriptionBackendworks against any OpenAI-compatible/audio/transcriptionsendpoint (the[openai]extra). - Model reasoning — when a reasoning model is configured to think (via
MODEL_SETTINGS), its chain-of-thought streams to the client as standard AG-UI reasoning events (pure pass-through);FORWARD_REASONING = Falsekeeps it server-side. - Reach external tools — compose any Pydantic-AI toolset, including an
in-process
drf-mcpbridge (the[drf-mcp]extra) so the agent can query DRF-exposed data. - drf-services specs as tools, no MCP hop — pass
service_specs=aname → specmapping and the agent calls them in-process viadjangorestframework-pydantic-ai'sSpecCapability(the[spec-tools]extra) — permission-checked, acting as the logged-in user, with the spec conventions taught to the model. - 100% test coverage, type-checked, Python 3.10–3.14, Django 4.2–6.0.
📖 Full documentation: https://artui.github.io/django-ag-ui/
pip install "django-ag-ui[anthropic]" # or [openai], or [google]
# or, with uv:
uv add "django-ag-ui[anthropic]"
The core dep is
pydantic-ai-slim[ag-ui], which ships no model-provider library — pick one via a provider extra (anthropic/openai/
ASGI required. The agent endpoint streams Server-Sent Events, which the sync WSGI worker can't serve — deploy under Daphne / Uvicorn.
Quick start
Register a read-only tool, mount the endpoint, and point a browser AG-UI client at it.
# tools.py
from django_ag_ui import ToolRegistry, tool
registry = ToolRegistry()
@tool(registry)
def count_active_users() -> int:
"""How many users are currently active."""
from django.contrib.auth import get_user_model
return get_user_model().objects.filter(is_active=True).count()
# urls.py
from django.urls import path
from django_ag_ui import AGUIServer
from .tools import registry
urlpatterns = [
path("agent/", AGUIServer(registry).urls),
]
# settings.py
DJANGO_AG_UI = {
"MODEL": "anthropic:claude-sonnet-4.6", # any Pydantic-AI model string
# "API_KEY": os.environ["ANTHROPIC_API_KEY"], # else inferred from env
# "MODEL_SETTINGS": {"temperature": 0.2},
}
POSTing an AG-UI RunAgentInput to /agent/ now streams the agent's run.
Frontend-declared tools in the request are merged into the agent's catalog
automatically; server-side tools run in-process. See the
docs for the full settings reference,
the persistence stores, and the drf-mcp bridge.
Security defaults
An agent endpoint is not an ordinary view: server-side tools act as
request.user, so who reaches the endpoint decides what the model can read
and change. Three defaults are worth knowing before you deploy.
1. Anonymous requests are refused
require_authenticated defaults to True on the agent endpoint and on every
sub-view AGUIServer mounts — the tool and skill catalogs, the thread drawer,
the attachment routes, transcription, the run index. An anonymous request gets
401 with JSON {"error": "authentication required"}.
AGUIServer(registry, require_authenticated=False) # serve anonymous runs
Waiving it is a real choice for a public demo assistant with no user-scoped
tools. It is the wrong choice anywhere the tools read user data: without an
authenticated user, request.user is AnonymousUser and every visitor shares
one identity.
2. Establish the acting user with get_user
Refusing anonymous callers is not the same as knowing who is calling. Django's auth middleware answers that for cookie-authenticated sites; for token clients, pass a hook. It may be sync or async — a sync hook runs off the event loop, so a plain ORM lookup is fully supported:
def get_user(request):
token = request.headers.get("Authorization", "").removeprefix("Bearer ").strip()
return Token.objects.select_related("user").get(key=token).user
AGUIServer(registry, get_user=get_user)
Its return value is assigned onto request.user. A hook that raises propagates
as a 500 — return AnonymousUser (or None) for a clean 401 instead. An
authorize= predicate runs after the user is established and denies with 403
(JSON, never an HTML login redirect), which is the seam for a staff gate.
3. CSRF is exempt unless you say otherwise
AG-UI clients typically authenticate by header (Bearer / API key), where CSRF does not apply — so the view is CSRF-exempt by default.
If your deployment authenticates with session cookies, that default is
wrong for you. Tools act as request.user, so a cookie-authenticated endpoint
with CSRF off lets any third-party page drive the agent as whoever is logged in
— mitigated, but not eliminated, by Django's default SameSite=Lax cookie.
AGUIServer(registry, csrf_exempt=False) # and send X-CSRFToken from the client
Leaving csrf_exempt unset and passing no get_user hook emits a
RuntimeWarning when the endpoint is built. That combination says nothing about
how requests authenticate, and the likeliest reading is the dangerous one. It is
the case the require_authenticated default cannot see — those requests are
authenticated. Any of three answers settles it and silences the warning:
csrf_exempt=False, csrf_exempt=True (deliberately exempt), or a get_user
hook.
Anonymous requests and the stores
The model-backed stores refuse anonymous thread / attachment operations unless
built with allow_anonymous=True (which buckets per browser session). Owner
scoping alone cannot isolate anonymous visitors from one another — they have no
user id — so prefer an authenticated endpoint over allow_anonymous=True
whenever a store persists.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_ag_ui-0.36.0.tar.gz.
File metadata
- Download URL: django_ag_ui-0.36.0.tar.gz
- Upload date:
- Size: 326.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91df9be28c62b156ed5f77f0d71aef97ce26bf414db9fc3fb3bb5a92cd6e343c
|
|
| MD5 |
a86653567b8fdcefd986feae2e068c2e
|
|
| BLAKE2b-256 |
ea298e4f65eac6befef6c393572b8407131a88f19ac04ce75a753430de2626ed
|
Provenance
The following attestation bundles were made for django_ag_ui-0.36.0.tar.gz:
Publisher:
release.yml on Artui/django-ag-ui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_ag_ui-0.36.0.tar.gz -
Subject digest:
91df9be28c62b156ed5f77f0d71aef97ce26bf414db9fc3fb3bb5a92cd6e343c - Sigstore transparency entry: 2417803443
- Sigstore integration time:
-
Permalink:
Artui/django-ag-ui@b60a28d48cfdf044775e5c0939088a173f545012 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Artui
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b60a28d48cfdf044775e5c0939088a173f545012 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_ag_ui-0.36.0-py3-none-any.whl.
File metadata
- Download URL: django_ag_ui-0.36.0-py3-none-any.whl
- Upload date:
- Size: 72.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55c16e00327d17efd6f1619fe56758c7f1c9dba3fdbd69bd4c3ffd088ffdf763
|
|
| MD5 |
fdaa1a6f4d59d8980a753dcd63f0d3a5
|
|
| BLAKE2b-256 |
c2e044e89a0e9fe7f13fb28f332dd9af172c4b93f004ea857da833e87450f4ce
|
Provenance
The following attestation bundles were made for django_ag_ui-0.36.0-py3-none-any.whl:
Publisher:
release.yml on Artui/django-ag-ui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_ag_ui-0.36.0-py3-none-any.whl -
Subject digest:
55c16e00327d17efd6f1619fe56758c7f1c9dba3fdbd69bd4c3ffd088ffdf763 - Sigstore transparency entry: 2417803477
- Sigstore integration time:
-
Permalink:
Artui/django-ag-ui@b60a28d48cfdf044775e5c0939088a173f545012 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Artui
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b60a28d48cfdf044775e5c0939088a173f545012 -
Trigger Event:
push
-
Statement type: