jfastframework 0.1.0a1__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 (280) hide show
  1. jfastframework-0.1.0a1/.env.example +37 -0
  2. jfastframework-0.1.0a1/.github/workflows/ci.yml +125 -0
  3. jfastframework-0.1.0a1/.github/workflows/pages.yml +121 -0
  4. jfastframework-0.1.0a1/.github/workflows/release.yml +99 -0
  5. jfastframework-0.1.0a1/.gitignore +32 -0
  6. jfastframework-0.1.0a1/.jfast/skills/README.md +65 -0
  7. jfastframework-0.1.0a1/.jfast/skills/add-rag/SKILL.md +145 -0
  8. jfastframework-0.1.0a1/.jfast/skills/add-service/SKILL.md +125 -0
  9. jfastframework-0.1.0a1/.jfast/skills/build-frontend/SKILL.md +172 -0
  10. jfastframework-0.1.0a1/.jfast/skills/create-module/SKILL.md +126 -0
  11. jfastframework-0.1.0a1/.jfast/skills/create-plugin/SKILL.md +102 -0
  12. jfastframework-0.1.0a1/.jfast/skills/design-system/SKILL.md +90 -0
  13. jfastframework-0.1.0a1/.jfast/skills/respect-contracts/SKILL.md +113 -0
  14. jfastframework-0.1.0a1/AGENTS.md +189 -0
  15. jfastframework-0.1.0a1/ARCHITECTURE.md +184 -0
  16. jfastframework-0.1.0a1/CHANGELOG.md +577 -0
  17. jfastframework-0.1.0a1/PKG-INFO +471 -0
  18. jfastframework-0.1.0a1/PLAN-NEXT.md +603 -0
  19. jfastframework-0.1.0a1/PLAN.md +312 -0
  20. jfastframework-0.1.0a1/README.md +373 -0
  21. jfastframework-0.1.0a1/STATUS.md +88 -0
  22. jfastframework-0.1.0a1/docs/auth.md +275 -0
  23. jfastframework-0.1.0a1/docs/cloud.md +157 -0
  24. jfastframework-0.1.0a1/docs/contracts.md +274 -0
  25. jfastframework-0.1.0a1/docs/datastores.md +140 -0
  26. jfastframework-0.1.0a1/docs/deploy.md +116 -0
  27. jfastframework-0.1.0a1/docs/frontend.md +171 -0
  28. jfastframework-0.1.0a1/docs/kubernetes.md +145 -0
  29. jfastframework-0.1.0a1/docs/local-setup.md +228 -0
  30. jfastframework-0.1.0a1/docs/migrations-and-tests.md +147 -0
  31. jfastframework-0.1.0a1/docs/modules.md +184 -0
  32. jfastframework-0.1.0a1/docs/multitenancy.md +133 -0
  33. jfastframework-0.1.0a1/docs/plugins.md +198 -0
  34. jfastframework-0.1.0a1/docs/queues-and-events.md +191 -0
  35. jfastframework-0.1.0a1/docs/service-contract.md +156 -0
  36. jfastframework-0.1.0a1/docs/skills.md +103 -0
  37. jfastframework-0.1.0a1/docs/storage.md +174 -0
  38. jfastframework-0.1.0a1/docs/workspaces.md +281 -0
  39. jfastframework-0.1.0a1/docs-site/assets/BRAND.md +54 -0
  40. jfastframework-0.1.0a1/docs-site/assets/favicon.svg +17 -0
  41. jfastframework-0.1.0a1/docs-site/assets/mark.svg +22 -0
  42. jfastframework-0.1.0a1/docs-site/assets/site.css +586 -0
  43. jfastframework-0.1.0a1/docs-site/build.py +533 -0
  44. jfastframework-0.1.0a1/docs-site/check.py +139 -0
  45. jfastframework-0.1.0a1/examples/README.md +13 -0
  46. jfastframework-0.1.0a1/examples/hello/jfast.toml +9 -0
  47. jfastframework-0.1.0a1/examples/hello/main.py +34 -0
  48. jfastframework-0.1.0a1/jfast.toml +44 -0
  49. jfastframework-0.1.0a1/legacy/README.v0.md +45 -0
  50. jfastframework-0.1.0a1/legacy/commands.py +412 -0
  51. jfastframework-0.1.0a1/pyproject.toml +193 -0
  52. jfastframework-0.1.0a1/requirements.txt +8 -0
  53. jfastframework-0.1.0a1/scripts/smoke.sh +156 -0
  54. jfastframework-0.1.0a1/scripts/smoke_auth_k8s.sh +165 -0
  55. jfastframework-0.1.0a1/scripts/smoke_contracts.sh +147 -0
  56. jfastframework-0.1.0a1/scripts/smoke_docs.sh +80 -0
  57. jfastframework-0.1.0a1/scripts/smoke_frontend.sh +71 -0
  58. jfastframework-0.1.0a1/scripts/smoke_go.sh +78 -0
  59. jfastframework-0.1.0a1/scripts/smoke_start.sh +76 -0
  60. jfastframework-0.1.0a1/scripts/smoke_storage_tenancy.sh +155 -0
  61. jfastframework-0.1.0a1/scripts/smoke_workspace.sh +175 -0
  62. jfastframework-0.1.0a1/src/jfastframework/__init__.py +44 -0
  63. jfastframework-0.1.0a1/src/jfastframework/app.py +155 -0
  64. jfastframework-0.1.0a1/src/jfastframework/auth/__init__.py +56 -0
  65. jfastframework-0.1.0a1/src/jfastframework/auth/jwks.py +117 -0
  66. jfastframework-0.1.0a1/src/jfastframework/auth/oidc.py +268 -0
  67. jfastframework-0.1.0a1/src/jfastframework/auth/principal.py +64 -0
  68. jfastframework-0.1.0a1/src/jfastframework/auth/store.py +141 -0
  69. jfastframework-0.1.0a1/src/jfastframework/auth/tokens.py +197 -0
  70. jfastframework-0.1.0a1/src/jfastframework/cli/__init__.py +5 -0
  71. jfastframework-0.1.0a1/src/jfastframework/cli/main.py +1549 -0
  72. jfastframework-0.1.0a1/src/jfastframework/cli/patcher.py +160 -0
  73. jfastframework-0.1.0a1/src/jfastframework/cli/scaffold.py +478 -0
  74. jfastframework-0.1.0a1/src/jfastframework/context.py +69 -0
  75. jfastframework-0.1.0a1/src/jfastframework/contracts/__init__.py +36 -0
  76. jfastframework-0.1.0a1/src/jfastframework/contracts/_scan.py +141 -0
  77. jfastframework-0.1.0a1/src/jfastframework/contracts/blocking.py +425 -0
  78. jfastframework-0.1.0a1/src/jfastframework/contracts/checker.py +318 -0
  79. jfastframework-0.1.0a1/src/jfastframework/contracts/model.py +306 -0
  80. jfastframework-0.1.0a1/src/jfastframework/contracts/render.py +116 -0
  81. jfastframework-0.1.0a1/src/jfastframework/db/__init__.py +13 -0
  82. jfastframework-0.1.0a1/src/jfastframework/db/base.py +47 -0
  83. jfastframework-0.1.0a1/src/jfastframework/db/repository.py +152 -0
  84. jfastframework-0.1.0a1/src/jfastframework/deploy/__init__.py +17 -0
  85. jfastframework-0.1.0a1/src/jfastframework/deploy/compose.py +182 -0
  86. jfastframework-0.1.0a1/src/jfastframework/deploy/kubernetes.py +431 -0
  87. jfastframework-0.1.0a1/src/jfastframework/deploy/serverless.py +199 -0
  88. jfastframework-0.1.0a1/src/jfastframework/deploy/workspace.py +256 -0
  89. jfastframework-0.1.0a1/src/jfastframework/errors.py +141 -0
  90. jfastframework-0.1.0a1/src/jfastframework/graph.py +109 -0
  91. jfastframework-0.1.0a1/src/jfastframework/health.py +110 -0
  92. jfastframework-0.1.0a1/src/jfastframework/languages.py +123 -0
  93. jfastframework-0.1.0a1/src/jfastframework/middleware.py +169 -0
  94. jfastframework-0.1.0a1/src/jfastframework/plugins/__init__.py +17 -0
  95. jfastframework-0.1.0a1/src/jfastframework/plugins/base.py +162 -0
  96. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/__init__.py +11 -0
  97. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/auth.py +698 -0
  98. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/cache.py +148 -0
  99. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/database.py +149 -0
  100. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/events.py +306 -0
  101. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/gateway.py +239 -0
  102. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/metrics.py +162 -0
  103. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/mongo.py +108 -0
  104. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/notifications.py +324 -0
  105. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/observability.py +186 -0
  106. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/qdrant.py +104 -0
  107. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/queue.py +262 -0
  108. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/rag.py +308 -0
  109. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/sentry.py +74 -0
  110. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/storage.py +293 -0
  111. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/tenancy.py +222 -0
  112. jfastframework-0.1.0a1/src/jfastframework/plugins/builtin/web.py +209 -0
  113. jfastframework-0.1.0a1/src/jfastframework/plugins/registry.py +182 -0
  114. jfastframework-0.1.0a1/src/jfastframework/py.typed +0 -0
  115. jfastframework-0.1.0a1/src/jfastframework/queues/__init__.py +10 -0
  116. jfastframework-0.1.0a1/src/jfastframework/queues/base.py +131 -0
  117. jfastframework-0.1.0a1/src/jfastframework/queues/postgres.py +211 -0
  118. jfastframework-0.1.0a1/src/jfastframework/queues/rabbitmq.py +139 -0
  119. jfastframework-0.1.0a1/src/jfastframework/queues/redis.py +235 -0
  120. jfastframework-0.1.0a1/src/jfastframework/queues/worker.py +204 -0
  121. jfastframework-0.1.0a1/src/jfastframework/resources.py +241 -0
  122. jfastframework-0.1.0a1/src/jfastframework/secrets.py +172 -0
  123. jfastframework-0.1.0a1/src/jfastframework/settings.py +173 -0
  124. jfastframework-0.1.0a1/src/jfastframework/sql.py +45 -0
  125. jfastframework-0.1.0a1/src/jfastframework/storage/__init__.py +38 -0
  126. jfastframework-0.1.0a1/src/jfastframework/storage/base.py +198 -0
  127. jfastframework-0.1.0a1/src/jfastframework/storage/local.py +192 -0
  128. jfastframework-0.1.0a1/src/jfastframework/storage/s3.py +295 -0
  129. jfastframework-0.1.0a1/src/jfastframework/templates/contracts_layered/contracts.toml.j2 +123 -0
  130. jfastframework-0.1.0a1/src/jfastframework/templates/contracts_screaming/contracts.toml.j2 +125 -0
  131. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/.env.example.j2 +4 -0
  132. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/.env.j2 +4 -0
  133. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/.env.production.j2 +7 -0
  134. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/.gitignore.j2 +6 -0
  135. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/README.md.j2 +69 -0
  136. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/index.html.j2 +13 -0
  137. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/package.json.j2 +25 -0
  138. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/components/BaseIcon.jsx.j2 +13 -0
  139. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/components/CardBox.jsx.j2 +9 -0
  140. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/components/SectionMain.jsx.j2 +3 -0
  141. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/components/SectionTitleLineWithButton.jsx.j2 +18 -0
  142. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/layouts/LayoutAuthenticated.jsx.j2 +39 -0
  143. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/main.jsx.j2 +12 -0
  144. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/menuAside.js.j2 +15 -0
  145. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/router/index.jsx.j2 +20 -0
  146. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/services/api.js.j2 +29 -0
  147. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/style.css.j2 +17 -0
  148. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/src/views/HomeView.jsx.j2 +58 -0
  149. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_react/vite.config.js.j2 +14 -0
  150. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/.env.example.j2 +2 -0
  151. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/.env.j2 +4 -0
  152. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/.env.production.j2 +7 -0
  153. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/.gitignore.j2 +6 -0
  154. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/README.md.j2 +69 -0
  155. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/index.html.j2 +13 -0
  156. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/package.json.j2 +25 -0
  157. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/App.vue.j2 +7 -0
  158. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/components/BaseIcon.vue.j2 +22 -0
  159. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/components/CardBox.vue.j2 +5 -0
  160. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/components/SectionMain.vue.j2 +5 -0
  161. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/components/SectionTitleLineWithButton.vue.j2 +22 -0
  162. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/layouts/LayoutAuthenticated.vue.j2 +35 -0
  163. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/main.js.j2 +8 -0
  164. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/menuAside.js.j2 +15 -0
  165. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/router/index.js.j2 +33 -0
  166. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/services/api.js.j2 +29 -0
  167. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/style.css.j2 +17 -0
  168. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/src/views/HomeView.vue.j2 +53 -0
  169. jfastframework-0.1.0a1/src/jfastframework/templates/frontend_vue/vite.config.js.j2 +19 -0
  170. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/__init__.py.j2 +7 -0
  171. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/README.md.j2 +48 -0
  172. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/__init__.py.j2 +22 -0
  173. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/models.py.j2 +19 -0
  174. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/repository.py.j2 +22 -0
  175. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/router.py.j2 +80 -0
  176. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/schemas.py.j2 +35 -0
  177. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/service.py.j2 +39 -0
  178. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/tests/__init__.py.j2 +7 -0
  179. jfastframework-0.1.0a1/src/jfastframework/templates/module_layered/{{module}}/tests/test_{{module}}.py.j2 +103 -0
  180. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/__init__.py.j2 +7 -0
  181. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/README.md.j2 +66 -0
  182. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/__init__.py.j2 +30 -0
  183. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/http.py.j2 +102 -0
  184. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/storage.py.j2 +41 -0
  185. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/tests/__init__.py.j2 +6 -0
  186. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/tests/test_{{module}}_domain.py.j2 +45 -0
  187. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/tests/test_{{module}}_use_cases.py.j2 +137 -0
  188. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/__init__.py.j2 +54 -0
  189. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/create_{{module}}.py.j2 +33 -0
  190. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/delete_{{module}}.py.j2 +10 -0
  191. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/get_{{module}}.py.j2 +10 -0
  192. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/list_{{module}}.py.j2 +12 -0
  193. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/use_cases/update_{{module}}.py.j2 +30 -0
  194. jfastframework-0.1.0a1/src/jfastframework/templates/module_screaming/{{module}}/{{module}}.py.j2 +51 -0
  195. jfastframework-0.1.0a1/src/jfastframework/templates/proto/proto/README.md.j2 +60 -0
  196. jfastframework-0.1.0a1/src/jfastframework/templates/proto/proto/{{service_slug}}.proto.j2 +96 -0
  197. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/.env.example.j2 +66 -0
  198. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/.gitignore.j2 +10 -0
  199. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/README.md.j2 +101 -0
  200. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/alembic.ini.j2 +44 -0
  201. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/conftest.py.j2 +27 -0
  202. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/jfast.toml.j2 +122 -0
  203. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/main.py.j2 +30 -0
  204. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/migrations/env.py.j2 +105 -0
  205. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/migrations/script.py.mako.j2 +31 -0
  206. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/migrations/versions/.gitkeep.j2 +0 -0
  207. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/pytest.ini.j2 +6 -0
  208. jfastframework-0.1.0a1/src/jfastframework/templates/service_base/requirements.txt.j2 +2 -0
  209. jfastframework-0.1.0a1/src/jfastframework/templates/service_gateway/.env.example.j2 +9 -0
  210. jfastframework-0.1.0a1/src/jfastframework/templates/service_gateway/README.md.j2 +56 -0
  211. jfastframework-0.1.0a1/src/jfastframework/templates/service_gateway/jfast.toml.j2 +31 -0
  212. jfastframework-0.1.0a1/src/jfastframework/templates/service_gateway/main.py.j2 +16 -0
  213. jfastframework-0.1.0a1/src/jfastframework/templates/service_gateway/requirements.txt.j2 +1 -0
  214. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/.env.example.j2 +17 -0
  215. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/.gitignore.j2 +4 -0
  216. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/Dockerfile.j2 +25 -0
  217. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/README.md.j2 +81 -0
  218. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/go.mod.j2 +3 -0
  219. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/jfast/config.go.j2 +66 -0
  220. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/jfast/health.go.j2 +112 -0
  221. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/jfast/jfast_test.go.j2 +167 -0
  222. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/jfast/middleware.go.j2 +120 -0
  223. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/jfast/problem.go.j2 +59 -0
  224. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/modules/{{module}}/helpers.go.j2 +26 -0
  225. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/modules/{{module}}/module.go.j2 +155 -0
  226. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/internal/modules/{{module}}/module_test.go.j2 +84 -0
  227. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/jfast.service.toml.j2 +14 -0
  228. jfastframework-0.1.0a1/src/jfastframework/templates/service_go/main.go.j2 +78 -0
  229. jfastframework-0.1.0a1/src/jfastframework/templates/service_web/static/app.css.j2 +116 -0
  230. jfastframework-0.1.0a1/src/jfastframework/templates/service_web/templates/base.html.j2 +37 -0
  231. jfastframework-0.1.0a1/src/jfastframework/templates/service_web/templates/index.html.j2 +19 -0
  232. jfastframework-0.1.0a1/src/jfastframework/templates/service_web/web.py.j2 +18 -0
  233. jfastframework-0.1.0a1/src/jfastframework/templates/ui_htmx/templates/{{module}}/_row.html.j2 +21 -0
  234. jfastframework-0.1.0a1/src/jfastframework/templates/ui_htmx/templates/{{module}}/_rows.html.j2 +13 -0
  235. jfastframework-0.1.0a1/src/jfastframework/templates/ui_htmx/templates/{{module}}/index.html.j2 +43 -0
  236. jfastframework-0.1.0a1/src/jfastframework/templates/ui_htmx/{{modules_dir}}/{{module}}/web.py.j2 +65 -0
  237. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Components/Modals/.gitkeep.j2 +0 -0
  238. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Components/README.md.j2 +12 -0
  239. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Components/Tables/.gitkeep.j2 +0 -0
  240. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Pages/{{View}}View.jsx.j2 +70 -0
  241. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Routes/router.jsx.j2 +10 -0
  242. jfastframework-0.1.0a1/src/jfastframework/templates/view_react/src/Modulo{{View}}/Services/{{view_slug}}.service.js.j2 +34 -0
  243. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Components/Modals/.gitkeep.j2 +0 -0
  244. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Components/README.md.j2 +12 -0
  245. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Components/Tables/.gitkeep.j2 +0 -0
  246. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Pages/{{View}}View.vue.j2 +60 -0
  247. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Routes/router.js.j2 +10 -0
  248. jfastframework-0.1.0a1/src/jfastframework/templates/view_vue/src/Modulo{{View}}/Services/{{view_slug}}.service.js.j2 +34 -0
  249. jfastframework-0.1.0a1/src/jfastframework/testing/__init__.py +25 -0
  250. jfastframework-0.1.0a1/src/jfastframework/testing/fixtures.py +102 -0
  251. jfastframework-0.1.0a1/src/jfastframework/vectors/__init__.py +8 -0
  252. jfastframework-0.1.0a1/src/jfastframework/vectors/base.py +85 -0
  253. jfastframework-0.1.0a1/src/jfastframework/vectors/pgvector.py +147 -0
  254. jfastframework-0.1.0a1/src/jfastframework/vectors/qdrant.py +155 -0
  255. jfastframework-0.1.0a1/src/jfastframework/workspace.py +517 -0
  256. jfastframework-0.1.0a1/tests/test_app.py +105 -0
  257. jfastframework-0.1.0a1/tests/test_auth.py +435 -0
  258. jfastframework-0.1.0a1/tests/test_blocking.py +228 -0
  259. jfastframework-0.1.0a1/tests/test_contracts.py +276 -0
  260. jfastframework-0.1.0a1/tests/test_deploy.py +67 -0
  261. jfastframework-0.1.0a1/tests/test_edge.py +153 -0
  262. jfastframework-0.1.0a1/tests/test_gateway.py +185 -0
  263. jfastframework-0.1.0a1/tests/test_kubernetes.py +189 -0
  264. jfastframework-0.1.0a1/tests/test_notifications.py +141 -0
  265. jfastframework-0.1.0a1/tests/test_oidc.py +351 -0
  266. jfastframework-0.1.0a1/tests/test_patcher.py +138 -0
  267. jfastframework-0.1.0a1/tests/test_queue.py +266 -0
  268. jfastframework-0.1.0a1/tests/test_rag_store_selection.py +83 -0
  269. jfastframework-0.1.0a1/tests/test_redis_queue.py +272 -0
  270. jfastframework-0.1.0a1/tests/test_registry.py +96 -0
  271. jfastframework-0.1.0a1/tests/test_repository.py +153 -0
  272. jfastframework-0.1.0a1/tests/test_resources.py +241 -0
  273. jfastframework-0.1.0a1/tests/test_scaffold.py +114 -0
  274. jfastframework-0.1.0a1/tests/test_secrets_and_serverless.py +195 -0
  275. jfastframework-0.1.0a1/tests/test_settings.py +61 -0
  276. jfastframework-0.1.0a1/tests/test_storage.py +326 -0
  277. jfastframework-0.1.0a1/tests/test_tenancy.py +268 -0
  278. jfastframework-0.1.0a1/tests/test_web.py +107 -0
  279. jfastframework-0.1.0a1/tests/test_workspace.py +131 -0
  280. jfastframework-0.1.0a1/tests/test_workspace_deploy.py +143 -0
@@ -0,0 +1,37 @@
1
+ # Copy to .env and fill in. Never commit .env.
2
+
3
+ # --- kernel ---
4
+ JFAST_APP_NAME=jfast-service
5
+ JFAST_ENV=local # local | dev | staging | prod
6
+ JFAST_DEBUG=false # true leaks exception messages to clients
7
+ JFAST_PORT=8000
8
+
9
+ # --- edge protections (all off unless set) ---
10
+ # JFAST_READINESS_TIMEOUT=2.0 # seconds one plugin gets to answer /ready
11
+ # JFAST_CORS_ORIGINS=["https://app.example.com"]
12
+ # JFAST_CORS_ALLOW_CREDENTIALS=false
13
+ # JFAST_TRUSTED_HOSTS=["api.example.com"]
14
+ # JFAST_MAX_BODY_BYTES=10485760 # 10 MiB; unset means no limit
15
+ # JFAST_REQUEST_TIMEOUT=30 # seconds before a request is answered 504
16
+ # JFAST_DOCS_URL=/docs # closed in prod unless set explicitly
17
+
18
+ # --- observability plugin ---
19
+ JFAST_LOG_LEVEL=INFO
20
+ JFAST_LOG_JSON_LOGS=true
21
+
22
+ # --- database plugin (extra: jfastframework[db]) ---
23
+ JFAST_DB_DSN=postgresql+asyncpg://app:CHANGEME@localhost:5432/app
24
+ JFAST_DB_POOL_SIZE=10
25
+ POSTGRES_PASSWORD=CHANGEME
26
+
27
+ # --- cache plugin (extra: jfastframework[cache]) ---
28
+ JFAST_CACHE_URL=redis://localhost:6379/0
29
+
30
+ # --- sentry plugin (extra: jfastframework[sentry]) ---
31
+ # JFAST_SENTRY_DSN=
32
+ JFAST_SENTRY_TRACES_SAMPLE_RATE=0.1
33
+
34
+ # --- rag plugin (extra: jfastframework[rag,db]) ---
35
+ JFAST_RAG_DIMENSIONS=768
36
+ JFAST_RAG_OLLAMA_URL=http://localhost:11434
37
+ JFAST_RAG_OLLAMA_MODEL=nomic-embed-text
@@ -0,0 +1,125 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ check:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ cache: pip
23
+
24
+ - name: Install
25
+ # setuptools is upgraded first. It is not a dependency of this
26
+ # package -- hatchling does the building -- but setup-python ships one
27
+ # in the runner's environment, pip-audit sees it, and the version it
28
+ # ships has lagged behind published advisories. Upgrading is the honest
29
+ # fix; waiving a real advisory because it is inconvenient is not.
30
+ run: |
31
+ python -m pip install --upgrade pip setuptools wheel
32
+ pip install -e ".[all,dev]"
33
+
34
+ - name: Lint
35
+ run: ruff check src tests
36
+
37
+ - name: Format
38
+ run: ruff format --check src tests
39
+
40
+ - name: Types
41
+ run: mypy src
42
+
43
+ - name: Tests
44
+ run: pytest -q
45
+
46
+ # A framework shipping opinionated auth and storage defaults should audit
47
+ # its own dependency tree. Both are hard gates.
48
+ #
49
+ # When a finding lands on an unrelated pull request, the order is: try
50
+ # upgrading the package first, and only if it cannot be upgraded, waive
51
+ # it here explicitly -- `pip-audit --ignore-vuln PYSEC-0000-000` with a
52
+ # comment saying which code path makes it inapplicable. Same for bandit
53
+ # with `# nosec BXXX` and the reason on the line above. What must not
54
+ # happen is a step going yellow and being ignored, which is how a
55
+ # security check stops meaning anything.
56
+ - name: Audit dependencies
57
+ # --skip-editable: this project is installed from the checkout and is
58
+ # not on PyPI, so there is nothing to look up for it.
59
+ run: pip-audit --skip-editable --desc
60
+
61
+ - name: Static security scan
62
+ run: bandit -c pyproject.toml -r src -ll
63
+
64
+ # The generator is the part most likely to break silently: templates
65
+ # render fine but produce code that does not import, or a Jinja
66
+ # environment eats the runtime braces a Vue file needs. Both smoke
67
+ # scripts render real trees and run what they produce.
68
+ - name: Scaffold smoke
69
+ run: bash scripts/smoke.sh
70
+
71
+ - name: Contracts smoke
72
+ run: bash scripts/smoke_contracts.sh
73
+
74
+ - name: Auth and Kubernetes smoke
75
+ run: bash scripts/smoke_auth_k8s.sh
76
+
77
+ - name: Storage, tenancy and serverless smoke
78
+ run: bash scripts/smoke_storage_tenancy.sh
79
+
80
+ - name: Workspace smoke
81
+ run: bash scripts/smoke_workspace.sh
82
+
83
+ - name: Quickstart smoke
84
+ # The documented commands, run exactly as documented. Docs that have
85
+ # never been executed are a guess.
86
+ run: bash scripts/smoke_docs.sh
87
+
88
+ - name: Docs site builds and validates
89
+ run: |
90
+ pip install -e ".[docs]"
91
+ python docs-site/build.py --version ci --output site/ci
92
+ python docs-site/check.py site/ci
93
+
94
+ # Generated projects in other languages get their own job, because the
95
+ # point of generating them is that they build. A scaffold nobody has run
96
+ # is a liability that looks like a feature.
97
+ go:
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - uses: actions/checkout@v4
101
+ - uses: actions/setup-python@v5
102
+ with:
103
+ python-version: "3.12"
104
+ cache: pip
105
+ - uses: actions/setup-go@v5
106
+ with:
107
+ go-version: "1.23"
108
+ - run: pip install -e ".[all,dev]"
109
+ - name: Generate, vet, test, build and run a Go service
110
+ run: bash scripts/smoke_go.sh
111
+
112
+ frontend:
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+ - uses: actions/setup-python@v5
117
+ with:
118
+ python-version: "3.12"
119
+ cache: pip
120
+ - uses: actions/setup-node@v4
121
+ with:
122
+ node-version: "22"
123
+ - run: pip install -e ".[all,dev]"
124
+ - name: Generate, install and build the Vue and React frontends
125
+ run: bash scripts/smoke_frontend.sh
@@ -0,0 +1,121 @@
1
+ name: Docs site
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ workflow_dispatch:
8
+
9
+ # Only one deploy at a time, and never cancel one halfway: a cancelled Pages
10
+ # deploy can leave the site partially replaced.
11
+ concurrency:
12
+ group: pages
13
+ cancel-in-progress: false
14
+
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ jobs:
21
+ build:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ with:
26
+ # Tags are needed to build the version picker.
27
+ fetch-depth: 0
28
+
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+ cache: pip
33
+
34
+ - name: Install
35
+ run: pip install -e ".[docs]"
36
+
37
+ - name: Work out the version
38
+ id: version
39
+ run: |
40
+ if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
41
+ echo "name=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
42
+ else
43
+ echo "name=latest" >> "$GITHUB_OUTPUT"
44
+ fi
45
+ # Minor-series directories: v0.3 rather than v0.3.0, so a patch
46
+ # release replaces its predecessor instead of littering the picker.
47
+ VERSIONS=$(git tag --list 'v*' --sort=-v:refname \
48
+ | sed -E 's/^(v[0-9]+\.[0-9]+).*/\1/' | uniq | head -8 | paste -sd, -)
49
+ echo "all=latest${VERSIONS:+,$VERSIONS}" >> "$GITHUB_OUTPUT"
50
+
51
+ - name: Build latest
52
+ run: |
53
+ python docs-site/build.py \
54
+ --version latest \
55
+ --versions "${{ steps.version.outputs.all }}" \
56
+ --output site/latest
57
+
58
+ - name: Build every released version
59
+ # The Pages artifact replaces the whole site, so previous versions have
60
+ # to be rebuilt here or they vanish on the next deploy. Each one is
61
+ # built from its own tag, which is the point of versioned docs:
62
+ # someone pinned to 0.3 should read 0.3's documentation.
63
+ run: |
64
+ for MINOR in $(git tag --list 'v*' --sort=-v:refname \
65
+ | sed -E 's/^(v[0-9]+\.[0-9]+).*/\1/' | uniq | head -8); do
66
+ TAG=$(git tag --list "${MINOR}*" --sort=-v:refname | head -1)
67
+ echo "::group::${MINOR} (from ${TAG})"
68
+ rm -rf .worktree
69
+ git worktree add --detach .worktree "${TAG}" >/dev/null
70
+ if [ -f .worktree/docs-site/build.py ]; then
71
+ (cd .worktree && python docs-site/build.py \
72
+ --version "${MINOR}" \
73
+ --versions "${{ steps.version.outputs.all }}" \
74
+ --output "../site/${MINOR}")
75
+ else
76
+ echo "no docs-site/ at ${TAG}; skipping"
77
+ fi
78
+ git worktree remove --force .worktree
79
+ echo "::endgroup::"
80
+ done
81
+
82
+ - name: Check every built version
83
+ # A docs site fails quietly: a broken link still renders as a page.
84
+ run: |
85
+ FAILED=0
86
+ for DIR in site/*/; do
87
+ echo "checking ${DIR}"
88
+ python docs-site/check.py "${DIR}" || FAILED=1
89
+ done
90
+ exit "${FAILED}"
91
+
92
+ - name: Root redirect to latest
93
+ run: |
94
+ cat > site/index.html <<'HTML'
95
+ <!doctype html>
96
+ <html lang="en">
97
+ <head>
98
+ <meta charset="utf-8">
99
+ <title>JFastFramework</title>
100
+ <link rel="canonical" href="latest/">
101
+ <meta http-equiv="refresh" content="0; url=latest/">
102
+ </head>
103
+ <body><p><a href="latest/">JFastFramework documentation</a></p></body>
104
+ </html>
105
+ HTML
106
+ # Jekyll would otherwise skip files and directories starting with _.
107
+ touch site/.nojekyll
108
+
109
+ - uses: actions/upload-pages-artifact@v3
110
+ with:
111
+ path: site
112
+
113
+ deploy:
114
+ needs: build
115
+ runs-on: ubuntu-latest
116
+ environment:
117
+ name: github-pages
118
+ url: ${{ steps.deploy.outputs.page_url }}
119
+ steps:
120
+ - id: deploy
121
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,99 @@
1
+ name: Release
2
+
3
+ # Publishing runs through PyPI's trusted publishing: GitHub mints a short-lived
4
+ # OIDC token for this exact workflow in this exact repository, and PyPI trades
5
+ # it for an upload. No API token is stored anywhere, so there is none to leak
6
+ # and none to rotate.
7
+ #
8
+ # Registering it on PyPI means filling in, exactly:
9
+ #
10
+ # Owner JFabrizzio5
11
+ # Repository name JFastFramework
12
+ # Workflow name release.yml
13
+ # Environment name pypi (testpypi, on test.pypi.org)
14
+ #
15
+ # The environment names below have to match what is registered there, or the
16
+ # token is minted for a claim PyPI will not accept.
17
+
18
+ on:
19
+ workflow_dispatch:
20
+ inputs:
21
+ target:
22
+ description: Where to publish
23
+ type: choice
24
+ options: [testpypi, pypi]
25
+ default: testpypi
26
+ push:
27
+ tags: ["v*"]
28
+
29
+ permissions:
30
+ contents: read
31
+
32
+ jobs:
33
+ build:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+
38
+ - uses: actions/setup-python@v5
39
+ with:
40
+ python-version: "3.12"
41
+
42
+ - name: Build sdist and wheel
43
+ run: pipx run build
44
+
45
+ # --strict fails on a malformed long description, which is the failure
46
+ # that otherwise shows up as a blank project page after the upload has
47
+ # already happened and the version is burned.
48
+ - name: Check the metadata
49
+ run: pipx run twine check --strict dist/*
50
+
51
+ # A sdist that does not install is only discovered by someone else.
52
+ - name: Install the built wheel and run the CLI
53
+ run: |
54
+ python -m venv /tmp/probe
55
+ /tmp/probe/bin/pip install --quiet dist/*.whl
56
+ /tmp/probe/bin/jfast version
57
+ /tmp/probe/bin/python -c "import jfastframework; print(jfastframework.__version__)"
58
+
59
+ - uses: actions/upload-artifact@v4
60
+ with:
61
+ name: dist
62
+ path: dist/
63
+
64
+ testpypi:
65
+ needs: build
66
+ if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
67
+ runs-on: ubuntu-latest
68
+ environment: testpypi
69
+ permissions:
70
+ id-token: write
71
+ steps:
72
+ - uses: actions/download-artifact@v4
73
+ with:
74
+ name: dist
75
+ path: dist/
76
+
77
+ - uses: pypa/gh-action-pypi-publish@release/v1
78
+ with:
79
+ repository-url: https://test.pypi.org/legacy/
80
+ # TestPyPI keeps old uploads from experiments; re-running a build
81
+ # that produced identical files should not fail the job.
82
+ skip-existing: true
83
+
84
+ pypi:
85
+ needs: build
86
+ if: >-
87
+ github.event_name == 'push'
88
+ || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
89
+ runs-on: ubuntu-latest
90
+ environment: pypi
91
+ permissions:
92
+ id-token: write
93
+ steps:
94
+ - uses: actions/download-artifact@v4
95
+ with:
96
+ name: dist
97
+ path: dist/
98
+
99
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ .python-version
11
+
12
+ # Tooling
13
+ .pytest_cache/
14
+ .ruff_cache/
15
+ .mypy_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Env
20
+ .env
21
+ .env.local
22
+ .env.*.local
23
+
24
+ # Generated deploy artifacts
25
+ /generated/
26
+ docker-compose.generated.yml
27
+
28
+ # OS
29
+ .DS_Store
30
+
31
+ # Generated documentation site
32
+ /site/
@@ -0,0 +1,65 @@
1
+ # Skills
2
+
3
+ A skill is a folder holding one `SKILL.md`: instructions for a task an agent
4
+ performs repeatedly in this codebase.
5
+
6
+ The point is selective loading. An agent should not read the entire manual to
7
+ add one module — it reads the front matter of each skill, picks the one that
8
+ matches, and loads only that file.
9
+
10
+ ## Layout
11
+
12
+ ```
13
+ .jfast/skills/
14
+ ├── README.md
15
+ ├── add-service/SKILL.md add a service: datastores, ports, gateway
16
+ ├── respect-contracts/SKILL.md read the project contract, then verify against it
17
+ ├── create-module/SKILL.md scaffold a domain module: layout, UI, wiring
18
+ ├── create-plugin/SKILL.md build a plugin that adds a capability
19
+ ├── build-frontend/SKILL.md UI: server-rendered HTMX, or a Vue/React SPA
20
+ ├── add-rag/SKILL.md semantic search over pgvector or Qdrant
21
+ └── design-system/SKILL.md apply a DESIGN.md to generated UI
22
+ ```
23
+
24
+ The first decision an agent makes is usually **service or module**, and getting
25
+ it wrong is expensive in one direction only: a module promoted to a service
26
+ later is a move, while a premature service is a network hop, a second deploy
27
+ target and an integration test nobody asked for. `add-service` opens with that
28
+ question for exactly that reason.
29
+
30
+ ## Format
31
+
32
+ Front matter, then the steps.
33
+
34
+ ```markdown
35
+ ---
36
+ name: create-module
37
+ description: Scaffold a domain module. Use when adding a new business entity
38
+ with its own table and endpoints.
39
+ when_to_use: The user asks for a new resource, entity, CRUD surface, or table.
40
+ when_not_to_use: The change fits inside an existing module, or adds a
41
+ cross-cutting capability (that is a plugin).
42
+ ---
43
+
44
+ ## Steps
45
+ 1. ...
46
+ ```
47
+
48
+ `description` and `when_to_use` are the routing signal. Write them so an agent
49
+ can decide from those two lines alone whether to open the file.
50
+
51
+ ## Rules
52
+
53
+ - One task per skill. If a skill has two "or else" branches, it is two skills.
54
+ - Give exact commands, not descriptions of commands.
55
+ - State the verification step. A skill that does not say how to check its own
56
+ output produces work nobody validated.
57
+ - Keep it under 150 lines. Longer means the task is not decomposed.
58
+
59
+ ## Related conventions
60
+
61
+ - `DESIGN.md` next to a module or frontend describes the visual language:
62
+ palette, type scale, spacing, component patterns. The `design-system` skill
63
+ consumes it.
64
+ - `AGENTS.md` at the repository root holds rules that apply to every task.
65
+ Skills hold rules for one task. Do not duplicate between them.
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: add-rag
3
+ description: Enable semantic search over pgvector or Qdrant, with a local
4
+ Ollama embedder or a custom one.
5
+ when_to_use: The user wants semantic search, a knowledge base, "chat with my
6
+ documents", or retrieval to ground an LLM answer.
7
+ when_not_to_use: Exact-match or keyword search is enough — use a PostgreSQL
8
+ index or full-text search. RAG is not a substitute for a WHERE clause.
9
+ ---
10
+
11
+ ## What you get
12
+
13
+ `POST /rag/documents` (ingest), `POST /rag/search`, `DELETE /rag/documents/{id}`,
14
+ plus `rag.store` and `rag.embedder` providers for code that needs them
15
+ directly.
16
+
17
+ ## Known limits — say these before building on it
18
+
19
+ - Chunking is fixed-size with overlap. Fine for prose, poor for code and tables.
20
+ - No reranking. Top-k by cosine similarity only.
21
+ - No hybrid search. Pure vector, no BM25 blend.
22
+ - `auto_migrate` runs DDL at startup. Fine in development; move to migrations
23
+ before production.
24
+
25
+ If the use case needs better retrieval than that, say so rather than shipping
26
+ this and calling it done.
27
+
28
+ ## Step 1: choose the store
29
+
30
+ | | pgvector (default) | Qdrant |
31
+ | --- | --- | --- |
32
+ | Operational cost | none — the database you already run | a second service to run and back up |
33
+ | Scale | comfortable to a few million chunks | far beyond |
34
+ | Filtering | tenant id, plus whatever SQL you write | rich indexed payload filters |
35
+
36
+ **Start with pgvector.** Move to Qdrant when you hit a wall you can name —
37
+ filter complexity, index build time, memory. "It might scale better" is not
38
+ that wall.
39
+
40
+ ## Step 2: enable the plugins
41
+
42
+ pgvector:
43
+
44
+ ```bash
45
+ pip install -e ".[rag,db]"
46
+ ```
47
+
48
+ ```toml
49
+ [plugins]
50
+ enabled = ["observability", "database", "rag"]
51
+
52
+ [plugin.rag]
53
+ store = "pgvector"
54
+ collection = "rag_chunks"
55
+ dimensions = 768
56
+ chunk_size = 1000
57
+ chunk_overlap = 150
58
+ top_k = 5
59
+ ```
60
+
61
+ Qdrant:
62
+
63
+ ```bash
64
+ pip install -e ".[rag,qdrant]"
65
+ ```
66
+
67
+ ```toml
68
+ [plugins]
69
+ enabled = ["observability", "qdrant", "rag"]
70
+
71
+ [plugin.rag]
72
+ store = "qdrant"
73
+ collection = "rag_chunks"
74
+ dimensions = 768
75
+ ```
76
+
77
+ Getting this pairing wrong fails at startup with the fix in the message, not at
78
+ the first search:
79
+
80
+ ```
81
+ rag store 'qdrant' needs the 'qdrant' plugin. Add "qdrant" to [plugins].enabled.
82
+ ```
83
+
84
+ ## Step 3: match dimensions to the model
85
+
86
+ The mistake that costs an afternoon. `nomic-embed-text` is 768;
87
+ `mxbai-embed-large` is 1024. A mismatch fails at insert, and changing it later
88
+ means re-embedding everything already ingested. Decide before the first ingest.
89
+
90
+ ```bash
91
+ ollama pull nomic-embed-text
92
+ curl -s localhost:11434/api/tags | jq -r '.models[].name'
93
+ ```
94
+
95
+ For pgvector, the image must carry the extension — `pgvector/pgvector:pg16`,
96
+ which the `database` plugin already defaults to.
97
+
98
+ ## Step 4: ingest and search
99
+
100
+ ```bash
101
+ curl -X POST localhost:8000/rag/documents \
102
+ -H 'content-type: application/json' \
103
+ -d '{"document_id":"doc-1","content":"...","metadata":{"source":"manual"}}'
104
+
105
+ curl -X POST localhost:8000/rag/search \
106
+ -H 'content-type: application/json' \
107
+ -d '{"query":"how do refunds work","limit":5}'
108
+ ```
109
+
110
+ Re-ingesting a document replaces its chunks in both stores — pgvector deletes
111
+ by `document_id`, Qdrant derives point ids deterministically from
112
+ `(document_id, chunk_index)`.
113
+
114
+ ## Swapping the embedder
115
+
116
+ Implement the protocol — a `dimensions` attribute and an async
117
+ `embed(texts) -> list[list[float]]` — then:
118
+
119
+ ```toml
120
+ [plugin.rag]
121
+ embedder = "myapp.embeddings:OpenAIEmbedder"
122
+ ```
123
+
124
+ A custom store works the same way (`store = "myapp.stores:MyStore"`); it is
125
+ constructed with the `AppContext`, so it can pull its client from the provider
126
+ registry.
127
+
128
+ ## Multi-tenancy
129
+
130
+ Pass `tenant_id` on ingest and search. Both stores filter on it and index it.
131
+ Until row-level security lands (PLAN.md phase 2) that filter is a convention:
132
+ a caller that omits `tenant_id` searches across tenants. If the data is
133
+ tenant-sensitive, set the value server-side from the JWT — never from the
134
+ request body.
135
+
136
+ ## Verification
137
+
138
+ ```bash
139
+ jfast doctor
140
+ curl -s localhost:8000/ready | jq '.checks.rag'
141
+ ```
142
+
143
+ Then ingest one known document and search for a phrase inside it. If the top
144
+ result is not that document, the embedder or the dimensions are wrong — check
145
+ those before touching `chunk_size`.