django-pydantic-agent 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 (131) hide show
  1. django_pydantic_agent-0.1.0/.github/CODEOWNERS +1 -0
  2. django_pydantic_agent-0.1.0/.github/SECURITY.md +9 -0
  3. django_pydantic_agent-0.1.0/.github/dependabot.yml +16 -0
  4. django_pydantic_agent-0.1.0/.github/workflows/release.yml +81 -0
  5. django_pydantic_agent-0.1.0/.github/workflows/tests.yml +250 -0
  6. django_pydantic_agent-0.1.0/.gitignore +122 -0
  7. django_pydantic_agent-0.1.0/.pre-commit-config.yaml +50 -0
  8. django_pydantic_agent-0.1.0/CHANGELOG.md +30 -0
  9. django_pydantic_agent-0.1.0/CLAUDE.md +182 -0
  10. django_pydantic_agent-0.1.0/LICENSE +21 -0
  11. django_pydantic_agent-0.1.0/Makefile +79 -0
  12. django_pydantic_agent-0.1.0/PKG-INFO +81 -0
  13. django_pydantic_agent-0.1.0/README.md +33 -0
  14. django_pydantic_agent-0.1.0/django_pydantic_agent/__init__.py +81 -0
  15. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/__init__.py +0 -0
  16. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/agent_factory.py +66 -0
  17. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/attachment_toolset.py +78 -0
  18. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/build_model.py +66 -0
  19. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/build_tool_catalog.py +81 -0
  20. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/types/__init__.py +0 -0
  21. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/types/agent_config.py +56 -0
  22. django_pydantic_agent-0.1.0/django_pydantic_agent/agent/types/agent_factory_fn.py +29 -0
  23. django_pydantic_agent-0.1.0/django_pydantic_agent/constants.py +60 -0
  24. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/__init__.py +0 -0
  25. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/__init__.py +0 -0
  26. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/apps.py +25 -0
  27. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/default_attachment_store.py +85 -0
  28. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/default_conversation_store.py +101 -0
  29. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/default_step_store.py +297 -0
  30. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/migrations/0001_initial.py +261 -0
  31. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/migrations/__init__.py +0 -0
  32. django_pydantic_agent-0.1.0/django_pydantic_agent/contrib/store/models.py +215 -0
  33. django_pydantic_agent-0.1.0/django_pydantic_agent/integrations/__init__.py +0 -0
  34. django_pydantic_agent-0.1.0/django_pydantic_agent/integrations/build_spec_capability.py +53 -0
  35. django_pydantic_agent-0.1.0/django_pydantic_agent/integrations/drf_mcp.py +233 -0
  36. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/__init__.py +0 -0
  37. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/anonymous_operation_error.py +15 -0
  38. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/django_session_conversation_store.py +109 -0
  39. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/model_attachment_store.py +95 -0
  40. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/model_conversation_store.py +139 -0
  41. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/null_attachment_store.py +34 -0
  42. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/null_conversation_store.py +36 -0
  43. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/scoped_conversation_store.py +95 -0
  44. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/__init__.py +0 -0
  45. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/attachment_ref.py +32 -0
  46. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/attachment_store.py +45 -0
  47. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/conversation.py +27 -0
  48. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/conversation_meta.py +34 -0
  49. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/conversation_store.py +44 -0
  50. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/types/opened_attachment.py +29 -0
  51. django_pydantic_agent-0.1.0/django_pydantic_agent/persistence/utils.py +108 -0
  52. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/__init__.py +0 -0
  53. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/__init__.py +0 -0
  54. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/audit_capability.py +121 -0
  55. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/logging_audit_logger.py +59 -0
  56. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/null_audit_logger.py +13 -0
  57. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/types/__init__.py +0 -0
  58. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/types/audit_event.py +45 -0
  59. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/audit/types/audit_logger.py +22 -0
  60. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/guard/__init__.py +0 -0
  61. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/guard/tool_guard.py +82 -0
  62. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/guard/types/__init__.py +0 -0
  63. django_pydantic_agent-0.1.0/django_pydantic_agent/policy/guard/types/tool_guard_config.py +36 -0
  64. django_pydantic_agent-0.1.0/django_pydantic_agent/py.typed +0 -0
  65. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/__init__.py +0 -0
  66. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/build_input_schema.py +123 -0
  67. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/decorator.py +62 -0
  68. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/tool_registry.py +97 -0
  69. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/types/__init__.py +0 -0
  70. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/types/tool_binding.py +22 -0
  71. django_pydantic_agent-0.1.0/django_pydantic_agent/registry/types/tool_spec.py +52 -0
  72. django_pydantic_agent-0.1.0/django_pydantic_agent/utils.py +150 -0
  73. django_pydantic_agent-0.1.0/django_pydantic_agent/version.py +5 -0
  74. django_pydantic_agent-0.1.0/docs/index.md +14 -0
  75. django_pydantic_agent-0.1.0/mkdocs.yml +75 -0
  76. django_pydantic_agent-0.1.0/pyproject.toml +164 -0
  77. django_pydantic_agent-0.1.0/scripts/release-publish.sh +211 -0
  78. django_pydantic_agent-0.1.0/tests/__init__.py +0 -0
  79. django_pydantic_agent-0.1.0/tests/agent/__init__.py +0 -0
  80. django_pydantic_agent-0.1.0/tests/agent/catalog_server.py +45 -0
  81. django_pydantic_agent-0.1.0/tests/agent/factories.py +27 -0
  82. django_pydantic_agent-0.1.0/tests/agent/test_agent_factory.py +183 -0
  83. django_pydantic_agent-0.1.0/tests/agent/test_attachment_toolset.py +71 -0
  84. django_pydantic_agent-0.1.0/tests/agent/test_build_model.py +74 -0
  85. django_pydantic_agent-0.1.0/tests/agent/test_build_tool_catalog.py +92 -0
  86. django_pydantic_agent-0.1.0/tests/conftest_settings.py +36 -0
  87. django_pydantic_agent-0.1.0/tests/contrib/__init__.py +0 -0
  88. django_pydantic_agent-0.1.0/tests/contrib/store/__init__.py +0 -0
  89. django_pydantic_agent-0.1.0/tests/contrib/store/test_default_attachment_store.py +102 -0
  90. django_pydantic_agent-0.1.0/tests/contrib/store/test_default_conversation_store.py +113 -0
  91. django_pydantic_agent-0.1.0/tests/contrib/store/test_default_step_store.py +292 -0
  92. django_pydantic_agent-0.1.0/tests/contrib/store/test_models.py +43 -0
  93. django_pydantic_agent-0.1.0/tests/integrations/__init__.py +0 -0
  94. django_pydantic_agent-0.1.0/tests/integrations/drf_server.py +44 -0
  95. django_pydantic_agent-0.1.0/tests/integrations/drf_server_typed.py +47 -0
  96. django_pydantic_agent-0.1.0/tests/integrations/drf_specs.py +15 -0
  97. django_pydantic_agent-0.1.0/tests/integrations/drf_specs_colliding.py +25 -0
  98. django_pydantic_agent-0.1.0/tests/integrations/test_build_spec_capability.py +63 -0
  99. django_pydantic_agent-0.1.0/tests/integrations/test_drf_mcp.py +239 -0
  100. django_pydantic_agent-0.1.0/tests/persistence/__init__.py +0 -0
  101. django_pydantic_agent-0.1.0/tests/persistence/test_django_session_conversation_store.py +144 -0
  102. django_pydantic_agent-0.1.0/tests/persistence/test_model_attachment_store.py +66 -0
  103. django_pydantic_agent-0.1.0/tests/persistence/test_model_conversation_store.py +99 -0
  104. django_pydantic_agent-0.1.0/tests/persistence/test_null_attachment_store.py +24 -0
  105. django_pydantic_agent-0.1.0/tests/persistence/test_null_conversation_store.py +27 -0
  106. django_pydantic_agent-0.1.0/tests/persistence/test_scoped_conversation_store.py +99 -0
  107. django_pydantic_agent-0.1.0/tests/persistence/test_utils.py +137 -0
  108. django_pydantic_agent-0.1.0/tests/persistence/types/__init__.py +0 -0
  109. django_pydantic_agent-0.1.0/tests/persistence/types/test_attachment_ref.py +24 -0
  110. django_pydantic_agent-0.1.0/tests/persistence/types/test_conversation_meta.py +24 -0
  111. django_pydantic_agent-0.1.0/tests/persistence/types/test_opened_attachment.py +11 -0
  112. django_pydantic_agent-0.1.0/tests/policy/__init__.py +0 -0
  113. django_pydantic_agent-0.1.0/tests/policy/audit/__init__.py +0 -0
  114. django_pydantic_agent-0.1.0/tests/policy/audit/test_audit_capability.py +110 -0
  115. django_pydantic_agent-0.1.0/tests/policy/audit/test_logging_audit_logger.py +75 -0
  116. django_pydantic_agent-0.1.0/tests/policy/audit/test_null_audit_logger.py +10 -0
  117. django_pydantic_agent-0.1.0/tests/policy/audit/types/__init__.py +0 -0
  118. django_pydantic_agent-0.1.0/tests/policy/audit/types/test_audit_event.py +19 -0
  119. django_pydantic_agent-0.1.0/tests/policy/guard/__init__.py +0 -0
  120. django_pydantic_agent-0.1.0/tests/policy/guard/test_tool_guard.py +116 -0
  121. django_pydantic_agent-0.1.0/tests/policy/guard/types/__init__.py +0 -0
  122. django_pydantic_agent-0.1.0/tests/registry/__init__.py +0 -0
  123. django_pydantic_agent-0.1.0/tests/registry/test_build_input_schema.py +136 -0
  124. django_pydantic_agent-0.1.0/tests/registry/test_decorator.py +88 -0
  125. django_pydantic_agent-0.1.0/tests/registry/test_tool_registry.py +70 -0
  126. django_pydantic_agent-0.1.0/tests/registry/types/__init__.py +0 -0
  127. django_pydantic_agent-0.1.0/tests/registry/types/test_tool_spec.py +25 -0
  128. django_pydantic_agent-0.1.0/tests/test_constants.py +24 -0
  129. django_pydantic_agent-0.1.0/tests/test_utils.py +156 -0
  130. django_pydantic_agent-0.1.0/tests/test_version.py +8 -0
  131. django_pydantic_agent-0.1.0/uv.lock +2475 -0
@@ -0,0 +1 @@
1
+ * @Artui
@@ -0,0 +1,9 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ Please report security issues **privately** via GitHub's
6
+ [Report a vulnerability](../../security/advisories/new) button rather than opening a public issue.
7
+ I aim to acknowledge reports within 72 hours.
8
+
9
+ Supported: the latest released version on the default branch.
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ groups:
8
+ actions:
9
+ patterns: ["*"]
10
+ - package-ecosystem: "uv"
11
+ directory: "/"
12
+ schedule:
13
+ interval: "weekly"
14
+ groups:
15
+ python:
16
+ patterns: ["*"]
@@ -0,0 +1,81 @@
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-pydantic-agent
21
+ permissions:
22
+ id-token: write # PyPI OIDC trusted publishing
23
+ contents: write # tag push + gh release create
24
+ steps:
25
+ - uses: actions/checkout@v7
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
+ # Embed a coverage-badge JSON into the docs build so `mkdocs gh-deploy`
49
+ # carries it to the gh-pages root, where the README's shields.io endpoint
50
+ # badge reads it. `release-publish-prepare`'s pytest run wrote .coverage
51
+ # to the workspace root.
52
+ - name: Render coverage badge for the docs site
53
+ if: steps.prepare.outputs.released == 'true' && hashFiles('mkdocs.yml') != ''
54
+ run: |
55
+ set -euo pipefail
56
+ uv run coverage xml -o coverage.xml
57
+ python - <<'PY'
58
+ import json
59
+ import xml.etree.ElementTree as ET
60
+
61
+ pct = round(float(ET.parse("coverage.xml").getroot().attrib["line-rate"]) * 100, 1)
62
+ color = (
63
+ "brightgreen" if pct >= 95
64
+ else "green" if pct >= 85
65
+ else "yellow" if pct >= 70
66
+ else "red"
67
+ )
68
+ payload = {"schemaVersion": 1, "label": "coverage", "message": f"{pct}%", "color": color}
69
+ with open("docs/coverage.json", "w") as fh:
70
+ json.dump(payload, fh)
71
+ fh.write("\n")
72
+ print(json.dumps(payload))
73
+ PY
74
+
75
+ - name: Deploy docs + coverage badge to gh-pages
76
+ if: steps.prepare.outputs.released == 'true' && hashFiles('mkdocs.yml') != ''
77
+ run: |
78
+ git config user.name 'github-actions[bot]'
79
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
80
+ DJANGO_SETTINGS_MODULE=tests.conftest_settings \
81
+ uv run mkdocs gh-deploy --force --clean --config-file mkdocs.yml
@@ -0,0 +1,250 @@
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@v7
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_pydantic_agent
29
+
30
+ docs:
31
+ name: docs build
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v7
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@v7
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@v7
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
96
+
97
+ coverage-badge:
98
+ # Publishes a shields.io-compatible coverage.json to gh-pages so the README
99
+ # badge reflects reality. Runs only on push to main, after the matrix has
100
+ # produced an artifact we can read from.
101
+ name: coverage badge
102
+ needs: test
103
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
104
+ runs-on: ubuntu-latest
105
+ permissions:
106
+ contents: write
107
+ steps:
108
+ - uses: actions/checkout@v7
109
+ with:
110
+ # Need the parent commit so the gate step below can diff
111
+ # version.py against ${{ github.event.before }}. The default
112
+ # shallow checkout (depth 1) would make `git show "$before:…"`
113
+ # fail with exit 128 and turn the whole job red.
114
+ fetch-depth: 2
115
+ # release.yml runs in parallel on the same push and owns gh-pages
116
+ # publishing on release commits (it bundles coverage.json into the
117
+ # mkdocs gh-deploy). If we also push here we get a second built-in
118
+ # "pages build and deployment" run that the first one cancels. The
119
+ # version-diff against ${{ github.event.before }} is the reliable
120
+ # signal — release.yml's tag isn't pushed until after PyPI publish,
121
+ # so a tag-existence check would race.
122
+ - name: Skip when release.yml owns this push
123
+ id: gate
124
+ run: |
125
+ set -euo pipefail
126
+ before="${{ github.event.before }}"
127
+ if [[ -z "$before" || "$before" =~ ^0+$ ]]; then
128
+ # First push to main on a brand-new repo — no prior state to diff.
129
+ echo "skip=false" >> "$GITHUB_OUTPUT"
130
+ exit 0
131
+ fi
132
+ # `before` may not be in the local object DB if the push
133
+ # contained more than one commit (fetch-depth: 2 only guarantees
134
+ # HEAD + 1 parent). Try to fetch just that commit; if it's not
135
+ # reachable, fall back to "don't skip" — better to double-push
136
+ # to gh-pages once than to turn the pipeline red.
137
+ if ! git cat-file -e "$before^{commit}" 2>/dev/null; then
138
+ git fetch --depth=1 origin "$before" 2>/dev/null || true
139
+ fi
140
+ old=""
141
+ if git cat-file -e "$before:django_pydantic_agent/version.py" 2>/dev/null; then
142
+ old="$(git show "$before:django_pydantic_agent/version.py" \
143
+ | awk -F'"' '/^__version__/ { print $2; exit }')"
144
+ fi
145
+ new="$(awk -F'"' '/^__version__/ { print $2; exit }' \
146
+ django_pydantic_agent/version.py)"
147
+ if [[ -n "$old" && "$old" != "$new" ]]; then
148
+ echo "Version bumped $old → $new; release.yml will deploy gh-pages."
149
+ echo "skip=true" >> "$GITHUB_OUTPUT"
150
+ else
151
+ echo "skip=false" >> "$GITHUB_OUTPUT"
152
+ fi
153
+ - name: Download canonical coverage artifact
154
+ if: steps.gate.outputs.skip != 'true'
155
+ uses: actions/download-artifact@v8
156
+ with:
157
+ # Pin to one cell of the matrix — coverage is the same shape across
158
+ # all cells and we only need one number.
159
+ name: coverage-py3.13-django5.2
160
+ path: coverage-artifact/
161
+ - name: Render coverage.json
162
+ if: steps.gate.outputs.skip != 'true'
163
+ run: |
164
+ python - <<'PY'
165
+ import json
166
+ import xml.etree.ElementTree as ET
167
+
168
+ tree = ET.parse("coverage-artifact/coverage.xml")
169
+ pct = round(float(tree.getroot().attrib["line-rate"]) * 100, 1)
170
+ if pct >= 95:
171
+ color = "brightgreen"
172
+ elif pct >= 85:
173
+ color = "green"
174
+ elif pct >= 70:
175
+ color = "yellow"
176
+ else:
177
+ color = "red"
178
+ payload = {
179
+ "schemaVersion": 1,
180
+ "label": "coverage",
181
+ "message": f"{pct}%",
182
+ "color": color,
183
+ }
184
+ with open("coverage.json", "w") as fh:
185
+ json.dump(payload, fh)
186
+ fh.write("\n")
187
+ print(json.dumps(payload))
188
+ PY
189
+ - name: Publish coverage.json to gh-pages
190
+ if: steps.gate.outputs.skip != 'true'
191
+ env:
192
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
193
+ run: |
194
+ set -euo pipefail
195
+ git config user.name 'github-actions[bot]'
196
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
197
+
198
+ tmp="$(mktemp -d)"
199
+ cp coverage.json "$tmp/coverage.json"
200
+
201
+ git fetch origin gh-pages --depth=1 2>/dev/null || true
202
+ if git rev-parse --verify origin/gh-pages >/dev/null 2>&1; then
203
+ git worktree add gh-pages origin/gh-pages
204
+ cp "$tmp/coverage.json" gh-pages/coverage.json
205
+ cd gh-pages
206
+ if [[ -n "$(git status --porcelain coverage.json)" ]]; then
207
+ git add coverage.json
208
+ git commit -m "coverage badge: update from ${GITHUB_SHA::7}"
209
+ git push origin HEAD:gh-pages
210
+ else
211
+ echo "coverage.json unchanged, skipping push"
212
+ fi
213
+ else
214
+ # First run before docs deploy. Lay down an orphan gh-pages with
215
+ # just the badge file; the docs deploy will overwrite the rest.
216
+ git worktree add --detach gh-pages
217
+ cd gh-pages
218
+ git checkout --orphan gh-pages
219
+ git rm -rf . >/dev/null 2>&1 || true
220
+ cp "$tmp/coverage.json" coverage.json
221
+ git add coverage.json
222
+ git commit -m "coverage badge: initial publish from ${GITHUB_SHA::7}"
223
+ git push origin gh-pages
224
+ fi
225
+
226
+ secrets:
227
+ name: secret scan
228
+ runs-on: ubuntu-latest
229
+ steps:
230
+ - uses: actions/checkout@v7
231
+ with:
232
+ fetch-depth: 0
233
+ - name: Install gitleaks
234
+ run: |
235
+ curl -sSL https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz | tar -xz gitleaks
236
+ sudo mv gitleaks /usr/local/bin/
237
+ - name: Scan repo + history
238
+ run: gitleaks git --redact --no-banner .
239
+
240
+ tests-passed:
241
+ name: tests
242
+ if: always()
243
+ needs: [lint, docs, test]
244
+ runs-on: ubuntu-latest
245
+ steps:
246
+ - name: Verify required jobs succeeded
247
+ run: |
248
+ if [ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "true" ]; then
249
+ echo "a required job failed"; exit 1
250
+ fi
@@ -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,50 @@
1
+ repos:
2
+ - repo: https://github.com/gitleaks/gitleaks
3
+ rev: v8.30.1
4
+ hooks:
5
+ - id: gitleaks
6
+ - repo: https://github.com/pre-commit/pre-commit-hooks
7
+ rev: v6.0.0
8
+ hooks:
9
+ - id: trailing-whitespace
10
+ - id: end-of-file-fixer
11
+ - id: check-yaml
12
+ - id: check-added-large-files
13
+ args: ["--maxkb=2000"]
14
+
15
+ - repo: local
16
+ hooks:
17
+ - id: ruff-check
18
+ name: "ruff check --fix"
19
+ entry: make lint-fix
20
+ pass_filenames: false
21
+ language: system
22
+ stages: [pre-commit]
23
+
24
+ - id: ruff-format
25
+ name: "ruff format"
26
+ entry: make format
27
+ pass_filenames: false
28
+ language: system
29
+ stages: [pre-commit]
30
+
31
+ - id: backend-type-check
32
+ name: "type check (ty)"
33
+ entry: make type-check
34
+ pass_filenames: false
35
+ language: system
36
+ stages: [pre-commit]
37
+
38
+ - id: forbid-local-paths
39
+ name: "forbid local filesystem paths / personal handles"
40
+ entry: "/Users/|arturveres"
41
+ language: pygrep
42
+ types: [text]
43
+ exclude: '^\.pre-commit-config\.yaml$'
44
+
45
+ - id: forbid-plan-labels
46
+ name: "forbid internal plan-step labels"
47
+ entry: '(?<![A-Za-z0-9])(SPEC|SURF|DISP|KEY|FILT|BULK|NEST|NAME|THINK|VOICE|THEME|WELL|PAGE|CARD|REST|I18N|CUST|TOOL|PAI|AUTHZ|CONF|AGH|OAS|WCH|HYG|ADOPT|ANNO|ASYNC|ERR|DUP|PROTO|SEC|VER|GATE|STATE|SESS|CTX|AUDIT|HITL|CODE|STEP|SHARE|SKILL|CAP|MOUNT|SAN|PF|PROV|SCH|LIFE|SUB|CI|DOC|EX)-\d|(?<![A-Za-z0-9])(Phase|Wave)\s\d'
48
+ language: pygrep
49
+ types: [text]
50
+ exclude: '^\.pre-commit-config\.yaml$'
@@ -0,0 +1,30 @@
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-07-23
11
+
12
+ ### Added
13
+
14
+ - **Initial extraction** of the settings-agnostic agent-host substrate from
15
+ `django-ag-ui`: `AgentConfig` + `build_agent`, the `ToolRegistry` / `@tool`
16
+ registry and typed schema derivation, toolset & capability composition
17
+ (including the optional `[drf-mcp]` bridge and `[spec-tools]` capability), the
18
+ `AuditLogger` protocol and audit capability, the `ToolGuard` policy, the
19
+ `get_user` / authorization helpers, the `ConversationStore` / `AttachmentStore`
20
+ / `StepStore` contracts, and the reference `contrib.store` models and stores.
21
+ - **The core reads no Django settings.** Anything that previously resolved from
22
+ `DJANGO_AG_UI` is now an explicit argument — notably the model stores take
23
+ `allow_anonymous: bool = False` rather than consulting a settings key.
24
+ - **`Conversation.messages` is transport-owned.** The core persists and returns
25
+ JSON-serialisable message records verbatim and never interprets them, so it
26
+ carries no dependency on any wire format; the calling transport validates its
27
+ own shape (and its message ids survive a round trip untouched).
28
+
29
+ [Unreleased]: https://github.com/Artui/django-pydantic-agent/compare/v0.1.0...HEAD
30
+ [0.1.0]: https://github.com/Artui/django-pydantic-agent/releases/tag/v0.1.0