collective.aitools 1.0.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 (154) hide show
  1. collective_aitools-1.0.0/.dockerignore +14 -0
  2. collective_aitools-1.0.0/.editorconfig +35 -0
  3. collective_aitools-1.0.0/.flake8 +22 -0
  4. collective_aitools-1.0.0/.gitignore +50 -0
  5. collective_aitools-1.0.0/AGENTS.md +203 -0
  6. collective_aitools-1.0.0/CHANGELOG.md +56 -0
  7. collective_aitools-1.0.0/CONTRIBUTORS.md +3 -0
  8. collective_aitools-1.0.0/Dockerfile +38 -0
  9. collective_aitools-1.0.0/Dockerfile.acceptance +40 -0
  10. collective_aitools-1.0.0/LICENSE.GPL +339 -0
  11. collective_aitools-1.0.0/LICENSE.md +15 -0
  12. collective_aitools-1.0.0/Makefile +207 -0
  13. collective_aitools-1.0.0/PKG-INFO +155 -0
  14. collective_aitools-1.0.0/README.md +116 -0
  15. collective_aitools-1.0.0/bobtemplate.cfg +7 -0
  16. collective_aitools-1.0.0/instance.yaml +5 -0
  17. collective_aitools-1.0.0/mx.ini +37 -0
  18. collective_aitools-1.0.0/news/.changelog_template.jinja +15 -0
  19. collective_aitools-1.0.0/news/.gitkeep +1 -0
  20. collective_aitools-1.0.0/package-lock.json +23070 -0
  21. collective_aitools-1.0.0/package.json +30 -0
  22. collective_aitools-1.0.0/pyproject.toml +186 -0
  23. collective_aitools-1.0.0/resources/aitools-config.js +8 -0
  24. collective_aitools-1.0.0/resources/index.js +3 -0
  25. collective_aitools-1.0.0/resources/js/assistant.pattern.js +255 -0
  26. collective_aitools-1.0.0/resources/js/assistant.scss +241 -0
  27. collective_aitools-1.0.0/resources/js/help-me-write.pattern.js +431 -0
  28. collective_aitools-1.0.0/resources/js/help-me-write.scss +123 -0
  29. collective_aitools-1.0.0/resources/js/recognize-image.pattern.js +236 -0
  30. collective_aitools-1.0.0/resources/js/recognize-image.scss +69 -0
  31. collective_aitools-1.0.0/resources/js/suggest-tags.pattern.js +15 -0
  32. collective_aitools-1.0.0/resources/js/summarize.pattern.js +15 -0
  33. collective_aitools-1.0.0/scripts/create_site.py +71 -0
  34. collective_aitools-1.0.0/scripts/recognize_images.py +132 -0
  35. collective_aitools-1.0.0/scripts/suggest_tags.py +92 -0
  36. collective_aitools-1.0.0/scripts/summarize_content.py +91 -0
  37. collective_aitools-1.0.0/src/collective/aitools/AGENTS.md +289 -0
  38. collective_aitools-1.0.0/src/collective/aitools/__init__.py +14 -0
  39. collective_aitools-1.0.0/src/collective/aitools/backfill.py +110 -0
  40. collective_aitools-1.0.0/src/collective/aitools/browser/__init__.py +0 -0
  41. collective_aitools-1.0.0/src/collective/aitools/browser/assistant.py +66 -0
  42. collective_aitools-1.0.0/src/collective/aitools/browser/configure.zcml +57 -0
  43. collective_aitools-1.0.0/src/collective/aitools/browser/help_me_write.py +70 -0
  44. collective_aitools-1.0.0/src/collective/aitools/browser/recognize_image.py +57 -0
  45. collective_aitools-1.0.0/src/collective/aitools/browser/static/aitools-remote.min.js +2 -0
  46. collective_aitools-1.0.0/src/collective/aitools/browser/static/aitools.min.js +2 -0
  47. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/46.17392fd62e1893d8f4e0.min.js +2 -0
  48. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/46.17392fd62e1893d8f4e0.min.js.map +1 -0
  49. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/692.cd4653742130a6d95d03.min.js +12 -0
  50. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/692.cd4653742130a6d95d03.min.js.map +1 -0
  51. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/709.828f420ec7e88f982470.min.js +2 -0
  52. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/709.828f420ec7e88f982470.min.js.map +1 -0
  53. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/87.2eb8178be9c49f8e5ae5.min.js +2 -0
  54. collective_aitools-1.0.0/src/collective/aitools/browser/static/chunks/87.2eb8178be9c49f8e5ae5.min.js.map +1 -0
  55. collective_aitools-1.0.0/src/collective/aitools/browser/suggest_tags.py +53 -0
  56. collective_aitools-1.0.0/src/collective/aitools/browser/summarize.py +53 -0
  57. collective_aitools-1.0.0/src/collective/aitools/browser/templates/assistant_viewlet.pt +7 -0
  58. collective_aitools-1.0.0/src/collective/aitools/browser/templates/help_me_write_viewlet.pt +8 -0
  59. collective_aitools-1.0.0/src/collective/aitools/browser/templates/recognize_image_viewlet.pt +8 -0
  60. collective_aitools-1.0.0/src/collective/aitools/browser/templates/suggest_tags_viewlet.pt +8 -0
  61. collective_aitools-1.0.0/src/collective/aitools/browser/templates/summarize_viewlet.pt +8 -0
  62. collective_aitools-1.0.0/src/collective/aitools/capabilities/__init__.py +18 -0
  63. collective_aitools-1.0.0/src/collective/aitools/capabilities/base.py +76 -0
  64. collective_aitools-1.0.0/src/collective/aitools/capabilities/configure.zcml +24 -0
  65. collective_aitools-1.0.0/src/collective/aitools/capabilities/propose_draft.py +250 -0
  66. collective_aitools-1.0.0/src/collective/aitools/capabilities/search_site.py +68 -0
  67. collective_aitools-1.0.0/src/collective/aitools/capabilities/types.py +151 -0
  68. collective_aitools-1.0.0/src/collective/aitools/capabilities/where_to_add.py +103 -0
  69. collective_aitools-1.0.0/src/collective/aitools/configure.zcml +30 -0
  70. collective_aitools-1.0.0/src/collective/aitools/content/__init__.py +0 -0
  71. collective_aitools-1.0.0/src/collective/aitools/controlpanels/__init__.py +0 -0
  72. collective_aitools-1.0.0/src/collective/aitools/controlpanels/ai_tools.py +36 -0
  73. collective_aitools-1.0.0/src/collective/aitools/controlpanels/configure.zcml +22 -0
  74. collective_aitools-1.0.0/src/collective/aitools/dependencies.zcml +8 -0
  75. collective_aitools-1.0.0/src/collective/aitools/indexers/__init__.py +0 -0
  76. collective_aitools-1.0.0/src/collective/aitools/indexers/configure.zcml +7 -0
  77. collective_aitools-1.0.0/src/collective/aitools/interfaces.py +312 -0
  78. collective_aitools-1.0.0/src/collective/aitools/locales/__init__.py +0 -0
  79. collective_aitools-1.0.0/src/collective/aitools/locales/__main__.py +69 -0
  80. collective_aitools-1.0.0/src/collective/aitools/locales/collective.aitools.pot +239 -0
  81. collective_aitools-1.0.0/src/collective/aitools/locales/de/LC_MESSAGES/collective.aitools.mo +0 -0
  82. collective_aitools-1.0.0/src/collective/aitools/locales/de/LC_MESSAGES/collective.aitools.po +236 -0
  83. collective_aitools-1.0.0/src/collective/aitools/locales/en/LC_MESSAGES/collective.aitools.mo +0 -0
  84. collective_aitools-1.0.0/src/collective/aitools/locales/en/LC_MESSAGES/collective.aitools.po +236 -0
  85. collective_aitools-1.0.0/src/collective/aitools/locales/es/LC_MESSAGES/collective.aitools.mo +0 -0
  86. collective_aitools-1.0.0/src/collective/aitools/locales/es/LC_MESSAGES/collective.aitools.po +236 -0
  87. collective_aitools-1.0.0/src/collective/aitools/locales/it/LC_MESSAGES/collective.aitools.mo +0 -0
  88. collective_aitools-1.0.0/src/collective/aitools/locales/it/LC_MESSAGES/collective.aitools.po +236 -0
  89. collective_aitools-1.0.0/src/collective/aitools/locales/pt_BR/LC_MESSAGES/collective.aitools.mo +0 -0
  90. collective_aitools-1.0.0/src/collective/aitools/locales/pt_BR/LC_MESSAGES/collective.aitools.po +236 -0
  91. collective_aitools-1.0.0/src/collective/aitools/permissions.zcml +5 -0
  92. collective_aitools-1.0.0/src/collective/aitools/profiles/default/browserlayer.xml +6 -0
  93. collective_aitools-1.0.0/src/collective/aitools/profiles/default/catalog.xml +13 -0
  94. collective_aitools-1.0.0/src/collective/aitools/profiles/default/controlpanel.xml +21 -0
  95. collective_aitools-1.0.0/src/collective/aitools/profiles/default/diff_tool.xml +6 -0
  96. collective_aitools-1.0.0/src/collective/aitools/profiles/default/metadata.xml +7 -0
  97. collective_aitools-1.0.0/src/collective/aitools/profiles/default/registry/.gitkeep +0 -0
  98. collective_aitools-1.0.0/src/collective/aitools/profiles/default/registry/main.xml +10 -0
  99. collective_aitools-1.0.0/src/collective/aitools/profiles/default/registry/resources.xml +18 -0
  100. collective_aitools-1.0.0/src/collective/aitools/profiles/default/repositorytool.xml +6 -0
  101. collective_aitools-1.0.0/src/collective/aitools/profiles/default/rolemap.xml +6 -0
  102. collective_aitools-1.0.0/src/collective/aitools/profiles/default/types/.gitkeep +0 -0
  103. collective_aitools-1.0.0/src/collective/aitools/profiles/default/types.xml +10 -0
  104. collective_aitools-1.0.0/src/collective/aitools/profiles/initial/metadata.xml +4 -0
  105. collective_aitools-1.0.0/src/collective/aitools/profiles/uninstall/browserlayer.xml +6 -0
  106. collective_aitools-1.0.0/src/collective/aitools/profiles/uninstall/registry.xml +10 -0
  107. collective_aitools-1.0.0/src/collective/aitools/profiles.zcml +41 -0
  108. collective_aitools-1.0.0/src/collective/aitools/serializers/__init__.py +0 -0
  109. collective_aitools-1.0.0/src/collective/aitools/serializers/configure.zcml +10 -0
  110. collective_aitools-1.0.0/src/collective/aitools/serializers/summary.py +10 -0
  111. collective_aitools-1.0.0/src/collective/aitools/services/__init__.py +0 -0
  112. collective_aitools-1.0.0/src/collective/aitools/services/assistant.py +187 -0
  113. collective_aitools-1.0.0/src/collective/aitools/services/configure.zcml +59 -0
  114. collective_aitools-1.0.0/src/collective/aitools/services/help_me_write.py +79 -0
  115. collective_aitools-1.0.0/src/collective/aitools/services/recognize_image.py +65 -0
  116. collective_aitools-1.0.0/src/collective/aitools/services/suggest_tags.py +55 -0
  117. collective_aitools-1.0.0/src/collective/aitools/services/summarize.py +55 -0
  118. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/__init__.py +17 -0
  119. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/.gitkeep +0 -0
  120. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/content/__metadata__.json +40 -0
  121. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/data.json +56 -0
  122. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/content/a58ccead718140c1baa98d43595fc3e6/image/plone-foundation.png +0 -0
  123. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/content/a720393b3c0240e5bd27c43fcd2cfd1e/data.json +98 -0
  124. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/content/plone_site_root/data.json +267 -0
  125. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/principals.json +4 -0
  126. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/redirects.json +1 -0
  127. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/relations.json +1 -0
  128. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/examplecontent/translations.json +1 -0
  129. collective_aitools-1.0.0/src/collective/aitools/setuphandlers/initial.py +16 -0
  130. collective_aitools-1.0.0/src/collective/aitools/testing.py +49 -0
  131. collective_aitools-1.0.0/src/collective/aitools/tools/__init__.py +13 -0
  132. collective_aitools-1.0.0/src/collective/aitools/tools/assistant.py +124 -0
  133. collective_aitools-1.0.0/src/collective/aitools/tools/base.py +75 -0
  134. collective_aitools-1.0.0/src/collective/aitools/tools/configure.zcml +35 -0
  135. collective_aitools-1.0.0/src/collective/aitools/tools/help_me_write.py +83 -0
  136. collective_aitools-1.0.0/src/collective/aitools/tools/recognize_image.py +131 -0
  137. collective_aitools-1.0.0/src/collective/aitools/tools/suggest_tags.py +102 -0
  138. collective_aitools-1.0.0/src/collective/aitools/tools/summarize.py +53 -0
  139. collective_aitools-1.0.0/src/collective/aitools/tools/text.py +108 -0
  140. collective_aitools-1.0.0/src/collective/aitools/upgrades/__init__.py +0 -0
  141. collective_aitools-1.0.0/src/collective/aitools/upgrades/configure.zcml +21 -0
  142. collective_aitools-1.0.0/src/collective/aitools/vocabularies/__init__.py +0 -0
  143. collective_aitools-1.0.0/src/collective/aitools/vocabularies/configure.zcml +5 -0
  144. collective_aitools-1.0.0/tests/conftest.py +16 -0
  145. collective_aitools-1.0.0/tests/controlpanels/test_ai_tools.py +26 -0
  146. collective_aitools-1.0.0/tests/services/test_help_me_write.py +94 -0
  147. collective_aitools-1.0.0/tests/services/test_recognize_image.py +90 -0
  148. collective_aitools-1.0.0/tests/services/test_text_tools.py +107 -0
  149. collective_aitools-1.0.0/tests/setup/test_setup_install.py +28 -0
  150. collective_aitools-1.0.0/tests/setup/test_setup_uninstall.py +30 -0
  151. collective_aitools-1.0.0/tests/tools/test_text.py +63 -0
  152. collective_aitools-1.0.0/tests/tools/test_tools.py +252 -0
  153. collective_aitools-1.0.0/version.txt +1 -0
  154. collective_aitools-1.0.0/webpack.config.js +36 -0
@@ -0,0 +1,14 @@
1
+ .editorconfig
2
+ .gitattributes
3
+ bin
4
+ Dockerfile
5
+ Dockerfile.acceptance
6
+ include
7
+ instance
8
+ instance.yaml
9
+ lib
10
+ lib64
11
+ Makefile
12
+ pyvenv.cfg
13
+ var
14
+ .venv
@@ -0,0 +1,35 @@
1
+
2
+ # EditorConfig Configuration file, for more details see:
3
+ # https://EditorConfig.org
4
+ # EditorConfig is a convention description, that could be interpreted
5
+ # by multiple editors to enforce common coding conventions for specific
6
+ # file types
7
+
8
+ # top-most EditorConfig file:
9
+ # Will ignore other EditorConfig files in Home directory or upper tree level.
10
+ root = true
11
+
12
+
13
+ [*] # For All Files
14
+ # Unix-style newlines with a newline ending every file
15
+ end_of_line = lf
16
+ insert_final_newline = true
17
+ trim_trailing_whitespace = true
18
+ # Set default charset
19
+ charset = utf-8
20
+ # Indent style default
21
+ indent_style = space
22
+
23
+ [*.{py,cfg,ini}]
24
+ # 4 space indentation
25
+ indent_size = 4
26
+
27
+ [*.{html,dtml,pt,zpt,xml,zcml,js,json,ts,less,scss,css,sass,yml,yaml}]
28
+ # 2 space indentation
29
+ indent_size = 2
30
+
31
+ [{Makefile,.gitmodules}]
32
+ # Tab indentation (no size specified, but view as 4 spaces)
33
+ indent_style = tab
34
+ indent_size = unset
35
+ tab_width = unset
@@ -0,0 +1,22 @@
1
+ # Generated from:
2
+ # https://github.com/plone/meta/tree/master/config/default
3
+ # See the inline comments on how to expand/tweak this configuration file
4
+ [flake8]
5
+ doctests = 1
6
+ ignore =
7
+ # black takes care of line length
8
+ E501,
9
+ # black takes care of where to break lines
10
+ W503,
11
+ # black takes care of spaces within slicing (list[:])
12
+ E203,
13
+ # black takes care of spaces after commas
14
+ E231,
15
+
16
+ ##
17
+ # Add extra configuration options in .meta.toml:
18
+ # [flake8]
19
+ # extra_lines = """
20
+ # _your own configuration lines_
21
+ # """
22
+ ##
@@ -0,0 +1,50 @@
1
+ # python related
2
+ *.egg-info
3
+ *.pyc
4
+ *.pyo
5
+
6
+ # tools related
7
+ __pycache__/
8
+ .coverage
9
+ .mypy_cache
10
+ .pytest_cache
11
+ .ruff_cache
12
+ /build/
13
+ coverage.xml
14
+ dist/
15
+ docs/_build
16
+ node_modules/
17
+
18
+ # Classic UI resources build (webpack); source lives in resources/, output
19
+ # under src/collective/aitools/browser/static is committed.
20
+ /stats.json
21
+ /src/collective/aitools/browser/static/*.map
22
+
23
+ # venv / buildout related
24
+ .eggs/
25
+ .installed.cfg
26
+ .mr.developer.cfg
27
+ .venv/
28
+ bin/
29
+ develop-eggs/
30
+ eggs/
31
+ etc/
32
+ include/
33
+ lib/
34
+ lib64
35
+ parts/
36
+ pyvenv.cfg
37
+ var/
38
+
39
+ # mxdev
40
+ .installed.txt
41
+ .lock
42
+ /.mxdev_cache/
43
+ /.make-sentinels/
44
+ /*-mxdev.txt
45
+ /instance/
46
+ /reports/
47
+ /sources/
48
+ /venv/
49
+ constraints*.txt
50
+ requirements*.txt
@@ -0,0 +1,203 @@
1
+ # AGENTS.md — backend
2
+
3
+ Agent guide for the `collective.aitools` Plone add-on (`backend/`). For
4
+ the cross-cutting picture of how this half relates to the Volto half
5
+ and to the `collective.aisettings` dependency, read
6
+ [`../AGENTS.md`](../AGENTS.md) first.
7
+
8
+ ## Scope
9
+
10
+ Anything under `backend/src/collective/aitools/` plus the package
11
+ metadata (`pyproject.toml`, `instance.yaml`, `mx.ini`, profiles, ZCML).
12
+
13
+ The frontend Volto add-on (`frontend/packages/volto-collective-aitools/`)
14
+ is documented at [`../frontend/AGENTS.md`](../frontend/AGENTS.md). Don't
15
+ edit it from this side; coordinate cross-half changes via REST/registry
16
+ contracts and land both sides together.
17
+
18
+ **Do not edit `backend/sources/`** — it is an mxdev checkout of
19
+ `collective.aisettings` (and any other VCS sources from `mx.ini`),
20
+ read-only as far as this repo is concerned.
21
+
22
+ ## The `collective.aisettings` dependency
23
+
24
+ This add-on connects to LLMs **only** through `collective.aisettings`.
25
+ It is a hard dependency (declared in [`pyproject.toml`](pyproject.toml)
26
+ `dependencies`) and pulled into the dev venv from a local checkout via
27
+ [`mx.ini`](mx.ini) (`url = collective.aisettings`,
28
+ `subdirectory = backend`). `mx.ini` also pins a few transitive
29
+ versions (`pydantic-ai-slim`, `pydantic`, `h11`, …) to keep
30
+ `collective.aisettings`' pydantic-ai stack compatible with Plone's
31
+ constraints — see the comments there before changing any pin.
32
+
33
+ To make an AI call from Python, get the `IAIService` utility from
34
+ `collective.aisettings` rather than calling an LLM SDK directly:
35
+
36
+ ```python
37
+ from zope.component import getUtility
38
+ from collective.aisettings.interfaces import IAIService
39
+
40
+ service = getUtility(IAIService)
41
+ result = service.chat(prompt="…", context=context)
42
+ ```
43
+
44
+ Capability resolution, the permission gate, and the registry JSON
45
+ schema all live in `collective.aisettings`. Its
46
+ `backend/sources/collective.aisettings/backend/AGENTS.md` is the
47
+ reference; don't reimplement model resolution or key handling here.
48
+
49
+ ## Package layout
50
+
51
+ ```
52
+ backend/src/collective/aitools/
53
+ ├── __init__.py ← __version__, PACKAGE_NAME, MessageFactory (_), logger
54
+ ├── configure.zcml ← package root: translations + includes subpackages
55
+ ├── AGENTS.md ← in-package guide: the AI tools framework + control panel ← READ
56
+ ├── dependencies.zcml ← <include> plone.restapi + plone.volto + collective.aisettings
57
+ ├── permissions.zcml ← add/declare permissions here
58
+ ├── profiles.zcml ← GenericSetup profile registrations (default/uninstall/initial)
59
+ ├── interfaces.py ← IBrowserLayer + IAITool + IAIAssistantCapability + IContentTypeSynonyms + IAIToolsSettings
60
+ ├── testing.py ← PloneSandboxLayer + INTEGRATION/FUNCTIONAL/ACCEPTANCE layers
61
+ ├── setuphandlers/ ← INonInstallable (hidden profiles) + post-install + example content
62
+ ├── tools/ ← the AI tools (named IAITool utilities) — see src AGENTS.md
63
+ ├── capabilities/ ← assistant capabilities (named IAIAssistantCapability utilities) — see src AGENTS.md
64
+ ├── controlpanels/ ← "AI Tools" control panel (RegistryEditForm + restapi adapter)
65
+ ├── services/ ← plone.restapi services — @aitools-* endpoints (incl. @aitools-assistant)
66
+ ├── browser/ ← viewlets (Patternslib triggers) + static/ (committed webpack bundle output)
67
+ ├── content/ ← (empty stub) dexterity content types
68
+ ├── indexers/ ← (empty stub) portal_catalog indexers
69
+ ├── serializers/ ← plone.restapi serializers — summary.py adds listing metadata
70
+ ├── vocabularies/ ← (empty stub) zope.schema vocabularies
71
+ ├── upgrades/ ← (empty stub) GenericSetup upgrade steps
72
+ ├── locales/ ← i18n (.pot/.po); __main__.py drives `make i18n`
73
+ └── profiles/
74
+ ├── default/ ← install profile (registry, catalog, types, browserlayer, controlpanel)
75
+ ├── initial/ ← initial metadata
76
+ └── uninstall/ ← uninstall profile (hidden via setuphandlers)
77
+ ```
78
+
79
+ The product's features are **AI tools** — named `IAITool` utilities under
80
+ `tools/`, enabled/configured from the "AI Tools" control panel
81
+ (`controlpanels/`), each calling the `collective.aisettings` `IAIService`. One
82
+ of them, `assistant`, is an **agentic chat** that dispatches to **capabilities**
83
+ (named `IAIAssistantCapability` utilities under `capabilities/`) via
84
+ `IAIService.tool_call`, with a classic-UI floating chat widget. The detailed
85
+ framework docs (anatomy of a tool, the assistant + capabilities, the add-a-tool
86
+ / add-a-capability checklists, what's deferred) live in
87
+ [`src/collective/aitools/AGENTS.md`](src/collective/aitools/AGENTS.md) — read
88
+ that before adding or changing a tool.
89
+
90
+ `serializers/summary.py` is the other non-trivial piece: an
91
+ `IJSONSummarySerializerMetadata` utility that adds `image_field`,
92
+ `image_scales`, `effective`, and `Subject` to REST listing summaries. The
93
+ `content` / `indexers` / `vocabularies` / `upgrades` subpackages are still
94
+ empty `__init__.py` stubs wired into
95
+ [`configure.zcml`](src/collective/aitools/configure.zcml).
96
+
97
+ ## How it's wired (ZCML cheat sheet)
98
+
99
+ - [`configure.zcml`](src/collective/aitools/configure.zcml) — registers
100
+ `locales`, includes `Products.CMFCore` permissions, then
101
+ `dependencies.zcml`, `profiles.zcml`, `permissions.zcml`, and the
102
+ `.browser` / `.controlpanels` / `.indexers` / `.serializers` /
103
+ `.services` / `.tools` / `.vocabularies` subpackages. **Add new
104
+ subpackages to the include list here.** The `++plone++collective.aitools`
105
+ static directory (`browser/static/`, the committed webpack bundle output) is
106
+ registered with `<plone:static>` in `browser/configure.zcml`; the Classic UI
107
+ source lives in `backend/resources/` (see the src AGENTS.md for the build).
108
+ - Auto-included into Plone via the `z3c.autoinclude.plugin` entry point
109
+ (`target = "plone"`) in [`pyproject.toml`](pyproject.toml).
110
+ - [`dependencies.zcml`](src/collective/aitools/dependencies.zcml) —
111
+ `plone.restapi` + `plone.volto` + `collective.aisettings`. Add backend
112
+ dependency ZCML here.
113
+ - Profile metadata version is `1000`
114
+ ([`profiles/default/metadata.xml`](src/collective/aitools/profiles/default/metadata.xml));
115
+ it depends on `profile-plone.volto:default` and
116
+ `profile-collective.aisettings:default`. The install test asserts this
117
+ version, so when you add an upgrade step, bump the version here and add
118
+ the step under `upgrades/`.
119
+ - `plone.exportimport` (Plone 6.1 core, used by `setuphandlers/initial.py`
120
+ for example content) must be present in the venv — both this package and
121
+ `collective.aisettings` import it at profile-registration time. If a
122
+ fresh `make install` lacks it, install `plone.exportimport==2.0.1`.
123
+
124
+ ## Conventions
125
+
126
+ - Imports: ruff isort config is `force-single-line`, `from-first`,
127
+ `no-sections` (one import per line, `from x import y` style, no
128
+ grouping). `make format` enforces it.
129
+ - Use the module-level `_ = MessageFactory("collective.aitools")` from
130
+ `__init__.py` for translatable strings; i18n domain is
131
+ `collective.aitools` everywhere (ZCML `i18n_domain`, locales).
132
+ - Use the package `logger` from `__init__.py` rather than ad-hoc
133
+ logging.
134
+ - Python ≥ 3.11; line length 88; target Plone 6.1 / 6.2.
135
+
136
+ ## Tests
137
+
138
+ Pytest via `pytest-plone`, configured in
139
+ [`pyproject.toml`](pyproject.toml) (`testpaths = ["tests"]`). Tests live
140
+ in [`tests/`](tests/) (sibling of `src/`, not inside the package):
141
+
142
+ - [`tests/conftest.py`](tests/conftest.py) builds fixtures from the
143
+ three layers in
144
+ [`testing.py`](src/collective/aitools/testing.py) via
145
+ `pytest_plone.fixtures_factory` (`integration`, `functional`,
146
+ `acceptance` fixtures).
147
+ - [`tests/setup/`](tests/setup/) covers install/uninstall (profile
148
+ version, browserlayer registration, product (un)installed).
149
+ - [`tests/tools/`](tests/tools/) covers tool registration, enable/model
150
+ resolution, and `run` (using a `fake_service` fixture that swaps the
151
+ global `IAIService` for a recorder — no network/model needed).
152
+ - [`tests/controlpanels/`](tests/controlpanels/) covers the AI Tools
153
+ control panel (registry records, classic page, restapi configlet).
154
+
155
+ Mirror the `tests/<area>/test_*.py` layout when you add a feature.
156
+ `tests/*` files relax some ruff rules (`E501`, `RUF001`, `S101`) so
157
+ asserts and long lines are fine in tests.
158
+
159
+ ## Validation commands
160
+
161
+ ```sh
162
+ # From backend/
163
+ make install # uv venv + mxdev-resolved deps (pulls collective.aisettings) + instance config
164
+ make create-site # create the Plone site with the addon profile
165
+ make start # backend on :8080 (runwsgi)
166
+ make console # zconsole debug shell into the instance
167
+
168
+ make format # ruff (incl. import sort) + zpretty for XML/ZCML
169
+ make lint # ruff check + pyroma + check-python-versions + zpretty --check
170
+ make test # pytest
171
+ make test-coverage # pytest --cov
172
+ make i18n # rebuild .pot/.po (python -m collective.aitools.locales)
173
+ make install-npm # npm install (Classic UI resources, first time)
174
+ make build-resources # npm run build → browser/static/aitools-*.min.js (webpack)
175
+ make check-resources # CI: build + fail if the committed bundle is stale
176
+ ```
177
+
178
+ `make install` is required before tests (it creates `.venv` and
179
+ resolves dependencies through mxdev). Lint/format run via `uvx` and
180
+ don't need the venv.
181
+
182
+ ## Editing rules
183
+
184
+ - **Keep this file and the in-package guide current.** When you add real
185
+ code under `src/collective/aitools/` — a content type, REST service,
186
+ control panel, vocabulary, indexer, upgrade step — update the layout
187
+ and the relevant section above. If the package grows enough that a
188
+ subtree needs its own detailed notes, create an `AGENTS.md` inside
189
+ `src/collective/aitools/` (or the specific subpackage) and keep it in
190
+ sync as that code changes. Treat a code change in those folders and
191
+ its `AGENTS.md` update as one unit of work.
192
+ - Anything AI/LLM-related goes through `collective.aisettings`' public
193
+ API (the `IAIService` utility / its REST endpoints). Don't add LLM
194
+ SDK calls, API-key fields, or model-resolution logic here.
195
+ - When you add a subpackage with ZCML, include it from
196
+ `configure.zcml`; when you add an upgrade, bump the profile version in
197
+ `profiles/default/metadata.xml` and register the step under
198
+ `upgrades/`.
199
+ - Don't edit `backend/sources/` or import private internals of
200
+ `collective.aisettings`; depend on its documented interfaces.
201
+ - Changes sharing a contract with the Volto add-on (a REST service it
202
+ calls, a registry record both UIs read) must land together with the
203
+ frontend change — see [`../frontend/AGENTS.md`](../frontend/AGENTS.md).
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ <!--
4
+ You should *NOT* be adding new change log entries to this file.
5
+ You should create a file in the news directory instead.
6
+ For helpful instructions, please see:
7
+ https://github.com/plone/plone.releaser/blob/master/ADD-A-NEWS-ITEM.rst
8
+ -->
9
+
10
+ <!-- towncrier release notes start -->
11
+
12
+ ## 1.0.0 (2026-07-15)
13
+
14
+
15
+ ### New features:
16
+
17
+ - "Recognize image" now returns structured metadata — a title, a description and
18
+ 5–10 tags — as JSON. A new "Get image details" button on Classic UI edit/add
19
+ forms sends the chosen image to the ``@aitools-recognize-image`` REST endpoint
20
+ and fills in the Title, Description and Tags fields that are still empty. A
21
+ ``scripts/recognize_images.py`` CLI script (``make recognize-images``) backfills
22
+ those fields across every image on the site, with an optional ``--path`` to
23
+ limit it to a subtree. @frapell
24
+ - Add an "AI Tools" control panel and a tools framework (named ``IAITool``
25
+ utilities), with two initial Classic UI tools: "Recognize image" (vision)
26
+ and "Help me write" (drafting/rewriting). "Help me write" adds a button to
27
+ TinyMCE rich-text fields on edit/add forms, backed by the
28
+ ``@aitools-help-me-write`` REST endpoint. Tools reach a model through
29
+ ``collective.aisettings``. @frapell
30
+ - Add an agentic chat "Assistant" for authenticated users. It runs a tool-calling
31
+ loop (via ``collective.aisettings``' ``IAIService.tool_call``) over a set of
32
+ *capabilities* — named ``IAIAssistantCapability`` utilities — that ground its
33
+ answers: "Search the site", "Where to add" (suggest folders for a content type)
34
+ and "Propose a draft" (generate a draft from a rough brief for the editor to
35
+ confirm). Capabilities are permission-gated per user, and any content creation
36
+ only happens after an explicit confirmation, re-validated server-side by the
37
+ ``@aitools-assistant`` / ``@aitools-assistant-apply`` endpoints. Ships a
38
+ Classic UI floating chat widget (Markdown rendering, session persistence,
39
+ "start new chat"). Loose content-type names are accepted and add-ons can
40
+ contribute more via an ``IContentTypeSynonyms`` utility. @frapell
41
+ - Add two editor tools: "Summarize" fills an item's Description from its body
42
+ text (also usable as the SEO meta description), and "Suggest tags" proposes
43
+ Subject keywords from the body text, reusing tags already in use on the site.
44
+ Both offer a Classic UI edit-form button (``@aitools-summarize`` /
45
+ ``@aitools-suggest-tags`` endpoints) and a CLI backfill script
46
+ (``make summarize-content`` / ``make suggest-tags``, with an optional
47
+ ``--path``). @frapell
48
+
49
+
50
+ ### Internal:
51
+
52
+ - Refactor the Classic UI JavaScript into ES6 Patternslib modules under
53
+ ``backend/resources/``, built with webpack (``@patternslib/dev``) into a single
54
+ ``aitools`` bundle registered through the Plone resource registry — replacing
55
+ the hand-minified ``browser/static`` assets and the ``rjsmin``/``rcssmin`` step.
56
+ Build with ``make build-resources``. @frapell
@@ -0,0 +1,3 @@
1
+ # Contributors
2
+
3
+ - Franco Pellegrini [frapell@gmail.com]
@@ -0,0 +1,38 @@
1
+ # syntax=docker/dockerfile:1
2
+ ARG PLONE_VERSION=6.0.15
3
+ FROM plone/server-builder:${PLONE_VERSION} AS builder
4
+
5
+ WORKDIR /app
6
+
7
+
8
+ # Add local code
9
+ COPY scripts/ scripts/
10
+ COPY . src
11
+
12
+ # Install local requirements and pre-compile mo files
13
+ RUN <<EOT
14
+ set -e
15
+ bin/pip install mxdev uv
16
+ sed -i 's/-e .\[test\]/./g' src/mx.ini
17
+ cd /app/src
18
+ # remove potentially existing virtualenv from local build
19
+ rm -rf .venv
20
+ ../bin/mxdev -c mx.ini
21
+ ../bin/uv pip install -r requirements-mxdev.txt
22
+ ../bin/python /compile_mo.py
23
+ EOT
24
+
25
+ FROM plone/server-prod-config:${PLONE_VERSION}
26
+
27
+ LABEL maintainer="Franco Pellegrini <frapell@gmail.com>" \
28
+ org.label-schema.name="collective-ai-tools-backend" \
29
+ org.label-schema.description="Collective AI Tools backend image." \
30
+ org.label-schema.vendor="Franco Pellegrini"
31
+
32
+ # Copy /app from builder
33
+ COPY --from=builder /app /app
34
+
35
+ RUN <<EOT
36
+ set -e
37
+ ln -s /data /app/var
38
+ EOT
@@ -0,0 +1,40 @@
1
+ # syntax=docker/dockerfile:1
2
+ ARG PLONE_VERSION=6.0.15
3
+ FROM plone/server-builder:${PLONE_VERSION} AS builder
4
+
5
+ WORKDIR /app
6
+
7
+
8
+ # Add local code
9
+ COPY scripts/ scripts/
10
+ COPY . src
11
+
12
+ # Install local requirements and pre-compile mo files
13
+ RUN <<EOT
14
+ set -e
15
+ bin/pip install mxdev uv
16
+ sed -i 's/-e .\[test\]/./g' src/mx.ini
17
+ cd /app/src
18
+ # remove potentially existing virtualenv from local build
19
+ rm -rf .venv
20
+ ../bin/mxdev -c mx.ini
21
+ ../bin/uv pip install -r requirements-mxdev.txt
22
+ ../bin/python /compile_mo.py
23
+ EOT
24
+
25
+ FROM plone/server-acceptance:${PLONE_VERSION}
26
+
27
+ LABEL maintainer="Franco Pellegrini <frapell@gmail.com>" \
28
+ org.label-schema.name="collective-ai-tools-acceptance" \
29
+ org.label-schema.description="Collective AI Tools backend acceptance image." \
30
+ org.label-schema.vendor="Franco Pellegrini"
31
+
32
+ ENV CONFIGURE_PACKAGES="plone.restapi,plone.volto,plone.volto.cors,collective.aitools"
33
+ ENV APPLY_PROFILES="collective.aitools:default"
34
+
35
+ # Copy /app from builder
36
+ COPY --from=builder /app /app
37
+
38
+ RUN <<EOT
39
+ ln -s /data /app/var
40
+ EOT