agentic-django 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentic_django-0.1.0/LICENSE +21 -0
- agentic_django-0.1.0/PKG-INFO +306 -0
- agentic_django-0.1.0/README.md +250 -0
- agentic_django-0.1.0/pyproject.toml +94 -0
- agentic_django-0.1.0/src/agentic_django/__init__.py +0 -0
- agentic_django-0.1.0/src/agentic_django/admin.py +75 -0
- agentic_django-0.1.0/src/agentic_django/apps.py +14 -0
- agentic_django-0.1.0/src/agentic_django/conf.py +218 -0
- agentic_django-0.1.0/src/agentic_django/management/__init__.py +1 -0
- agentic_django-0.1.0/src/agentic_django/management/commands/__init__.py +1 -0
- agentic_django-0.1.0/src/agentic_django/management/commands/agentic_django_cleanup.py +145 -0
- agentic_django-0.1.0/src/agentic_django/management/commands/agentic_django_recover_runs.py +24 -0
- agentic_django-0.1.0/src/agentic_django/migrations/0001_initial.py +198 -0
- agentic_django-0.1.0/src/agentic_django/migrations/__init__.py +0 -0
- agentic_django-0.1.0/src/agentic_django/models.py +151 -0
- agentic_django-0.1.0/src/agentic_django/py.typed +1 -0
- agentic_django-0.1.0/src/agentic_django/registry.py +25 -0
- agentic_django-0.1.0/src/agentic_django/serializers.py +103 -0
- agentic_django-0.1.0/src/agentic_django/services.py +408 -0
- agentic_django-0.1.0/src/agentic_django/sessions.py +121 -0
- agentic_django-0.1.0/src/agentic_django/signals.py +9 -0
- agentic_django-0.1.0/src/agentic_django/static/agentic_django/agentic_django.css +90 -0
- agentic_django-0.1.0/src/agentic_django/tasks.py +10 -0
- agentic_django-0.1.0/src/agentic_django/templates/agentic_django/partials/conversation.html +11 -0
- agentic_django-0.1.0/src/agentic_django/templates/agentic_django/partials/run_fragment.html +39 -0
- agentic_django-0.1.0/src/agentic_django/templatetags/__init__.py +0 -0
- agentic_django-0.1.0/src/agentic_django/templatetags/agentic_django_tags.py +35 -0
- agentic_django-0.1.0/src/agentic_django/urls.py +27 -0
- agentic_django-0.1.0/src/agentic_django/views.py +244 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Agentic Django contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: agentic-django
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Django integration for building agentic apps with the OpenAI Agents SDK
|
|
5
|
+
Author-Email: "B.T. Franklin" <brandon.franklin@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Agentic Django contributors
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: Programming Language :: Python :: 3
|
|
29
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
33
|
+
Classifier: Framework :: Django
|
|
34
|
+
Classifier: Typing :: Typed
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Operating System :: OS Independent
|
|
37
|
+
Classifier: Intended Audience :: Developers
|
|
38
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
39
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
41
|
+
Project-URL: Homepage, https://github.com/btfranklin/agentic-django
|
|
42
|
+
Project-URL: Issues, https://github.com/btfranklin/agentic-django/issues
|
|
43
|
+
Project-URL: Changelog, https://github.com/btfranklin/agentic-django/releases
|
|
44
|
+
Project-URL: Repository, https://github.com/btfranklin/agentic-django.git
|
|
45
|
+
Requires-Python: >=3.12
|
|
46
|
+
Requires-Dist: Django<7,>=6
|
|
47
|
+
Requires-Dist: django-tasks>=0.11.0
|
|
48
|
+
Requires-Dist: openai-agents>=0.6.5
|
|
49
|
+
Requires-Dist: pydantic>=2.11.0
|
|
50
|
+
Provides-Extra: rq
|
|
51
|
+
Requires-Dist: django-tasks[rq]>=0.11.0; extra == "rq"
|
|
52
|
+
Requires-Dist: redis>=5.0.0; extra == "rq"
|
|
53
|
+
Provides-Extra: postgres
|
|
54
|
+
Requires-Dist: psycopg[binary]>=3.2.0; extra == "postgres"
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
# Agentic Django
|
|
58
|
+
|
|
59
|
+
Agentic Django is a reusable Django 6 app that wraps the OpenAI Agents SDK with
|
|
60
|
+
Django-friendly primitives (sessions, runs, and background tasks). The example
|
|
61
|
+
project lives in the sibling `agentic-django-example` repo.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Python 3.12+
|
|
66
|
+
- Django 6.x
|
|
67
|
+
|
|
68
|
+
## Why use it
|
|
69
|
+
|
|
70
|
+
Building agentic workflows in Django usually means stitching together the OpenAI
|
|
71
|
+
Agents SDK, persistence, and async execution on your own. This project gives you
|
|
72
|
+
a consistent, Django-native way to:
|
|
73
|
+
|
|
74
|
+
- kick off multi-step agent runs from views or services
|
|
75
|
+
- persist conversation history and run status in your database
|
|
76
|
+
- check progress later from any UI or API client
|
|
77
|
+
- keep runs private to each authenticated user
|
|
78
|
+
- reuse the same primitives across multiple apps or projects
|
|
79
|
+
|
|
80
|
+
## Benefits
|
|
81
|
+
|
|
82
|
+
- Django-first integration with models, admin, templates, and URL patterns
|
|
83
|
+
- simple async model using Django 6 tasks (no Celery required)
|
|
84
|
+
- stable polling UX for HTMX or REST clients
|
|
85
|
+
- per-user ownership baked into queries and views
|
|
86
|
+
- flexible registry so each project can provide its own agents
|
|
87
|
+
|
|
88
|
+
## How it helps
|
|
89
|
+
|
|
90
|
+
If you have a workflow that can take minutes, branch into tools, or write to
|
|
91
|
+
session memory, you can run it as a background task and poll for status just
|
|
92
|
+
like any other Django async job. You do not need to keep a request open or build
|
|
93
|
+
custom state tracking.
|
|
94
|
+
|
|
95
|
+
## Usage examples
|
|
96
|
+
|
|
97
|
+
Create a run from a view and enqueue it for background execution:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from agentic_django.models import AgentRun, AgentSession
|
|
101
|
+
from agentic_django.services import enqueue_agent_run
|
|
102
|
+
|
|
103
|
+
def submit_run(request):
|
|
104
|
+
session, _ = AgentSession.objects.get_or_create(
|
|
105
|
+
owner=request.user,
|
|
106
|
+
session_key=request.POST["session_key"],
|
|
107
|
+
)
|
|
108
|
+
run = AgentRun.objects.create(
|
|
109
|
+
session=session,
|
|
110
|
+
owner=request.user,
|
|
111
|
+
agent_key="demo",
|
|
112
|
+
input_payload=request.POST["input"],
|
|
113
|
+
)
|
|
114
|
+
enqueue_agent_run(str(run.id))
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Check run status later from a UI or API client:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from django.shortcuts import get_object_or_404
|
|
121
|
+
from agentic_django.models import AgentRun
|
|
122
|
+
|
|
123
|
+
def run_status(request, run_id):
|
|
124
|
+
run = get_object_or_404(AgentRun, id=run_id, owner=request.user)
|
|
125
|
+
return {
|
|
126
|
+
"status": run.status,
|
|
127
|
+
"final_output": run.final_output,
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## HTMX
|
|
132
|
+
|
|
133
|
+
HTMX polling + coordinated updates:
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<div
|
|
137
|
+
id="run-container-{{ run.id }}"
|
|
138
|
+
data-status="{{ run.status }}"
|
|
139
|
+
hx-get="{% url 'agents:run-fragment' run.id %}"
|
|
140
|
+
hx-trigger="load delay:1s, every 2s"
|
|
141
|
+
hx-target="#run-container-{{ run.id }}"
|
|
142
|
+
hx-swap="outerHTML"
|
|
143
|
+
hx-on::afterSwap="if (this.dataset.status === 'completed' || this.dataset.status === 'failed') { this.removeAttribute('hx-get'); this.removeAttribute('hx-trigger'); }"
|
|
144
|
+
>
|
|
145
|
+
{% load agentic_django_tags %}
|
|
146
|
+
{% agent_run_fragment run %}
|
|
147
|
+
</div>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Coordinate panels with `HX-Trigger` so you only update when there is run activity:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# views.py
|
|
154
|
+
response = render(request, "agentic_django/partials/run_fragment.html", {"run": run})
|
|
155
|
+
response["HX-Trigger"] = "run-update"
|
|
156
|
+
return response
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<div id="conversation-panel"
|
|
161
|
+
hx-get="{% url 'agents:session-items' session.session_key %}"
|
|
162
|
+
hx-trigger="run-update from:body"
|
|
163
|
+
hx-target="#conversation-contents"
|
|
164
|
+
hx-swap="innerHTML">
|
|
165
|
+
...
|
|
166
|
+
</div>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
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.
|
|
170
|
+
|
|
171
|
+
## Styling (optional)
|
|
172
|
+
|
|
173
|
+
The package ships a minimal stylesheet for the default fragments. Include it in your base template:
|
|
174
|
+
|
|
175
|
+
```html
|
|
176
|
+
{% load static %}
|
|
177
|
+
<link rel="stylesheet" href="{% static 'agentic_django/agentic_django.css' %}">
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
Add the app and configure the agent registry in `settings.py`:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
INSTALLED_APPS = [
|
|
186
|
+
# ...
|
|
187
|
+
"agentic_django.apps.AgenticDjangoConfig",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
AGENTIC_DJANGO_AGENT_REGISTRY = "my_project.agent_registry.get_agent_registry"
|
|
191
|
+
AGENTIC_DJANGO_DEFAULT_AGENT_KEY = "default"
|
|
192
|
+
AGENTIC_DJANGO_DEFAULT_RUN_OPTIONS = {"max_turns": 6}
|
|
193
|
+
AGENTIC_DJANGO_CONCURRENCY_LIMIT = None # auto: CPU count
|
|
194
|
+
|
|
195
|
+
# django-tasks backend selection (Immediate by default, RQ in production)
|
|
196
|
+
TASKS = {
|
|
197
|
+
"default": {
|
|
198
|
+
"BACKEND": "django_tasks.backends.immediate.ImmediateBackend",
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
RQ_QUEUES = {
|
|
203
|
+
"default": {
|
|
204
|
+
"URL": "redis://localhost:6379/0",
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Switch to RQ-backed tasks in production
|
|
209
|
+
TASKS["default"]["BACKEND"] = "django_tasks.backends.rq.RQBackend"
|
|
210
|
+
|
|
211
|
+
# Optional: enable event streaming persistence
|
|
212
|
+
AGENTIC_DJANGO_ENABLE_EVENTS = True
|
|
213
|
+
|
|
214
|
+
# Optional: basic abuse protection for run creation
|
|
215
|
+
AGENTIC_DJANGO_RATE_LIMIT = "20/m"
|
|
216
|
+
AGENTIC_DJANGO_MAX_INPUT_BYTES = 20_000
|
|
217
|
+
AGENTIC_DJANGO_MAX_INPUT_ITEMS = 20
|
|
218
|
+
|
|
219
|
+
# Optional: override startup recovery (default: requeue)
|
|
220
|
+
AGENTIC_DJANGO_STARTUP_RECOVERY = "fail"
|
|
221
|
+
|
|
222
|
+
# Optional: cleanup policy for old records
|
|
223
|
+
AGENTIC_DJANGO_CLEANUP_POLICY = {
|
|
224
|
+
"events_days": 7,
|
|
225
|
+
"runs_days": 30,
|
|
226
|
+
"runs_statuses": ["completed", "failed"],
|
|
227
|
+
"sessions_days": 90,
|
|
228
|
+
"sessions_require_empty": True,
|
|
229
|
+
"batch_size": 500,
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Optional dependencies
|
|
234
|
+
|
|
235
|
+
- RQ-backed tasks: `pdm install -G rq`
|
|
236
|
+
- Postgres driver: `pdm install -G postgres`
|
|
237
|
+
|
|
238
|
+
## Event streaming (optional)
|
|
239
|
+
|
|
240
|
+
When `AGENTIC_DJANGO_ENABLE_EVENTS = True`, each agent run persists semantic events
|
|
241
|
+
(tool calls, tool outputs, message items). Poll for events with:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
GET /runs/<uuid:run_id>/events/?after=<sequence>&limit=<n>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
You can also subscribe to the Django signal `agent_run_event` to push UI updates
|
|
248
|
+
after each event is stored.
|
|
249
|
+
|
|
250
|
+
Provide a registry that returns agent factories:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from agents import Agent
|
|
254
|
+
from my_project.models import MyModelProvider
|
|
255
|
+
|
|
256
|
+
def get_agent_registry():
|
|
257
|
+
def build_default():
|
|
258
|
+
return Agent(
|
|
259
|
+
name="Support Agent",
|
|
260
|
+
instructions="Help the user with account issues.",
|
|
261
|
+
model=MyModelProvider(),
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return {"default": build_default}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Operations
|
|
268
|
+
|
|
269
|
+
Prune old data with the cleanup command (uses `AGENTIC_DJANGO_CLEANUP_POLICY` by default):
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
python manage.py agentic_django_cleanup --dry-run
|
|
273
|
+
python manage.py agentic_django_cleanup --events-days 14 --runs-days 60
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Recover runs stuck in `running` after a restart:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
python manage.py agentic_django_recover_runs --mode=fail
|
|
280
|
+
python manage.py agentic_django_recover_runs --mode=requeue
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Startup recovery runs on the first run dispatch/execution in each process, so it does
|
|
284
|
+
not touch the database during app initialization.
|
|
285
|
+
|
|
286
|
+
## Security notes
|
|
287
|
+
|
|
288
|
+
- Enable Django 6’s Content Security Policy support where feasible, and open
|
|
289
|
+
`connect-src` only to the endpoints your UI needs (for polling or tooling).
|
|
290
|
+
- Keep agent tool registries scoped; do not expose powerful tools to untrusted
|
|
291
|
+
user input without additional validation or allowlists.
|
|
292
|
+
|
|
293
|
+
## Example project
|
|
294
|
+
|
|
295
|
+
The sample project lives in the sibling `agentic-django-example` repo. See its
|
|
296
|
+
README for setup, Docker, and run instructions.
|
|
297
|
+
|
|
298
|
+
## Tests
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
pdm run test
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Agentic Django
|
|
2
|
+
|
|
3
|
+
Agentic Django is a reusable Django 6 app that wraps the OpenAI Agents SDK with
|
|
4
|
+
Django-friendly primitives (sessions, runs, and background tasks). The example
|
|
5
|
+
project lives in the sibling `agentic-django-example` repo.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Python 3.12+
|
|
10
|
+
- Django 6.x
|
|
11
|
+
|
|
12
|
+
## Why use it
|
|
13
|
+
|
|
14
|
+
Building agentic workflows in Django usually means stitching together the OpenAI
|
|
15
|
+
Agents SDK, persistence, and async execution on your own. This project gives you
|
|
16
|
+
a consistent, Django-native way to:
|
|
17
|
+
|
|
18
|
+
- kick off multi-step agent runs from views or services
|
|
19
|
+
- persist conversation history and run status in your database
|
|
20
|
+
- check progress later from any UI or API client
|
|
21
|
+
- keep runs private to each authenticated user
|
|
22
|
+
- reuse the same primitives across multiple apps or projects
|
|
23
|
+
|
|
24
|
+
## Benefits
|
|
25
|
+
|
|
26
|
+
- Django-first integration with models, admin, templates, and URL patterns
|
|
27
|
+
- simple async model using Django 6 tasks (no Celery required)
|
|
28
|
+
- stable polling UX for HTMX or REST clients
|
|
29
|
+
- per-user ownership baked into queries and views
|
|
30
|
+
- flexible registry so each project can provide its own agents
|
|
31
|
+
|
|
32
|
+
## How it helps
|
|
33
|
+
|
|
34
|
+
If you have a workflow that can take minutes, branch into tools, or write to
|
|
35
|
+
session memory, you can run it as a background task and poll for status just
|
|
36
|
+
like any other Django async job. You do not need to keep a request open or build
|
|
37
|
+
custom state tracking.
|
|
38
|
+
|
|
39
|
+
## Usage examples
|
|
40
|
+
|
|
41
|
+
Create a run from a view and enqueue it for background execution:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from agentic_django.models import AgentRun, AgentSession
|
|
45
|
+
from agentic_django.services import enqueue_agent_run
|
|
46
|
+
|
|
47
|
+
def submit_run(request):
|
|
48
|
+
session, _ = AgentSession.objects.get_or_create(
|
|
49
|
+
owner=request.user,
|
|
50
|
+
session_key=request.POST["session_key"],
|
|
51
|
+
)
|
|
52
|
+
run = AgentRun.objects.create(
|
|
53
|
+
session=session,
|
|
54
|
+
owner=request.user,
|
|
55
|
+
agent_key="demo",
|
|
56
|
+
input_payload=request.POST["input"],
|
|
57
|
+
)
|
|
58
|
+
enqueue_agent_run(str(run.id))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Check run status later from a UI or API client:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from django.shortcuts import get_object_or_404
|
|
65
|
+
from agentic_django.models import AgentRun
|
|
66
|
+
|
|
67
|
+
def run_status(request, run_id):
|
|
68
|
+
run = get_object_or_404(AgentRun, id=run_id, owner=request.user)
|
|
69
|
+
return {
|
|
70
|
+
"status": run.status,
|
|
71
|
+
"final_output": run.final_output,
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## HTMX
|
|
76
|
+
|
|
77
|
+
HTMX polling + coordinated updates:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<div
|
|
81
|
+
id="run-container-{{ run.id }}"
|
|
82
|
+
data-status="{{ run.status }}"
|
|
83
|
+
hx-get="{% url 'agents:run-fragment' run.id %}"
|
|
84
|
+
hx-trigger="load delay:1s, every 2s"
|
|
85
|
+
hx-target="#run-container-{{ run.id }}"
|
|
86
|
+
hx-swap="outerHTML"
|
|
87
|
+
hx-on::afterSwap="if (this.dataset.status === 'completed' || this.dataset.status === 'failed') { this.removeAttribute('hx-get'); this.removeAttribute('hx-trigger'); }"
|
|
88
|
+
>
|
|
89
|
+
{% load agentic_django_tags %}
|
|
90
|
+
{% agent_run_fragment run %}
|
|
91
|
+
</div>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Coordinate panels with `HX-Trigger` so you only update when there is run activity:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# views.py
|
|
98
|
+
response = render(request, "agentic_django/partials/run_fragment.html", {"run": run})
|
|
99
|
+
response["HX-Trigger"] = "run-update"
|
|
100
|
+
return response
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<div id="conversation-panel"
|
|
105
|
+
hx-get="{% url 'agents:session-items' session.session_key %}"
|
|
106
|
+
hx-trigger="run-update from:body"
|
|
107
|
+
hx-target="#conversation-contents"
|
|
108
|
+
hx-swap="innerHTML">
|
|
109
|
+
...
|
|
110
|
+
</div>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
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.
|
|
114
|
+
|
|
115
|
+
## Styling (optional)
|
|
116
|
+
|
|
117
|
+
The package ships a minimal stylesheet for the default fragments. Include it in your base template:
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
{% load static %}
|
|
121
|
+
<link rel="stylesheet" href="{% static 'agentic_django/agentic_django.css' %}">
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
Add the app and configure the agent registry in `settings.py`:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
INSTALLED_APPS = [
|
|
130
|
+
# ...
|
|
131
|
+
"agentic_django.apps.AgenticDjangoConfig",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
AGENTIC_DJANGO_AGENT_REGISTRY = "my_project.agent_registry.get_agent_registry"
|
|
135
|
+
AGENTIC_DJANGO_DEFAULT_AGENT_KEY = "default"
|
|
136
|
+
AGENTIC_DJANGO_DEFAULT_RUN_OPTIONS = {"max_turns": 6}
|
|
137
|
+
AGENTIC_DJANGO_CONCURRENCY_LIMIT = None # auto: CPU count
|
|
138
|
+
|
|
139
|
+
# django-tasks backend selection (Immediate by default, RQ in production)
|
|
140
|
+
TASKS = {
|
|
141
|
+
"default": {
|
|
142
|
+
"BACKEND": "django_tasks.backends.immediate.ImmediateBackend",
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
RQ_QUEUES = {
|
|
147
|
+
"default": {
|
|
148
|
+
"URL": "redis://localhost:6379/0",
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Switch to RQ-backed tasks in production
|
|
153
|
+
TASKS["default"]["BACKEND"] = "django_tasks.backends.rq.RQBackend"
|
|
154
|
+
|
|
155
|
+
# Optional: enable event streaming persistence
|
|
156
|
+
AGENTIC_DJANGO_ENABLE_EVENTS = True
|
|
157
|
+
|
|
158
|
+
# Optional: basic abuse protection for run creation
|
|
159
|
+
AGENTIC_DJANGO_RATE_LIMIT = "20/m"
|
|
160
|
+
AGENTIC_DJANGO_MAX_INPUT_BYTES = 20_000
|
|
161
|
+
AGENTIC_DJANGO_MAX_INPUT_ITEMS = 20
|
|
162
|
+
|
|
163
|
+
# Optional: override startup recovery (default: requeue)
|
|
164
|
+
AGENTIC_DJANGO_STARTUP_RECOVERY = "fail"
|
|
165
|
+
|
|
166
|
+
# Optional: cleanup policy for old records
|
|
167
|
+
AGENTIC_DJANGO_CLEANUP_POLICY = {
|
|
168
|
+
"events_days": 7,
|
|
169
|
+
"runs_days": 30,
|
|
170
|
+
"runs_statuses": ["completed", "failed"],
|
|
171
|
+
"sessions_days": 90,
|
|
172
|
+
"sessions_require_empty": True,
|
|
173
|
+
"batch_size": 500,
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Optional dependencies
|
|
178
|
+
|
|
179
|
+
- RQ-backed tasks: `pdm install -G rq`
|
|
180
|
+
- Postgres driver: `pdm install -G postgres`
|
|
181
|
+
|
|
182
|
+
## Event streaming (optional)
|
|
183
|
+
|
|
184
|
+
When `AGENTIC_DJANGO_ENABLE_EVENTS = True`, each agent run persists semantic events
|
|
185
|
+
(tool calls, tool outputs, message items). Poll for events with:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
GET /runs/<uuid:run_id>/events/?after=<sequence>&limit=<n>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
You can also subscribe to the Django signal `agent_run_event` to push UI updates
|
|
192
|
+
after each event is stored.
|
|
193
|
+
|
|
194
|
+
Provide a registry that returns agent factories:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from agents import Agent
|
|
198
|
+
from my_project.models import MyModelProvider
|
|
199
|
+
|
|
200
|
+
def get_agent_registry():
|
|
201
|
+
def build_default():
|
|
202
|
+
return Agent(
|
|
203
|
+
name="Support Agent",
|
|
204
|
+
instructions="Help the user with account issues.",
|
|
205
|
+
model=MyModelProvider(),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return {"default": build_default}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Operations
|
|
212
|
+
|
|
213
|
+
Prune old data with the cleanup command (uses `AGENTIC_DJANGO_CLEANUP_POLICY` by default):
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
python manage.py agentic_django_cleanup --dry-run
|
|
217
|
+
python manage.py agentic_django_cleanup --events-days 14 --runs-days 60
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Recover runs stuck in `running` after a restart:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python manage.py agentic_django_recover_runs --mode=fail
|
|
224
|
+
python manage.py agentic_django_recover_runs --mode=requeue
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Startup recovery runs on the first run dispatch/execution in each process, so it does
|
|
228
|
+
not touch the database during app initialization.
|
|
229
|
+
|
|
230
|
+
## Security notes
|
|
231
|
+
|
|
232
|
+
- Enable Django 6’s Content Security Policy support where feasible, and open
|
|
233
|
+
`connect-src` only to the endpoints your UI needs (for polling or tooling).
|
|
234
|
+
- Keep agent tool registries scoped; do not expose powerful tools to untrusted
|
|
235
|
+
user input without additional validation or allowlists.
|
|
236
|
+
|
|
237
|
+
## Example project
|
|
238
|
+
|
|
239
|
+
The sample project lives in the sibling `agentic-django-example` repo. See its
|
|
240
|
+
README for setup, Docker, and run instructions.
|
|
241
|
+
|
|
242
|
+
## Tests
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
pdm run test
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "agentic-django"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Django integration for building agentic apps with the OpenAI Agents SDK"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "B.T. Franklin", email = "brandon.franklin@gmail.com" },
|
|
7
|
+
]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"Django>=6,<7",
|
|
10
|
+
"django-tasks>=0.11.0",
|
|
11
|
+
"openai-agents>=0.6.5",
|
|
12
|
+
"pydantic>=2.11.0",
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Framework :: Django",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
28
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.license]
|
|
33
|
+
file = "LICENSE"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/btfranklin/agentic-django"
|
|
37
|
+
Issues = "https://github.com/btfranklin/agentic-django/issues"
|
|
38
|
+
Changelog = "https://github.com/btfranklin/agentic-django/releases"
|
|
39
|
+
Repository = "https://github.com/btfranklin/agentic-django.git"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
rq = [
|
|
43
|
+
"django-tasks[rq]>=0.11.0",
|
|
44
|
+
"redis>=5.0.0",
|
|
45
|
+
]
|
|
46
|
+
postgres = [
|
|
47
|
+
"psycopg[binary]>=3.2.0",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = [
|
|
52
|
+
"pdm-backend",
|
|
53
|
+
]
|
|
54
|
+
build-backend = "pdm.backend"
|
|
55
|
+
|
|
56
|
+
[tool.pdm]
|
|
57
|
+
distribution = true
|
|
58
|
+
|
|
59
|
+
[tool.pdm.build]
|
|
60
|
+
package-dir = "src"
|
|
61
|
+
includes = [
|
|
62
|
+
"src/agentic_django/**",
|
|
63
|
+
]
|
|
64
|
+
excludes = [
|
|
65
|
+
"tests/**",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.pdm.scripts]
|
|
69
|
+
test = "pytest"
|
|
70
|
+
lint = "ruff check src tests"
|
|
71
|
+
|
|
72
|
+
[tool.pytest.ini_options]
|
|
73
|
+
DJANGO_SETTINGS_MODULE = "tests.settings"
|
|
74
|
+
django_find_project = false
|
|
75
|
+
pythonpath = [
|
|
76
|
+
".",
|
|
77
|
+
]
|
|
78
|
+
python_files = [
|
|
79
|
+
"test_*.py",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint]
|
|
83
|
+
select = [
|
|
84
|
+
"E",
|
|
85
|
+
"F",
|
|
86
|
+
"W",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[dependency-groups]
|
|
90
|
+
dev = [
|
|
91
|
+
"pytest>=9.0.2",
|
|
92
|
+
"pytest-django>=4.11.1",
|
|
93
|
+
"ruff>=0.14.11",
|
|
94
|
+
]
|
|
File without changes
|