Agentic Django
Agentic Django is a reusable Django 6 app that wraps the OpenAI Agents SDK with
Django-friendly primitives (sessions, runs, and background tasks). The example
project lives in the sibling agentic-django-example repo.
Requirements
- Python 3.12+
- Django 6.x
Quickstart
Start with an existing Django project that has authentication and session
middleware configured. Replace my_project below with your project package name.
1. Install the package
pdm add agentic-django
For the default OpenAI provider, set OPENAI_API_KEY in the environment used by
your Django process. The package does not load .env files itself.
2. Define the agent registry
Create my_project/agent_registry.py:
from collections.abc import Callable
from agents import Agent
def build_default() -> Agent:
return Agent(name="Support Agent")
def get_agent_registry() -> dict[str, Callable[[], Agent]]:
return {"default": build_default}
This minimal agent uses the SDK's default model. Add your agent instructions,
model, and tools in build_default when you extend the integration.
3. Configure the app and local execution
Add these entries to settings.py:
INSTALLED_APPS = [
# Keep your existing apps.
"agentic_django.apps.AgenticDjangoConfig",
]
AGENTIC_DJANGO_AGENT_REGISTRY = "my_project.agent_registry.get_agent_registry"
AGENTIC_DJANGO_DEFAULT_AGENT_KEY = "default"
TASKS = {
"default": {
"BACKEND": "django_tasks.backends.immediate.ImmediateBackend",
}
}
The immediate backend runs the agent during the request. It needs no worker and is useful for a first local run. Use a background task backend for production.
4. Add the URLs and run migrations
Add the package URLs to your project's urls.py:
from django.urls import include, path
urlpatterns = [
# Keep your existing URL patterns.
path("agents/", include("agentic_django.urls", namespace="agents")),
]
pdm run python manage.py migrate
pdm run python manage.py runserver
5. Submit a run and read its result
From an authenticated client, send a JSON request to POST /agents/runs/.
Include the session cookie and a valid CSRF token, as required by your project.
{
"session_key": "first-conversation",
"input": "Hello"
}
The response contains a run_id. Request GET /agents/runs/<run_id>/ to read
its status and final_output. With a background backend, repeat that request
until the status is completed or failed.
The JSON endpoints need no HTMX setup. Continue with HTMX for HTML polling, optional configuration for background tasks and limits, or usage examples for custom views.
Repository docs
Maintainers and coding agents should start with docs/index.md. The current
architecture map lives in docs/architecture.md, validation guidance lives in
docs/quality.md, and the embedded downstream-integration skill is documented
in docs/skills.md.
Why use it
Building agentic workflows in Django usually means stitching together the OpenAI Agents SDK, persistence, and async execution on your own. This project gives you a consistent, Django-native way to:
- kick off multi-step agent runs from views or services
- persist conversation history and run status in your database
- check progress later from any UI or API client
- keep runs private to each authenticated user
- reuse the same primitives across multiple apps or projects
Benefits
- Django-first integration with models, admin, templates, and URL patterns
- simple async model using Django 6 tasks (no Celery required)
- stable polling UX for HTMX or REST clients
- per-user ownership baked into queries and views
- flexible registry so each project can provide its own agents
How it helps
If you have a workflow that can take minutes, branch into tools, or write to session memory, you can run it as a background task and poll for status just like any other Django async job. You do not need to keep a request open or build custom state tracking.
Usage examples
Create a run from a view and enqueue it for background execution:
from django.http import JsonResponse
from agentic_django.models import AgentRun, AgentSession
from agentic_django.services import enqueue_agent_run
def submit_run(request):
session, _ = AgentSession.objects.get_or_create(
owner=request.user,
session_key=request.POST["session_key"],
)
run = AgentRun.objects.create(
session=session,
owner=request.user,
agent_key="default",
input_payload=request.POST["input"],
)
enqueue_agent_run(str(run.id))
return JsonResponse({"run_id": str(run.id), "status": run.status})
Check run status later from a UI or API client:
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from agentic_django.models import AgentRun
def run_status(request, run_id):
run = get_object_or_404(AgentRun, id=run_id, owner=request.user)
return JsonResponse({
"status": run.status,
"final_output": run.final_output,
})
HTMX
If you are using the package's HTMX-oriented views and fragments, add
django-htmx to your project so requests expose request.htmx and you can use
the vendored script tags with Django 6 CSP nonces:
INSTALLED_APPS = [
# ...
"django_htmx",
"agentic_django.apps.AgenticDjangoConfig",
]
MIDDLEWARE = [
# ...
"django_htmx.middleware.HtmxMiddleware",
]
{% load django_htmx static %}
<link rel="stylesheet" href="{% static 'agentic_django/agentic_django.css' %}">
{% htmx_script %}
{% django_htmx_script %}
If you are only using the JSON endpoints, you can omit django_htmx and its
middleware.
HTMX polling + coordinated updates:
<div
id="run-container-{{ run.id }}"
data-status="{{ run.status }}"
hx-get="{% url 'agents:run-fragment' run.id %}"
hx-trigger="load delay:1s, every 2s"
hx-target="#run-container-{{ run.id }}"
hx-swap="outerHTML"
>
{% load agentic_django_tags %}
{% agent_run_fragment run %}
</div>
The fragment endpoint returns HttpResponseStopPolling when a run reaches a
terminal state, so HTMX swaps in the final HTML and stops polling without extra
client-side teardown code.
The package's fragment responses also emit HX-Trigger: run-update on each
refresh so dependent panels can piggyback on the run poll loop instead of
starting their own:
# Implemented in the package's fragment views.
from django.shortcuts import render
from django_htmx.http import trigger_client_event
response = render(request, "agentic_django/partials/run_fragment.html", {"run": run})
return trigger_client_event(response, "run-update")
<div id="conversation-panel"
hx-get="{% url 'agents:session-items' session.session_key %}"
hx-trigger="run-update from:body"
hx-target="#conversation-contents"
hx-swap="innerHTML">
...
</div>
Template override note: if you create templates/agentic_django/... in your project, Django will use those files instead of the package templates with the same path. This is useful for customization, but it can hide edits made in the package templates.
Styling (optional)
The package ships a minimal stylesheet for the default fragments. If you are not already including it via the HTMX setup above, add it to your base template:
{% load static %}
<link rel="stylesheet" href="{% static 'agentic_django/agentic_django.css' %}">
Optional configuration
After the quickstart works, configure run limits and a background backend in
settings.py as needed. An RQ backend also needs Redis and a running RQ worker.
AGENTIC_DJANGO_DEFAULT_RUN_OPTIONS = {"max_turns": 6}
AGENTIC_DJANGO_CONCURRENCY_LIMIT = None # auto: CPU count
RQ_QUEUES = {
"default": {
"URL": "redis://localhost:6379/0",
}
}
# Switch to RQ-backed tasks in production
TASKS["default"]["BACKEND"] = "django_tasks.backends.rq.RQBackend"
# Optional: enable event streaming persistence
AGENTIC_DJANGO_ENABLE_EVENTS = True
# Optional: basic abuse protection for run creation
AGENTIC_DJANGO_RATE_LIMIT = "20/m"
AGENTIC_DJANGO_MAX_INPUT_BYTES = 20_000
AGENTIC_DJANGO_MAX_INPUT_ITEMS = 20
# Optional: cleanup policy for old records
AGENTIC_DJANGO_CLEANUP_POLICY = {
"events_days": 7,
"runs_days": 30,
"runs_statuses": ["completed", "failed"],
"sessions_days": 90,
"sessions_require_empty": True,
"batch_size": 500,
}
Request limits use one database counter per user. Atomic updates enforce the limit across workers, without a cache dependency. Run migrations before use.
Optional dependencies
- RQ-backed tasks:
pdm install -G rq - Postgres driver:
pdm install -G postgres
Event streaming (optional)
When AGENTIC_DJANGO_ENABLE_EVENTS = True, each agent run persists semantic events
(tool calls, tool outputs, message items). Poll for events with:
GET /runs/<uuid:run_id>/events/?after=<sequence>&limit=<n>
You can also subscribe to the Django signal agent_run_event to push UI updates
after each event is stored.
Operations
Prune old data with the cleanup command (uses AGENTIC_DJANGO_CLEANUP_POLICY by default):
python manage.py agentic_django_cleanup --dry-run
python manage.py agentic_django_cleanup --events-days 14 --runs-days 60
Recover runs stuck in running after a restart:
python manage.py agentic_django_recover_runs --mode=fail
python manage.py agentic_django_recover_runs --mode=requeue
Recovery is manual. Stop all run workers and pause submissions before recovery.
A new process must not reset work that another worker still executes. The
AGENTIC_DJANGO_STARTUP_RECOVERY setting has been removed. Requeue only when
repeating the run and its tool actions is safe.
Security notes
- Enable Django 6’s Content Security Policy support where feasible, and open
connect-srconly to the endpoints your UI needs (for polling or tooling). - Keep agent tool registries scoped; do not expose powerful tools to untrusted user input without additional validation or allowlists.
Example project
The sample project lives in the sibling agentic-django-example repo. See its
README for setup, Docker, and run instructions.
Tests
pdm run test
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 agentic_django-0.3.0.tar.gz.
File metadata
- Download URL: agentic_django-0.3.0.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7ce76c2cb89ce1f4a75d543738fc5a16c160cc2bbd3d6ab4d9e019690a59631
|
|
| MD5 |
5c4e6248c7efd1e826d44e21c2096bf5
|
|
| BLAKE2b-256 |
75ede467c01083f0d4bff914b4f930287495b0fa474afd5015897735722fc209
|
Provenance
The following attestation bundles were made for agentic_django-0.3.0.tar.gz:
Publisher:
python-publish.yml on btfranklin/agentic-django
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentic_django-0.3.0.tar.gz -
Subject digest:
c7ce76c2cb89ce1f4a75d543738fc5a16c160cc2bbd3d6ab4d9e019690a59631 - Sigstore transparency entry: 2732409366
- Sigstore integration time:
-
Permalink:
btfranklin/agentic-django@db18a4730055ba4449d84fb967c69d233cb31372 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/btfranklin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@db18a4730055ba4449d84fb967c69d233cb31372 -
Trigger Event:
release
-
Statement type:
File details
Details for the file agentic_django-0.3.0-py3-none-any.whl.
File metadata
- Download URL: agentic_django-0.3.0-py3-none-any.whl
- Upload date:
- Size: 30.5 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 |
94f0c0a5ba71bcd91105bd3c5fadaa2f2e84791684e2fa675c5c670ddf7a37c6
|
|
| MD5 |
29ccc5b77fe20f36a179d416b18f3d49
|
|
| BLAKE2b-256 |
d6dd8c53f7aef1129272f5e34f873a81636345bf2dcb0912b2a086ca22a77cf3
|
Provenance
The following attestation bundles were made for agentic_django-0.3.0-py3-none-any.whl:
Publisher:
python-publish.yml on btfranklin/agentic-django
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentic_django-0.3.0-py3-none-any.whl -
Subject digest:
94f0c0a5ba71bcd91105bd3c5fadaa2f2e84791684e2fa675c5c670ddf7a37c6 - Sigstore transparency entry: 2732410060
- Sigstore integration time:
-
Permalink:
btfranklin/agentic-django@db18a4730055ba4449d84fb967c69d233cb31372 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/btfranklin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@db18a4730055ba4449d84fb967c69d233cb31372 -
Trigger Event:
release
-
Statement type: