django-ag-ui 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.
Files changed (100) hide show
  1. django_ag_ui-0.1.0/.github/workflows/release.yml +54 -0
  2. django_ag_ui-0.1.0/.github/workflows/tests.yml +95 -0
  3. django_ag_ui-0.1.0/.gitignore +122 -0
  4. django_ag_ui-0.1.0/.pre-commit-config.yaml +32 -0
  5. django_ag_ui-0.1.0/CHANGELOG.md +28 -0
  6. django_ag_ui-0.1.0/CLAUDE.md +173 -0
  7. django_ag_ui-0.1.0/LICENSE +21 -0
  8. django_ag_ui-0.1.0/Makefile +79 -0
  9. django_ag_ui-0.1.0/PKG-INFO +54 -0
  10. django_ag_ui-0.1.0/README.md +16 -0
  11. django_ag_ui-0.1.0/django_ag_ui/__init__.py +66 -0
  12. django_ag_ui-0.1.0/django_ag_ui/agent/__init__.py +0 -0
  13. django_ag_ui-0.1.0/django_ag_ui/agent/agent_factory.py +124 -0
  14. django_ag_ui-0.1.0/django_ag_ui/agent/agui_view.py +188 -0
  15. django_ag_ui-0.1.0/django_ag_ui/agent/resolve_agent_factory.py +19 -0
  16. django_ag_ui-0.1.0/django_ag_ui/agent/resolve_dotted_instances.py +24 -0
  17. django_ag_ui-0.1.0/django_ag_ui/agent/system_prompt.py +10 -0
  18. django_ag_ui-0.1.0/django_ag_ui/agent/types/__init__.py +0 -0
  19. django_ag_ui-0.1.0/django_ag_ui/agent/types/agent_config.py +44 -0
  20. django_ag_ui-0.1.0/django_ag_ui/agent/types/agent_factory_fn.py +24 -0
  21. django_ag_ui-0.1.0/django_ag_ui/agent/urls.py +21 -0
  22. django_ag_ui-0.1.0/django_ag_ui/conf.py +87 -0
  23. django_ag_ui-0.1.0/django_ag_ui/constants.py +38 -0
  24. django_ag_ui-0.1.0/django_ag_ui/integrations/__init__.py +0 -0
  25. django_ag_ui-0.1.0/django_ag_ui/integrations/drf_mcp.py +77 -0
  26. django_ag_ui-0.1.0/django_ag_ui/persistence/__init__.py +0 -0
  27. django_ag_ui-0.1.0/django_ag_ui/persistence/django_session_conversation_store.py +60 -0
  28. django_ag_ui-0.1.0/django_ag_ui/persistence/model_conversation_store.py +52 -0
  29. django_ag_ui-0.1.0/django_ag_ui/persistence/null_conversation_store.py +26 -0
  30. django_ag_ui-0.1.0/django_ag_ui/persistence/resolve_conversation_store.py +30 -0
  31. django_ag_ui-0.1.0/django_ag_ui/persistence/types/__init__.py +0 -0
  32. django_ag_ui-0.1.0/django_ag_ui/persistence/types/conversation.py +22 -0
  33. django_ag_ui-0.1.0/django_ag_ui/persistence/types/conversation_store.py +26 -0
  34. django_ag_ui-0.1.0/django_ag_ui/persistence/utils.py +31 -0
  35. django_ag_ui-0.1.0/django_ag_ui/policy/__init__.py +0 -0
  36. django_ag_ui-0.1.0/django_ag_ui/policy/audit/__init__.py +0 -0
  37. django_ag_ui-0.1.0/django_ag_ui/policy/audit/logging_audit_logger.py +37 -0
  38. django_ag_ui-0.1.0/django_ag_ui/policy/audit/null_audit_logger.py +13 -0
  39. django_ag_ui-0.1.0/django_ag_ui/policy/audit/resolve_audit_logger.py +31 -0
  40. django_ag_ui-0.1.0/django_ag_ui/policy/audit/types/__init__.py +0 -0
  41. django_ag_ui-0.1.0/django_ag_ui/policy/audit/types/audit_event.py +23 -0
  42. django_ag_ui-0.1.0/django_ag_ui/policy/audit/types/audit_logger.py +22 -0
  43. django_ag_ui-0.1.0/django_ag_ui/policy/auto_confirm.py +18 -0
  44. django_ag_ui-0.1.0/django_ag_ui/py.typed +0 -0
  45. django_ag_ui-0.1.0/django_ag_ui/registry/__init__.py +0 -0
  46. django_ag_ui-0.1.0/django_ag_ui/registry/build_input_schema.py +99 -0
  47. django_ag_ui-0.1.0/django_ag_ui/registry/decorator.py +55 -0
  48. django_ag_ui-0.1.0/django_ag_ui/registry/tool_registry.py +95 -0
  49. django_ag_ui-0.1.0/django_ag_ui/registry/types/__init__.py +0 -0
  50. django_ag_ui-0.1.0/django_ag_ui/registry/types/tool_binding.py +22 -0
  51. django_ag_ui-0.1.0/django_ag_ui/registry/types/tool_spec.py +42 -0
  52. django_ag_ui-0.1.0/django_ag_ui/version.py +5 -0
  53. django_ag_ui-0.1.0/docs/api.md +80 -0
  54. django_ag_ui-0.1.0/docs/concepts.md +171 -0
  55. django_ag_ui-0.1.0/docs/configuration.md +167 -0
  56. django_ag_ui-0.1.0/docs/index.md +76 -0
  57. django_ag_ui-0.1.0/docs/installation.md +51 -0
  58. django_ag_ui-0.1.0/docs/quickstart.md +107 -0
  59. django_ag_ui-0.1.0/mkdocs.yml +80 -0
  60. django_ag_ui-0.1.0/pyproject.toml +144 -0
  61. django_ag_ui-0.1.0/scripts/release-publish.sh +211 -0
  62. django_ag_ui-0.1.0/tests/__init__.py +0 -0
  63. django_ag_ui-0.1.0/tests/agent/__init__.py +0 -0
  64. django_ag_ui-0.1.0/tests/agent/factories.py +28 -0
  65. django_ag_ui-0.1.0/tests/agent/test_agent_factory.py +139 -0
  66. django_ag_ui-0.1.0/tests/agent/test_agui_view.py +219 -0
  67. django_ag_ui-0.1.0/tests/agent/test_resolve_agent_factory.py +13 -0
  68. django_ag_ui-0.1.0/tests/agent/test_resolve_dotted_instances.py +22 -0
  69. django_ag_ui-0.1.0/tests/agent/test_system_prompt.py +8 -0
  70. django_ag_ui-0.1.0/tests/agent/test_urls.py +21 -0
  71. django_ag_ui-0.1.0/tests/conftest.py +0 -0
  72. django_ag_ui-0.1.0/tests/conftest_settings.py +32 -0
  73. django_ag_ui-0.1.0/tests/integrations/__init__.py +0 -0
  74. django_ag_ui-0.1.0/tests/integrations/drf_server.py +26 -0
  75. django_ag_ui-0.1.0/tests/integrations/test_drf_mcp.py +38 -0
  76. django_ag_ui-0.1.0/tests/persistence/__init__.py +0 -0
  77. django_ag_ui-0.1.0/tests/persistence/test_django_session_conversation_store.py +53 -0
  78. django_ag_ui-0.1.0/tests/persistence/test_model_conversation_store.py +34 -0
  79. django_ag_ui-0.1.0/tests/persistence/test_null_conversation_store.py +14 -0
  80. django_ag_ui-0.1.0/tests/persistence/test_resolve_conversation_store.py +31 -0
  81. django_ag_ui-0.1.0/tests/persistence/test_utils.py +48 -0
  82. django_ag_ui-0.1.0/tests/persistence/types/__init__.py +0 -0
  83. django_ag_ui-0.1.0/tests/policy/__init__.py +0 -0
  84. django_ag_ui-0.1.0/tests/policy/audit/__init__.py +0 -0
  85. django_ag_ui-0.1.0/tests/policy/audit/test_logging_audit_logger.py +38 -0
  86. django_ag_ui-0.1.0/tests/policy/audit/test_null_audit_logger.py +10 -0
  87. django_ag_ui-0.1.0/tests/policy/audit/test_resolve_audit_logger.py +28 -0
  88. django_ag_ui-0.1.0/tests/policy/audit/types/__init__.py +0 -0
  89. django_ag_ui-0.1.0/tests/policy/audit/types/test_audit_event.py +19 -0
  90. django_ag_ui-0.1.0/tests/policy/test_auto_confirm.py +28 -0
  91. django_ag_ui-0.1.0/tests/registry/__init__.py +0 -0
  92. django_ag_ui-0.1.0/tests/registry/test_build_input_schema.py +117 -0
  93. django_ag_ui-0.1.0/tests/registry/test_decorator.py +82 -0
  94. django_ag_ui-0.1.0/tests/registry/test_tool_registry.py +70 -0
  95. django_ag_ui-0.1.0/tests/registry/types/__init__.py +0 -0
  96. django_ag_ui-0.1.0/tests/registry/types/test_tool_spec.py +23 -0
  97. django_ag_ui-0.1.0/tests/test_conf.py +58 -0
  98. django_ag_ui-0.1.0/tests/test_constants.py +20 -0
  99. django_ag_ui-0.1.0/tests/test_version.py +8 -0
  100. django_ag_ui-0.1.0/uv.lock +4811 -0
@@ -0,0 +1,54 @@
1
+ name: release
2
+
3
+ # Main-triggered: every merge to `main` runs `release-publish-prepare`, which
4
+ # short-circuits to a no-op unless the version in source has been bumped past
5
+ # the most recent `vX.Y.Z` tag.
6
+ on:
7
+ push:
8
+ branches: [main]
9
+
10
+ concurrency:
11
+ group: release-main
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ release:
16
+ name: release
17
+ runs-on: ubuntu-latest
18
+ environment:
19
+ name: pypi
20
+ url: https://pypi.org/p/django-ag-ui
21
+ permissions:
22
+ id-token: write # PyPI OIDC trusted publishing
23
+ contents: write # tag push + gh release create
24
+ steps:
25
+ - uses: actions/checkout@v5
26
+ with:
27
+ fetch-depth: 0
28
+ - uses: astral-sh/setup-uv@v7
29
+ with:
30
+ enable-cache: true
31
+ - name: Install
32
+ run: uv sync --all-groups
33
+
34
+ - name: Prepare release (version check, test, build dist)
35
+ id: prepare
36
+ run: make release-publish-prepare
37
+
38
+ - name: Publish to PyPI
39
+ if: steps.prepare.outputs.released == 'true'
40
+ uses: pypa/gh-action-pypi-publish@release/v1
41
+
42
+ - name: Tag + GitHub Release
43
+ if: steps.prepare.outputs.released == 'true'
44
+ env:
45
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+ run: make release-publish-finalize
47
+
48
+ - name: Deploy docs to gh-pages
49
+ if: steps.prepare.outputs.released == 'true' && hashFiles('mkdocs.yml') != ''
50
+ run: |
51
+ git config user.name 'github-actions[bot]'
52
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
53
+ DJANGO_SETTINGS_MODULE=tests.conftest_settings \
54
+ uv run mkdocs gh-deploy --force --clean --config-file mkdocs.yml
@@ -0,0 +1,95 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: tests-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint:
14
+ name: lint
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v5
18
+ - uses: astral-sh/setup-uv@v7
19
+ with:
20
+ enable-cache: true
21
+ - name: Install
22
+ run: uv sync --all-groups
23
+ - name: ruff check
24
+ run: uv run ruff check .
25
+ - name: ruff format check
26
+ run: uv run ruff format --check --diff .
27
+ - name: ty
28
+ run: uv run ty check django_ag_ui
29
+
30
+ docs:
31
+ name: docs build
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v5
35
+ - uses: astral-sh/setup-uv@v7
36
+ with:
37
+ enable-cache: true
38
+ - name: Install with docs group
39
+ run: uv sync --all-groups
40
+ - name: mkdocs build --strict
41
+ run: DJANGO_SETTINGS_MODULE=tests.conftest_settings uv run mkdocs build --strict
42
+
43
+ test:
44
+ name: ${{ matrix.python-version }} / Django ${{ matrix.django-version }}
45
+ runs-on: ubuntu-latest
46
+ strategy:
47
+ fail-fast: false
48
+ matrix:
49
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
50
+ django-version: ["4.2", "5.0", "5.1", "5.2", "6.0"]
51
+ exclude:
52
+ # Django 4.2 supports Python 3.8–3.12
53
+ - python-version: "3.13"
54
+ django-version: "4.2"
55
+ - python-version: "3.14"
56
+ django-version: "4.2"
57
+ # Django 5.0 supports Python 3.10–3.12
58
+ - python-version: "3.13"
59
+ django-version: "5.0"
60
+ - python-version: "3.14"
61
+ django-version: "5.0"
62
+ # Django 5.1 supports Python 3.10–3.13
63
+ - python-version: "3.14"
64
+ django-version: "5.1"
65
+ # Django 6.0 requires Python 3.12+
66
+ - python-version: "3.10"
67
+ django-version: "6.0"
68
+ - python-version: "3.11"
69
+ django-version: "6.0"
70
+
71
+ steps:
72
+ - uses: actions/checkout@v5
73
+ - uses: astral-sh/setup-uv@v7
74
+ with:
75
+ enable-cache: true
76
+ - name: Install Python ${{ matrix.python-version }}
77
+ run: uv python install ${{ matrix.python-version }}
78
+ - name: Sync dev deps
79
+ run: uv sync --all-groups --python ${{ matrix.python-version }}
80
+ - name: Pin Django ${{ matrix.django-version }}
81
+ run: uv pip install --reinstall-package django "django~=${{ matrix.django-version }}.0"
82
+ - name: pytest
83
+ run: |
84
+ uv run --no-sync pytest \
85
+ --cov-report=xml:coverage.xml \
86
+ --cov-report=html:htmlcov
87
+ - name: Upload coverage artifact
88
+ if: always()
89
+ uses: actions/upload-artifact@v5
90
+ with:
91
+ name: coverage-py${{ matrix.python-version }}-django${{ matrix.django-version }}
92
+ path: |
93
+ coverage.xml
94
+ htmlcov/
95
+ if-no-files-found: ignore
@@ -0,0 +1,122 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+ coverage*.json
52
+ coverage*.xml
53
+
54
+ # release.yml renders this at workflow-time and drops it under docs/ so
55
+ # mkdocs gh-deploy carries it to the gh-pages root.
56
+ docs/coverage.json
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Sphinx documentation
69
+ docs/_build/
70
+
71
+ # Environments
72
+ .env
73
+ .venv
74
+ env/
75
+ venv/
76
+ ENV/
77
+ env.bak/
78
+ venv.bak/
79
+
80
+ # Virtualenv-style scripts dirs (we re-include the repo-root scripts/ directory).
81
+ [Ss]cripts
82
+ !/scripts/
83
+ pyvenv.cfg
84
+ .Python
85
+ [Bb]in
86
+ [Ii]nclude
87
+ [Ll]ib
88
+ [Ll]ib64
89
+ [Ll]ocal
90
+
91
+ # uv
92
+ .uv-cache/
93
+
94
+ # mkdocs
95
+ /site
96
+
97
+ # Type checkers
98
+ .mypy_cache/
99
+ .dmypy.json
100
+ dmypy.json
101
+ .pyre/
102
+ .pytype/
103
+ .ruff_cache/
104
+ .ty_cache/
105
+
106
+ # IDE
107
+ .vscode/
108
+ .idea/
109
+ *.iml
110
+ *.code-workspace
111
+ .history/
112
+
113
+ # OS
114
+ .DS_Store
115
+ Thumbs.db
116
+
117
+ # Release metadata (rendered by scripts/release-publish.sh during CI)
118
+ .release-metadata/
119
+
120
+ # AI-specific items
121
+ .ai/
122
+ .claude/
@@ -0,0 +1,32 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ args: ["--maxkb=2000"]
10
+
11
+ - repo: local
12
+ hooks:
13
+ - id: ruff-check
14
+ name: "ruff check --fix"
15
+ entry: make lint-fix
16
+ pass_filenames: false
17
+ language: system
18
+ stages: [pre-commit]
19
+
20
+ - id: ruff-format
21
+ name: "ruff format"
22
+ entry: make format
23
+ pass_filenames: false
24
+ language: system
25
+ stages: [pre-commit]
26
+
27
+ - id: backend-type-check
28
+ name: "type check (ty)"
29
+ entry: make type-check
30
+ pass_filenames: false
31
+ language: system
32
+ stages: [pre-commit]
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] — 2026-06-01
11
+
12
+ ### Added
13
+ - `ToolRegistry` + the `@tool` decorator (`destructive=` / `category=`), with
14
+ JSON-Schema derived from signatures and `x-destructive` / `x-category`
15
+ extensions.
16
+ - `DjangoAGUIView`, an async endpoint over Pydantic-AI's `AGUIAdapter` (SSE),
17
+ plus `get_urls()` for mounting.
18
+ - `AgentConfig` + `build_agent`, and the `DJANGO_AG_UI` settings (`MODEL`,
19
+ `MODEL_SETTINGS`, `RETRIES`, `AGENT_FACTORY`, `TOOLSETS`, `CAPABILITIES`,
20
+ `AUTO_CONFIRM`, `SYSTEM_PROMPT`).
21
+ - `AuditLogger` protocol with `NullAuditLogger` / `LoggingAuditLogger`.
22
+ - Opt-in server-side conversation persistence: the `ConversationStore` protocol,
23
+ `NullConversationStore` (default, stateless), `DjangoSessionConversationStore`,
24
+ and the abstract `ModelConversationStore` base.
25
+ - In-process `drf-mcp` toolset bridge behind the `[drf-mcp]` extra.
26
+
27
+ [Unreleased]: https://github.com/Artui/django-ag-ui/compare/v0.1.0...HEAD
28
+ [0.1.0]: https://github.com/Artui/django-ag-ui/releases/tag/v0.1.0
@@ -0,0 +1,173 @@
1
+ # Repo conventions for `django-ag-ui`
2
+
3
+ This file is the single source of truth for how to write code in this package.
4
+ Rules are non-negotiable unless flagged as a heuristic.
5
+
6
+ ## What this package is
7
+
8
+ A Django ↔ Pydantic-AI ↔ [AG-UI](https://docs.ag-ui.com) integration. Provides:
9
+ - A tool registry (`ToolRegistry`, `@tool` decorator) with `destructive=` and `category=`
10
+ metadata, and a `build_input_schema` helper that emits an `x-destructive` JSON-Schema
11
+ extension when set.
12
+ - An async Django view (`DjangoAGUIView`) that wraps Pydantic-AI's
13
+ `pydantic_ai.ui.ag_ui.AGUIAdapter` and returns a `StreamingHttpResponse` of AG-UI events.
14
+ - An `AuditLogger` Protocol with `NullAuditLogger` and `LoggingAuditLogger` implementations.
15
+ - `conf.py` reading the `DJANGO_AG_UI` settings dict.
16
+
17
+ Downstream packages (e.g. `django-admin-agent`) build on this. **No admin specifics live in
18
+ this package.**
19
+
20
+ The design is at `/Users/arturveres/code/opensource/docs/plans/django-ag-ui-plan.md`.
21
+
22
+ ## Commands
23
+
24
+ | Target | What it does |
25
+ | --- | --- |
26
+ | `make init` | `uv sync --all-groups` + install pre-commit hooks |
27
+ | `make test` | pytest with 100% line+branch coverage gate |
28
+ | `make lint` | `ruff check .` + `ty check django_ag_ui` |
29
+ | `make format` | `ruff format .` |
30
+ | `make docs-serve` | live-reload mkdocs at `localhost:8000` |
31
+ | `make docs-build` | `mkdocs build --strict` |
32
+ | `make release-bump VERSION=X.Y.Z` | rewrite `version.py` + promote `[Unreleased]` in CHANGELOG |
33
+ | `make release-publish` | end-to-end workstation release |
34
+
35
+ ## Structural rules
36
+
37
+ 1. **One exported class or function per file.** File name = `snake_case` of the symbol.
38
+ `ToolRegistry` → `tool_registry.py`; `build_input_schema` → `build_input_schema.py`.
39
+ **Exception:** `django_ag_ui/constants.py` is the package's single home for enums and
40
+ constant-like module-level values, and is the only file allowed to export multiple symbols.
41
+ 2. **Private helpers used in only one file** stay there with a leading `_`.
42
+ 3. **Non-exported helpers shared across files** go into a sibling `utils.py`. Classes are
43
+ allowed in `utils.py` if they are internal infrastructure.
44
+ 4. **Top-level imports only.** No function-local / lazy imports unless a circular import is
45
+ genuine and documented inline at the import site, **or** the dependency is optional —
46
+ those imports go inside the function body with a clear `ImportError` message.
47
+ 5. **Full type annotations on every function and method signature.** `Any` is allowed only at
48
+ Django/Pydantic-AI boundaries where the type genuinely is `Any`.
49
+ 6. **`__init__.py` is the only re-export point.** Each `__init__.py` lists the public surface
50
+ in `__all__`. Internal modules import from leaf paths, never from the package's `__init__`.
51
+ 7. **Always `from __future__ import annotations`** at the top of any file with type
52
+ annotations. Python 3.10+, so no PEP 695 `type` statements.
53
+ 8. **Absolute imports only.** Imports are ordered stdlib → third-party → first-party
54
+ (`django_ag_ui`). Within each block, alphabetical.
55
+ 9. **NEVER use relative imports.** `from . import x`, `from .foo import bar`, any dotted-
56
+ relative form is forbidden everywhere in the package, including `__init__.py`. Always
57
+ write the full absolute path (`from django_ag_ui.foo import bar`).
58
+ 10. **Types and functionality live in separate sub-packages.** When a directory contains both
59
+ type declarations (dataclasses, Protocols, frozen wire-shape records) and functionality
60
+ (callables, registries, dispatch helpers), the types move into a `types/` sibling.
61
+ `constants.py` remains the multi-export exception.
62
+
63
+ ## API style rules
64
+
65
+ 11. **Always dataclasses over `dict[str, Any]` for structured data.** Every wire-shape
66
+ payload, response envelope, configuration record, and tool spec field is a frozen
67
+ `@dataclass` with explicit field types. `dict[str, Any]` survives only at genuine
68
+ serialisation boundaries.
69
+ 12. **Tool callables are typed.** Every registered tool declares typed parameters and a typed
70
+ return — no `**kwargs: Any` escape hatches. The registry uses signatures to derive JSON
71
+ Schema for AG-UI's tool definitions; an untyped tool breaks the schema.
72
+
73
+ ## Security boundary
74
+
75
+ Per-tool `destructive: bool` metadata is the surfaced risk signal. The registry stamps it into
76
+ the JSON Schema as `x-destructive: true`; client-side AG-UI consumers (e.g. the
77
+ `@artui/ag-ui-web-component`) gate execution behind a confirmation modal. The wire stays
78
+ vanilla AG-UI.
79
+
80
+ The `AuditLogger` Protocol is the audit boundary. `LoggingAuditLogger` is the default;
81
+ projects supply their own (Sentry, Honeycomb, custom) by setting `DJANGO_AG_UI["AUDIT_LOGGER"]`
82
+ to a dotted path.
83
+
84
+ ## No module-level or class-level mutable state
85
+
86
+ State lives on instances. Module-level constants (lookup tables, regexes, frozen settings
87
+ defaults, dispatch tables) are fine — module-level **mutable** state is not.
88
+
89
+ - No module-level mutable singletons (registries, caches, "warned-once" flags).
90
+ - No class-level mutable attributes declared on the class body. Initialise mutables in
91
+ `__init__`.
92
+ - The `ToolRegistry` is an instance: a `DjangoAGUIView` holds one; tests build a fresh
93
+ registry per scenario.
94
+
95
+ ## Tests
96
+
97
+ - `make test` runs pytest with `--cov=django_ag_ui --cov-fail-under=100` (line + branch).
98
+ Restructure rather than reach for `# pragma: no cover`.
99
+ - Test layout mirrors the source tree under `tests/`. `django_ag_ui/foo/bar.py` →
100
+ `tests/foo/test_bar.py`.
101
+ - `tests/conftest_settings.py` is the Django settings module pytest uses (set via
102
+ `DJANGO_SETTINGS_MODULE` in `pyproject.toml`).
103
+ - Async tests: `async def test_...` with pytest-asyncio (`asyncio_mode = "auto"`).
104
+ - For the async view, use `httpx.AsyncClient` against the ASGI app to drive `RunAgentInput`
105
+ POSTs and parse the SSE event stream.
106
+
107
+ ## Lint and types
108
+
109
+ - `make lint` runs `ruff check .` + `ty check django_ag_ui`. CI fails on either.
110
+ - `ruff format` is the source of truth for layout.
111
+ - Pre-commit runs `make lint-fix`, `make format`, `make type-check`. Commits must be clean
112
+ before push — never `--no-verify`.
113
+ - `ty` is scoped to `django_ag_ui/` only (not tests).
114
+
115
+ ## Boundaries
116
+
117
+ - The package depends on `pydantic-ai[ag-ui]` for the AGUIAdapter. The AG-UI wire types come
118
+ from there; don't re-implement them.
119
+ - No admin specifics. Anything that touches `django.contrib.admin` belongs in
120
+ `django-admin-agent`, not here.
121
+ - The `agent/` layer does not import from `registry/types`; it imports the public re-exports
122
+ from `django_ag_ui.registry`.
123
+
124
+ ## Compatibility floor
125
+
126
+ | Component | Floor | Tested |
127
+ | --- | --- | --- |
128
+ | Python | 3.10 | 3.10, 3.11, 3.12, 3.13, 3.14 |
129
+ | Django | 4.2 LTS | 4.2, 5.0, 5.1, 5.2, 6.0 |
130
+ | Pydantic-AI | 1.0 (with `[ag-ui]` extra) | latest in matrix |
131
+
132
+ ## Branching
133
+
134
+ When working on a new feature or version bump, **ALWAYS** switch to a new branch first
135
+ (`git checkout -b feat/...` or `release/vX.Y.Z`) and push to that branch. Never commit
136
+ feature work or version bumps directly to `main`, and never push to `main` from the local
137
+ checkout — `main` only advances via merged PRs (or, for releases, the tagged commit produced
138
+ on the release branch).
139
+
140
+ ## Releases
141
+
142
+ Merge-to-main triggered. `.github/workflows/release.yml` runs on every push to `main` and
143
+ calls `make release-publish-prepare`. The script in `scripts/release-publish.sh` is the
144
+ single source of truth:
145
+
146
+ 1. Extract version from `django_ag_ui/version.py`.
147
+ 2. Short-circuit if `vX.Y.Z` already exists locally or on origin.
148
+ 3. Run `uv run pytest` as a final gate.
149
+ 4. `uv build` into `dist/`.
150
+ 5. Extract the `## [X.Y.Z]` section from `CHANGELOG.md` into release notes.
151
+ 6. Emit `released=true`.
152
+
153
+ If released:
154
+ - Publish to PyPI via OIDC trusted publishing.
155
+ - Tag, push, create GitHub Release.
156
+ - `mkdocs gh-deploy` to `gh-pages`.
157
+
158
+ ### Cutting a release
159
+
160
+ ```bash
161
+ make release-bump VERSION=0.2.0
162
+ git diff
163
+ git commit -am "Release 0.2.0"
164
+ git push -u origin release/0.2.0
165
+ gh pr create
166
+ # Merge to main; release.yml fires on the merge commit.
167
+ ```
168
+
169
+ ### One-time setup (manual)
170
+
171
+ 1. **PyPI Trusted Publisher** — `Artui/django-ag-ui`, workflow `release.yml`, environment `pypi`.
172
+ 2. **GitHub Environment** — create `pypi` (no secrets; OIDC).
173
+ 3. **GitHub Pages** — branch `gh-pages` (created on first release with docs).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artur Veres
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,79 @@
1
+ .PHONY: help init test lint lint-fix format format-check type-check deps-bump docs-serve docs-build release-bump release-publish release-publish-prepare release-publish-finalize
2
+
3
+ help:
4
+ @echo "Available targets:"
5
+ @echo " init Sync deps (all groups) and install pre-commit hooks"
6
+ @echo " test Run pytest with coverage (100% required)"
7
+ @echo " lint Run ruff check + ty check"
8
+ @echo " lint-fix Auto-fix lint issues with ruff"
9
+ @echo " format Format with ruff"
10
+ @echo " format-check Verify formatting"
11
+ @echo " type-check Run ty over the package"
12
+ @echo " deps-bump Upgrade pinned dependencies"
13
+ @echo " docs-serve Live-reload docs at http://localhost:8000 (needs mkdocs.yml)"
14
+ @echo " docs-build Build docs into ./site (strict — fails on broken links)"
15
+ @echo " release-bump Bump version files + CHANGELOG. Usage: make release-bump VERSION=X.Y.Z"
16
+ @echo " release-publish prepare → uv publish → finalize (workstation release)"
17
+ @echo " release-publish-prepare Run by release.yml on push to main (no-op unless bumped)"
18
+ @echo " release-publish-finalize Tag vX.Y.Z + create GitHub Release after PyPI publish"
19
+
20
+ init:
21
+ uv sync --all-groups
22
+ uv run pre-commit install
23
+
24
+ test:
25
+ uv run pytest
26
+
27
+ lint:
28
+ uv run ruff check .
29
+ uv run ty check django_ag_ui
30
+
31
+ lint-fix:
32
+ uv run ruff check --fix .
33
+
34
+ format:
35
+ uv run ruff format .
36
+
37
+ format-check:
38
+ uv run ruff format --check --diff .
39
+
40
+ type-check:
41
+ uv run ty check django_ag_ui
42
+
43
+ deps-bump:
44
+ uvx uv-upx upgrade run --profile with_pinned
45
+
46
+ docs-serve:
47
+ uv run --group docs mkdocs serve
48
+
49
+ docs-build:
50
+ uv run --group docs mkdocs build --strict
51
+
52
+ release-bump:
53
+ @if [ -z "$(VERSION)" ]; then \
54
+ echo "Usage: make release-bump VERSION=X.Y.Z"; exit 1; \
55
+ fi
56
+ uvx bump-my-version bump --new-version "$(VERSION)" patch
57
+ @echo ""
58
+ @echo "Bumped to $(VERSION). Edit CHANGELOG.md to fill the new section,"
59
+ @echo "review with 'git diff', then run 'make release-publish'."
60
+
61
+ # Release pipeline. Version lives in django_ag_ui/version.py
62
+ # (pyproject pulls it in via [tool.hatch.version] dynamic).
63
+ RELEASE_PACKAGE_NAME := django-ag-ui
64
+ RELEASE_VERSION_FILES := django_ag_ui/version.py|^__version__[^=]*= *
65
+
66
+ release-publish:
67
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
68
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
69
+ bash scripts/release-publish.sh all
70
+
71
+ release-publish-prepare:
72
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
73
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
74
+ bash scripts/release-publish.sh prepare
75
+
76
+ release-publish-finalize:
77
+ @PACKAGE_NAME='$(RELEASE_PACKAGE_NAME)' \
78
+ VERSION_FILES="$$(printf '$(RELEASE_VERSION_FILES)')" \
79
+ bash scripts/release-publish.sh finalize
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-ag-ui
3
+ Version: 0.1.0
4
+ Summary: Django ↔ Pydantic-AI ↔ AG-UI integration: async view, tool registry, audit logger.
5
+ Project-URL: Homepage, https://github.com/Artui/django-ag-ui
6
+ Project-URL: Repository, https://github.com/Artui/django-ag-ui
7
+ Project-URL: Issues, https://github.com/Artui/django-ag-ui/issues
8
+ Author-email: Artur Veres <artur8118@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ag-ui,agent,ai,django,llm,pydantic-ai
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Framework :: Django
15
+ Classifier: Framework :: Django :: 4.2
16
+ Classifier: Framework :: Django :: 5.0
17
+ Classifier: Framework :: Django :: 5.1
18
+ Classifier: Framework :: Django :: 5.2
19
+ Classifier: Framework :: Django :: 6.0
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: License :: OSI Approved :: MIT License
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Programming Language :: Python
24
+ Classifier: Programming Language :: Python :: 3
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3.12
28
+ Classifier: Programming Language :: Python :: 3.13
29
+ Classifier: Programming Language :: Python :: 3.14
30
+ Classifier: Topic :: Internet :: WWW/HTTP
31
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
32
+ Requires-Python: >=3.10
33
+ Requires-Dist: django>=4.2
34
+ Requires-Dist: pydantic-ai[ag-ui]>=1.0
35
+ Provides-Extra: drf-mcp
36
+ Requires-Dist: djangorestframework-mcp-server>=0.5; extra == 'drf-mcp'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # django-ag-ui
40
+
41
+ [![CI](https://github.com/Artui/django-ag-ui/workflows/tests/badge.svg)](https://github.com/Artui/django-ag-ui/actions/workflows/tests.yml)
42
+ [![PyPI](https://img.shields.io/pypi/v/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
43
+ [![Python versions](https://img.shields.io/pypi/pyversions/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
44
+ [![Django versions](https://img.shields.io/pypi/djversions/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
45
+ [![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)
46
+ [![License](https://img.shields.io/pypi/l/django-ag-ui.svg)](https://github.com/Artui/django-ag-ui/blob/main/LICENSE)
47
+
48
+ Django ↔ [Pydantic-AI](https://ai.pydantic.dev) ↔ [AG-UI](https://docs.ag-ui.com) integration: async view, tool registry with `destructive`/`category` metadata, audit logger.
49
+
50
+ ```bash
51
+ pip install django-ag-ui
52
+ ```
53
+
54
+ See the [docs](https://artui.github.io/django-ag-ui/) for the full surface.
@@ -0,0 +1,16 @@
1
+ # django-ag-ui
2
+
3
+ [![CI](https://github.com/Artui/django-ag-ui/workflows/tests/badge.svg)](https://github.com/Artui/django-ag-ui/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
6
+ [![Django versions](https://img.shields.io/pypi/djversions/django-ag-ui.svg)](https://pypi.org/project/django-ag-ui/)
7
+ [![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)
8
+ [![License](https://img.shields.io/pypi/l/django-ag-ui.svg)](https://github.com/Artui/django-ag-ui/blob/main/LICENSE)
9
+
10
+ Django ↔ [Pydantic-AI](https://ai.pydantic.dev) ↔ [AG-UI](https://docs.ag-ui.com) integration: async view, tool registry with `destructive`/`category` metadata, audit logger.
11
+
12
+ ```bash
13
+ pip install django-ag-ui
14
+ ```
15
+
16
+ See the [docs](https://artui.github.io/django-ag-ui/) for the full surface.