solace-agent-mesh 1.6.1__py3-none-any.whl → 1.13.2__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 (481) hide show
  1. solace_agent_mesh/agent/adk/alembic/README +74 -0
  2. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  3. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  4. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  5. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  6. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  7. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
  8. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
  9. solace_agent_mesh/agent/adk/callbacks.py +852 -109
  10. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
  11. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
  12. solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
  13. solace_agent_mesh/agent/adk/models/lite_llm.py +77 -21
  14. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
  15. solace_agent_mesh/agent/adk/runner.py +85 -20
  16. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  17. solace_agent_mesh/agent/adk/services.py +94 -18
  18. solace_agent_mesh/agent/adk/setup.py +281 -65
  19. solace_agent_mesh/agent/adk/stream_parser.py +231 -37
  20. solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
  21. solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
  22. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  23. solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
  24. solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
  25. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  26. solace_agent_mesh/agent/proxies/base/component.py +188 -22
  27. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
  28. solace_agent_mesh/agent/sac/app.py +91 -3
  29. solace_agent_mesh/agent/sac/component.py +591 -157
  30. solace_agent_mesh/agent/sac/patch_adk.py +8 -16
  31. solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
  32. solace_agent_mesh/agent/tools/__init__.py +3 -0
  33. solace_agent_mesh/agent/tools/audio_tools.py +3 -3
  34. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +710 -171
  35. solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
  36. solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
  37. solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
  38. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  39. solace_agent_mesh/agent/tools/tool_config_types.py +57 -2
  40. solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
  41. solace_agent_mesh/agent/tools/web_tools.py +125 -17
  42. solace_agent_mesh/agent/utils/artifact_helpers.py +248 -6
  43. solace_agent_mesh/agent/utils/context_helpers.py +17 -0
  44. solace_agent_mesh/assets/docs/404.html +6 -6
  45. solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
  46. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  51. solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
  52. solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
  53. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  54. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
  56. solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
  57. solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
  60. solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
  61. solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
  62. solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
  63. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  64. solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
  65. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
  66. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  67. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  68. solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
  69. solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
  70. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  71. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  72. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  73. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  74. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  75. solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
  76. solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
  77. solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
  78. solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
  79. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  80. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  81. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  82. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  83. solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
  84. solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
  85. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  86. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  95. solace_agent_mesh/assets/docs/assets/js/e04b235d.06d23db6.js +1 -0
  96. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
  101. solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
  102. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
  104. solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
  105. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
  106. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
  107. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
  108. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
  109. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +82 -68
  110. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
  111. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
  112. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
  113. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
  114. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
  115. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
  116. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
  117. solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
  118. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
  119. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  120. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  121. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
  122. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  123. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -49
  124. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
  125. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
  126. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
  127. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
  128. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +90 -0
  129. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
  130. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  131. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
  132. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +162 -171
  133. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
  134. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
  135. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
  136. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
  137. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
  138. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
  139. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
  140. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
  141. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
  142. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
  143. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
  144. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
  145. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
  146. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
  147. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  148. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  149. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  150. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
  151. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
  152. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  153. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +112 -87
  154. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  155. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +87 -64
  156. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  157. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
  158. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -37
  159. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
  160. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
  161. solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
  162. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +311 -0
  163. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
  164. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
  165. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
  166. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
  167. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
  168. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  169. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  170. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
  171. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
  172. solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
  173. solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
  174. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  175. solace_agent_mesh/assets/docs/search-doc-1768329217460.json +1 -0
  176. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  177. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  178. solace_agent_mesh/cli/__init__.py +1 -1
  179. solace_agent_mesh/cli/commands/add_cmd/__init__.py +3 -1
  180. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
  181. solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
  182. solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
  183. solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
  184. solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
  185. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
  186. solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
  187. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
  188. solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
  189. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
  190. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
  191. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
  192. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
  193. solace_agent_mesh/cli/commands/run_cmd.py +64 -49
  194. solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
  195. solace_agent_mesh/cli/main.py +15 -0
  196. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-KnKMP_vb.js} +1 -1
  197. solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
  198. solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
  199. solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
  200. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CGk8Suyh.js +565 -0
  201. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  202. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  203. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  204. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  205. solace_agent_mesh/common/a2a/events.py +2 -1
  206. solace_agent_mesh/common/a2a/protocol.py +5 -0
  207. solace_agent_mesh/common/a2a/types.py +2 -1
  208. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
  209. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  210. solace_agent_mesh/common/agent_registry.py +38 -11
  211. solace_agent_mesh/common/data_parts.py +144 -4
  212. solace_agent_mesh/common/error_handlers.py +83 -0
  213. solace_agent_mesh/common/exceptions.py +24 -0
  214. solace_agent_mesh/common/oauth/__init__.py +17 -0
  215. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  216. solace_agent_mesh/common/oauth/utils.py +50 -0
  217. solace_agent_mesh/common/rag_dto.py +156 -0
  218. solace_agent_mesh/common/sac/sam_component_base.py +97 -19
  219. solace_agent_mesh/common/sam_events/event_service.py +2 -2
  220. solace_agent_mesh/common/services/employee_service.py +1 -1
  221. solace_agent_mesh/common/utils/embeds/constants.py +1 -0
  222. solace_agent_mesh/common/utils/embeds/converter.py +1 -8
  223. solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
  224. solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
  225. solace_agent_mesh/common/utils/embeds/types.py +9 -0
  226. solace_agent_mesh/common/utils/log_formatters.py +20 -0
  227. solace_agent_mesh/common/utils/mime_helpers.py +12 -5
  228. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  229. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  230. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  231. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  232. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  233. solace_agent_mesh/config_portal/backend/common.py +12 -0
  234. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
  235. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
  236. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
  237. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
  238. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
  239. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
  240. solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
  241. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  242. solace_agent_mesh/core_a2a/service.py +3 -2
  243. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  244. solace_agent_mesh/gateway/adapter/base.py +170 -0
  245. solace_agent_mesh/gateway/adapter/types.py +230 -0
  246. solace_agent_mesh/gateway/base/app.py +39 -2
  247. solace_agent_mesh/gateway/base/auth_interface.py +103 -0
  248. solace_agent_mesh/gateway/base/component.py +1027 -151
  249. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  250. solace_agent_mesh/gateway/generic/app.py +50 -0
  251. solace_agent_mesh/gateway/generic/component.py +894 -0
  252. solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
  253. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  254. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  255. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  256. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  257. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  258. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  259. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  260. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  261. solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
  262. solace_agent_mesh/gateway/http_sse/app.py +40 -11
  263. solace_agent_mesh/gateway/http_sse/component.py +285 -160
  264. solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
  265. solace_agent_mesh/gateway/http_sse/main.py +68 -450
  266. solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
  267. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
  268. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  269. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  270. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +26 -3
  271. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
  272. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
  273. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
  274. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
  275. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  276. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  277. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  278. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +8 -2
  279. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
  280. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  281. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  282. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +177 -11
  283. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
  284. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
  285. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +256 -58
  286. solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
  287. solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
  288. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  289. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  290. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  291. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -1
  292. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
  293. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  294. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
  295. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  296. solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
  297. solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
  298. solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
  299. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
  300. solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
  301. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  302. solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
  303. solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
  304. solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
  305. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  306. solace_agent_mesh/gateway/http_sse/routers/visualization.py +92 -9
  307. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  308. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  309. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
  310. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
  311. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  312. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  313. solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
  314. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
  315. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  316. solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
  317. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  318. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
  319. solace_agent_mesh/services/__init__.py +0 -0
  320. solace_agent_mesh/services/platform/__init__.py +29 -0
  321. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  322. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  323. solace_agent_mesh/services/platform/alembic.ini +109 -0
  324. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  325. solace_agent_mesh/services/platform/api/dependencies.py +154 -0
  326. solace_agent_mesh/services/platform/api/main.py +314 -0
  327. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  328. solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
  329. solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
  330. solace_agent_mesh/services/platform/app.py +215 -0
  331. solace_agent_mesh/services/platform/component.py +777 -0
  332. solace_agent_mesh/shared/__init__.py +14 -0
  333. solace_agent_mesh/shared/api/__init__.py +42 -0
  334. solace_agent_mesh/shared/auth/__init__.py +26 -0
  335. solace_agent_mesh/shared/auth/dependencies.py +204 -0
  336. solace_agent_mesh/shared/auth/middleware.py +347 -0
  337. solace_agent_mesh/shared/database/__init__.py +20 -0
  338. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
  339. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
  340. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
  341. solace_agent_mesh/shared/exceptions/__init__.py +36 -0
  342. solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +19 -5
  343. solace_agent_mesh/shared/utils/__init__.py +21 -0
  344. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  345. solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
  346. solace_agent_mesh/templates/platform.yaml +49 -0
  347. solace_agent_mesh/templates/plugin_readme_template.md +3 -25
  348. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  349. solace_agent_mesh/templates/proxy_template.yaml +62 -0
  350. solace_agent_mesh/templates/webui.yaml +148 -6
  351. solace_agent_mesh/tools/web_search/__init__.py +18 -0
  352. solace_agent_mesh/tools/web_search/base.py +84 -0
  353. solace_agent_mesh/tools/web_search/google_search.py +247 -0
  354. solace_agent_mesh/tools/web_search/models.py +99 -0
  355. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
  356. solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
  357. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
  358. solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
  359. solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
  360. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
  361. solace_agent_mesh/agent/adk/models/models_llm.txt +0 -142
  362. solace_agent_mesh/agent/agent_llm.txt +0 -378
  363. solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
  364. solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
  365. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
  366. solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
  367. solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
  368. solace_agent_mesh/agent/testing/testing_llm.txt +0 -57
  369. solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
  370. solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
  371. solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
  372. solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
  373. solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
  374. solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
  375. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
  376. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  377. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
  378. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
  379. solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
  380. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
  381. solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
  382. solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
  383. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
  384. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  385. solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
  386. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
  387. solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
  388. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  389. solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
  390. solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
  391. solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
  392. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
  393. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  394. solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
  395. solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
  396. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +0 -1
  397. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
  398. solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
  399. solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
  400. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
  401. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
  402. solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
  403. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +0 -1
  404. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  405. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  406. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
  407. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  408. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  409. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  410. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  411. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
  412. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
  413. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
  414. solace_agent_mesh/client/webui/frontend/static/assets/client-CaY59VuC.js +0 -25
  415. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  416. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  417. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
  418. solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
  419. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
  420. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
  421. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
  422. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
  423. solace_agent_mesh/common/common_llm.txt +0 -251
  424. solace_agent_mesh/common/common_llm_detail.txt +0 -2562
  425. solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
  426. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
  427. solace_agent_mesh/common/sac/sac_llm.txt +0 -71
  428. solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
  429. solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
  430. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
  431. solace_agent_mesh/common/services/providers/providers_llm.txt +0 -80
  432. solace_agent_mesh/common/services/services_llm.txt +0 -363
  433. solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
  434. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
  435. solace_agent_mesh/common/utils/utils_llm.txt +0 -336
  436. solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
  437. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
  438. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
  439. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
  440. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
  441. solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
  442. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
  443. solace_agent_mesh/gateway/base/base_llm.txt +0 -224
  444. solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
  445. solace_agent_mesh/gateway/gateway_llm.txt +0 -373
  446. solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
  447. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
  448. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
  449. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
  450. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
  451. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
  452. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -263
  453. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
  454. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
  455. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
  456. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
  457. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
  458. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
  459. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
  460. solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
  461. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
  462. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
  463. solace_agent_mesh/llm.txt +0 -228
  464. solace_agent_mesh/llm_detail.txt +0 -2835
  465. solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
  466. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
  467. solace_agent_mesh/templates/logging_config_template.ini +0 -45
  468. solace_agent_mesh/templates/templates_llm.txt +0 -147
  469. solace_agent_mesh-1.6.1.dist-info/RECORD +0 -525
  470. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
  471. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
  472. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
  473. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
  474. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
  475. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
  476. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
  477. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
  478. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
  479. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
  480. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
  481. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,768 @@
1
+ """
2
+ Project API controller using 3-tiered architecture.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ from typing import List, Optional, Dict, Any
8
+ from fastapi import (
9
+ APIRouter,
10
+ Depends,
11
+ HTTPException,
12
+ status,
13
+ Request,
14
+ Form,
15
+ File,
16
+ UploadFile,
17
+ )
18
+ from fastapi.responses import StreamingResponse
19
+ from sqlalchemy.orm import Session
20
+ from solace_ai_connector.common.log import log
21
+
22
+ from ..dependencies import get_project_service, get_sac_component, get_api_config, get_db
23
+ from ..services.project_service import ProjectService
24
+ from solace_agent_mesh.shared.api.auth_utils import get_current_user
25
+ from ....common.a2a.types import ArtifactInfo
26
+ from typing import TYPE_CHECKING
27
+
28
+ if TYPE_CHECKING:
29
+ from ..component import WebUIBackendComponent
30
+
31
+ from .dto.requests.project_requests import (
32
+ CreateProjectRequest,
33
+ UpdateProjectRequest,
34
+ GetProjectRequest,
35
+ GetProjectsRequest,
36
+ DeleteProjectRequest,
37
+ )
38
+ from .dto.responses.project_responses import (
39
+ ProjectResponse,
40
+ ProjectListResponse,
41
+ )
42
+ from .dto.project_dto import (
43
+ ProjectImportOptions,
44
+ ProjectImportResponse,
45
+ )
46
+
47
+ router = APIRouter()
48
+
49
+
50
+ def check_projects_enabled(
51
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
52
+ api_config: Dict[str, Any] = Depends(get_api_config),
53
+ ) -> None:
54
+ """
55
+ Dependency to check if projects feature is enabled.
56
+ Raises HTTPException if projects are disabled.
57
+ """
58
+ # Check if persistence is enabled (required for projects)
59
+ persistence_enabled = api_config.get("persistence_enabled", False)
60
+ if not persistence_enabled:
61
+ log.warning("Projects API called but persistence is not enabled")
62
+ raise HTTPException(
63
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
64
+ detail="Projects feature requires persistence to be enabled. Please configure session_service.type as 'sql'."
65
+ )
66
+
67
+ # Check explicit projects config
68
+ projects_config = component.get_config("projects", {})
69
+ if isinstance(projects_config, dict):
70
+ projects_explicitly_enabled = projects_config.get("enabled", True)
71
+ if not projects_explicitly_enabled:
72
+ log.warning("Projects API called but projects are explicitly disabled in config")
73
+ raise HTTPException(
74
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
75
+ detail="Projects feature is disabled. Please enable it in the configuration."
76
+ )
77
+
78
+ # Check frontend_feature_enablement override
79
+ feature_flags = component.get_config("frontend_feature_enablement", {})
80
+ if "projects" in feature_flags:
81
+ projects_flag = feature_flags.get("projects", True)
82
+ if not projects_flag:
83
+ log.warning("Projects API called but projects are disabled via feature flag")
84
+ raise HTTPException(
85
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
86
+ detail="Projects feature is disabled via feature flag."
87
+ )
88
+
89
+
90
+ @router.post("/projects", response_model=ProjectResponse, status_code=status.HTTP_201_CREATED)
91
+ async def create_project(
92
+ name: str = Form(...),
93
+ description: Optional[str] = Form(None),
94
+ system_prompt: Optional[str] = Form(None, alias="systemPrompt"),
95
+ default_agent_id: Optional[str] = Form(None, alias="defaultAgentId"),
96
+ file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
97
+ files: Optional[List[UploadFile]] = File(None),
98
+ user: dict = Depends(get_current_user),
99
+ project_service: ProjectService = Depends(get_project_service),
100
+ db: Session = Depends(get_db),
101
+ _: None = Depends(check_projects_enabled),
102
+ ):
103
+ """
104
+ Create a new project for the authenticated user.
105
+ """
106
+ user_id = user.get("id")
107
+ log.info(f"Creating project '{name}' for user {user_id}")
108
+ log.info(f"Received system_prompt: {system_prompt}")
109
+ log.info(f"Received file_metadata: {file_metadata}")
110
+
111
+ try:
112
+ if files:
113
+ log.info(f"Received {len(files)} files for project creation:")
114
+ for file in files:
115
+ log.info(f" - Filename: {file.filename}, Content-Type: {file.content_type}")
116
+ else:
117
+ log.info("No files received for project creation.")
118
+
119
+ request_dto = CreateProjectRequest(
120
+ name=name,
121
+ description=description,
122
+ system_prompt=system_prompt,
123
+ default_agent_id=default_agent_id,
124
+ file_metadata=file_metadata,
125
+ user_id=user_id
126
+ )
127
+
128
+ parsed_file_metadata = {}
129
+ if request_dto.file_metadata:
130
+ try:
131
+ parsed_file_metadata = json.loads(request_dto.file_metadata)
132
+ except json.JSONDecodeError:
133
+ log.warning("Could not parse file_metadata JSON string, ignoring.")
134
+ pass
135
+
136
+ project = await project_service.create_project(
137
+ db=db,
138
+ name=request_dto.name,
139
+ user_id=request_dto.user_id,
140
+ description=request_dto.description,
141
+ system_prompt=request_dto.system_prompt,
142
+ default_agent_id=request_dto.default_agent_id,
143
+ files=files,
144
+ file_metadata=parsed_file_metadata,
145
+ )
146
+
147
+ return ProjectResponse(
148
+ id=project.id,
149
+ name=project.name,
150
+ user_id=project.user_id,
151
+ description=project.description,
152
+ system_prompt=project.system_prompt,
153
+ default_agent_id=project.default_agent_id,
154
+ created_at=project.created_at,
155
+ updated_at=project.updated_at,
156
+ )
157
+
158
+ except ValueError as e:
159
+ error_msg = str(e)
160
+ log.warning(f"Validation error creating project: {error_msg}")
161
+ # Check if this is a file size error
162
+ if "exceeds maximum" in error_msg.lower() and "bytes" in error_msg.lower():
163
+ raise HTTPException(
164
+ status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
165
+ detail=error_msg
166
+ )
167
+ raise HTTPException(
168
+ status_code=status.HTTP_400_BAD_REQUEST,
169
+ detail=error_msg
170
+ )
171
+ except Exception as e:
172
+ log.error("Error creating project for user %s: %s", user_id, e)
173
+ raise HTTPException(
174
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
175
+ detail="Failed to create project"
176
+ )
177
+
178
+
179
+ @router.get("/projects", response_model=ProjectListResponse)
180
+ async def get_user_projects(
181
+ include_artifact_count: bool = False,
182
+ user: dict = Depends(get_current_user),
183
+ project_service: ProjectService = Depends(get_project_service),
184
+ db: Session = Depends(get_db),
185
+ _: None = Depends(check_projects_enabled),
186
+ ):
187
+ """
188
+ Get all projects owned by the authenticated user.
189
+
190
+ Args:
191
+ include_artifact_count: If True, includes artifact count for each project
192
+ """
193
+ user_id = user.get("id")
194
+ log.info(f"Fetching projects for user_id: {user_id}, include_artifact_count: {include_artifact_count}")
195
+
196
+ try:
197
+ request_dto = GetProjectsRequest(user_id=user_id)
198
+
199
+ if include_artifact_count:
200
+ # Fetch projects with artifact counts
201
+ projects_with_counts = await project_service.get_user_projects_with_counts(db, request_dto.user_id)
202
+
203
+ project_responses = [
204
+ ProjectResponse(
205
+ id=p.id,
206
+ name=p.name,
207
+ user_id=p.user_id,
208
+ description=p.description,
209
+ system_prompt=p.system_prompt,
210
+ default_agent_id=p.default_agent_id,
211
+ artifact_count=count,
212
+ created_at=p.created_at,
213
+ updated_at=p.updated_at,
214
+ )
215
+ for p, count in projects_with_counts
216
+ ]
217
+ else:
218
+ # Fetch projects without counts (faster)
219
+ projects = project_service.get_user_projects(db, request_dto.user_id)
220
+
221
+ project_responses = [
222
+ ProjectResponse(
223
+ id=p.id,
224
+ name=p.name,
225
+ user_id=p.user_id,
226
+ description=p.description,
227
+ system_prompt=p.system_prompt,
228
+ default_agent_id=p.default_agent_id,
229
+ created_at=p.created_at,
230
+ updated_at=p.updated_at,
231
+ )
232
+ for p in projects
233
+ ]
234
+
235
+ return ProjectListResponse(
236
+ projects=project_responses,
237
+ total=len(project_responses)
238
+ )
239
+
240
+ except Exception as e:
241
+ log.error("Error fetching projects for user %s: %s", user_id, e)
242
+ raise HTTPException(
243
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
244
+ detail="Failed to retrieve projects"
245
+ )
246
+
247
+
248
+ @router.get("/projects/{project_id}", response_model=ProjectResponse)
249
+ async def get_project(
250
+ project_id: str,
251
+ user: dict = Depends(get_current_user),
252
+ project_service: ProjectService = Depends(get_project_service),
253
+ db: Session = Depends(get_db),
254
+ _: None = Depends(check_projects_enabled),
255
+ ):
256
+ """
257
+ Get a specific project by ID.
258
+ """
259
+ user_id = user.get("id")
260
+ log.info("User %s attempting to fetch project_id: %s", user_id, project_id)
261
+
262
+ try:
263
+ if (
264
+ not project_id
265
+ or project_id.strip() == ""
266
+ or project_id in ["null", "undefined"]
267
+ ):
268
+ raise HTTPException(
269
+ status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
270
+ )
271
+
272
+ request_dto = GetProjectRequest(project_id=project_id, user_id=user_id)
273
+
274
+ project = project_service.get_project(
275
+ db=db,
276
+ project_id=request_dto.project_id,
277
+ user_id=request_dto.user_id
278
+ )
279
+
280
+ if not project:
281
+ raise HTTPException(
282
+ status_code=status.HTTP_404_NOT_FOUND,
283
+ detail="Project not found."
284
+ )
285
+
286
+ log.info("User %s authorized. Fetching project_id: %s", user_id, project_id)
287
+
288
+ return ProjectResponse(
289
+ id=project.id,
290
+ name=project.name,
291
+ user_id=project.user_id,
292
+ description=project.description,
293
+ system_prompt=project.system_prompt,
294
+ default_agent_id=project.default_agent_id,
295
+ created_at=project.created_at,
296
+ updated_at=project.updated_at,
297
+ )
298
+
299
+ except HTTPException:
300
+ raise
301
+ except Exception as e:
302
+ log.error(
303
+ "Error fetching project %s for user %s: %s",
304
+ project_id,
305
+ user_id,
306
+ e,
307
+ )
308
+ raise HTTPException(
309
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
310
+ detail="Failed to retrieve project"
311
+ )
312
+
313
+
314
+ @router.get("/projects/{project_id}/artifacts", response_model=List[ArtifactInfo])
315
+ async def get_project_artifacts(
316
+ project_id: str,
317
+ user: dict = Depends(get_current_user),
318
+ project_service: ProjectService = Depends(get_project_service),
319
+ db: Session = Depends(get_db),
320
+ _: None = Depends(check_projects_enabled),
321
+ ):
322
+ """
323
+ Get all artifacts for a specific project.
324
+ """
325
+ user_id = user.get("id")
326
+ log.info("User %s attempting to fetch artifacts for project_id: %s", user_id, project_id)
327
+
328
+ try:
329
+ artifacts = await project_service.get_project_artifacts(
330
+ db=db, project_id=project_id, user_id=user_id
331
+ )
332
+ return artifacts
333
+ except ValueError as e:
334
+ log.warning(f"Validation error getting project artifacts: {e}")
335
+ raise HTTPException(
336
+ status_code=status.HTTP_404_NOT_FOUND,
337
+ detail=str(e)
338
+ )
339
+ except Exception as e:
340
+ log.error(
341
+ "Error fetching artifacts for project %s for user %s: %s",
342
+ project_id,
343
+ user_id,
344
+ e,
345
+ )
346
+ raise HTTPException(
347
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
348
+ detail="Failed to retrieve project artifacts"
349
+ )
350
+
351
+
352
+ @router.post("/projects/{project_id}/artifacts", status_code=status.HTTP_201_CREATED)
353
+ async def add_project_artifacts(
354
+ project_id: str,
355
+ files: List[UploadFile] = File(...),
356
+ file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
357
+ user: dict = Depends(get_current_user),
358
+ project_service: ProjectService = Depends(get_project_service),
359
+ db: Session = Depends(get_db),
360
+ _: None = Depends(check_projects_enabled),
361
+ ):
362
+ """
363
+ Add one or more artifacts to a project.
364
+ """
365
+ user_id = user.get("id")
366
+ log.info(f"User {user_id} attempting to add artifacts to project {project_id}")
367
+
368
+ try:
369
+ parsed_file_metadata = {}
370
+ if file_metadata:
371
+ try:
372
+ parsed_file_metadata = json.loads(file_metadata)
373
+ except json.JSONDecodeError:
374
+ log.warning(f"Could not parse file_metadata for project {project_id}, ignoring.")
375
+ pass
376
+
377
+ results = await project_service.add_artifacts_to_project(
378
+ db=db,
379
+ project_id=project_id,
380
+ user_id=user_id,
381
+ files=files,
382
+ file_metadata=parsed_file_metadata
383
+ )
384
+ return results
385
+ except ValueError as e:
386
+ error_msg = str(e)
387
+ log.warning(f"Validation error adding artifacts to project {project_id}: {error_msg}")
388
+ # Could be 404 if project not found, 413 if file too large, or 400 if other validation fails
389
+ if "not found" in error_msg.lower():
390
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error_msg)
391
+ if "exceeds maximum" in error_msg.lower() and "bytes" in error_msg.lower():
392
+ raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail=error_msg)
393
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error_msg)
394
+ except Exception as e:
395
+ log.error(
396
+ "Error adding artifacts to project %s for user %s: %s",
397
+ project_id,
398
+ user_id,
399
+ e,
400
+ )
401
+ raise HTTPException(
402
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
403
+ detail="Failed to add artifacts to project"
404
+ )
405
+
406
+
407
+ @router.patch("/projects/{project_id}/artifacts/{filename}", status_code=status.HTTP_200_OK)
408
+ async def update_project_artifact_metadata(
409
+ project_id: str,
410
+ filename: str,
411
+ description: Optional[str] = Form(None),
412
+ user: dict = Depends(get_current_user),
413
+ project_service: ProjectService = Depends(get_project_service),
414
+ db: Session = Depends(get_db),
415
+ _: None = Depends(check_projects_enabled),
416
+ ):
417
+ """
418
+ Update metadata (description) for a project artifact.
419
+ """
420
+ user_id = user.get("id")
421
+ log.info(f"User {user_id} attempting to update metadata for artifact '{filename}' in project {project_id}")
422
+
423
+ try:
424
+ success = await project_service.update_artifact_metadata(
425
+ db=db,
426
+ project_id=project_id,
427
+ user_id=user_id,
428
+ filename=filename,
429
+ description=description,
430
+ )
431
+ if not success:
432
+ raise HTTPException(
433
+ status_code=status.HTTP_404_NOT_FOUND,
434
+ detail="Project or artifact not found, or access denied."
435
+ )
436
+
437
+ return {"message": "Artifact metadata updated successfully"}
438
+ except ValueError as e:
439
+ log.warning(f"Validation error updating artifact metadata in project {project_id}: {e}")
440
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
441
+ except HTTPException:
442
+ raise
443
+ except Exception as e:
444
+ log.error(
445
+ "Error updating metadata for artifact '%s' in project %s for user %s: %s",
446
+ filename,
447
+ project_id,
448
+ user_id,
449
+ e,
450
+ )
451
+ raise HTTPException(
452
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
453
+ detail="Failed to update artifact metadata"
454
+ )
455
+
456
+
457
+ @router.delete("/projects/{project_id}/artifacts/{filename}", status_code=status.HTTP_204_NO_CONTENT)
458
+ async def delete_project_artifact(
459
+ project_id: str,
460
+ filename: str,
461
+ user: dict = Depends(get_current_user),
462
+ project_service: ProjectService = Depends(get_project_service),
463
+ db: Session = Depends(get_db),
464
+ _: None = Depends(check_projects_enabled),
465
+ ):
466
+ """
467
+ Delete an artifact from a project.
468
+ """
469
+ user_id = user.get("id")
470
+ log.info(f"User {user_id} attempting to delete artifact '{filename}' from project {project_id}")
471
+
472
+ try:
473
+ success = await project_service.delete_artifact_from_project(
474
+ db=db,
475
+ project_id=project_id,
476
+ user_id=user_id,
477
+ filename=filename,
478
+ )
479
+ if not success:
480
+ raise HTTPException(
481
+ status_code=status.HTTP_404_NOT_FOUND,
482
+ detail="Project not found or access denied."
483
+ )
484
+
485
+ return
486
+ except ValueError as e:
487
+ log.warning(f"Validation error deleting artifact from project {project_id}: {e}")
488
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
489
+ except HTTPException:
490
+ raise
491
+ except Exception as e:
492
+ log.error(
493
+ "Error deleting artifact '%s' from project %s for user %s: %s",
494
+ filename,
495
+ project_id,
496
+ user_id,
497
+ e,
498
+ )
499
+ raise HTTPException(
500
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
501
+ detail="Failed to delete artifact from project"
502
+ )
503
+
504
+
505
+ @router.put("/projects/{project_id}", response_model=ProjectResponse)
506
+ async def update_project(
507
+ project_id: str,
508
+ request: UpdateProjectRequest,
509
+ user: dict = Depends(get_current_user),
510
+ project_service: ProjectService = Depends(get_project_service),
511
+ db: Session = Depends(get_db),
512
+ _: None = Depends(check_projects_enabled),
513
+ ):
514
+ """
515
+ Update a project's details.
516
+ """
517
+ user_id = user.get("id")
518
+ log.info("User %s attempting to update project %s", user_id, project_id)
519
+
520
+ try:
521
+ if (
522
+ not project_id
523
+ or project_id.strip() == ""
524
+ or project_id in ["null", "undefined"]
525
+ ):
526
+ raise HTTPException(
527
+ status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
528
+ )
529
+
530
+ update_fields = request.model_dump(exclude_unset=True, by_alias=False)
531
+
532
+ # Pass only explicitly set fields to the service
533
+ kwargs = {
534
+ 'db': db,
535
+ 'project_id': project_id,
536
+ 'user_id': user_id,
537
+ 'name': update_fields.get('name', ...),
538
+ 'description': update_fields.get('description', ...),
539
+ 'system_prompt': update_fields.get('system_prompt', ...),
540
+ 'default_agent_id': update_fields.get('default_agent_id', ...),
541
+ }
542
+
543
+ project = project_service.update_project(**kwargs)
544
+
545
+ if not project:
546
+ raise HTTPException(
547
+ status_code=status.HTTP_404_NOT_FOUND,
548
+ detail="Project not found."
549
+ )
550
+
551
+ log.info("Project %s updated successfully", project_id)
552
+
553
+ return ProjectResponse(
554
+ id=project.id,
555
+ name=project.name,
556
+ user_id=project.user_id,
557
+ description=project.description,
558
+ system_prompt=project.system_prompt,
559
+ default_agent_id=project.default_agent_id,
560
+ created_at=project.created_at,
561
+ updated_at=project.updated_at,
562
+ )
563
+
564
+ except HTTPException:
565
+ raise
566
+ except ValueError as e:
567
+ log.warning("Validation error updating project %s: %s", project_id, e)
568
+ raise HTTPException(
569
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
570
+ )
571
+ except Exception as e:
572
+ log.error(
573
+ "Error updating project %s for user %s: %s",
574
+ project_id,
575
+ user_id,
576
+ e,
577
+ )
578
+ raise HTTPException(
579
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
580
+ detail="Failed to update project"
581
+ )
582
+
583
+
584
+ @router.delete("/projects/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
585
+ async def delete_project(
586
+ project_id: str,
587
+ user: dict = Depends(get_current_user),
588
+ project_service: ProjectService = Depends(get_project_service),
589
+ db: Session = Depends(get_db),
590
+ _: None = Depends(check_projects_enabled),
591
+ ):
592
+ """
593
+ Soft delete a project (marks as deleted without removing from database).
594
+ """
595
+ user_id = user.get("id")
596
+ log.info("User %s attempting to soft delete project %s", user_id, project_id)
597
+
598
+ try:
599
+ request_dto = DeleteProjectRequest(project_id=project_id, user_id=user_id)
600
+
601
+ success = project_service.soft_delete_project(
602
+ db=db,
603
+ project_id=request_dto.project_id,
604
+ user_id=request_dto.user_id
605
+ )
606
+
607
+ if not success:
608
+ raise HTTPException(
609
+ status_code=status.HTTP_404_NOT_FOUND,
610
+ detail="Project not found."
611
+ )
612
+
613
+ log.info("Project %s soft deleted successfully", project_id)
614
+
615
+ except HTTPException:
616
+ raise
617
+ except ValueError as e:
618
+ log.warning("Validation error deleting project %s: %s", project_id, e)
619
+ raise HTTPException(
620
+ status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
621
+ )
622
+ except Exception as e:
623
+ log.error(
624
+ "Error deleting project %s for user %s: %s",
625
+ project_id,
626
+ user_id,
627
+ e,
628
+ )
629
+ raise HTTPException(
630
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
631
+ detail="Failed to delete project"
632
+ )
633
+
634
+
635
+ @router.get("/projects/{project_id}/export")
636
+ async def export_project(
637
+ project_id: str,
638
+ user: dict = Depends(get_current_user),
639
+ project_service: ProjectService = Depends(get_project_service),
640
+ db: Session = Depends(get_db),
641
+ _: None = Depends(check_projects_enabled),
642
+ ):
643
+ """
644
+ Export project as ZIP containing:
645
+ - project.json (metadata)
646
+ - artifacts/ (all project files)
647
+
648
+ Excludes: chat history, sessions
649
+ """
650
+ user_id = user.get("id")
651
+ log.info(f"User {user_id} exporting project {project_id}")
652
+
653
+ try:
654
+ # Create ZIP file
655
+ zip_buffer = await project_service.export_project_as_zip(
656
+ db=db,
657
+ project_id=project_id,
658
+ user_id=user_id
659
+ )
660
+
661
+ # Get project for filename
662
+ project = project_service.get_project(db, project_id, user_id)
663
+ if not project:
664
+ raise HTTPException(
665
+ status_code=status.HTTP_404_NOT_FOUND,
666
+ detail="Project not found"
667
+ )
668
+
669
+ # Create safe filename
670
+ safe_name = project.name.replace(' ', '-').replace('/', '-')
671
+ filename = f"project-{safe_name}-{project_id[:8]}.zip"
672
+
673
+ log.info(f"Project {project_id} exported successfully")
674
+
675
+ return StreamingResponse(
676
+ zip_buffer,
677
+ media_type="application/zip",
678
+ headers={
679
+ "Content-Disposition": f'attachment; filename="{filename}"'
680
+ }
681
+ )
682
+
683
+ except ValueError as e:
684
+ log.warning(f"Validation error exporting project {project_id}: {e}")
685
+ raise HTTPException(
686
+ status_code=status.HTTP_404_NOT_FOUND,
687
+ detail=str(e)
688
+ )
689
+ except Exception as e:
690
+ log.error(f"Error exporting project {project_id}: {e}")
691
+ raise HTTPException(
692
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
693
+ detail="Failed to export project"
694
+ )
695
+
696
+
697
+ @router.post("/projects/import", response_model=ProjectImportResponse)
698
+ async def import_project(
699
+ file: UploadFile = File(...),
700
+ options: Optional[str] = Form(None),
701
+ user: dict = Depends(get_current_user),
702
+ project_service: ProjectService = Depends(get_project_service),
703
+ db: Session = Depends(get_db),
704
+ _: None = Depends(check_projects_enabled),
705
+ ):
706
+ """
707
+ Import project from ZIP file.
708
+ Handles name conflicts automatically.
709
+ """
710
+ user_id = user.get("id")
711
+ log.info(f"User {user_id} importing project from {file.filename}")
712
+
713
+ try:
714
+ # Validate file type
715
+ if not file.filename.endswith('.zip'):
716
+ raise HTTPException(
717
+ status_code=status.HTTP_400_BAD_REQUEST,
718
+ detail="File must be a ZIP archive"
719
+ )
720
+
721
+ # Parse options
722
+ import_options = ProjectImportOptions()
723
+ if options:
724
+ try:
725
+ options_dict = json.loads(options)
726
+ import_options = ProjectImportOptions(**options_dict)
727
+ except (json.JSONDecodeError, ValueError) as e:
728
+ log.warning(f"Invalid import options: {e}")
729
+
730
+ # Import project
731
+ project, artifacts_count, warnings = await project_service.import_project_from_zip(
732
+ db=db,
733
+ zip_file=file,
734
+ user_id=user_id,
735
+ preserve_name=import_options.preserve_name,
736
+ custom_name=import_options.custom_name,
737
+ )
738
+
739
+ log.info(
740
+ f"Project imported successfully: {project.id} with {artifacts_count} artifacts"
741
+ )
742
+
743
+ return ProjectImportResponse(
744
+ project_id=project.id,
745
+ name=project.name,
746
+ artifacts_imported=artifacts_count,
747
+ warnings=warnings,
748
+ )
749
+
750
+ except ValueError as e:
751
+ error_msg = str(e)
752
+ log.warning(f"Validation error importing project: {error_msg}")
753
+ # Check if this is a file size error
754
+ if "exceeds maximum" in error_msg.lower():
755
+ raise HTTPException(
756
+ status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
757
+ detail=error_msg
758
+ )
759
+ raise HTTPException(
760
+ status_code=status.HTTP_400_BAD_REQUEST,
761
+ detail=error_msg
762
+ )
763
+ except Exception as e:
764
+ log.error(f"Error importing project: {e}")
765
+ raise HTTPException(
766
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
767
+ detail="Failed to import project"
768
+ )