solace-agent-mesh 1.6.2__py3-none-any.whl → 1.7.0__py3-none-any.whl

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.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (245) hide show
  1. solace_agent_mesh/agent/adk/adk_llm.txt +12 -18
  2. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +1 -1
  3. solace_agent_mesh/agent/adk/callbacks.py +138 -20
  4. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +2 -0
  5. solace_agent_mesh/agent/adk/models/lite_llm.py +38 -5
  6. solace_agent_mesh/agent/adk/models/models_llm.txt +82 -35
  7. solace_agent_mesh/agent/adk/runner.py +9 -0
  8. solace_agent_mesh/agent/adk/services.py +3 -3
  9. solace_agent_mesh/agent/adk/stream_parser.py +6 -1
  10. solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
  11. solace_agent_mesh/agent/agent_llm.txt +61 -70
  12. solace_agent_mesh/agent/protocol/event_handlers.py +29 -1
  13. solace_agent_mesh/agent/protocol/protocol_llm.txt +1 -1
  14. solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
  15. solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
  16. solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
  17. solace_agent_mesh/agent/sac/app.py +22 -0
  18. solace_agent_mesh/agent/sac/component.py +76 -40
  19. solace_agent_mesh/agent/sac/sac_llm.txt +1 -1
  20. solace_agent_mesh/agent/sac/task_execution_context.py +21 -0
  21. solace_agent_mesh/agent/testing/testing_llm.txt +2 -1
  22. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +13 -148
  23. solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
  24. solace_agent_mesh/agent/tools/tools_llm.txt +93 -80
  25. solace_agent_mesh/agent/tools/tools_llm_detail.txt +3 -2
  26. solace_agent_mesh/agent/utils/artifact_helpers.py +4 -0
  27. solace_agent_mesh/agent/utils/utils_llm.txt +16 -2
  28. solace_agent_mesh/assets/docs/404.html +3 -3
  29. solace_agent_mesh/assets/docs/assets/js/05749d90.c70b2be9.js +1 -0
  30. solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/3ac1795d.e4870a49.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/3ff0015d.b63ee53a.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.45b32c2b.js +1 -0
  37. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
  39. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  40. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  41. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  42. solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
  43. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  44. solace_agent_mesh/assets/docs/assets/js/94e8668d.09ed9234.js +1 -0
  45. solace_agent_mesh/assets/docs/assets/js/{ab9708a8.3e6dd091.js → ab9708a8.245ae0ef.js} +1 -1
  46. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.f902fad8.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  51. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  52. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  53. solace_agent_mesh/assets/docs/assets/js/e04b235d.c9c50c7b.js +1 -0
  54. solace_agent_mesh/assets/docs/assets/js/e3d9abda.d11c67a7.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/{e6f9706b.e74a984d.js → e6f9706b.045d0fa1.js} +1 -1
  56. solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
  57. solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/main.e6488e8b.js +2 -0
  60. solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +1 -0
  61. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
  65. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +18 -4
  66. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
  67. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +5 -5
  69. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
  70. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
  71. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
  72. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +196 -0
  73. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +4 -4
  74. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +5 -5
  75. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +6 -7
  76. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
  77. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
  78. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +4 -4
  79. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +4 -4
  80. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
  81. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +160 -169
  82. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
  83. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
  84. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +4 -4
  85. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +4 -4
  86. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
  87. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
  88. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +5 -5
  89. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +4 -4
  90. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
  91. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
  92. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
  93. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
  94. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
  95. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
  96. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +59 -0
  97. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +62 -0
  98. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +10 -6
  99. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +4 -4
  100. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +24 -29
  101. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  102. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +27 -4
  103. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  104. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +4 -4
  105. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +5 -4
  106. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  107. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  108. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
  109. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +9 -9
  110. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +4 -4
  111. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +4 -4
  112. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +4 -4
  113. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +4 -4
  114. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  115. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  116. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  117. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  118. solace_agent_mesh/assets/docs/lunr-index-1762189824009.json +1 -0
  119. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  120. solace_agent_mesh/assets/docs/search-doc-1762189824009.json +1 -0
  121. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  122. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  123. solace_agent_mesh/cli/__init__.py +1 -1
  124. solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
  125. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +1 -1
  126. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-D4_RMYRh.js → authCallback-tcIFZLis.js} +1 -1
  127. solace_agent_mesh/client/webui/frontend/static/assets/{client-UZ3qU6Bq.js → client-CRYdKo2Q.js} +3 -3
  128. solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +1 -0
  129. solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +353 -0
  130. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CINwxvwV.js +470 -0
  131. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  132. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  133. solace_agent_mesh/common/a2a/a2a_llm.txt +13 -20
  134. solace_agent_mesh/common/a2a/protocol.py +5 -0
  135. solace_agent_mesh/common/a2a/types.py +1 -0
  136. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +49 -11
  137. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
  138. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  139. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +26 -9
  140. solace_agent_mesh/common/common_llm.txt +13 -34
  141. solace_agent_mesh/common/data_parts.py +20 -4
  142. solace_agent_mesh/common/middleware/middleware_llm.txt +1 -1
  143. solace_agent_mesh/common/sac/sac_llm.txt +1 -1
  144. solace_agent_mesh/common/sam_events/sam_events_llm.txt +1 -1
  145. solace_agent_mesh/common/services/employee_service.py +1 -1
  146. solace_agent_mesh/common/services/providers/providers_llm.txt +3 -2
  147. solace_agent_mesh/common/services/services_llm.txt +9 -4
  148. solace_agent_mesh/common/utils/embeds/constants.py +1 -0
  149. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +1 -1
  150. solace_agent_mesh/common/utils/embeds/modifiers.py +2 -1
  151. solace_agent_mesh/common/utils/embeds/resolver.py +58 -6
  152. solace_agent_mesh/common/utils/embeds/types.py +8 -0
  153. solace_agent_mesh/common/utils/utils_llm.txt +5 -6
  154. solace_agent_mesh/core_a2a/core_a2a_llm.txt +1 -1
  155. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  156. solace_agent_mesh/gateway/adapter/base.py +143 -0
  157. solace_agent_mesh/gateway/adapter/types.py +221 -0
  158. solace_agent_mesh/gateway/base/app.py +29 -2
  159. solace_agent_mesh/gateway/base/base_llm.txt +10 -8
  160. solace_agent_mesh/gateway/base/component.py +573 -142
  161. solace_agent_mesh/gateway/gateway_llm.txt +55 -59
  162. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  163. solace_agent_mesh/gateway/generic/app.py +50 -0
  164. solace_agent_mesh/gateway/generic/component.py +650 -0
  165. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +99 -49
  166. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +92 -0
  167. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  168. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +150 -0
  169. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  170. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  171. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +26 -20
  172. solace_agent_mesh/gateway/http_sse/app.py +19 -14
  173. solace_agent_mesh/gateway/http_sse/component.py +150 -118
  174. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +1 -1
  175. solace_agent_mesh/gateway/http_sse/dependencies.py +21 -3
  176. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +8 -8
  177. solace_agent_mesh/gateway/http_sse/main.py +55 -14
  178. solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
  179. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +56 -98
  180. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  181. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  182. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +23 -1
  183. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
  184. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +112 -4
  185. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -1
  186. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +51 -60
  187. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  188. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  189. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +7 -1
  190. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  191. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  192. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +125 -157
  193. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +269 -8
  194. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +143 -51
  195. solace_agent_mesh/gateway/http_sse/routers/config.py +69 -0
  196. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +198 -94
  197. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  198. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +68 -18
  199. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +13 -0
  200. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +30 -0
  201. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +51 -35
  202. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +2 -0
  203. solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
  204. solace_agent_mesh/gateway/http_sse/routers/projects.py +542 -0
  205. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +9 -11
  206. solace_agent_mesh/gateway/http_sse/routers/sessions.py +154 -3
  207. solace_agent_mesh/gateway/http_sse/routers/tasks.py +296 -4
  208. solace_agent_mesh/gateway/http_sse/services/project_service.py +403 -0
  209. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +16 -10
  210. solace_agent_mesh/gateway/http_sse/services/session_service.py +178 -6
  211. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +2 -3
  212. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +48 -14
  213. solace_agent_mesh/solace_agent_mesh_llm.txt +1 -1
  214. {solace_agent_mesh-1.6.2.dist-info → solace_agent_mesh-1.7.0.dist-info}/METADATA +3 -5
  215. {solace_agent_mesh-1.6.2.dist-info → solace_agent_mesh-1.7.0.dist-info}/RECORD +219 -176
  216. solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
  217. solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
  218. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
  219. solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
  220. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
  221. solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +0 -1
  222. solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
  223. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
  224. solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +0 -1
  225. solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
  226. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  227. solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
  228. solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
  229. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
  230. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
  231. solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
  232. solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +0 -1
  233. solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +0 -1
  234. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
  235. solace_agent_mesh/assets/docs/assets/js/main.d1643f0b.js +0 -2
  236. solace_agent_mesh/assets/docs/assets/js/runtime~main.97f920d4.js +0 -1
  237. solace_agent_mesh/assets/docs/lunr-index-1761663789856.json +0 -1
  238. solace_agent_mesh/assets/docs/search-doc-1761663789856.json +0 -1
  239. solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +0 -1
  240. solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +0 -342
  241. solace_agent_mesh/client/webui/frontend/static/assets/vendor-DSqhjwq_.js +0 -405
  242. /solace_agent_mesh/assets/docs/assets/js/{main.d1643f0b.js.LICENSE.txt → main.e6488e8b.js.LICENSE.txt} +0 -0
  243. {solace_agent_mesh-1.6.2.dist-info → solace_agent_mesh-1.7.0.dist-info}/WHEEL +0 -0
  244. {solace_agent_mesh-1.6.2.dist-info → solace_agent_mesh-1.7.0.dist-info}/entry_points.txt +0 -0
  245. {solace_agent_mesh-1.6.2.dist-info → solace_agent_mesh-1.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -9,6 +9,8 @@ from sqlalchemy.orm import Session as DBSession
9
9
  from ..shared.pagination import PaginationParams
10
10
  from ..shared.types import SessionId, UserId
11
11
  from .entities import Feedback, Session, Task, TaskEvent
12
+ from .entities.project import Project
13
+ from ..routers.dto.requests.project_requests import ProjectFilter
12
14
 
13
15
  if TYPE_CHECKING:
14
16
  from .entities import ChatTask
@@ -19,14 +21,14 @@ class ISessionRepository(ABC):
19
21
 
20
22
  @abstractmethod
21
23
  def find_by_user(
22
- self, session: DBSession, user_id: UserId, pagination: PaginationParams | None = None
24
+ self, session: DBSession, user_id: UserId, pagination: PaginationParams | None = None, project_id: str | None = None
23
25
  ) -> list[Session]:
24
- """Find all sessions for a specific user."""
26
+ """Find all sessions for a specific user, optionally filtered by project."""
25
27
  pass
26
28
 
27
29
  @abstractmethod
28
- def count_by_user(self, session: DBSession, user_id: UserId) -> int:
29
- """Count total sessions for a specific user."""
30
+ def count_by_user(self, session: DBSession, user_id: UserId, project_id: str | None = None) -> int:
31
+ """Count total sessions for a specific user, optionally filtered by project."""
30
32
  pass
31
33
 
32
34
  @abstractmethod
@@ -46,6 +48,41 @@ class ISessionRepository(ABC):
46
48
  """Delete a session belonging to a user."""
47
49
  pass
48
50
 
51
+ @abstractmethod
52
+ def soft_delete(self, session: DBSession, session_id: SessionId, user_id: UserId) -> bool:
53
+ """Soft delete a session belonging to a user."""
54
+ pass
55
+
56
+ @abstractmethod
57
+ def move_to_project(
58
+ self, session: DBSession, session_id: SessionId, user_id: UserId, new_project_id: str | None
59
+ ) -> Session | None:
60
+ """Move a session to a different project."""
61
+ pass
62
+
63
+ @abstractmethod
64
+ def search(
65
+ self,
66
+ session: DBSession,
67
+ user_id: UserId,
68
+ query: str,
69
+ pagination: PaginationParams | None = None,
70
+ project_id: str | None = None
71
+ ) -> list[Session]:
72
+ """Search sessions by name or content."""
73
+ pass
74
+
75
+ @abstractmethod
76
+ def count_search_results(
77
+ self,
78
+ session: DBSession,
79
+ user_id: UserId,
80
+ query: str,
81
+ project_id: str | None = None
82
+ ) -> int:
83
+ """Count search results for pagination."""
84
+ pass
85
+
49
86
 
50
87
  class ITaskRepository(ABC):
51
88
  """Interface for task data access operations."""
@@ -99,6 +136,37 @@ class IFeedbackRepository(ABC):
99
136
  """Save feedback."""
100
137
  pass
101
138
 
139
+ @abstractmethod
140
+ def search(
141
+ self,
142
+ session: DBSession,
143
+ user_id: UserId,
144
+ start_date: int | None = None,
145
+ end_date: int | None = None,
146
+ task_id: str | None = None,
147
+ session_id: str | None = None,
148
+ rating: str | None = None,
149
+ pagination: PaginationParams | None = None,
150
+ ) -> list[Feedback]:
151
+ """
152
+ Search feedback with flexible filtering.
153
+ All filters are optional and can be combined.
154
+
155
+ Args:
156
+ session: Database session
157
+ user_id: User ID to filter by, or "*" for all users (admin)
158
+ start_date: Start of date range in epoch milliseconds
159
+ end_date: End of date range in epoch milliseconds
160
+ task_id: Filter by specific task ID
161
+ session_id: Filter by specific session ID
162
+ rating: Filter by rating type ("up" or "down")
163
+ pagination: Pagination parameters
164
+
165
+ Returns:
166
+ List of feedback entries matching the filters
167
+ """
168
+ pass
169
+
102
170
  @abstractmethod
103
171
  def delete_feedback_older_than(self, session: DBSession, cutoff_time_ms: int, batch_size: int) -> int:
104
172
  """Delete feedback older than cutoff time using batch deletion."""
@@ -129,3 +197,43 @@ class IChatTaskRepository(ABC):
129
197
  def delete_by_session(self, session: DBSession, session_id: SessionId) -> bool:
130
198
  """Delete all tasks for a session."""
131
199
  pass
200
+
201
+
202
+ class IProjectRepository(ABC):
203
+ """Interface for project repository operations."""
204
+
205
+ @abstractmethod
206
+ def create_project(self, name: str, user_id: str, description: Optional[str] = None,
207
+ system_prompt: Optional[str] = None) -> Project:
208
+ """Create a new user project."""
209
+ pass
210
+
211
+ @abstractmethod
212
+ def get_user_projects(self, user_id: str) -> list[Project]:
213
+ """Get all projects owned by a specific user."""
214
+ pass
215
+
216
+ @abstractmethod
217
+ def get_filtered_projects(self, project_filter: ProjectFilter) -> list[Project]:
218
+ """Get projects based on filter criteria."""
219
+ pass
220
+
221
+ @abstractmethod
222
+ def get_by_id(self, project_id: str, user_id: str) -> Optional[Project]:
223
+ """Get a project by its ID, ensuring user access."""
224
+ pass
225
+
226
+ @abstractmethod
227
+ def update(self, project_id: str, user_id: str, update_data: dict) -> Optional[Project]:
228
+ """Update a project with the given data, ensuring user access."""
229
+ pass
230
+
231
+ @abstractmethod
232
+ def delete(self, project_id: str, user_id: str) -> bool:
233
+ """Delete a project by its ID, ensuring user access."""
234
+ pass
235
+
236
+ @abstractmethod
237
+ def soft_delete(self, project_id: str, user_id: str) -> bool:
238
+ """Soft delete a project by its ID, ensuring user access."""
239
+ pass
@@ -5,6 +5,8 @@ SQLAlchemy models and Pydantic models for database persistence.
5
5
  from .base import Base
6
6
  from .chat_task_model import ChatTaskModel
7
7
  from .feedback_model import FeedbackModel
8
+ from .project_model import ProjectModel, CreateProjectModel, UpdateProjectModel
9
+ from .project_user_model import ProjectUserModel, CreateProjectUserModel, UpdateProjectUserModel
8
10
  from .session_model import SessionModel, CreateSessionModel, UpdateSessionModel
9
11
  from .task_event_model import TaskEventModel
10
12
  from .task_model import TaskModel
@@ -12,10 +14,16 @@ from .task_model import TaskModel
12
14
  __all__ = [
13
15
  "Base",
14
16
  "ChatTaskModel",
17
+ "FeedbackModel",
18
+ "ProjectModel",
19
+ "ProjectUserModel",
15
20
  "SessionModel",
21
+ "CreateProjectModel",
22
+ "UpdateProjectModel",
23
+ "CreateProjectUserModel",
24
+ "UpdateProjectUserModel",
16
25
  "CreateSessionModel",
17
26
  "UpdateSessionModel",
18
27
  "TaskEventModel",
19
28
  "TaskModel",
20
- "FeedbackModel",
21
29
  ]
@@ -1,11 +1,11 @@
1
1
  ## Quick Summary
2
- This directory contains SQLAlchemy ORM models and Pydantic schemas for database persistence in the HTTP SSE gateway. It provides models for managing chat sessions, messages, tasks, task events, and user feedback with proper relationships and database schema definitions.
2
+ This directory contains SQLAlchemy ORM models and Pydantic schemas for database persistence in the HTTP SSE gateway. It provides models for managing chat sessions, tasks, task events, and user feedback with proper relationships and database schema definitions.
3
3
 
4
4
  ## Files Overview
5
5
  - `__init__.py` - Package initialization exposing all SQLAlchemy and Pydantic models
6
6
  - `base.py` - SQLAlchemy declarative base configuration
7
+ - `chat_task_model.py` - ChatTaskModel for storing chat tasks with session relationships
7
8
  - `feedback_model.py` - FeedbackModel for storing user feedback on tasks
8
- - `message_model.py` - MessageModel and Pydantic schemas for chat messages with session relationships
9
9
  - `session_model.py` - SessionModel and Pydantic schemas for managing chat sessions
10
10
  - `task_event_model.py` - TaskEventModel for storing A2A task events with task relationships
11
11
  - `task_model.py` - TaskModel for managing tasks with event relationships and token usage tracking
@@ -14,7 +14,7 @@ This directory contains SQLAlchemy ORM models and Pydantic schemas for database
14
14
 
15
15
  ### __init__.py
16
16
  **Purpose:** Package entry point that exposes all SQLAlchemy models and Pydantic schemas
17
- **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models import Base, MessageModel, SessionModel, CreateMessageModel, UpdateMessageModel, CreateSessionModel, UpdateSessionModel, TaskEventModel, TaskModel, FeedbackModel`
17
+ **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models import Base, ChatTaskModel, FeedbackModel, SessionModel, CreateSessionModel, UpdateSessionModel, TaskEventModel, TaskModel`
18
18
 
19
19
  **Constants/Variables:**
20
20
  - `__all__: List[str]` - Public API exports including all models and schemas
@@ -36,6 +36,49 @@ engine = create_engine("sqlite:///example.db")
36
36
  Base.metadata.create_all(engine)
37
37
  ```
38
38
 
39
+ ### chat_task_model.py
40
+ **Purpose:** SQLAlchemy model for storing chat tasks with session relationships
41
+ **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.chat_task_model import ChatTaskModel`
42
+
43
+ **Classes:**
44
+ - `ChatTaskModel(Base)` - SQLAlchemy model for chat tasks
45
+ - `id: Column[String]` - Primary key task identifier
46
+ - `session_id: Column[String]` - Foreign key to sessions table with CASCADE delete (indexed)
47
+ - `user_id: Column[String]` - User identifier (indexed)
48
+ - `user_message: Column[Text]` - Optional user message content
49
+ - `message_bubbles: Column[Text]` - Required message bubbles data
50
+ - `task_metadata: Column[Text]` - Optional task metadata
51
+ - `created_time: Column[BigInteger]` - Creation timestamp in epoch milliseconds (indexed)
52
+ - `updated_time: Column[BigInteger]` - Optional update timestamp
53
+ - `session: relationship` - SQLAlchemy relationship to SessionModel
54
+
55
+ **Usage Examples:**
56
+ ```python
57
+ from solace_agent_mesh.gateway.http_sse.repository.models.chat_task_model import ChatTaskModel
58
+ from sqlalchemy.orm import sessionmaker
59
+
60
+ # Create a chat task
61
+ chat_task = ChatTaskModel(
62
+ id="task_123",
63
+ session_id="session_456",
64
+ user_id="user_789",
65
+ user_message="Hello, how can you help me?",
66
+ message_bubbles='[{"type": "user", "content": "Hello"}]',
67
+ task_metadata='{"priority": "high"}',
68
+ created_time=1640995200000,
69
+ updated_time=1640995260000
70
+ )
71
+
72
+ # Add to database
73
+ Session = sessionmaker(bind=engine)
74
+ db_session = Session()
75
+ db_session.add(chat_task)
76
+ db_session.commit()
77
+
78
+ # Access related session
79
+ session = chat_task.session
80
+ ```
81
+
39
82
  ### feedback_model.py
40
83
  **Purpose:** SQLAlchemy model for storing user feedback on tasks
41
84
  **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.feedback_model import FeedbackModel`
@@ -73,60 +116,8 @@ db_session.add(feedback)
73
116
  db_session.commit()
74
117
  ```
75
118
 
76
- ### message_model.py
77
- **Purpose:** SQLAlchemy model and Pydantic schemas for storing chat messages with session relationships
78
- **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.message_model import MessageModel, CreateMessageModel, UpdateMessageModel`
79
-
80
- **Classes:**
81
- - `MessageModel(Base)` - SQLAlchemy model for chat messages
82
- - `id: Column[String]` - Primary key message identifier
83
- - `session_id: Column[String]` - Foreign key to sessions table with CASCADE delete
84
- - `message: Column[Text]` - Message content
85
- - `created_time: Column[BigInteger]` - Creation timestamp (auto-generated)
86
- - `sender_type: Column[String]` - Type of message sender (max 50 chars)
87
- - `sender_name: Column[String]` - Name of message sender (max 255 chars)
88
- - `session: relationship` - SQLAlchemy relationship to SessionModel
89
-
90
- - `CreateMessageModel(BaseModel)` - Pydantic model for creating messages
91
- - `id: str` - Message identifier
92
- - `session_id: str` - Session identifier
93
- - `message: str` - Message content
94
- - `sender_type: str` - Sender type
95
- - `sender_name: str` - Sender name
96
- - `created_time: int` - Creation timestamp
97
-
98
- - `UpdateMessageModel(BaseModel)` - Pydantic model for updating messages
99
- - `message: str` - Updated message content
100
- - `sender_type: str` - Updated sender type
101
- - `sender_name: str` - Updated sender name
102
-
103
- **Usage Examples:**
104
- ```python
105
- from solace_agent_mesh.gateway.http_sse.repository.models.message_model import MessageModel, CreateMessageModel
106
- from sqlalchemy.orm import sessionmaker
107
-
108
- # Create using SQLAlchemy model
109
- message = MessageModel(
110
- id="msg_123",
111
- session_id="session_456",
112
- message="Hello, world!",
113
- sender_type="user",
114
- sender_name="John Doe"
115
- )
116
-
117
- # Create using Pydantic model
118
- create_data = CreateMessageModel(
119
- id="msg_124",
120
- session_id="session_456",
121
- message="How are you?",
122
- sender_type="user",
123
- sender_name="John Doe",
124
- created_time=1640995200000
125
- )
126
- ```
127
-
128
119
  ### session_model.py
129
- **Purpose:** SQLAlchemy model and Pydantic schemas for managing chat sessions with message relationships
120
+ **Purpose:** SQLAlchemy model and Pydantic schemas for managing chat sessions with chat task relationships
130
121
  **Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.session_model import SessionModel, CreateSessionModel, UpdateSessionModel`
131
122
 
132
123
  **Classes:**
@@ -137,7 +128,7 @@ create_data = CreateMessageModel(
137
128
  - `agent_id: Column[String]` - Optional agent identifier
138
129
  - `created_time: Column[BigInteger]` - Creation timestamp (auto-generated)
139
130
  - `updated_time: Column[BigInteger]` - Last update timestamp (auto-updated)
140
- - `messages: relationship` - SQLAlchemy relationship to MessageModel with cascade delete
131
+ - `chat_tasks: relationship` - SQLAlchemy relationship to ChatTaskModel with cascade delete
141
132
 
142
133
  - `CreateSessionModel(BaseModel)` - Pydantic model for creating sessions
143
134
  - `id: str` - Session identifier
@@ -175,8 +166,8 @@ create_data = CreateSessionModel(
175
166
  updated_time=1640995200000
176
167
  )
177
168
 
178
- # Access related messages
179
- messages = session.messages # Returns list of MessageModel instances
169
+ # Access related chat tasks
170
+ chat_tasks = session.chat_tasks # Returns list of ChatTaskModel instances
180
171
  ```
181
172
 
182
173
  ### task_event_model.py
@@ -263,4 +254,4 @@ db_session.commit()
263
254
  events = task.events # Returns list of TaskEventModel instances
264
255
  ```
265
256
 
266
- # content_hash: 0583795702abeb023659a686bf3cfa551016bdd0284b01f2448dd9d4d2050f3b
257
+ # content_hash: 65d43bc49dd561efb9955aae2fc39945567e4302c406eea9718ce797d083646c
@@ -0,0 +1,51 @@
1
+ """
2
+ SQLAlchemy model for project data.
3
+ """
4
+
5
+ from sqlalchemy import Column, String, Boolean, BigInteger, Text
6
+ from sqlalchemy.orm import relationship
7
+ from pydantic import BaseModel
8
+
9
+ from .base import Base
10
+
11
+
12
+ class ProjectModel(Base):
13
+ """SQLAlchemy model for projects."""
14
+
15
+ __tablename__ = "projects"
16
+
17
+ id = Column(String, primary_key=True)
18
+ name = Column(String, nullable=False)
19
+ user_id = Column(String, nullable=False)
20
+ description = Column(Text, nullable=True)
21
+ system_prompt = Column(Text, nullable=True)
22
+ default_agent_id = Column(String, nullable=True)
23
+ created_at = Column(BigInteger, nullable=False)
24
+ updated_at = Column(BigInteger, nullable=True)
25
+ deleted_at = Column(BigInteger, nullable=True)
26
+ deleted_by = Column(String, nullable=True)
27
+
28
+ # Relationships
29
+ sessions = relationship("SessionModel", back_populates="project")
30
+ project_users = relationship("ProjectUserModel", back_populates="project", cascade="all, delete-orphan")
31
+
32
+
33
+ class CreateProjectModel(BaseModel):
34
+ """Pydantic model for creating a project."""
35
+ id: str
36
+ name: str
37
+ user_id: str | None = None
38
+ description: str | None = None
39
+ system_prompt: str | None = None
40
+ default_agent_id: str | None = None
41
+ created_at: int
42
+ updated_at: int | None = None
43
+
44
+
45
+ class UpdateProjectModel(BaseModel):
46
+ """Pydantic model for updating a project."""
47
+ name: str | None = None
48
+ description: str | None = None
49
+ system_prompt: str | None = None
50
+ default_agent_id: str | None = None
51
+ updated_at: int
@@ -0,0 +1,75 @@
1
+ """
2
+ SQLAlchemy model for project user access (junction table).
3
+ """
4
+
5
+ from enum import Enum
6
+ from sqlalchemy import Column, String, BigInteger, ForeignKey, UniqueConstraint, Enum as SQLEnum
7
+ from sqlalchemy.orm import relationship
8
+ from pydantic import BaseModel, field_validator
9
+ from typing import Literal
10
+
11
+ from .base import Base
12
+
13
+
14
+ class ProjectRole(str, Enum):
15
+ """Valid roles for project users."""
16
+ OWNER = "owner"
17
+ EDITOR = "editor"
18
+ VIEWER = "viewer"
19
+
20
+
21
+ class ProjectUserModel(Base):
22
+ """
23
+ SQLAlchemy model for project user access.
24
+
25
+ This junction table tracks which users have access to which projects,
26
+ enabling multi-user collaboration on projects.
27
+ """
28
+
29
+ __tablename__ = "project_users"
30
+
31
+ id = Column(String, primary_key=True)
32
+ project_id = Column(String, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
33
+ user_id = Column(String, nullable=False)
34
+ role = Column(SQLEnum(ProjectRole), nullable=False, default=ProjectRole.VIEWER)
35
+ added_at = Column(BigInteger, nullable=False) # Epoch timestamp in milliseconds
36
+ added_by_user_id = Column(String, nullable=False) # User who granted access
37
+
38
+ # Ensure a user can only be added once per project
39
+ __table_args__ = (
40
+ UniqueConstraint('project_id', 'user_id', name='uq_project_user'),
41
+ )
42
+
43
+ # Relationships
44
+ project = relationship("ProjectModel", back_populates="project_users")
45
+
46
+
47
+ class CreateProjectUserModel(BaseModel):
48
+ """Pydantic model for creating a project user access record."""
49
+ id: str
50
+ project_id: str
51
+ user_id: str
52
+ role: Literal["owner", "editor", "viewer"] = "viewer"
53
+ added_at: int
54
+ added_by_user_id: str
55
+
56
+ @field_validator('role')
57
+ @classmethod
58
+ def validate_role(cls, v: str) -> str:
59
+ """Validate that role is one of the allowed values."""
60
+ if v not in [role.value for role in ProjectRole]:
61
+ raise ValueError(f"Role must be one of: {', '.join([role.value for role in ProjectRole])}")
62
+ return v
63
+
64
+
65
+ class UpdateProjectUserModel(BaseModel):
66
+ """Pydantic model for updating a project user access record."""
67
+ role: Literal["owner", "editor", "viewer"] | None = None
68
+
69
+ @field_validator('role')
70
+ @classmethod
71
+ def validate_role(cls, v: str | None) -> str | None:
72
+ """Validate that role is one of the allowed values."""
73
+ if v is not None and v not in [role.value for role in ProjectRole]:
74
+ raise ValueError(f"Role must be one of: {', '.join([role.value for role in ProjectRole])}")
75
+ return v
@@ -2,8 +2,8 @@
2
2
  Session SQLAlchemy model and Pydantic models for strongly-typed operations.
3
3
  """
4
4
 
5
+ from sqlalchemy import BigInteger, Column, String, ForeignKey
5
6
  from pydantic import BaseModel
6
- from sqlalchemy import BigInteger, Column, String
7
7
  from sqlalchemy.orm import relationship
8
8
 
9
9
  from ...shared import now_epoch_ms
@@ -19,15 +19,19 @@ class SessionModel(Base):
19
19
  name = Column(String, nullable=True)
20
20
  user_id = Column(String, nullable=False)
21
21
  agent_id = Column(String, nullable=True)
22
+ project_id = Column(String, ForeignKey("projects.id"), nullable=True)
22
23
  created_time = Column(BigInteger, nullable=False, default=now_epoch_ms)
23
24
  updated_time = Column(
24
25
  BigInteger, nullable=False, default=now_epoch_ms, onupdate=now_epoch_ms
25
26
  )
27
+ deleted_at = Column(BigInteger, nullable=True)
28
+ deleted_by = Column(String, nullable=True)
26
29
 
27
30
  # Relationship to chat tasks
28
31
  chat_tasks = relationship(
29
32
  "ChatTaskModel", back_populates="session", cascade="all, delete-orphan"
30
33
  )
34
+ project = relationship("ProjectModel", back_populates="sessions")
31
35
 
32
36
 
33
37
  class CreateSessionModel(BaseModel):
@@ -36,6 +40,7 @@ class CreateSessionModel(BaseModel):
36
40
  name: str | None
37
41
  user_id: str
38
42
  agent_id: str | None
43
+ project_id: str | None = None
39
44
  created_time: int
40
45
  updated_time: int
41
46
 
@@ -44,4 +49,5 @@ class UpdateSessionModel(BaseModel):
44
49
  """Pydantic model for updating a session."""
45
50
  name: str | None = None
46
51
  agent_id: str | None = None
52
+ project_id: str | None = None
47
53
  updated_time: int
@@ -0,0 +1,172 @@
1
+ """
2
+ Repository implementation for project data access operations.
3
+ """
4
+ from typing import List, Optional
5
+ import uuid
6
+
7
+ from sqlalchemy.orm import Session as DBSession
8
+ from sqlalchemy import or_
9
+
10
+ from .interfaces import IProjectRepository
11
+ from .models import ProjectModel, ProjectUserModel
12
+ from .entities.project import Project
13
+ from ..routers.dto.requests.project_requests import ProjectFilter
14
+ from ..shared import now_epoch_ms
15
+
16
+
17
+ class ProjectRepository(IProjectRepository):
18
+ """SQLAlchemy implementation of project repository."""
19
+
20
+ def __init__(self, db: DBSession):
21
+ self.db = db
22
+
23
+ def create_project(self, name: str, user_id: str, description: Optional[str] = None,
24
+ system_prompt: Optional[str] = None, default_agent_id: Optional[str] = None) -> Project:
25
+ """Create a new user project."""
26
+ model = ProjectModel(
27
+ id=str(uuid.uuid4()),
28
+ name=name,
29
+ user_id=user_id,
30
+ description=description,
31
+ system_prompt=system_prompt,
32
+ default_agent_id=default_agent_id,
33
+ created_at=now_epoch_ms(),
34
+ )
35
+ self.db.add(model)
36
+ self.db.flush()
37
+ self.db.refresh(model)
38
+ return self._model_to_entity(model)
39
+
40
+ def get_user_projects(self, user_id: str) -> List[Project]:
41
+ """
42
+ Get all projects owned by a specific user.
43
+
44
+ Note: This returns only projects where the user is the owner (user_id matches).
45
+ For projects the user has access to via project_users table, use get_accessible_projects().
46
+ """
47
+ models = self.db.query(ProjectModel).filter(
48
+ ProjectModel.user_id == user_id,
49
+ ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
50
+ ).all()
51
+ return [self._model_to_entity(model) for model in models]
52
+
53
+ def get_accessible_projects(self, user_id: str) -> List[Project]:
54
+ """
55
+ Get all projects accessible by a user (owned or shared).
56
+
57
+ This includes:
58
+ - Projects owned by the user (user_id matches)
59
+ - Projects shared with the user (via project_users table)
60
+
61
+ Args:
62
+ user_id: The user ID
63
+
64
+ Returns:
65
+ List[Project]: List of accessible projects
66
+ """
67
+ # Query for projects where user is owner OR has access via project_users
68
+ models = self.db.query(ProjectModel).outerjoin(
69
+ ProjectUserModel,
70
+ ProjectModel.id == ProjectUserModel.project_id
71
+ ).filter(
72
+ ProjectModel.deleted_at.is_(None), # Exclude soft-deleted projects
73
+ or_(
74
+ ProjectModel.user_id == user_id,
75
+ ProjectUserModel.user_id == user_id
76
+ )
77
+ ).distinct().all()
78
+
79
+ return [self._model_to_entity(model) for model in models]
80
+
81
+ def get_filtered_projects(self, project_filter: ProjectFilter) -> List[Project]:
82
+ """Get projects based on filter criteria."""
83
+ query = self.db.query(ProjectModel).filter(
84
+ ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
85
+ )
86
+
87
+ if project_filter.user_id is not None:
88
+ query = query.filter(ProjectModel.user_id == project_filter.user_id)
89
+
90
+ models = query.all()
91
+ return [self._model_to_entity(model) for model in models]
92
+
93
+ def get_by_id(self, project_id: str, user_id: str) -> Optional[Project]:
94
+ """
95
+ Get a project by its ID, ensuring user access.
96
+
97
+ This checks if the user is the owner OR has access via project_users table.
98
+ """
99
+ model = self.db.query(ProjectModel).outerjoin(
100
+ ProjectUserModel,
101
+ ProjectModel.id == ProjectUserModel.project_id
102
+ ).filter(
103
+ ProjectModel.id == project_id,
104
+ ProjectModel.deleted_at.is_(None), # Exclude soft-deleted projects
105
+ or_(
106
+ ProjectModel.user_id == user_id,
107
+ ProjectUserModel.user_id == user_id
108
+ )
109
+ ).first()
110
+
111
+ return self._model_to_entity(model) if model else None
112
+
113
+ def update(self, project_id: str, user_id: str, update_data: dict) -> Optional[Project]:
114
+ """Update a project with the given data, ensuring user access."""
115
+ model = self.db.query(ProjectModel).filter(
116
+ ProjectModel.id == project_id,
117
+ ProjectModel.user_id == user_id, # Only allow updates to user's own projects
118
+ ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
119
+ ).first()
120
+
121
+ if not model:
122
+ return None
123
+
124
+ for field, value in update_data.items():
125
+ if hasattr(model, field):
126
+ setattr(model, field, value)
127
+
128
+ model.updated_at = now_epoch_ms()
129
+ self.db.flush()
130
+ self.db.refresh(model)
131
+ return self._model_to_entity(model)
132
+
133
+ def delete(self, project_id: str, user_id: str) -> bool:
134
+ """Delete a project by its ID, ensuring user access."""
135
+ result = self.db.query(ProjectModel).filter(
136
+ ProjectModel.id == project_id,
137
+ ProjectModel.user_id == user_id # Only allow deletion of user's own projects
138
+ ).delete()
139
+ self.db.flush()
140
+ return result > 0
141
+
142
+ def soft_delete(self, project_id: str, user_id: str) -> bool:
143
+ """Soft delete a project by its ID, ensuring user access."""
144
+ model = self.db.query(ProjectModel).filter(
145
+ ProjectModel.id == project_id,
146
+ ProjectModel.user_id == user_id, # Only allow deletion of user's own projects
147
+ ProjectModel.deleted_at.is_(None) # Only delete if not already deleted
148
+ ).first()
149
+
150
+ if not model:
151
+ return False
152
+
153
+ model.deleted_at = now_epoch_ms()
154
+ model.deleted_by = user_id
155
+ model.updated_at = now_epoch_ms()
156
+ self.db.flush()
157
+ return True
158
+
159
+ def _model_to_entity(self, model: ProjectModel) -> Project:
160
+ """Convert SQLAlchemy model to domain entity."""
161
+ return Project(
162
+ id=model.id,
163
+ name=model.name,
164
+ user_id=model.user_id,
165
+ description=model.description,
166
+ system_prompt=model.system_prompt,
167
+ default_agent_id=model.default_agent_id,
168
+ created_at=model.created_at,
169
+ updated_at=model.updated_at,
170
+ deleted_at=model.deleted_at,
171
+ deleted_by=model.deleted_by,
172
+ )