solace-agent-mesh 1.11.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.
Files changed (624) hide show
  1. solace_agent_mesh/__init__.py +0 -0
  2. solace_agent_mesh/agent/__init__.py +0 -0
  3. solace_agent_mesh/agent/adk/__init__.py +0 -0
  4. solace_agent_mesh/agent/adk/adk_llm.txt +226 -0
  5. solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
  6. solace_agent_mesh/agent/adk/alembic/README +74 -0
  7. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  8. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  9. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  10. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  11. solace_agent_mesh/agent/adk/app_llm_agent.py +52 -0
  12. solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
  13. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
  14. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +545 -0
  15. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +609 -0
  16. solace_agent_mesh/agent/adk/callbacks.py +2318 -0
  17. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +406 -0
  18. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +415 -0
  19. solace_agent_mesh/agent/adk/mcp_content_processor.py +666 -0
  20. solace_agent_mesh/agent/adk/models/lite_llm.py +1026 -0
  21. solace_agent_mesh/agent/adk/models/models_llm.txt +189 -0
  22. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +132 -0
  23. solace_agent_mesh/agent/adk/runner.py +390 -0
  24. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  25. solace_agent_mesh/agent/adk/services.py +468 -0
  26. solace_agent_mesh/agent/adk/setup.py +1325 -0
  27. solace_agent_mesh/agent/adk/stream_parser.py +415 -0
  28. solace_agent_mesh/agent/adk/tool_wrapper.py +165 -0
  29. solace_agent_mesh/agent/agent_llm.txt +369 -0
  30. solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
  31. solace_agent_mesh/agent/protocol/__init__.py +0 -0
  32. solace_agent_mesh/agent/protocol/event_handlers.py +2041 -0
  33. solace_agent_mesh/agent/protocol/protocol_llm.txt +81 -0
  34. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
  35. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  36. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  37. solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
  38. solace_agent_mesh/agent/proxies/a2a/app.py +56 -0
  39. solace_agent_mesh/agent/proxies/a2a/component.py +1585 -0
  40. solace_agent_mesh/agent/proxies/a2a/config.py +216 -0
  41. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  42. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  43. solace_agent_mesh/agent/proxies/base/app.py +100 -0
  44. solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
  45. solace_agent_mesh/agent/proxies/base/component.py +816 -0
  46. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  47. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +19 -0
  48. solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
  49. solace_agent_mesh/agent/sac/__init__.py +0 -0
  50. solace_agent_mesh/agent/sac/app.py +595 -0
  51. solace_agent_mesh/agent/sac/component.py +3668 -0
  52. solace_agent_mesh/agent/sac/patch_adk.py +103 -0
  53. solace_agent_mesh/agent/sac/sac_llm.txt +189 -0
  54. solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
  55. solace_agent_mesh/agent/sac/task_execution_context.py +415 -0
  56. solace_agent_mesh/agent/testing/__init__.py +3 -0
  57. solace_agent_mesh/agent/testing/debug_utils.py +135 -0
  58. solace_agent_mesh/agent/testing/testing_llm.txt +58 -0
  59. solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
  60. solace_agent_mesh/agent/tools/__init__.py +16 -0
  61. solace_agent_mesh/agent/tools/audio_tools.py +1740 -0
  62. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +2500 -0
  63. solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +244 -0
  64. solace_agent_mesh/agent/tools/dynamic_tool.py +396 -0
  65. solace_agent_mesh/agent/tools/general_agent_tools.py +572 -0
  66. solace_agent_mesh/agent/tools/image_tools.py +1185 -0
  67. solace_agent_mesh/agent/tools/peer_agent_tool.py +363 -0
  68. solace_agent_mesh/agent/tools/registry.py +38 -0
  69. solace_agent_mesh/agent/tools/test_tools.py +136 -0
  70. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  71. solace_agent_mesh/agent/tools/tool_config_types.py +93 -0
  72. solace_agent_mesh/agent/tools/tool_definition.py +53 -0
  73. solace_agent_mesh/agent/tools/tools_llm.txt +276 -0
  74. solace_agent_mesh/agent/tools/tools_llm_detail.txt +275 -0
  75. solace_agent_mesh/agent/tools/web_tools.py +392 -0
  76. solace_agent_mesh/agent/utils/__init__.py +0 -0
  77. solace_agent_mesh/agent/utils/artifact_helpers.py +1353 -0
  78. solace_agent_mesh/agent/utils/config_parser.py +49 -0
  79. solace_agent_mesh/agent/utils/context_helpers.py +77 -0
  80. solace_agent_mesh/agent/utils/utils_llm.txt +152 -0
  81. solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
  82. solace_agent_mesh/assets/docs/404.html +16 -0
  83. solace_agent_mesh/assets/docs/assets/css/styles.8162edfb.css +1 -0
  84. solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
  85. solace_agent_mesh/assets/docs/assets/images/sam-enterprise-credentials-b269f095349473118b2b33bdfcc40122.png +0 -0
  86. solace_agent_mesh/assets/docs/assets/js/032c2d61.f3d37824.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
  95. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
  96. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  101. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js.LICENSE.txt +13 -0
  102. solace_agent_mesh/assets/docs/assets/js/2334.1cf50a20.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/240a0364.9ad94d1b.js +1 -0
  104. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  105. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
  106. solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
  107. solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
  108. solace_agent_mesh/assets/docs/assets/js/3624.0eaa1fd0.js +1 -0
  109. solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
  110. solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
  111. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
  112. solace_agent_mesh/assets/docs/assets/js/3ac1795d.28b7c67b.js +1 -0
  113. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2ddc75c0.js +1 -0
  114. solace_agent_mesh/assets/docs/assets/js/41adc471.48b12a4e.js +1 -0
  115. solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
  116. solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
  117. solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
  118. solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
  119. solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
  120. solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
  121. solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
  122. solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
  123. solace_agent_mesh/assets/docs/assets/js/509e993c.a1fbf45a.js +1 -0
  124. solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
  125. solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
  126. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  127. solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
  128. solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
  129. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.90a87880.js +1 -0
  130. solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
  131. solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
  132. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  133. solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
  134. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  135. solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
  136. solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
  137. solace_agent_mesh/assets/docs/assets/js/66d4869e.b77431fc.js +1 -0
  138. solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
  139. solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
  140. solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.js +1 -0
  141. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  142. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  143. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  144. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  145. solace_agent_mesh/assets/docs/assets/js/6fdfefc7.99de744e.js +1 -0
  146. solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
  147. solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
  148. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  149. solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
  150. solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
  151. solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
  152. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  153. solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
  154. solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
  155. solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
  156. solace_agent_mesh/assets/docs/assets/js/81a99df0.2484b8d9.js +1 -0
  157. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  158. solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
  159. solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
  160. solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
  161. solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
  162. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js +2 -0
  163. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js.LICENSE.txt +61 -0
  164. solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
  165. solace_agent_mesh/assets/docs/assets/js/8731.6c1dbf0c.js +1 -0
  166. solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
  167. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  168. solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
  169. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  170. solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
  171. solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
  172. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  173. solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
  174. solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
  175. solace_agent_mesh/assets/docs/assets/js/9bb13469.b2333011.js +1 -0
  176. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  177. solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
  178. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  179. solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +1 -0
  180. solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
  181. solace_agent_mesh/assets/docs/assets/js/ad71b5ed.af3ecfd1.js +1 -0
  182. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  183. solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.js +1 -0
  184. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  185. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  186. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  187. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  188. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  189. solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
  190. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  191. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  192. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  193. solace_agent_mesh/assets/docs/assets/js/de5f4c65.e8241890.js +1 -0
  194. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  195. solace_agent_mesh/assets/docs/assets/js/e04b235d.52cb25ed.js +1 -0
  196. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.b1068f9b.js +1 -0
  197. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  198. solace_agent_mesh/assets/docs/assets/js/e6f9706b.4488e34c.js +1 -0
  199. solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
  200. solace_agent_mesh/assets/docs/assets/js/f284c35a.250993bf.js +1 -0
  201. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  202. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js +2 -0
  203. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js.LICENSE.txt +81 -0
  204. solace_agent_mesh/assets/docs/assets/js/runtime~main.9e0813a2.js +1 -0
  205. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +154 -0
  206. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +99 -0
  207. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +90 -0
  208. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +107 -0
  209. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +166 -0
  210. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +101 -0
  211. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +219 -0
  212. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +92 -0
  213. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +29 -0
  214. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +55 -0
  215. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +110 -0
  216. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  217. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  218. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +345 -0
  219. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  220. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +83 -0
  221. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +84 -0
  222. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
  223. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
  224. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +85 -0
  225. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +60 -0
  226. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  227. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +144 -0
  228. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +191 -0
  229. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +128 -0
  230. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
  231. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  232. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +34 -0
  233. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
  234. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +267 -0
  235. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +142 -0
  236. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +116 -0
  237. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +86 -0
  238. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +164 -0
  239. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +140 -0
  240. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +57 -0
  241. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
  242. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +102 -0
  243. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  244. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  245. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  246. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +37 -0
  247. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +86 -0
  248. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  249. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +247 -0
  250. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  251. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +184 -0
  252. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  253. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +75 -0
  254. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +54 -0
  255. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +85 -0
  256. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
  257. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
  258. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +78 -0
  259. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +25 -0
  260. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +78 -0
  261. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +160 -0
  262. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -0
  263. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  264. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  265. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +100 -0
  266. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +52 -0
  267. solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
  268. solace_agent_mesh/assets/docs/img/logo.png +0 -0
  269. solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
  270. solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
  271. solace_agent_mesh/assets/docs/img/sam-enterprise-credentials.png +0 -0
  272. solace_agent_mesh/assets/docs/img/solace-logo-text.svg +18 -0
  273. solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
  274. solace_agent_mesh/assets/docs/lunr-index-1765810064709.json +1 -0
  275. solace_agent_mesh/assets/docs/lunr-index.json +1 -0
  276. solace_agent_mesh/assets/docs/search-doc-1765810064709.json +1 -0
  277. solace_agent_mesh/assets/docs/search-doc.json +1 -0
  278. solace_agent_mesh/assets/docs/sitemap.xml +1 -0
  279. solace_agent_mesh/cli/__init__.py +1 -0
  280. solace_agent_mesh/cli/commands/__init__.py +0 -0
  281. solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
  282. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
  283. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +729 -0
  284. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
  285. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +102 -0
  286. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +114 -0
  287. solace_agent_mesh/cli/commands/docs_cmd.py +60 -0
  288. solace_agent_mesh/cli/commands/eval_cmd.py +46 -0
  289. solace_agent_mesh/cli/commands/init_cmd/__init__.py +439 -0
  290. solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
  291. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  292. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
  293. solace_agent_mesh/cli/commands/init_cmd/env_step.py +238 -0
  294. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
  295. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +464 -0
  296. solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
  297. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +119 -0
  298. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +215 -0
  299. solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +20 -0
  300. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +137 -0
  301. solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
  302. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +144 -0
  303. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +306 -0
  304. solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
  305. solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +175 -0
  306. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
  307. solace_agent_mesh/cli/commands/run_cmd.py +215 -0
  308. solace_agent_mesh/cli/main.py +52 -0
  309. solace_agent_mesh/cli/utils.py +262 -0
  310. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-Dj3JtK42.js +1 -0
  311. solace_agent_mesh/client/webui/frontend/static/assets/client-ZKk9kEJ5.js +25 -0
  312. solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
  313. solace_agent_mesh/client/webui/frontend/static/assets/main-BcUaNZ-Q.css +1 -0
  314. solace_agent_mesh/client/webui/frontend/static/assets/main-vjch4RYc.js +435 -0
  315. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BNV4kZN0.js +535 -0
  316. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +15 -0
  317. solace_agent_mesh/client/webui/frontend/static/index.html +16 -0
  318. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  319. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  320. solace_agent_mesh/common/__init__.py +1 -0
  321. solace_agent_mesh/common/a2a/__init__.py +241 -0
  322. solace_agent_mesh/common/a2a/a2a_llm.txt +175 -0
  323. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
  324. solace_agent_mesh/common/a2a/artifact.py +368 -0
  325. solace_agent_mesh/common/a2a/events.py +213 -0
  326. solace_agent_mesh/common/a2a/message.py +375 -0
  327. solace_agent_mesh/common/a2a/protocol.py +689 -0
  328. solace_agent_mesh/common/a2a/task.py +127 -0
  329. solace_agent_mesh/common/a2a/translation.py +655 -0
  330. solace_agent_mesh/common/a2a/types.py +55 -0
  331. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  332. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +445 -0
  333. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
  334. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  335. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +48 -0
  336. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  337. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +41 -0
  338. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +330 -0
  339. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  340. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +48 -0
  341. solace_agent_mesh/common/agent_registry.py +122 -0
  342. solace_agent_mesh/common/common_llm.txt +230 -0
  343. solace_agent_mesh/common/common_llm_detail.txt +2562 -0
  344. solace_agent_mesh/common/constants.py +6 -0
  345. solace_agent_mesh/common/data_parts.py +150 -0
  346. solace_agent_mesh/common/exceptions.py +49 -0
  347. solace_agent_mesh/common/middleware/__init__.py +12 -0
  348. solace_agent_mesh/common/middleware/config_resolver.py +132 -0
  349. solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
  350. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
  351. solace_agent_mesh/common/middleware/registry.py +127 -0
  352. solace_agent_mesh/common/oauth/__init__.py +17 -0
  353. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  354. solace_agent_mesh/common/oauth/utils.py +50 -0
  355. solace_agent_mesh/common/sac/__init__.py +0 -0
  356. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  357. solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
  358. solace_agent_mesh/common/sac/sam_component_base.py +730 -0
  359. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  360. solace_agent_mesh/common/sam_events/event_service.py +208 -0
  361. solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
  362. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
  363. solace_agent_mesh/common/services/__init__.py +4 -0
  364. solace_agent_mesh/common/services/employee_service.py +164 -0
  365. solace_agent_mesh/common/services/identity_service.py +134 -0
  366. solace_agent_mesh/common/services/providers/__init__.py +4 -0
  367. solace_agent_mesh/common/services/providers/local_file_identity_service.py +151 -0
  368. solace_agent_mesh/common/services/providers/providers_llm.txt +81 -0
  369. solace_agent_mesh/common/services/services_llm.txt +368 -0
  370. solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
  371. solace_agent_mesh/common/utils/__init__.py +7 -0
  372. solace_agent_mesh/common/utils/artifact_utils.py +31 -0
  373. solace_agent_mesh/common/utils/asyncio_macos_fix.py +88 -0
  374. solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
  375. solace_agent_mesh/common/utils/embeds/constants.py +56 -0
  376. solace_agent_mesh/common/utils/embeds/converter.py +447 -0
  377. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +220 -0
  378. solace_agent_mesh/common/utils/embeds/evaluators.py +395 -0
  379. solace_agent_mesh/common/utils/embeds/modifiers.py +793 -0
  380. solace_agent_mesh/common/utils/embeds/resolver.py +967 -0
  381. solace_agent_mesh/common/utils/embeds/types.py +23 -0
  382. solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
  383. solace_agent_mesh/common/utils/initializer.py +52 -0
  384. solace_agent_mesh/common/utils/log_formatters.py +64 -0
  385. solace_agent_mesh/common/utils/message_utils.py +80 -0
  386. solace_agent_mesh/common/utils/mime_helpers.py +172 -0
  387. solace_agent_mesh/common/utils/push_notification_auth.py +135 -0
  388. solace_agent_mesh/common/utils/pydantic_utils.py +159 -0
  389. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  390. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  391. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  392. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  393. solace_agent_mesh/common/utils/type_utils.py +28 -0
  394. solace_agent_mesh/common/utils/utils_llm.txt +335 -0
  395. solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
  396. solace_agent_mesh/config_portal/__init__.py +0 -0
  397. solace_agent_mesh/config_portal/backend/__init__.py +0 -0
  398. solace_agent_mesh/config_portal/backend/common.py +77 -0
  399. solace_agent_mesh/config_portal/backend/plugin_catalog/__init__.py +0 -0
  400. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +24 -0
  401. solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
  402. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +166 -0
  403. solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +521 -0
  404. solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +217 -0
  405. solace_agent_mesh/config_portal/backend/server.py +644 -0
  406. solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
  407. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DiOiAjzL.js +103 -0
  408. solace_agent_mesh/config_portal/frontend/static/client/assets/components-Rk0n-9cK.js +140 -0
  409. solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-mvZjNKiz.js +19 -0
  410. solace_agent_mesh/config_portal/frontend/static/client/assets/index-DzNKzXrc.js +68 -0
  411. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-ba77705e.js +1 -0
  412. solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
  413. solace_agent_mesh/config_portal/frontend/static/client/assets/root-V2BeTIUc.js +10 -0
  414. solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
  415. solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
  416. solace_agent_mesh/core_a2a/__init__.py +1 -0
  417. solace_agent_mesh/core_a2a/core_a2a_llm.txt +90 -0
  418. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
  419. solace_agent_mesh/core_a2a/service.py +307 -0
  420. solace_agent_mesh/evaluation/__init__.py +0 -0
  421. solace_agent_mesh/evaluation/evaluator.py +691 -0
  422. solace_agent_mesh/evaluation/message_organizer.py +553 -0
  423. solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
  424. solace_agent_mesh/evaluation/report/chart_section.html +141 -0
  425. solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
  426. solace_agent_mesh/evaluation/report/modal.html +59 -0
  427. solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
  428. solace_agent_mesh/evaluation/report/modal_script.js +296 -0
  429. solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
  430. solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
  431. solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
  432. solace_agent_mesh/evaluation/report/templates/header.html +340 -0
  433. solace_agent_mesh/evaluation/report_data_processor.py +970 -0
  434. solace_agent_mesh/evaluation/report_generator.py +607 -0
  435. solace_agent_mesh/evaluation/run.py +954 -0
  436. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  437. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  438. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  439. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  440. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  441. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  442. solace_agent_mesh/evaluation/subscriber.py +776 -0
  443. solace_agent_mesh/evaluation/summary_builder.py +880 -0
  444. solace_agent_mesh/gateway/__init__.py +0 -0
  445. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  446. solace_agent_mesh/gateway/adapter/base.py +143 -0
  447. solace_agent_mesh/gateway/adapter/types.py +221 -0
  448. solace_agent_mesh/gateway/base/__init__.py +1 -0
  449. solace_agent_mesh/gateway/base/app.py +345 -0
  450. solace_agent_mesh/gateway/base/base_llm.txt +226 -0
  451. solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
  452. solace_agent_mesh/gateway/base/component.py +2030 -0
  453. solace_agent_mesh/gateway/base/task_context.py +75 -0
  454. solace_agent_mesh/gateway/gateway_llm.txt +369 -0
  455. solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
  456. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  457. solace_agent_mesh/gateway/generic/app.py +50 -0
  458. solace_agent_mesh/gateway/generic/component.py +727 -0
  459. solace_agent_mesh/gateway/http_sse/__init__.py +0 -0
  460. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +345 -0
  461. solace_agent_mesh/gateway/http_sse/alembic/env.py +87 -0
  462. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  463. solace_agent_mesh/gateway/http_sse/alembic/versions/20250910_d5b3f8f2e9a0_create_initial_database.py +58 -0
  464. solace_agent_mesh/gateway/http_sse/alembic/versions/20250911_b1c2d3e4f5g6_add_database_indexes.py +83 -0
  465. solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +412 -0
  466. solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
  467. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  468. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  469. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  470. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  471. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  472. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  473. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  474. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  475. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  476. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +161 -0
  477. solace_agent_mesh/gateway/http_sse/alembic.ini +109 -0
  478. solace_agent_mesh/gateway/http_sse/app.py +351 -0
  479. solace_agent_mesh/gateway/http_sse/component.py +2360 -0
  480. solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
  481. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +105 -0
  482. solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
  483. solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +110 -0
  484. solace_agent_mesh/gateway/http_sse/dependencies.py +653 -0
  485. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +299 -0
  486. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
  487. solace_agent_mesh/gateway/http_sse/main.py +789 -0
  488. solace_agent_mesh/gateway/http_sse/repository/__init__.py +46 -0
  489. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +102 -0
  490. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +11 -0
  491. solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
  492. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +221 -0
  493. solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
  494. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  495. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  496. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +66 -0
  497. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -0
  498. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +32 -0
  499. solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
  500. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +125 -0
  501. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +239 -0
  502. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +34 -0
  503. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  504. solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
  505. solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
  506. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +257 -0
  507. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  508. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  509. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  510. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +53 -0
  511. solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
  512. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +39 -0
  513. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  514. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  515. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +308 -0
  516. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +268 -0
  517. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +248 -0
  518. solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
  519. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +74 -0
  520. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1137 -0
  521. solace_agent_mesh/gateway/http_sse/routers/auth.py +311 -0
  522. solace_agent_mesh/gateway/http_sse/routers/config.py +371 -0
  523. solace_agent_mesh/gateway/http_sse/routers/dto/__init__.py +10 -0
  524. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +450 -0
  525. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  526. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  527. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +15 -0
  528. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  529. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +133 -0
  530. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +33 -0
  531. solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
  532. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +18 -0
  533. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
  534. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  535. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +123 -0
  536. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +33 -0
  537. solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
  538. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  539. solace_agent_mesh/gateway/http_sse/routers/feedback.py +168 -0
  540. solace_agent_mesh/gateway/http_sse/routers/people.py +38 -0
  541. solace_agent_mesh/gateway/http_sse/routers/projects.py +767 -0
  542. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1415 -0
  543. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +312 -0
  544. solace_agent_mesh/gateway/http_sse/routers/sessions.py +634 -0
  545. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  546. solace_agent_mesh/gateway/http_sse/routers/sse.py +230 -0
  547. solace_agent_mesh/gateway/http_sse/routers/tasks.py +1089 -0
  548. solace_agent_mesh/gateway/http_sse/routers/users.py +83 -0
  549. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  550. solace_agent_mesh/gateway/http_sse/routers/visualization.py +1220 -0
  551. solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
  552. solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +71 -0
  553. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  554. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  555. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +273 -0
  556. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +250 -0
  557. solace_agent_mesh/gateway/http_sse/services/people_service.py +78 -0
  558. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  559. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  560. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +303 -0
  561. solace_agent_mesh/gateway/http_sse/services/session_service.py +702 -0
  562. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +593 -0
  563. solace_agent_mesh/gateway/http_sse/services/task_service.py +119 -0
  564. solace_agent_mesh/gateway/http_sse/session_manager.py +219 -0
  565. solace_agent_mesh/gateway/http_sse/shared/__init__.py +146 -0
  566. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  567. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +252 -0
  568. solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
  569. solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
  570. solace_agent_mesh/gateway/http_sse/shared/enums.py +40 -0
  571. solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
  572. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +217 -0
  573. solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
  574. solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
  575. solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
  576. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +319 -0
  577. solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
  578. solace_agent_mesh/gateway/http_sse/shared/types.py +50 -0
  579. solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
  580. solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +88 -0
  581. solace_agent_mesh/gateway/http_sse/sse_manager.py +491 -0
  582. solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
  583. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  584. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +72 -0
  585. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
  586. solace_agent_mesh/llm.txt +228 -0
  587. solace_agent_mesh/llm_detail.txt +2835 -0
  588. solace_agent_mesh/services/__init__.py +0 -0
  589. solace_agent_mesh/services/platform/__init__.py +18 -0
  590. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  591. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  592. solace_agent_mesh/services/platform/alembic.ini +109 -0
  593. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  594. solace_agent_mesh/services/platform/api/dependencies.py +147 -0
  595. solace_agent_mesh/services/platform/api/main.py +280 -0
  596. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  597. solace_agent_mesh/services/platform/api/routers/__init__.py +24 -0
  598. solace_agent_mesh/services/platform/app.py +114 -0
  599. solace_agent_mesh/services/platform/component.py +235 -0
  600. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  601. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
  602. solace_agent_mesh/templates/agent_template.yaml +53 -0
  603. solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
  604. solace_agent_mesh/templates/gateway_app_template.py +75 -0
  605. solace_agent_mesh/templates/gateway_component_template.py +484 -0
  606. solace_agent_mesh/templates/gateway_config_template.yaml +38 -0
  607. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  608. solace_agent_mesh/templates/main_orchestrator.yaml +66 -0
  609. solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
  610. solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
  611. solace_agent_mesh/templates/plugin_custom_template.py +10 -0
  612. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +60 -0
  613. solace_agent_mesh/templates/plugin_pyproject_template.toml +32 -0
  614. solace_agent_mesh/templates/plugin_readme_template.md +12 -0
  615. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  616. solace_agent_mesh/templates/plugin_tools_template.py +224 -0
  617. solace_agent_mesh/templates/shared_config.yaml +112 -0
  618. solace_agent_mesh/templates/templates_llm.txt +147 -0
  619. solace_agent_mesh/templates/webui.yaml +177 -0
  620. solace_agent_mesh-1.11.2.dist-info/METADATA +504 -0
  621. solace_agent_mesh-1.11.2.dist-info/RECORD +624 -0
  622. solace_agent_mesh-1.11.2.dist-info/WHEEL +4 -0
  623. solace_agent_mesh-1.11.2.dist-info/entry_points.txt +3 -0
  624. solace_agent_mesh-1.11.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,1415 @@
1
+ """
2
+ Prompts API router for prompt library feature.
3
+ """
4
+
5
+ import uuid
6
+ from typing import List, Optional, Dict, Any, Literal
7
+ from fastapi import APIRouter, HTTPException, Depends, Query, status
8
+ from sqlalchemy.orm import Session
9
+ from sqlalchemy import or_, func
10
+
11
+ from ..services.prompt_builder_assistant import PromptBuilderAssistant
12
+
13
+ from ..dependencies import get_db, get_user_id, get_sac_component, get_api_config, get_user_display_name
14
+ from ..repository.models import PromptGroupModel, PromptModel, PromptGroupUserModel
15
+ from .dto.prompt_dto import (
16
+ PromptGroupCreate,
17
+ PromptGroupUpdate,
18
+ PromptGroupResponse,
19
+ PromptGroupListResponse,
20
+ PromptCreate,
21
+ PromptResponse,
22
+ PromptBuilderChatRequest,
23
+ PromptBuilderChatResponse,
24
+ PromptExportResponse,
25
+ PromptExportData,
26
+ PromptExportMetadata,
27
+ PromptImportRequest,
28
+ PromptImportResponse,
29
+ )
30
+ from ..shared import now_epoch_ms
31
+ from solace_ai_connector.common.log import log
32
+
33
+ from typing import TYPE_CHECKING
34
+
35
+ if TYPE_CHECKING:
36
+ from ..component import WebUIBackendComponent
37
+
38
+ router = APIRouter()
39
+
40
+
41
+ # ============================================================================
42
+ # Permission Helper Functions
43
+ # ============================================================================
44
+
45
+ def get_user_role(db: Session, group_id: str, user_id: str) -> Optional[Literal["owner", "editor", "viewer"]]:
46
+ """
47
+ Get the user's role for a prompt group.
48
+ Returns 'owner' if user owns the group, or their assigned role from prompt_group_users.
49
+ Returns None if user has no access.
50
+ """
51
+ # Check if user is the owner
52
+ group = db.query(PromptGroupModel).filter(
53
+ PromptGroupModel.id == group_id,
54
+ PromptGroupModel.user_id == user_id
55
+ ).first()
56
+
57
+ if group:
58
+ return "owner"
59
+
60
+ # Check if user has shared access
61
+ share = db.query(PromptGroupUserModel).filter(
62
+ PromptGroupUserModel.prompt_group_id == group_id,
63
+ PromptGroupUserModel.user_id == user_id
64
+ ).first()
65
+
66
+ if share:
67
+ return share.role
68
+
69
+ return None
70
+
71
+
72
+ def check_permission(
73
+ db: Session,
74
+ group_id: str,
75
+ user_id: str,
76
+ required_permission: Literal["read", "write", "delete"]
77
+ ) -> None:
78
+ """
79
+ Check if user has the required permission for a prompt group.
80
+ Raises HTTPException if permission is denied.
81
+
82
+ Permission levels:
83
+ - owner: read, write, delete
84
+ - editor: read, write, delete
85
+ - viewer: read only
86
+ """
87
+ role = get_user_role(db, group_id, user_id)
88
+
89
+ if role is None:
90
+ raise HTTPException(
91
+ status_code=status.HTTP_404_NOT_FOUND,
92
+ detail="Prompt group not found"
93
+ )
94
+
95
+ # Check permissions based on role
96
+ if required_permission == "read":
97
+ # All roles can read
98
+ return
99
+
100
+ if required_permission in ("write", "delete"):
101
+ if role == "viewer":
102
+ raise HTTPException(
103
+ status_code=status.HTTP_403_FORBIDDEN,
104
+ detail=f"Viewer role cannot {required_permission} this prompt group"
105
+ )
106
+ # owner and editor can write and delete
107
+ return
108
+
109
+
110
+ def check_prompts_enabled(
111
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
112
+ api_config: Dict[str, Any] = Depends(get_api_config),
113
+ ) -> None:
114
+ """
115
+ Dependency to check if prompts feature is enabled.
116
+ Raises HTTPException if prompts are disabled.
117
+ """
118
+ # Check if persistence is enabled (required for prompts)
119
+ persistence_enabled = api_config.get("persistence_enabled", False)
120
+ if not persistence_enabled:
121
+ log.warning("Prompts API called but persistence is not enabled")
122
+ raise HTTPException(
123
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
124
+ detail="Prompts feature requires persistence to be enabled. Please configure session_service.type as 'sql'."
125
+ )
126
+
127
+ # Check explicit prompt_library config
128
+ prompt_library_config = component.get_config("prompt_library", {})
129
+ if isinstance(prompt_library_config, dict):
130
+ prompts_explicitly_enabled = prompt_library_config.get("enabled", True)
131
+ if not prompts_explicitly_enabled:
132
+ log.warning("Prompts API called but prompt library is explicitly disabled in config")
133
+ raise HTTPException(
134
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
135
+ detail="Prompt library feature is disabled. Please enable it in the configuration."
136
+ )
137
+
138
+ # Check frontend_feature_enablement override
139
+ feature_flags = component.get_config("frontend_feature_enablement", {})
140
+ if "promptLibrary" in feature_flags:
141
+ prompts_flag = feature_flags.get("promptLibrary", True)
142
+ if not prompts_flag:
143
+ log.warning("Prompts API called but prompts are disabled via feature flag")
144
+ raise HTTPException(
145
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
146
+ detail="Prompt library feature is disabled via feature flag."
147
+ )
148
+
149
+
150
+ # ============================================================================
151
+ # Prompt Groups Endpoints
152
+ # ============================================================================
153
+
154
+ @router.get("/groups/all", response_model=List[PromptGroupResponse])
155
+ async def get_all_prompt_groups(
156
+ db: Session = Depends(get_db),
157
+ user_id: str = Depends(get_user_id),
158
+ _: None = Depends(check_prompts_enabled),
159
+ ):
160
+ """
161
+ Get all prompt groups for quick access (used by "/" command).
162
+ Returns all groups owned by user or shared with them.
163
+ """
164
+ try:
165
+ # Get groups owned by user
166
+ owned_groups = db.query(PromptGroupModel).filter(
167
+ PromptGroupModel.user_id == user_id
168
+ ).all()
169
+
170
+ # Get groups shared with user
171
+ shared_group_ids = db.query(PromptGroupUserModel.prompt_group_id).filter(
172
+ PromptGroupUserModel.user_id == user_id
173
+ ).all()
174
+ shared_group_ids = [gid[0] for gid in shared_group_ids]
175
+
176
+ shared_groups = []
177
+ if shared_group_ids:
178
+ shared_groups = db.query(PromptGroupModel).filter(
179
+ PromptGroupModel.id.in_(shared_group_ids)
180
+ ).all()
181
+
182
+ # Combine and sort
183
+ all_groups = owned_groups + shared_groups
184
+ groups = sorted(
185
+ all_groups,
186
+ key=lambda g: (not g.is_pinned, -g.created_at)
187
+ )
188
+
189
+ # Fetch production prompts for each group
190
+ result = []
191
+ for group in groups:
192
+ try:
193
+ # Truncate fields that exceed max length to prevent validation errors
194
+ name = group.name[:255] if group.name and len(group.name) > 255 else group.name
195
+ description = group.description[:1000] if group.description and len(group.description) > 1000 else group.description
196
+ category = group.category[:100] if group.category and len(group.category) > 100 else group.category
197
+ command = group.command[:50] if group.command and len(group.command) > 50 else group.command
198
+ author_name = group.author_name[:255] if group.author_name and len(group.author_name) > 255 else group.author_name
199
+
200
+ group_dict = {
201
+ "id": group.id,
202
+ "name": name,
203
+ "description": description,
204
+ "category": category,
205
+ "command": command,
206
+ "user_id": group.user_id,
207
+ "author_name": author_name,
208
+ "production_prompt_id": group.production_prompt_id,
209
+ "is_shared": group.is_shared,
210
+ "is_pinned": group.is_pinned,
211
+ "created_at": group.created_at,
212
+ "updated_at": group.updated_at,
213
+ "production_prompt": None,
214
+ }
215
+
216
+ if group.production_prompt_id:
217
+ prod_prompt = db.query(PromptModel).filter(
218
+ PromptModel.id == group.production_prompt_id
219
+ ).first()
220
+ if prod_prompt:
221
+ group_dict["production_prompt"] = {
222
+ "id": prod_prompt.id,
223
+ "prompt_text": prod_prompt.prompt_text,
224
+ "group_id": prod_prompt.group_id,
225
+ "user_id": prod_prompt.user_id,
226
+ "version": prod_prompt.version,
227
+ "name": prod_prompt.name,
228
+ "description": prod_prompt.description,
229
+ "category": prod_prompt.category,
230
+ "command": prod_prompt.command,
231
+ "created_at": prod_prompt.created_at,
232
+ "updated_at": prod_prompt.updated_at,
233
+ }
234
+
235
+ result.append(PromptGroupResponse(**group_dict))
236
+ except Exception as e:
237
+ # Log the error but continue processing other groups
238
+ log.warning(f"Skipping invalid prompt group {group.id}: {e}")
239
+ continue
240
+
241
+ return result
242
+ except Exception as e:
243
+ log.error(f"Error fetching all prompt groups: {e}")
244
+ raise HTTPException(
245
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
246
+ detail="Failed to fetch prompt groups"
247
+ )
248
+
249
+
250
+ @router.get("/groups", response_model=PromptGroupListResponse)
251
+ async def list_prompt_groups(
252
+ skip: int = Query(0, ge=0),
253
+ limit: int = Query(100, ge=1, le=100),
254
+ category: Optional[str] = None,
255
+ search: Optional[str] = None,
256
+ db: Session = Depends(get_db),
257
+ user_id: str = Depends(get_user_id),
258
+ _: None = Depends(check_prompts_enabled),
259
+ ):
260
+ """
261
+ List all prompt groups accessible to the user (owned or shared).
262
+ Supports pagination, category filtering, and text search.
263
+ """
264
+ try:
265
+ # Get shared group IDs
266
+ shared_group_ids = db.query(PromptGroupUserModel.prompt_group_id).filter(
267
+ PromptGroupUserModel.user_id == user_id
268
+ ).all()
269
+ shared_group_ids = [gid[0] for gid in shared_group_ids]
270
+
271
+ # Build query for owned or shared groups
272
+ query = db.query(PromptGroupModel).filter(
273
+ or_(
274
+ PromptGroupModel.user_id == user_id,
275
+ PromptGroupModel.id.in_(shared_group_ids) if shared_group_ids else False
276
+ )
277
+ )
278
+
279
+ if category:
280
+ query = query.filter(PromptGroupModel.category == category)
281
+
282
+ if search:
283
+ search_pattern = f"%{search}%"
284
+ query = query.filter(
285
+ or_(
286
+ PromptGroupModel.name.ilike(search_pattern),
287
+ PromptGroupModel.description.ilike(search_pattern),
288
+ PromptGroupModel.command.ilike(search_pattern)
289
+ )
290
+ )
291
+
292
+ total = query.count()
293
+ groups = query.order_by(
294
+ PromptGroupModel.is_pinned.desc(), # Pinned first
295
+ PromptGroupModel.created_at.desc()
296
+ ).offset(skip).limit(limit).all()
297
+
298
+ # Fetch production prompts for each group
299
+ result_groups = []
300
+ for group in groups:
301
+ try:
302
+ # Truncate fields that exceed max length to prevent validation errors
303
+ name = group.name[:255] if group.name and len(group.name) > 255 else group.name
304
+ description = group.description[:1000] if group.description and len(group.description) > 1000 else group.description
305
+ category = group.category[:100] if group.category and len(group.category) > 100 else group.category
306
+ command = group.command[:50] if group.command and len(group.command) > 50 else group.command
307
+ author_name = group.author_name[:255] if group.author_name and len(group.author_name) > 255 else group.author_name
308
+
309
+ group_dict = {
310
+ "id": group.id,
311
+ "name": name,
312
+ "description": description,
313
+ "category": category,
314
+ "command": command,
315
+ "user_id": group.user_id,
316
+ "author_name": author_name,
317
+ "production_prompt_id": group.production_prompt_id,
318
+ "is_shared": group.is_shared,
319
+ "is_pinned": group.is_pinned,
320
+ "created_at": group.created_at,
321
+ "updated_at": group.updated_at,
322
+ "production_prompt": None,
323
+ }
324
+
325
+ if group.production_prompt_id:
326
+ prod_prompt = db.query(PromptModel).filter(
327
+ PromptModel.id == group.production_prompt_id
328
+ ).first()
329
+ if prod_prompt:
330
+ group_dict["production_prompt"] = {
331
+ "id": prod_prompt.id,
332
+ "prompt_text": prod_prompt.prompt_text,
333
+ "group_id": prod_prompt.group_id,
334
+ "user_id": prod_prompt.user_id,
335
+ "version": prod_prompt.version,
336
+ "name": prod_prompt.name,
337
+ "description": prod_prompt.description,
338
+ "category": prod_prompt.category,
339
+ "command": prod_prompt.command,
340
+ "created_at": prod_prompt.created_at,
341
+ "updated_at": prod_prompt.updated_at,
342
+ }
343
+
344
+ result_groups.append(PromptGroupResponse(**group_dict))
345
+ except Exception as e:
346
+ # Log the error but continue processing other groups
347
+ log.warning(f"Skipping invalid prompt group {group.id}: {e}")
348
+ continue
349
+
350
+ return PromptGroupListResponse(
351
+ groups=result_groups,
352
+ total=total,
353
+ skip=skip,
354
+ limit=limit,
355
+ )
356
+ except Exception as e:
357
+ log.error(f"Error listing prompt groups: {e}")
358
+ raise HTTPException(
359
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
360
+ detail="Failed to list prompt groups"
361
+ )
362
+
363
+
364
+ @router.get("/groups/{group_id}", response_model=PromptGroupResponse)
365
+ async def get_prompt_group(
366
+ group_id: str,
367
+ db: Session = Depends(get_db),
368
+ user_id: str = Depends(get_user_id),
369
+ _: None = Depends(check_prompts_enabled),
370
+ ):
371
+ """Get a specific prompt group by ID (requires read permission)."""
372
+ try:
373
+ # Check read permission (works for owner, editor, viewer)
374
+ check_permission(db, group_id, user_id, "read")
375
+
376
+ group = db.query(PromptGroupModel).filter(
377
+ PromptGroupModel.id == group_id
378
+ ).first()
379
+
380
+ if not group:
381
+ raise HTTPException(
382
+ status_code=status.HTTP_404_NOT_FOUND,
383
+ detail="Prompt group not found"
384
+ )
385
+
386
+ group_dict = {
387
+ "id": group.id,
388
+ "name": group.name,
389
+ "description": group.description,
390
+ "category": group.category,
391
+ "command": group.command,
392
+ "user_id": group.user_id,
393
+ "author_name": group.author_name,
394
+ "production_prompt_id": group.production_prompt_id,
395
+ "is_shared": group.is_shared,
396
+ "is_pinned": group.is_pinned,
397
+ "created_at": group.created_at,
398
+ "updated_at": group.updated_at,
399
+ "production_prompt": None,
400
+ }
401
+
402
+ if group.production_prompt_id:
403
+ prod_prompt = db.query(PromptModel).filter(
404
+ PromptModel.id == group.production_prompt_id
405
+ ).first()
406
+ if prod_prompt:
407
+ group_dict["production_prompt"] = {
408
+ "id": prod_prompt.id,
409
+ "prompt_text": prod_prompt.prompt_text,
410
+ "group_id": prod_prompt.group_id,
411
+ "user_id": prod_prompt.user_id,
412
+ "version": prod_prompt.version,
413
+ "name": prod_prompt.name,
414
+ "description": prod_prompt.description,
415
+ "category": prod_prompt.category,
416
+ "command": prod_prompt.command,
417
+ "created_at": prod_prompt.created_at,
418
+ "updated_at": prod_prompt.updated_at,
419
+ }
420
+
421
+ return PromptGroupResponse(**group_dict)
422
+ except HTTPException:
423
+ raise
424
+ except Exception as e:
425
+ log.error(f"Error fetching prompt group {group_id}: {e}")
426
+ raise HTTPException(
427
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
428
+ detail="Failed to fetch prompt group"
429
+ )
430
+
431
+
432
+ @router.post("/groups", response_model=PromptGroupResponse, status_code=status.HTTP_201_CREATED)
433
+ async def create_prompt_group(
434
+ group_data: PromptGroupCreate,
435
+ db: Session = Depends(get_db),
436
+ user_id: str = Depends(get_user_id),
437
+ user_display_name: str = Depends(get_user_display_name),
438
+ _: None = Depends(check_prompts_enabled),
439
+ ):
440
+ """
441
+ Create a new prompt group with an initial prompt.
442
+ The initial prompt is automatically set as the production version.
443
+ """
444
+ try:
445
+ # Check if command already exists
446
+ if group_data.command:
447
+ existing = db.query(PromptGroupModel).filter(
448
+ PromptGroupModel.command == group_data.command,
449
+ PromptGroupModel.user_id == user_id,
450
+ ).first()
451
+ if existing:
452
+ raise HTTPException(
453
+ status_code=status.HTTP_400_BAD_REQUEST,
454
+ detail=f"Command '/{group_data.command}' already exists"
455
+ )
456
+
457
+ # Create prompt group
458
+ group_id = str(uuid.uuid4())
459
+ now_ms = now_epoch_ms()
460
+
461
+ new_group = PromptGroupModel(
462
+ id=group_id,
463
+ name=group_data.name,
464
+ description=group_data.description,
465
+ category=group_data.category,
466
+ command=group_data.command,
467
+ user_id=user_id,
468
+ author_name=user_display_name,
469
+ production_prompt_id=None,
470
+ is_shared=False,
471
+ is_pinned=False,
472
+ created_at=now_ms,
473
+ updated_at=now_ms,
474
+ )
475
+ db.add(new_group)
476
+ db.flush()
477
+
478
+ # Create initial prompt with versioned metadata
479
+ prompt_id = str(uuid.uuid4())
480
+ new_prompt = PromptModel(
481
+ id=prompt_id,
482
+ prompt_text=group_data.initial_prompt,
483
+ name=group_data.name,
484
+ description=group_data.description,
485
+ category=group_data.category,
486
+ command=group_data.command,
487
+ group_id=group_id,
488
+ user_id=user_id,
489
+ version=1,
490
+ created_at=now_ms,
491
+ updated_at=now_ms,
492
+ )
493
+ db.add(new_prompt)
494
+ db.flush()
495
+
496
+ # Set production prompt reference
497
+ new_group.production_prompt_id = prompt_id
498
+ new_group.updated_at = now_epoch_ms()
499
+
500
+ db.commit()
501
+ db.refresh(new_group)
502
+
503
+ # Build response
504
+ return PromptGroupResponse(
505
+ id=new_group.id,
506
+ name=new_group.name,
507
+ description=new_group.description,
508
+ category=new_group.category,
509
+ command=new_group.command,
510
+ user_id=new_group.user_id,
511
+ author_name=new_group.author_name,
512
+ production_prompt_id=new_group.production_prompt_id,
513
+ is_shared=new_group.is_shared,
514
+ is_pinned=new_group.is_pinned,
515
+ created_at=new_group.created_at,
516
+ updated_at=new_group.updated_at,
517
+ production_prompt=PromptResponse(
518
+ id=new_prompt.id,
519
+ prompt_text=new_prompt.prompt_text,
520
+ group_id=new_prompt.group_id,
521
+ user_id=new_prompt.user_id,
522
+ version=new_prompt.version,
523
+ name=new_prompt.name,
524
+ description=new_prompt.description,
525
+ category=new_prompt.category,
526
+ command=new_prompt.command,
527
+ created_at=new_prompt.created_at,
528
+ updated_at=new_prompt.updated_at,
529
+ ),
530
+ )
531
+ except HTTPException:
532
+ db.rollback()
533
+ raise
534
+ except Exception as e:
535
+ db.rollback()
536
+ log.error(f"Error creating prompt group: {e}")
537
+ raise HTTPException(
538
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
539
+ detail="Failed to create prompt group"
540
+ )
541
+
542
+
543
+ @router.patch("/groups/{group_id}", response_model=PromptGroupResponse)
544
+ async def update_prompt_group(
545
+ group_id: str,
546
+ group_data: PromptGroupUpdate,
547
+ db: Session = Depends(get_db),
548
+ user_id: str = Depends(get_user_id),
549
+ _: None = Depends(check_prompts_enabled),
550
+ ):
551
+ """Update a prompt group's metadata (requires write permission - owner or editor only)."""
552
+ try:
553
+ # Check write permission (owner or editor only, not viewer)
554
+ check_permission(db, group_id, user_id, "write")
555
+
556
+ group = db.query(PromptGroupModel).filter(
557
+ PromptGroupModel.id == group_id
558
+ ).first()
559
+
560
+ if not group:
561
+ raise HTTPException(
562
+ status_code=status.HTTP_404_NOT_FOUND,
563
+ detail="Prompt group not found"
564
+ )
565
+
566
+ # Check command uniqueness if being updated
567
+ if group_data.command and group_data.command != group.command:
568
+ existing = db.query(PromptGroupModel).filter(
569
+ PromptGroupModel.command == group_data.command,
570
+ PromptGroupModel.user_id == user_id,
571
+ PromptGroupModel.id != group_id,
572
+ ).first()
573
+ if existing:
574
+ raise HTTPException(
575
+ status_code=status.HTTP_400_BAD_REQUEST,
576
+ detail=f"Command '/{group_data.command}' already exists"
577
+ )
578
+
579
+ # Update fields (excluding initial_prompt which is handled separately)
580
+ update_data = group_data.dict(exclude_unset=True, exclude={'initial_prompt'})
581
+ for field, value in update_data.items():
582
+ setattr(group, field, value)
583
+
584
+ # If initial_prompt is provided, always create a new version
585
+ # This happens when user clicks "Save New Version" button
586
+ if hasattr(group_data, 'initial_prompt') and group_data.initial_prompt:
587
+ # Get next version number
588
+ max_version_result = db.query(func.max(PromptModel.version)).filter(
589
+ PromptModel.group_id == group_id
590
+ ).scalar()
591
+
592
+ next_version = (max_version_result + 1) if max_version_result else 1
593
+
594
+ # Create new prompt version
595
+ prompt_id = str(uuid.uuid4())
596
+ now_ms = now_epoch_ms()
597
+
598
+ # Create new prompt version with current metadata
599
+ new_prompt = PromptModel(
600
+ id=prompt_id,
601
+ prompt_text=group_data.initial_prompt,
602
+ name=group_data.name if group_data.name else group.name,
603
+ description=group_data.description if group_data.description is not None else group.description,
604
+ category=group_data.category if group_data.category is not None else group.category,
605
+ command=group_data.command if group_data.command is not None else group.command,
606
+ group_id=group_id,
607
+ user_id=user_id,
608
+ version=next_version,
609
+ created_at=now_ms,
610
+ updated_at=now_ms,
611
+ )
612
+ db.add(new_prompt)
613
+ db.flush()
614
+
615
+ # Update production prompt reference
616
+ group.production_prompt_id = prompt_id
617
+
618
+ group.updated_at = now_epoch_ms()
619
+
620
+ db.commit()
621
+ db.refresh(group)
622
+
623
+ # Build response
624
+ group_dict = {
625
+ "id": group.id,
626
+ "name": group.name,
627
+ "description": group.description,
628
+ "category": group.category,
629
+ "command": group.command,
630
+ "user_id": group.user_id,
631
+ "author_name": group.author_name,
632
+ "production_prompt_id": group.production_prompt_id,
633
+ "is_shared": group.is_shared,
634
+ "is_pinned": group.is_pinned,
635
+ "created_at": group.created_at,
636
+ "updated_at": group.updated_at,
637
+ "production_prompt": None,
638
+ }
639
+
640
+ if group.production_prompt_id:
641
+ prod_prompt = db.query(PromptModel).filter(
642
+ PromptModel.id == group.production_prompt_id
643
+ ).first()
644
+ if prod_prompt:
645
+ group_dict["production_prompt"] = {
646
+ "id": prod_prompt.id,
647
+ "prompt_text": prod_prompt.prompt_text,
648
+ "group_id": prod_prompt.group_id,
649
+ "user_id": prod_prompt.user_id,
650
+ "version": prod_prompt.version,
651
+ "name": prod_prompt.name,
652
+ "description": prod_prompt.description,
653
+ "category": prod_prompt.category,
654
+ "command": prod_prompt.command,
655
+ "created_at": prod_prompt.created_at,
656
+ "updated_at": prod_prompt.updated_at,
657
+ }
658
+
659
+ return PromptGroupResponse(**group_dict)
660
+ except HTTPException:
661
+ db.rollback()
662
+ raise
663
+ except Exception as e:
664
+ db.rollback()
665
+ log.error(f"Error updating prompt group {group_id}: {e}")
666
+ raise HTTPException(
667
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
668
+ detail="Failed to update prompt group"
669
+ )
670
+
671
+
672
+ @router.patch("/groups/{group_id}/pin", response_model=PromptGroupResponse)
673
+ async def toggle_pin_prompt(
674
+ group_id: str,
675
+ db: Session = Depends(get_db),
676
+ user_id: str = Depends(get_user_id),
677
+ _: None = Depends(check_prompts_enabled),
678
+ ):
679
+ """Toggle pin status for a prompt group (requires write permission)."""
680
+ try:
681
+ # Check write permission
682
+ check_permission(db, group_id, user_id, "write")
683
+
684
+ group = db.query(PromptGroupModel).filter(
685
+ PromptGroupModel.id == group_id
686
+ ).first()
687
+
688
+ if not group:
689
+ raise HTTPException(
690
+ status_code=status.HTTP_404_NOT_FOUND,
691
+ detail="Prompt group not found"
692
+ )
693
+
694
+ # Toggle pin status
695
+ group.is_pinned = not group.is_pinned
696
+ group.updated_at = now_epoch_ms()
697
+
698
+ db.commit()
699
+ db.refresh(group)
700
+
701
+ # Build response
702
+ group_dict = {
703
+ "id": group.id,
704
+ "name": group.name,
705
+ "description": group.description,
706
+ "category": group.category,
707
+ "command": group.command,
708
+ "user_id": group.user_id,
709
+ "author_name": group.author_name,
710
+ "production_prompt_id": group.production_prompt_id,
711
+ "is_shared": group.is_shared,
712
+ "is_pinned": group.is_pinned,
713
+ "created_at": group.created_at,
714
+ "updated_at": group.updated_at,
715
+ "production_prompt": None,
716
+ }
717
+
718
+ if group.production_prompt_id:
719
+ prod_prompt = db.query(PromptModel).filter(
720
+ PromptModel.id == group.production_prompt_id
721
+ ).first()
722
+ if prod_prompt:
723
+ group_dict["production_prompt"] = {
724
+ "id": prod_prompt.id,
725
+ "prompt_text": prod_prompt.prompt_text,
726
+ "group_id": prod_prompt.group_id,
727
+ "user_id": prod_prompt.user_id,
728
+ "version": prod_prompt.version,
729
+ "name": prod_prompt.name,
730
+ "description": prod_prompt.description,
731
+ "category": prod_prompt.category,
732
+ "command": prod_prompt.command,
733
+ "created_at": prod_prompt.created_at,
734
+ "updated_at": prod_prompt.updated_at,
735
+ }
736
+
737
+ return PromptGroupResponse(**group_dict)
738
+ except HTTPException:
739
+ db.rollback()
740
+ raise
741
+ except Exception as e:
742
+ db.rollback()
743
+ log.error(f"Error toggling pin for prompt group {group_id}: {e}")
744
+ raise HTTPException(
745
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
746
+ detail="Failed to toggle pin status"
747
+ )
748
+
749
+
750
+ @router.delete("/groups/{group_id}", status_code=status.HTTP_204_NO_CONTENT)
751
+ async def delete_prompt_group(
752
+ group_id: str,
753
+ db: Session = Depends(get_db),
754
+ user_id: str = Depends(get_user_id),
755
+ _: None = Depends(check_prompts_enabled),
756
+ ):
757
+ """Delete a prompt group and all its prompts (requires delete permission - owner or editor only)."""
758
+ try:
759
+ # Check delete permission
760
+ check_permission(db, group_id, user_id, "delete")
761
+
762
+ group = db.query(PromptGroupModel).filter(
763
+ PromptGroupModel.id == group_id
764
+ ).first()
765
+
766
+ if not group:
767
+ raise HTTPException(
768
+ status_code=status.HTTP_404_NOT_FOUND,
769
+ detail="Prompt group not found"
770
+ )
771
+
772
+ # Delete all prompts in the group (cascade should handle this)
773
+ db.delete(group)
774
+ db.commit()
775
+
776
+ return None
777
+ except HTTPException:
778
+ db.rollback()
779
+ raise
780
+ except Exception as e:
781
+ db.rollback()
782
+ log.error(f"Error deleting prompt group {group_id}: {e}")
783
+ raise HTTPException(
784
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
785
+ detail="Failed to delete prompt group"
786
+ )
787
+
788
+
789
+ # ============================================================================
790
+ # Prompts Endpoints
791
+ # ============================================================================
792
+
793
+ @router.get("/groups/{group_id}/prompts", response_model=List[PromptResponse])
794
+ async def list_prompts_in_group(
795
+ group_id: str,
796
+ db: Session = Depends(get_db),
797
+ user_id: str = Depends(get_user_id),
798
+ _: None = Depends(check_prompts_enabled),
799
+ ):
800
+ """List all prompt versions in a group (requires read permission)."""
801
+ try:
802
+ # Check read permission
803
+ check_permission(db, group_id, user_id, "read")
804
+
805
+ group = db.query(PromptGroupModel).filter(
806
+ PromptGroupModel.id == group_id
807
+ ).first()
808
+
809
+ if not group:
810
+ raise HTTPException(
811
+ status_code=status.HTTP_404_NOT_FOUND,
812
+ detail="Prompt group not found"
813
+ )
814
+
815
+ prompts = db.query(PromptModel).filter(
816
+ PromptModel.group_id == group_id
817
+ ).order_by(PromptModel.created_at.desc()).all()
818
+
819
+ return [
820
+ PromptResponse(
821
+ id=p.id,
822
+ prompt_text=p.prompt_text,
823
+ group_id=p.group_id,
824
+ user_id=p.user_id,
825
+ version=p.version,
826
+ name=p.name,
827
+ description=p.description,
828
+ category=p.category,
829
+ command=p.command,
830
+ created_at=p.created_at,
831
+ updated_at=p.updated_at,
832
+ )
833
+ for p in prompts
834
+ ]
835
+ except HTTPException:
836
+ raise
837
+ except Exception as e:
838
+ log.error(f"Error listing prompts in group {group_id}: {e}")
839
+ raise HTTPException(
840
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
841
+ detail="Failed to list prompts"
842
+ )
843
+
844
+
845
+ @router.post("/groups/{group_id}/prompts", response_model=PromptResponse, status_code=status.HTTP_201_CREATED)
846
+ async def create_prompt_version(
847
+ group_id: str,
848
+ prompt_data: PromptCreate,
849
+ db: Session = Depends(get_db),
850
+ user_id: str = Depends(get_user_id),
851
+ _: None = Depends(check_prompts_enabled),
852
+ ):
853
+ """Create a new prompt version in a group (requires write permission)."""
854
+ try:
855
+ # Check write permission
856
+ check_permission(db, group_id, user_id, "write")
857
+
858
+ group = db.query(PromptGroupModel).filter(
859
+ PromptGroupModel.id == group_id
860
+ ).first()
861
+
862
+ if not group:
863
+ raise HTTPException(
864
+ status_code=status.HTTP_404_NOT_FOUND,
865
+ detail="Prompt group not found"
866
+ )
867
+
868
+ # Get next version number
869
+ max_version_result = db.query(func.max(PromptModel.version)).filter(
870
+ PromptModel.group_id == group_id
871
+ ).scalar()
872
+
873
+ next_version = (max_version_result + 1) if max_version_result else 1
874
+
875
+ # Create new prompt
876
+ prompt_id = str(uuid.uuid4())
877
+ now_ms = now_epoch_ms()
878
+
879
+ # Get current group metadata for the new version
880
+ new_prompt = PromptModel(
881
+ id=prompt_id,
882
+ prompt_text=prompt_data.prompt_text,
883
+ name=group.name,
884
+ description=group.description,
885
+ category=group.category,
886
+ command=group.command,
887
+ group_id=group_id,
888
+ user_id=user_id,
889
+ version=next_version,
890
+ created_at=now_ms,
891
+ updated_at=now_ms,
892
+ )
893
+ db.add(new_prompt)
894
+ db.commit()
895
+ db.refresh(new_prompt)
896
+
897
+ return PromptResponse(
898
+ id=new_prompt.id,
899
+ prompt_text=new_prompt.prompt_text,
900
+ group_id=new_prompt.group_id,
901
+ user_id=new_prompt.user_id,
902
+ version=new_prompt.version,
903
+ name=new_prompt.name,
904
+ description=new_prompt.description,
905
+ category=new_prompt.category,
906
+ command=new_prompt.command,
907
+ created_at=new_prompt.created_at,
908
+ updated_at=new_prompt.updated_at,
909
+ )
910
+ except HTTPException:
911
+ db.rollback()
912
+ raise
913
+ except Exception as e:
914
+ db.rollback()
915
+ log.error(f"Error creating prompt version in group {group_id}: {e}")
916
+ raise HTTPException(
917
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
918
+ detail="Failed to create prompt version"
919
+ )
920
+
921
+
922
+ @router.patch("/{prompt_id}", response_model=PromptResponse)
923
+ async def update_prompt(
924
+ prompt_id: str,
925
+ prompt_data: PromptCreate,
926
+ db: Session = Depends(get_db),
927
+ user_id: str = Depends(get_user_id),
928
+ _: None = Depends(check_prompts_enabled),
929
+ ):
930
+ """Update an existing prompt's content (requires write permission)."""
931
+ try:
932
+ prompt = db.query(PromptModel).filter(
933
+ PromptModel.id == prompt_id
934
+ ).first()
935
+
936
+ if not prompt:
937
+ raise HTTPException(
938
+ status_code=status.HTTP_404_NOT_FOUND,
939
+ detail="Prompt not found"
940
+ )
941
+
942
+ # Check write permission on the group
943
+ check_permission(db, prompt.group_id, user_id, "write")
944
+
945
+ # Update prompt text
946
+ prompt.prompt_text = prompt_data.prompt_text
947
+ prompt.updated_at = now_epoch_ms()
948
+
949
+ db.commit()
950
+ db.refresh(prompt)
951
+
952
+ return PromptResponse(
953
+ id=prompt.id,
954
+ prompt_text=prompt.prompt_text,
955
+ group_id=prompt.group_id,
956
+ user_id=prompt.user_id,
957
+ version=prompt.version,
958
+ name=prompt.name,
959
+ description=prompt.description,
960
+ category=prompt.category,
961
+ command=prompt.command,
962
+ created_at=prompt.created_at,
963
+ updated_at=prompt.updated_at,
964
+ )
965
+ except HTTPException:
966
+ db.rollback()
967
+ raise
968
+ except Exception as e:
969
+ db.rollback()
970
+ log.error(f"Error updating prompt {prompt_id}: {e}")
971
+ raise HTTPException(
972
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
973
+ detail="Failed to update prompt"
974
+ )
975
+
976
+
977
+ @router.patch("/{prompt_id}/make-production", response_model=PromptResponse)
978
+ async def make_prompt_production(
979
+ prompt_id: str,
980
+ db: Session = Depends(get_db),
981
+ user_id: str = Depends(get_user_id),
982
+ _: None = Depends(check_prompts_enabled),
983
+ ):
984
+ """Set a prompt as the production version for its group (requires write permission)."""
985
+ try:
986
+ prompt = db.query(PromptModel).filter(
987
+ PromptModel.id == prompt_id
988
+ ).first()
989
+
990
+ if not prompt:
991
+ raise HTTPException(
992
+ status_code=status.HTTP_404_NOT_FOUND,
993
+ detail="Prompt not found"
994
+ )
995
+
996
+ # Check write permission on the group
997
+ check_permission(db, prompt.group_id, user_id, "write")
998
+
999
+ # Update group's production prompt
1000
+ group = db.query(PromptGroupModel).filter(
1001
+ PromptGroupModel.id == prompt.group_id
1002
+ ).first()
1003
+
1004
+ if group:
1005
+ group.production_prompt_id = prompt_id
1006
+ group.updated_at = now_epoch_ms()
1007
+ db.commit()
1008
+ db.refresh(prompt)
1009
+
1010
+ return PromptResponse(
1011
+ id=prompt.id,
1012
+ prompt_text=prompt.prompt_text,
1013
+ group_id=prompt.group_id,
1014
+ user_id=prompt.user_id,
1015
+ version=prompt.version,
1016
+ name=prompt.name,
1017
+ description=prompt.description,
1018
+ category=prompt.category,
1019
+ command=prompt.command,
1020
+ created_at=prompt.created_at,
1021
+ updated_at=prompt.updated_at,
1022
+ )
1023
+ except HTTPException:
1024
+ db.rollback()
1025
+ raise
1026
+ except Exception as e:
1027
+ db.rollback()
1028
+ log.error(f"Error making prompt {prompt_id} production: {e}")
1029
+ raise HTTPException(
1030
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1031
+ detail="Failed to update production prompt"
1032
+ )
1033
+
1034
+
1035
+ @router.delete("/{prompt_id}", status_code=status.HTTP_204_NO_CONTENT)
1036
+ async def delete_prompt(
1037
+ prompt_id: str,
1038
+ db: Session = Depends(get_db),
1039
+ user_id: str = Depends(get_user_id),
1040
+ _: None = Depends(check_prompts_enabled),
1041
+ ):
1042
+ """Delete a specific prompt version (requires delete permission)."""
1043
+ try:
1044
+ prompt = db.query(PromptModel).filter(
1045
+ PromptModel.id == prompt_id
1046
+ ).first()
1047
+
1048
+ if not prompt:
1049
+ raise HTTPException(
1050
+ status_code=status.HTTP_404_NOT_FOUND,
1051
+ detail="Prompt not found"
1052
+ )
1053
+
1054
+ # Check delete permission on the group
1055
+ check_permission(db, prompt.group_id, user_id, "delete")
1056
+
1057
+ # Check if this is the only prompt in the group
1058
+ group = db.query(PromptGroupModel).filter(
1059
+ PromptGroupModel.id == prompt.group_id
1060
+ ).first()
1061
+
1062
+ prompt_count = db.query(PromptModel).filter(
1063
+ PromptModel.group_id == prompt.group_id
1064
+ ).count()
1065
+
1066
+ if prompt_count == 1:
1067
+ # Delete the entire group if this is the last prompt
1068
+ db.delete(group)
1069
+ else:
1070
+ # If this was the production prompt, set another as production
1071
+ if group and group.production_prompt_id == prompt_id:
1072
+ other_prompt = db.query(PromptModel).filter(
1073
+ PromptModel.group_id == prompt.group_id,
1074
+ PromptModel.id != prompt_id,
1075
+ ).order_by(PromptModel.created_at.desc()).first()
1076
+
1077
+ if other_prompt:
1078
+ group.production_prompt_id = other_prompt.id
1079
+ group.updated_at = now_epoch_ms()
1080
+
1081
+ db.delete(prompt)
1082
+
1083
+ db.commit()
1084
+ return None
1085
+ except HTTPException:
1086
+ db.rollback()
1087
+ raise
1088
+ except Exception as e:
1089
+ db.rollback()
1090
+ log.error(f"Error deleting prompt {prompt_id}: {e}")
1091
+ raise HTTPException(
1092
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1093
+ detail="Failed to delete prompt"
1094
+ )
1095
+
1096
+
1097
+ # ============================================================================
1098
+ # AI-Assisted Prompt Builder Endpoints
1099
+ # ============================================================================
1100
+
1101
+
1102
+ @router.get("/chat/init")
1103
+ async def init_prompt_builder_chat(
1104
+ db: Session = Depends(get_db),
1105
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
1106
+ ):
1107
+ """Initialize the prompt template builder chat"""
1108
+ model_config = component.get_config("model", {})
1109
+ assistant = PromptBuilderAssistant(db=db, model_config=model_config)
1110
+ greeting = assistant.get_initial_greeting()
1111
+ return {
1112
+ "message": greeting.message,
1113
+ "confidence": greeting.confidence
1114
+ }
1115
+
1116
+
1117
+ @router.post("/chat", response_model=PromptBuilderChatResponse)
1118
+ async def prompt_builder_chat(
1119
+ request: PromptBuilderChatRequest,
1120
+ db: Session = Depends(get_db),
1121
+ user_id: str = Depends(get_user_id),
1122
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
1123
+ _: None = Depends(check_prompts_enabled),
1124
+ ):
1125
+ """
1126
+ Handle conversational prompt template building using LLM.
1127
+
1128
+ Uses LLM to:
1129
+ 1. Analyze user's description or example transcript
1130
+ 2. Identify variable elements vs fixed instructions
1131
+ 3. Generate template structure
1132
+ 4. Suggest variable names and descriptions
1133
+ 5. Avoid command conflicts with existing prompts
1134
+ """
1135
+ try:
1136
+ # Get model configuration from component
1137
+ model_config = component.get_config("model", {})
1138
+
1139
+ # Initialize the assistant with database session and model config
1140
+ assistant = PromptBuilderAssistant(db=db, model_config=model_config)
1141
+
1142
+ # Process the message using real LLM with conflict checking
1143
+ response = await assistant.process_message(
1144
+ user_message=request.message,
1145
+ conversation_history=[msg.dict() for msg in request.conversation_history],
1146
+ current_template=request.current_template or {},
1147
+ user_id=user_id
1148
+ )
1149
+
1150
+ return PromptBuilderChatResponse(
1151
+ message=response.message,
1152
+ template_updates=response.template_updates,
1153
+ confidence=response.confidence,
1154
+ ready_to_save=response.ready_to_save
1155
+ )
1156
+
1157
+ except Exception as e:
1158
+ log.error(f"Error in prompt builder chat: {e}")
1159
+ raise HTTPException(
1160
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1161
+ detail="Failed to process chat message"
1162
+ )
1163
+
1164
+
1165
+ # ============================================================================
1166
+ # Export/Import Endpoints
1167
+ # ============================================================================
1168
+
1169
+ @router.get("/groups/{group_id}/export", response_model=PromptExportResponse)
1170
+ async def export_prompt_group(
1171
+ group_id: str,
1172
+ db: Session = Depends(get_db),
1173
+ user_id: str = Depends(get_user_id),
1174
+ user_display_name: str = Depends(get_user_display_name),
1175
+ _: None = Depends(check_prompts_enabled),
1176
+ ):
1177
+ """
1178
+ Export a prompt group's active/production version as a JSON file.
1179
+ Returns a downloadable JSON file containing the prompt data.
1180
+ Requires read permission on the prompt group.
1181
+ """
1182
+ try:
1183
+ # Check read permission
1184
+ check_permission(db, group_id, user_id, "read")
1185
+
1186
+ # Fetch the prompt group
1187
+ group = db.query(PromptGroupModel).filter(
1188
+ PromptGroupModel.id == group_id
1189
+ ).first()
1190
+
1191
+ if not group:
1192
+ raise HTTPException(
1193
+ status_code=status.HTTP_404_NOT_FOUND,
1194
+ detail="Prompt group not found"
1195
+ )
1196
+
1197
+ # Fetch the production prompt
1198
+ if not group.production_prompt_id:
1199
+ raise HTTPException(
1200
+ status_code=status.HTTP_400_BAD_REQUEST,
1201
+ detail="No active prompt version to export"
1202
+ )
1203
+
1204
+ prod_prompt = db.query(PromptModel).filter(
1205
+ PromptModel.id == group.production_prompt_id
1206
+ ).first()
1207
+
1208
+ if not prod_prompt:
1209
+ raise HTTPException(
1210
+ status_code=status.HTTP_404_NOT_FOUND,
1211
+ detail="Active prompt version not found"
1212
+ )
1213
+
1214
+ # Build export data
1215
+ # Use author_name if available, otherwise use current user's display name as fallback
1216
+ author_name = group.author_name or user_display_name
1217
+
1218
+ export_data = PromptExportResponse(
1219
+ version="1.0",
1220
+ exported_at=now_epoch_ms(),
1221
+ prompt=PromptExportData(
1222
+ name=group.name,
1223
+ description=group.description,
1224
+ category=group.category,
1225
+ command=group.command,
1226
+ prompt_text=prod_prompt.prompt_text,
1227
+ metadata=PromptExportMetadata(
1228
+ author_name=author_name,
1229
+ original_version=prod_prompt.version,
1230
+ original_created_at=prod_prompt.created_at
1231
+ )
1232
+ )
1233
+ )
1234
+
1235
+ return export_data
1236
+
1237
+ except HTTPException:
1238
+ raise
1239
+ except Exception as e:
1240
+ log.error(f"Error exporting prompt group {group_id}: {e}")
1241
+ raise HTTPException(
1242
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1243
+ detail="Failed to export prompt"
1244
+ )
1245
+
1246
+
1247
+ @router.post("/import", response_model=PromptImportResponse, status_code=status.HTTP_201_CREATED)
1248
+ async def import_prompt(
1249
+ import_request: PromptImportRequest,
1250
+ db: Session = Depends(get_db),
1251
+ user_id: str = Depends(get_user_id),
1252
+ user_display_name: str = Depends(get_user_display_name),
1253
+ _: None = Depends(check_prompts_enabled),
1254
+ ):
1255
+ """
1256
+ Import a prompt from exported JSON data.
1257
+ Creates a new prompt group with the imported data.
1258
+ Handles command conflicts automatically by generating alternative commands.
1259
+ """
1260
+ try:
1261
+ prompt_data = import_request.prompt_data
1262
+ options = import_request.options or PromptImportOptions()
1263
+ warnings = []
1264
+
1265
+ # Validate export format version
1266
+ export_version = prompt_data.get("version", "1.0")
1267
+ if export_version != "1.0":
1268
+ raise HTTPException(
1269
+ status_code=status.HTTP_400_BAD_REQUEST,
1270
+ detail=f"Unsupported export format version: {export_version}"
1271
+ )
1272
+
1273
+ # Extract prompt data
1274
+ prompt_info = prompt_data.get("prompt")
1275
+ if not prompt_info:
1276
+ raise HTTPException(
1277
+ status_code=status.HTTP_400_BAD_REQUEST,
1278
+ detail="Invalid export format: missing 'prompt' field"
1279
+ )
1280
+
1281
+ # Validate required fields
1282
+ required_fields = ["name", "prompt_text"]
1283
+ for field in required_fields:
1284
+ if field not in prompt_info or not prompt_info[field]:
1285
+ raise HTTPException(
1286
+ status_code=status.HTTP_400_BAD_REQUEST,
1287
+ detail=f"Invalid export format: missing required field '{field}'"
1288
+ )
1289
+
1290
+ # Extract fields with validation
1291
+ name = prompt_info["name"]
1292
+ # Truncate name if it exceeds max length (255 chars)
1293
+ if len(name) > 255:
1294
+ original_name = name
1295
+ name = name[:252] + "..."
1296
+ warnings.append(
1297
+ f"Name was truncated from {len(original_name)} to 255 characters"
1298
+ )
1299
+
1300
+ description = prompt_info.get("description")
1301
+ # Truncate description if it exceeds max length (1000 chars)
1302
+ if description and len(description) > 1000:
1303
+ description = description[:997] + "..."
1304
+ warnings.append("Description was truncated to 1000 characters")
1305
+
1306
+ category = prompt_info.get("category") if options.preserve_category else None
1307
+ # Truncate category if it exceeds max length (100 chars)
1308
+ if category and len(category) > 100:
1309
+ category = category[:97] + "..."
1310
+ warnings.append("Category was truncated to 100 characters")
1311
+
1312
+ command = prompt_info.get("command") if options.preserve_command else None
1313
+ # Truncate command if it exceeds max length (50 chars)
1314
+ if command and len(command) > 50:
1315
+ command = command[:50]
1316
+ warnings.append("Command was truncated to 50 characters")
1317
+
1318
+ prompt_text = prompt_info["prompt_text"]
1319
+ # Truncate prompt_text if it exceeds max length (10000 chars)
1320
+ if len(prompt_text) > 10000:
1321
+ prompt_text = prompt_text[:9997] + "..."
1322
+ warnings.append("Prompt text was truncated to 10000 characters")
1323
+
1324
+ # Handle command conflicts
1325
+ if command:
1326
+ original_command = command
1327
+ existing = db.query(PromptGroupModel).filter(
1328
+ PromptGroupModel.command == command,
1329
+ PromptGroupModel.user_id == user_id,
1330
+ ).first()
1331
+
1332
+ if existing:
1333
+ # Generate alternative command
1334
+ counter = 2
1335
+ while True:
1336
+ new_command = f"{original_command}-{counter}"
1337
+ existing_alt = db.query(PromptGroupModel).filter(
1338
+ PromptGroupModel.command == new_command,
1339
+ PromptGroupModel.user_id == user_id,
1340
+ ).first()
1341
+ if not existing_alt:
1342
+ command = new_command
1343
+ warnings.append(
1344
+ f"Command '/{original_command}' already exists, using '/{command}' instead"
1345
+ )
1346
+ break
1347
+ counter += 1
1348
+ if counter > 100: # Safety limit
1349
+ command = None
1350
+ warnings.append(
1351
+ f"Could not generate unique command, imported without command"
1352
+ )
1353
+ break
1354
+
1355
+ # Create new prompt group
1356
+ group_id = str(uuid.uuid4())
1357
+ now_ms = now_epoch_ms()
1358
+
1359
+ new_group = PromptGroupModel(
1360
+ id=group_id,
1361
+ name=name,
1362
+ description=description,
1363
+ category=category,
1364
+ command=command,
1365
+ user_id=user_id,
1366
+ author_name=user_display_name, # Set to importing user, not original author
1367
+ production_prompt_id=None,
1368
+ is_shared=False,
1369
+ is_pinned=False,
1370
+ created_at=now_ms,
1371
+ updated_at=now_ms,
1372
+ )
1373
+ db.add(new_group)
1374
+ db.flush()
1375
+
1376
+ # Create prompt version with versioned metadata
1377
+ prompt_id = str(uuid.uuid4())
1378
+ new_prompt = PromptModel(
1379
+ id=prompt_id,
1380
+ prompt_text=prompt_text,
1381
+ name=name,
1382
+ description=description,
1383
+ category=category,
1384
+ command=command,
1385
+ group_id=group_id,
1386
+ user_id=user_id,
1387
+ version=1, # Start at version 1 for imported prompts
1388
+ created_at=now_ms,
1389
+ updated_at=now_ms,
1390
+ )
1391
+ db.add(new_prompt)
1392
+ db.flush()
1393
+
1394
+ # Set as production prompt
1395
+ new_group.production_prompt_id = prompt_id
1396
+ new_group.updated_at = now_epoch_ms()
1397
+
1398
+ db.commit()
1399
+
1400
+ return PromptImportResponse(
1401
+ success=True,
1402
+ prompt_group_id=group_id,
1403
+ warnings=warnings
1404
+ )
1405
+
1406
+ except HTTPException:
1407
+ db.rollback()
1408
+ raise
1409
+ except Exception as e:
1410
+ db.rollback()
1411
+ log.error(f"Error importing prompt: {e}")
1412
+ raise HTTPException(
1413
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1414
+ detail=f"Failed to import prompt: {str(e)}"
1415
+ )