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,1220 @@
1
+ """
2
+ API Router for managing A2A message visualization streams.
3
+ """
4
+
5
+ import logging
6
+ import asyncio
7
+ import uuid
8
+ from fastapi import (
9
+ APIRouter,
10
+ Depends,
11
+ HTTPException,
12
+ Request as FastAPIRequest,
13
+ Response,
14
+ status,
15
+ )
16
+ from pydantic import BaseModel, Field
17
+ import json
18
+ from typing import List, Optional, Dict, Any, Set
19
+
20
+
21
+ from ....gateway.http_sse.dependencies import (
22
+ get_sac_component,
23
+ get_user_id,
24
+ get_sse_manager,
25
+ )
26
+ from ....gateway.http_sse.sse_manager import SSEManager
27
+ from ....common.middleware.registry import MiddlewareRegistry
28
+
29
+ from typing import TYPE_CHECKING
30
+
31
+ if TYPE_CHECKING:
32
+ from ....gateway.http_sse.component import WebUIBackendComponent
33
+
34
+ log = logging.getLogger(__name__)
35
+ trace_logger = logging.getLogger("sam_trace")
36
+
37
+ router = APIRouter()
38
+
39
+
40
+ class SubscriptionTarget(BaseModel):
41
+ """Defines an abstract target for A2A message visualization."""
42
+
43
+ type: str = Field(
44
+ ...,
45
+ description="Type of the target to monitor.",
46
+ examples=[
47
+ "my_a2a_messages",
48
+ "current_namespace_a2a_messages",
49
+ "namespace_a2a_messages",
50
+ "agent_a2a_messages",
51
+ ],
52
+ )
53
+ identifier: Optional[str] = Field(
54
+ default=None,
55
+ description="Identifier for the target (e.g., namespace string or agent name). Not required if type is 'current_namespace_a2a_messages'.",
56
+ )
57
+
58
+
59
+ class ActualSubscribedTarget(SubscriptionTarget):
60
+ """Represents an abstract target that the gateway attempted to subscribe to, including its status."""
61
+
62
+ status: str = Field(
63
+ ...,
64
+ description="Status of the subscription attempt for this target.",
65
+ examples=["subscribed", "denied_due_to_scope", "error_translating_target"],
66
+ )
67
+
68
+
69
+ class VisualizationSubscribeRequest(BaseModel):
70
+ """Request body for initiating a visualization stream."""
71
+
72
+ subscription_targets: Optional[List[SubscriptionTarget]] = Field(
73
+ default_factory=list,
74
+ description="Optional list of abstract targets to monitor.",
75
+ )
76
+ client_stream_id: Optional[str] = Field(
77
+ default=None,
78
+ description="Optional client-generated ID for idempotency or re-association. If not provided, a new one is generated.",
79
+ )
80
+
81
+
82
+ class VisualizationSubscribeResponse(BaseModel):
83
+ """Response body for a successful visualization subscription."""
84
+
85
+ stream_id: str = Field(..., description="Unique ID for the visualization stream.")
86
+ sse_endpoint_url: str = Field(..., description="URL for the SSE event stream.")
87
+ actual_subscribed_targets: List[ActualSubscribedTarget] = Field(
88
+ default_factory=list,
89
+ description="List of abstract targets processed, with their subscription status.",
90
+ )
91
+ message: str = "Visualization stream initiated. Connect to the SSE endpoint."
92
+
93
+
94
+ class VisualizationConfigUpdateRequest(BaseModel):
95
+ """Request body for updating an active visualization stream's configuration."""
96
+
97
+ subscription_targets_to_add: Optional[List[SubscriptionTarget]] = Field(
98
+ default=None,
99
+ description="List of new abstract targets to add to the subscription.",
100
+ )
101
+ subscription_targets_to_remove: Optional[List[SubscriptionTarget]] = Field(
102
+ default=None,
103
+ description="List of abstract targets to remove from the subscription.",
104
+ )
105
+
106
+
107
+ class VisualizationConfigUpdateResponse(BaseModel):
108
+ """Response body for a successful visualization configuration update."""
109
+
110
+ stream_id: str = Field(..., description="ID of the updated visualization stream.")
111
+ message: str = "Visualization stream configuration updated successfully."
112
+ current_subscribed_targets: List[ActualSubscribedTarget] = Field(
113
+ default_factory=list,
114
+ description="Current list of active abstract targets for this stream, with their status.",
115
+ )
116
+
117
+
118
+ class VisualizationSubscriptionError(BaseModel):
119
+ """Error response for subscription failures."""
120
+
121
+ message: str = Field(..., description="Human-readable error message")
122
+ failed_targets: List[ActualSubscribedTarget] = Field(
123
+ ..., description="List of targets that failed to subscribe"
124
+ )
125
+ error_type: str = Field(
126
+ ...,
127
+ description="Type of error: 'authorization_failure' or 'subscription_failure'",
128
+ )
129
+ suggested_action: Optional[str] = Field(
130
+ default=None, description="Suggested action for the user"
131
+ )
132
+
133
+
134
+ from sse_starlette.sse import EventSourceResponse
135
+
136
+
137
+ def _generate_sse_url(fastapi_request: FastAPIRequest, stream_id: str) -> str:
138
+ """
139
+ Generate SSE endpoint URL with proper scheme and host detection for reverse proxy scenarios.
140
+
141
+ Args:
142
+ fastapi_request: The FastAPI request object
143
+ stream_id: The stream ID for the SSE endpoint
144
+
145
+ Returns:
146
+ Complete SSE URL with correct scheme (http/https) and host.
147
+ """
148
+ base_url = fastapi_request.url_for(
149
+ "get_visualization_stream_events", stream_id=stream_id
150
+ )
151
+
152
+ forwarded_proto = fastapi_request.headers.get("x-forwarded-proto")
153
+ forwarded_host = fastapi_request.headers.get("x-forwarded-host")
154
+
155
+ if forwarded_proto and forwarded_host:
156
+ # In a reverse proxy environment like GitHub Codespaces, reconstruct the URL
157
+ # using the forwarded headers to ensure it's publicly accessible.
158
+ return str(base_url.replace(scheme=forwarded_proto, netloc=forwarded_host))
159
+ elif forwarded_proto:
160
+ # Handle cases with only a forwarded protocol (standard reverse proxy)
161
+ return str(base_url.replace(scheme=forwarded_proto))
162
+ else:
163
+ # Default behavior when not behind a reverse proxy
164
+ return str(base_url)
165
+
166
+
167
+ def _translate_target_to_solace_topics(
168
+ target: SubscriptionTarget, component_namespace: str
169
+ ) -> List[str]:
170
+ """Translates an abstract SubscriptionTarget to a list of Solace topic strings."""
171
+ topics = []
172
+ target_identifier = target.identifier.strip("/") if target.identifier else ""
173
+ component_namespace_formatted = component_namespace.strip("/")
174
+ if target.type == "current_namespace_a2a_messages":
175
+ topics.append(f"{component_namespace_formatted}/a2a/>")
176
+ elif target.type == "namespace_a2a_messages":
177
+ if not target.identifier:
178
+ log.warning(f"Identifier missing for target type {target.type}")
179
+ return []
180
+ topics.append(f"{target_identifier}/a2a/>")
181
+ elif target.type == "agent_a2a_messages":
182
+ if not target.identifier:
183
+ log.warning(f"Identifier missing for target type {target.type}")
184
+ return []
185
+ base_agent_topic = f"{component_namespace_formatted}/a2a/v1/agent"
186
+ topics.append(f"{base_agent_topic}/request/{target_identifier}/>")
187
+ topics.append(f"{base_agent_topic}/response/{target_identifier}/>")
188
+ topics.append(f"{base_agent_topic}/status/{target_identifier}/>")
189
+ else:
190
+ log.warning(f"Unknown subscription target type: {target.type}")
191
+ return topics
192
+
193
+
194
+ def _resolve_user_identity_for_authorization(
195
+ component: "WebUIBackendComponent", raw_user_id: str
196
+ ) -> str:
197
+ """
198
+ Applies the same user identity resolution logic as BaseGatewayComponent.submit_a2a_task().
199
+ This ensures visualization authorization uses the same identity resolution as task submission.
200
+
201
+ Args:
202
+ component: The WebUIBackendComponent instance
203
+ raw_user_id: The raw user ID from the session (e.g., web-client-xxxxx)
204
+
205
+ Returns:
206
+ The resolved user identity to use for authorization
207
+ """
208
+ log_id_prefix = f"{component.log_identifier}[ResolveUserIdentity]"
209
+ user_identity = raw_user_id
210
+
211
+ force_identity = component.get_config("force_user_identity")
212
+ if force_identity:
213
+ original_identity = user_identity
214
+ user_identity = force_identity
215
+ log.info(
216
+ "%s DEVELOPMENT MODE: Forcing user_identity from '%s' to '%s' for visualization",
217
+ log_id_prefix,
218
+ original_identity,
219
+ user_identity,
220
+ )
221
+ return user_identity
222
+
223
+ if not user_identity:
224
+ use_authorization = component.get_config("frontend_use_authorization", False)
225
+ if not use_authorization:
226
+ user_identity = "sam_dev_user"
227
+ log.info(
228
+ "%s No user_identity provided and auth is disabled, using sam_dev_user for visualization",
229
+ log_id_prefix,
230
+ )
231
+ else:
232
+ log.error(
233
+ "%s No user_identity provided but authorization is enabled. This should not happen.",
234
+ log_id_prefix,
235
+ )
236
+ raise ValueError(
237
+ "No user identity available when authorization is required"
238
+ )
239
+
240
+ return user_identity
241
+
242
+
243
+ def _include_for_visualization(event_payload: Dict[str, Any]) -> bool:
244
+ """
245
+ Check if an event should be included for visualization based on metadata.
246
+
247
+ Args:
248
+ event_payload: The event payload containing event data
249
+
250
+ Returns:
251
+ False if the event should be excluded from visualization (when visualization is False),
252
+ True otherwise (include by default)
253
+ """
254
+ try:
255
+ # Get the data field from the event payload
256
+ data_str = event_payload.get("data")
257
+ if not data_str:
258
+ return True # Include by default if no data
259
+
260
+ # Parse the JSON data
261
+ try:
262
+ data = json.loads(data_str)
263
+ except (json.JSONDecodeError, TypeError):
264
+ return True
265
+
266
+ # Look for the full_payload in the data
267
+ full_payload = data.get("full_payload")
268
+ if not full_payload:
269
+ return True
270
+
271
+ # Check if full_payload has params
272
+ params = full_payload.get("params")
273
+ if not params:
274
+ return True
275
+
276
+ # Check if params has message
277
+ message = params.get("message")
278
+ if not message:
279
+ return True
280
+
281
+ # Check if message has metadata
282
+ metadata = message.get("metadata")
283
+ if not metadata:
284
+ return True
285
+
286
+ # Check the visualization setting in metadata
287
+ visualization_setting = metadata.get("visualization")
288
+ if visualization_setting is not None and (
289
+ (
290
+ isinstance(visualization_setting, str)
291
+ and visualization_setting.lower() == "false"
292
+ )
293
+ or (
294
+ isinstance(visualization_setting, bool)
295
+ and visualization_setting is False
296
+ )
297
+ ):
298
+ return False
299
+
300
+ return True
301
+
302
+ except Exception as e:
303
+ log.warning("Error checking visualization filter for event: %s", e)
304
+ return True
305
+
306
+
307
+ @router.post(
308
+ "/subscribe",
309
+ response_model=VisualizationSubscribeResponse,
310
+ status_code=status.HTTP_201_CREATED,
311
+ )
312
+ async def subscribe_to_visualization_stream(
313
+ request_data: VisualizationSubscribeRequest,
314
+ fastapi_request: FastAPIRequest,
315
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
316
+ user_id: str = Depends(get_user_id),
317
+ sse_manager: SSEManager = Depends(get_sse_manager),
318
+ ):
319
+ """Initiates a new A2A message visualization stream using abstract targets."""
320
+ log_id_prefix = f"{component.log_identifier}[POST /viz/subscribe]"
321
+ log.info(
322
+ "%s Request received from user %s. Client Stream ID: %s",
323
+ log_id_prefix,
324
+ user_id,
325
+ request_data.client_stream_id,
326
+ )
327
+
328
+ try:
329
+ component._ensure_visualization_flow_is_running()
330
+ except Exception as e:
331
+ log.exception(
332
+ "%s Failed to ensure visualization flow is running: %s", log_id_prefix, e
333
+ )
334
+ raise HTTPException(
335
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
336
+ detail="Failed to initialize visualization backend.",
337
+ )
338
+
339
+ stream_id = request_data.client_stream_id or f"viz-stream-{uuid.uuid4().hex}"
340
+
341
+ log.debug(
342
+ "%s Acquiring viz lock to check for existing stream %s",
343
+ log_id_prefix,
344
+ stream_id,
345
+ )
346
+ async with component._get_visualization_lock():
347
+ if stream_id in component._active_visualization_streams:
348
+ existing_stream_data = component._active_visualization_streams[stream_id]
349
+ if existing_stream_data.get("user_id") != user_id:
350
+ raise HTTPException(
351
+ status_code=status.HTTP_403_FORBIDDEN,
352
+ detail="Client stream ID already in use by another user.",
353
+ )
354
+
355
+ log.warning(
356
+ "%s Stream ID %s (client-provided) already exists. Returning existing info.",
357
+ log_id_prefix,
358
+ stream_id,
359
+ )
360
+ sse_url = _generate_sse_url(fastapi_request, stream_id)
361
+ return VisualizationSubscribeResponse(
362
+ stream_id=stream_id,
363
+ sse_endpoint_url=sse_url,
364
+ actual_subscribed_targets=existing_stream_data.get(
365
+ "abstract_targets", []
366
+ ),
367
+ message="Visualization stream with this client_stream_id already exists and is active.",
368
+ )
369
+ log.debug(
370
+ "%s Released viz lock after checking for existing stream %s",
371
+ log_id_prefix,
372
+ stream_id,
373
+ )
374
+
375
+ try:
376
+ sse_queue = await sse_manager.create_sse_connection(stream_id)
377
+ except Exception as e:
378
+ log.exception(
379
+ "%s Failed to create SSE connection queue for stream %s: %s",
380
+ log_id_prefix,
381
+ stream_id,
382
+ e,
383
+ )
384
+ raise HTTPException(
385
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
386
+ detail="Failed to establish SSE infrastructure.",
387
+ )
388
+
389
+ resolved_user_identity = _resolve_user_identity_for_authorization(
390
+ component, user_id
391
+ )
392
+ log.debug(
393
+ "%s Resolved user identity for authorization: '%s' (from raw user_id: '%s')",
394
+ log_id_prefix,
395
+ resolved_user_identity,
396
+ user_id,
397
+ )
398
+
399
+ config_resolver = MiddlewareRegistry.get_config_resolver()
400
+ gateway_context = {
401
+ "gateway_id": component.gateway_id,
402
+ "request": fastapi_request,
403
+ "gateway_namespace": component.namespace,
404
+ }
405
+ user_config: Dict[str, Any] = {}
406
+ try:
407
+ user_config = await config_resolver.resolve_user_config(
408
+ resolved_user_identity, gateway_context, {}
409
+ )
410
+ log.debug(
411
+ "%s Resolved user_config for resolved_user_identity '%s': %s",
412
+ log_id_prefix,
413
+ resolved_user_identity,
414
+ {k: v for k, v in user_config.items() if not k.startswith("_")},
415
+ )
416
+ except Exception as config_err:
417
+ log.exception(
418
+ "%s Error resolving user_config for user %s: %s. Proceeding with empty config.",
419
+ log_id_prefix,
420
+ resolved_user_identity,
421
+ config_err,
422
+ )
423
+ user_config = {}
424
+ processed_targets_for_response: List[ActualSubscribedTarget] = []
425
+ current_solace_topics_for_stream: Set[str] = set()
426
+ current_abstract_targets_for_stream: List[ActualSubscribedTarget] = []
427
+
428
+ initial_stream_config = {
429
+ "user_id": user_id,
430
+ "user_config": user_config,
431
+ "solace_topics": current_solace_topics_for_stream,
432
+ "abstract_targets": current_abstract_targets_for_stream,
433
+ "sse_queue": sse_queue,
434
+ "client_stream_id": request_data.client_stream_id,
435
+ }
436
+ log.debug(
437
+ "%s Acquiring viz lock to add initial stream config for %s",
438
+ log_id_prefix,
439
+ stream_id,
440
+ )
441
+ async with component._get_visualization_lock():
442
+ component._active_visualization_streams[stream_id] = initial_stream_config
443
+ log.debug(
444
+ "%s Released viz lock after adding initial stream config for %s",
445
+ log_id_prefix,
446
+ stream_id,
447
+ )
448
+
449
+ targets_to_process = request_data.subscription_targets
450
+ if not targets_to_process:
451
+ log.info(
452
+ "%s No subscription targets provided, defaulting to current namespace.",
453
+ log_id_prefix,
454
+ )
455
+ targets_to_process = [SubscriptionTarget(type="current_namespace_a2a_messages")]
456
+
457
+ log.debug(
458
+ "%s Starting to process %d subscription targets.",
459
+ log_id_prefix,
460
+ len(targets_to_process),
461
+ )
462
+ for target_request_idx, target_request in enumerate(targets_to_process):
463
+ log.debug(
464
+ "%s Processing target %d/%d: %s",
465
+ log_id_prefix,
466
+ target_request_idx + 1,
467
+ len(targets_to_process),
468
+ target_request.model_dump(),
469
+ )
470
+ target_status = "denied_due_to_scope"
471
+ required_scope = ""
472
+ effective_identifier = target_request.identifier
473
+
474
+ if target_request.type == "current_namespace_a2a_messages":
475
+ effective_identifier = component.namespace
476
+ required_scope = (
477
+ f"monitor/namespace/{effective_identifier}:a2a_messages:subscribe"
478
+ )
479
+ elif target_request.type == "namespace_a2a_messages":
480
+ if not effective_identifier:
481
+ log.warning(
482
+ "%s Identifier missing for target type 'namespace_a2a_messages'",
483
+ log_id_prefix,
484
+ )
485
+ target_status = "error_missing_identifier"
486
+ processed_targets_for_response.append(
487
+ ActualSubscribedTarget(
488
+ **target_request.model_dump(), status=target_status
489
+ )
490
+ )
491
+ continue
492
+ required_scope = (
493
+ f"monitor/namespace/{effective_identifier}:a2a_messages:subscribe"
494
+ )
495
+ elif target_request.type == "agent_a2a_messages":
496
+ if not effective_identifier:
497
+ log.warning(
498
+ "%s Identifier missing for target type 'agent_a2a_messages'",
499
+ log_id_prefix,
500
+ )
501
+ target_status = "error_missing_identifier"
502
+ processed_targets_for_response.append(
503
+ ActualSubscribedTarget(
504
+ **target_request.model_dump(), status=target_status
505
+ )
506
+ )
507
+ continue
508
+
509
+ pass
510
+ elif target_request.type == "my_a2a_messages":
511
+ operation_spec = {
512
+ "operation_type": "visualization_subscription",
513
+ "target_type": "my_a2a_messages",
514
+ }
515
+ validation_result = config_resolver.validate_operation_config(
516
+ user_config, operation_spec, gateway_context
517
+ )
518
+ has_permission = validation_result.get("valid", False)
519
+
520
+ if has_permission:
521
+ target_status = "subscribed"
522
+ response_target_data = target_request.model_dump()
523
+ current_abstract_targets_for_stream.append(
524
+ ActualSubscribedTarget(**response_target_data, status=target_status)
525
+ )
526
+
527
+ firehose_topic = f"{component.namespace.strip('/')}/a2a/>"
528
+ log.debug(
529
+ "%s Adding firehose subscription '%s' for my_a2a_messages stream.",
530
+ log_id_prefix,
531
+ firehose_topic,
532
+ )
533
+ if not await component._add_visualization_subscription(
534
+ firehose_topic, stream_id
535
+ ):
536
+ log.error(
537
+ "%s Failed to add required firehose subscription for my_a2a_messages.",
538
+ log_id_prefix,
539
+ )
540
+ target_status = "error_adding_subscription"
541
+ current_abstract_targets_for_stream.pop()
542
+
543
+ else:
544
+ log.warning(
545
+ "%s User %s denied subscription to 'my_a2a_messages' due to missing scope.",
546
+ log_id_prefix,
547
+ resolved_user_identity,
548
+ )
549
+ processed_targets_for_response.append(
550
+ ActualSubscribedTarget(
551
+ **target_request.model_dump(), status=target_status
552
+ )
553
+ )
554
+ continue
555
+ else:
556
+ log.warning(
557
+ "%s Unknown subscription target type: %s for identifier %s",
558
+ log_id_prefix,
559
+ target_request.type,
560
+ effective_identifier,
561
+ )
562
+ target_status = "error_unknown_target_type"
563
+ processed_targets_for_response.append(
564
+ ActualSubscribedTarget(
565
+ **target_request.model_dump(), status=target_status
566
+ )
567
+ )
568
+ continue
569
+
570
+ identifier_for_spec = target_request.identifier
571
+ if (
572
+ target_request.type == "current_namespace_a2a_messages"
573
+ and target_request.identifier is None
574
+ ):
575
+ identifier_for_spec = None
576
+
577
+ operation_spec = {
578
+ "operation_type": "visualization_subscription",
579
+ "target_type": target_request.type,
580
+ "target_identifier": identifier_for_spec,
581
+ }
582
+
583
+ validation_result = config_resolver.validate_operation_config(
584
+ user_config, operation_spec, gateway_context
585
+ )
586
+ has_permission = validation_result.get("valid", False)
587
+
588
+ if has_permission:
589
+ target_for_translation = target_request
590
+ if target_request.type == "current_namespace_a2a_messages":
591
+ target_for_translation = SubscriptionTarget(
592
+ type="namespace_a2a_messages", identifier=effective_identifier
593
+ )
594
+
595
+ solace_topics_for_target = _translate_target_to_solace_topics(
596
+ target_for_translation, component.namespace
597
+ )
598
+ if not solace_topics_for_target:
599
+ log.warning(
600
+ "%s No Solace topics derived for target: %s",
601
+ log_id_prefix,
602
+ target_request.model_dump(),
603
+ )
604
+ target_status = "error_translating_target"
605
+ else:
606
+ all_topics_added_successfully = True
607
+ for topic_str in solace_topics_for_target:
608
+ success = await component._add_visualization_subscription(
609
+ topic_str, stream_id
610
+ )
611
+ if success:
612
+ current_solace_topics_for_stream.add(topic_str)
613
+ else:
614
+ all_topics_added_successfully = False
615
+ log.error(
616
+ "%s Failed to add subscription to Solace topic: %s for stream %s (target: %s)",
617
+ log_id_prefix,
618
+ topic_str,
619
+ stream_id,
620
+ effective_identifier,
621
+ )
622
+
623
+ if all_topics_added_successfully:
624
+ target_status = "subscribed"
625
+ response_target_data = target_request.model_dump()
626
+ if target_request.type == "current_namespace_a2a_messages":
627
+ response_target_data["identifier"] = effective_identifier
628
+ current_abstract_targets_for_stream.append(
629
+ ActualSubscribedTarget(
630
+ **response_target_data, status=target_status
631
+ )
632
+ )
633
+ else:
634
+ target_status = "error_adding_subscription"
635
+ else:
636
+ log.warning(
637
+ "%s User %s denied subscription to target %s (type: %s) due to missing scope: %s",
638
+ log_id_prefix,
639
+ resolved_user_identity,
640
+ effective_identifier,
641
+ target_request.type,
642
+ required_scope,
643
+ )
644
+
645
+ response_target_data_for_processed_list = target_request.model_dump()
646
+ if (
647
+ target_request.type == "current_namespace_a2a_messages"
648
+ and target_status == "subscribed"
649
+ ):
650
+ response_target_data_for_processed_list["identifier"] = effective_identifier
651
+
652
+ processed_targets_for_response.append(
653
+ ActualSubscribedTarget(
654
+ **response_target_data_for_processed_list, status=target_status
655
+ )
656
+ )
657
+ log.debug("%s Finished processing all subscription targets.", log_id_prefix)
658
+
659
+ successful_subscriptions = [
660
+ target
661
+ for target in processed_targets_for_response
662
+ if target.status == "subscribed"
663
+ ]
664
+
665
+ if not successful_subscriptions:
666
+ log.warning(
667
+ "%s All subscription targets failed for user %s. Cleaning up stream %s.",
668
+ log_id_prefix,
669
+ user_id,
670
+ stream_id,
671
+ )
672
+
673
+ try:
674
+ await sse_manager.close_all_for_task(stream_id)
675
+ except Exception as cleanup_error:
676
+ log.warning(
677
+ "%s Failed to cleanup SSE connection for stream %s: %s",
678
+ log_id_prefix,
679
+ stream_id,
680
+ cleanup_error,
681
+ )
682
+
683
+ log.debug(
684
+ "%s Acquiring viz lock to clean up failed stream %s",
685
+ log_id_prefix,
686
+ stream_id,
687
+ )
688
+ async with component._get_visualization_lock():
689
+ component._active_visualization_streams.pop(stream_id, None)
690
+ log.debug(
691
+ "%s Released viz lock after cleaning up failed stream %s",
692
+ log_id_prefix,
693
+ stream_id,
694
+ )
695
+
696
+ denied_targets = [
697
+ target
698
+ for target in processed_targets_for_response
699
+ if target.status == "denied_due_to_scope"
700
+ ]
701
+
702
+ if denied_targets:
703
+ raise HTTPException(
704
+ status_code=status.HTTP_403_FORBIDDEN,
705
+ detail={
706
+ "message": "Access denied: insufficient permissions for all requested targets",
707
+ "failed_targets": [
708
+ target.model_dump() for target in processed_targets_for_response
709
+ ],
710
+ "error_type": "authorization_failure",
711
+ "suggested_action": "Please check your permissions or contact your administrator",
712
+ },
713
+ )
714
+ else:
715
+ raise HTTPException(
716
+ status_code=status.HTTP_400_BAD_REQUEST,
717
+ detail={
718
+ "message": "All subscription targets failed to process",
719
+ "failed_targets": [
720
+ target.model_dump() for target in processed_targets_for_response
721
+ ],
722
+ "error_type": "subscription_failure",
723
+ "suggested_action": "Please check your target specifications and try again",
724
+ },
725
+ )
726
+
727
+ failed_targets = [
728
+ target
729
+ for target in processed_targets_for_response
730
+ if target.status != "subscribed"
731
+ ]
732
+
733
+ response_message = "Visualization stream initiated. Connect to the SSE endpoint."
734
+ if failed_targets:
735
+ response_message = f"Visualization stream initiated with {len(successful_subscriptions)} successful and {len(failed_targets)} failed subscriptions."
736
+ log.warning(
737
+ "%s Partial subscription success for user %s: %d successful, %d failed",
738
+ log_id_prefix,
739
+ user_id,
740
+ len(successful_subscriptions),
741
+ len(failed_targets),
742
+ )
743
+
744
+ sse_url = _generate_sse_url(fastapi_request, stream_id)
745
+ log.info(
746
+ "%s Visualization stream %s initiated for user %s. SSE URL: %s. Processed Targets: %s",
747
+ log_id_prefix,
748
+ stream_id,
749
+ user_id,
750
+ sse_url,
751
+ processed_targets_for_response,
752
+ )
753
+
754
+ return VisualizationSubscribeResponse(
755
+ stream_id=stream_id,
756
+ sse_endpoint_url=sse_url,
757
+ actual_subscribed_targets=processed_targets_for_response,
758
+ message=response_message,
759
+ )
760
+
761
+
762
+ @router.get("/{stream_id}/events")
763
+ async def get_visualization_stream_events(
764
+ stream_id: str,
765
+ fastapi_request: FastAPIRequest,
766
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
767
+ sse_manager: SSEManager = Depends(get_sse_manager),
768
+ user_id: str = Depends(get_user_id),
769
+ ):
770
+ """Establishes an SSE connection for receiving filtered A2A messages for a specific stream."""
771
+ log_id_prefix = f"{component.log_identifier}[GET /viz/{stream_id}/events]"
772
+ log.info("%s Client %s requesting SSE connection.", log_id_prefix, user_id)
773
+
774
+ stream_config: Optional[Dict[str, Any]] = None
775
+ log.debug(
776
+ "%s Acquiring viz lock to get stream config for %s", log_id_prefix, stream_id
777
+ )
778
+ async with component._get_visualization_lock():
779
+ stream_config = component._active_visualization_streams.get(stream_id)
780
+ log.debug(
781
+ "%s Released viz lock after getting stream config for %s",
782
+ log_id_prefix,
783
+ stream_id,
784
+ )
785
+
786
+ if not stream_config:
787
+ log.warning("%s Stream ID %s not found.", log_id_prefix, stream_id)
788
+ raise HTTPException(
789
+ status_code=status.HTTP_404_NOT_FOUND,
790
+ detail="Visualization stream not found.",
791
+ )
792
+
793
+ stream_owner_id = stream_config.get("user_id")
794
+ resolved_stream_owner = _resolve_user_identity_for_authorization(
795
+ component, stream_owner_id
796
+ )
797
+ resolved_requester = _resolve_user_identity_for_authorization(component, user_id)
798
+
799
+ if resolved_stream_owner != resolved_requester:
800
+ log.warning(
801
+ "%s User %s (resolved: %s) forbidden to access stream %s owned by %s (resolved: %s).",
802
+ log_id_prefix,
803
+ user_id,
804
+ resolved_requester,
805
+ stream_id,
806
+ stream_owner_id,
807
+ resolved_stream_owner,
808
+ )
809
+ raise HTTPException(
810
+ status_code=status.HTTP_403_FORBIDDEN,
811
+ detail="Access to this visualization stream is forbidden.",
812
+ )
813
+
814
+ sse_queue: Optional[asyncio.Queue] = stream_config.get("sse_queue")
815
+ if not sse_queue:
816
+ log.error(
817
+ "%s SSE queue not found for stream ID %s, though stream config exists.",
818
+ log_id_prefix,
819
+ stream_id,
820
+ )
821
+ raise HTTPException(
822
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
823
+ detail="Internal error: SSE queue missing for stream.",
824
+ )
825
+
826
+ async def event_generator():
827
+ log.debug(
828
+ "%s SSE event generator started for stream %s.", log_id_prefix, stream_id
829
+ )
830
+ try:
831
+ yield {
832
+ "comment": f"SSE connection established for visualization stream {stream_id}"
833
+ }
834
+ while True:
835
+ if await fastapi_request.is_disconnected():
836
+ log.info(
837
+ "%s Client disconnected from stream %s.",
838
+ log_id_prefix,
839
+ stream_id,
840
+ )
841
+ break
842
+ try:
843
+ event_payload = await asyncio.wait_for(sse_queue.get(), timeout=30)
844
+ if event_payload is None:
845
+ log.info(
846
+ "%s SSE queue for stream %s received None sentinel. Closing connection.",
847
+ log_id_prefix,
848
+ stream_id,
849
+ )
850
+ break
851
+ if _include_for_visualization(event_payload):
852
+ if trace_logger.isEnabledFor(logging.DEBUG):
853
+ trace_logger.debug(
854
+ "%s Yielding event for stream %s: %s",
855
+ log_id_prefix,
856
+ stream_id,
857
+ event_payload,
858
+ )
859
+ else:
860
+ log.debug(
861
+ "%s Yielding event for stream %s",
862
+ log_id_prefix,
863
+ stream_id,
864
+ )
865
+ yield event_payload
866
+ sse_queue.task_done()
867
+ except asyncio.TimeoutError:
868
+ yield {"comment": "keep-alive"}
869
+ continue
870
+ except asyncio.CancelledError:
871
+ log.debug(
872
+ "%s SSE event generator for stream %s cancelled.",
873
+ log_id_prefix,
874
+ stream_id,
875
+ )
876
+ break
877
+ except Exception as e:
878
+ log.exception(
879
+ "%s Error in SSE event generator for stream %s: %s",
880
+ log_id_prefix,
881
+ stream_id,
882
+ e,
883
+ )
884
+ finally:
885
+ log.debug(
886
+ "%s SSE event generator for stream %s finished.",
887
+ log_id_prefix,
888
+ stream_id,
889
+ )
890
+
891
+ return EventSourceResponse(event_generator())
892
+
893
+
894
+ @router.put("/{stream_id}/config", response_model=VisualizationConfigUpdateResponse)
895
+ async def update_visualization_stream_config(
896
+ stream_id: str,
897
+ update_request: VisualizationConfigUpdateRequest,
898
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
899
+ user_id: str = Depends(get_user_id),
900
+ ):
901
+ """Modifies the configuration of an active visualization stream."""
902
+ log_id_prefix = f"{component.log_identifier}[PUT /viz/{stream_id}/config]"
903
+ log.info(
904
+ "%s Request received from user %s to update stream.", log_id_prefix, user_id
905
+ )
906
+
907
+ log.debug(
908
+ "%s Acquiring viz lock to update stream config for %s", log_id_prefix, stream_id
909
+ )
910
+ async with component._get_visualization_lock():
911
+ stream_config = component._active_visualization_streams.get(stream_id)
912
+ if not stream_config:
913
+ raise HTTPException(
914
+ status_code=status.HTTP_404_NOT_FOUND,
915
+ detail="Visualization stream not found.",
916
+ )
917
+
918
+ if stream_config.get("user_id") != user_id:
919
+ raise HTTPException(
920
+ status_code=status.HTTP_403_FORBIDDEN,
921
+ detail="User not authorized to modify this stream.",
922
+ )
923
+
924
+ user_config = stream_config.get("user_config", {})
925
+ config_resolver = MiddlewareRegistry.get_config_resolver()
926
+ gateway_context_for_validation = {
927
+ "gateway_id": component.gateway_id,
928
+ "gateway_namespace": component.namespace,
929
+ }
930
+ current_abstract_targets: List[ActualSubscribedTarget] = [
931
+ ActualSubscribedTarget(**t.model_dump())
932
+ for t in stream_config.get("abstract_targets", [])
933
+ ]
934
+ current_solace_topics: Set[str] = stream_config.get(
935
+ "solace_topics", set()
936
+ ).copy()
937
+
938
+ if update_request.subscription_targets_to_remove:
939
+ targets_actually_removed_abstract = []
940
+ for target_to_remove_req in update_request.subscription_targets_to_remove:
941
+ effective_identifier_remove = target_to_remove_req.identifier
942
+ target_for_translation_remove = target_to_remove_req
943
+
944
+ if target_to_remove_req.type == "current_namespace_a2a_messages":
945
+ effective_identifier_remove = component.namespace
946
+ target_for_translation_remove = SubscriptionTarget(
947
+ type="namespace_a2a_messages",
948
+ identifier=effective_identifier_remove,
949
+ )
950
+
951
+ if (
952
+ not effective_identifier_remove
953
+ and target_to_remove_req.type != "current_namespace_a2a_messages"
954
+ ):
955
+ log.warning(
956
+ "%s Identifier missing for removal target type %s. Skipping.",
957
+ log_id_prefix,
958
+ target_to_remove_req.type,
959
+ )
960
+ continue
961
+
962
+ solace_topics_for_removal = _translate_target_to_solace_topics(
963
+ target_for_translation_remove, component.namespace
964
+ )
965
+ removed_any_solace_topic_for_this_abstract_target = False
966
+ for topic_str in solace_topics_for_removal:
967
+ if topic_str in current_solace_topics:
968
+ if await component._remove_visualization_subscription_nolock(
969
+ topic_str, stream_id
970
+ ):
971
+ log.info(
972
+ "%s Unsubscribed (no-lock) from Solace topic: %s for stream %s (due to removal of %s)",
973
+ log_id_prefix,
974
+ topic_str,
975
+ stream_id,
976
+ effective_identifier_remove,
977
+ )
978
+ current_solace_topics.remove(topic_str)
979
+ removed_any_solace_topic_for_this_abstract_target = True
980
+ else:
981
+ log.error(
982
+ "%s Failed to unsubscribe from Solace topic: %s for stream %s",
983
+ log_id_prefix,
984
+ topic_str,
985
+ stream_id,
986
+ )
987
+
988
+ if removed_any_solace_topic_for_this_abstract_target:
989
+ if target_to_remove_req.type == "current_namespace_a2a_messages":
990
+ current_abstract_targets = [
991
+ t
992
+ for t in current_abstract_targets
993
+ if not (
994
+ t.type == target_to_remove_req.type
995
+ or (
996
+ t.type == "namespace_a2a_messages"
997
+ and t.identifier == effective_identifier_remove
998
+ )
999
+ )
1000
+ ]
1001
+ else:
1002
+ current_abstract_targets = [
1003
+ t
1004
+ for t in current_abstract_targets
1005
+ if not (
1006
+ t.type == target_to_remove_req.type
1007
+ and t.identifier == effective_identifier_remove
1008
+ )
1009
+ ]
1010
+ targets_actually_removed_abstract.append(
1011
+ effective_identifier_remove
1012
+ )
1013
+
1014
+ log.info(
1015
+ "%s Processed removals. Abstract targets effectively removed identifiers: %s",
1016
+ log_id_prefix,
1017
+ targets_actually_removed_abstract,
1018
+ )
1019
+
1020
+ if update_request.subscription_targets_to_add:
1021
+ for target_to_add_req in update_request.subscription_targets_to_add:
1022
+ effective_identifier_add = target_to_add_req.identifier
1023
+ target_for_translation_add = target_to_add_req
1024
+ original_type_add = target_to_add_req.type
1025
+
1026
+ if target_to_add_req.type == "current_namespace_a2a_messages":
1027
+ effective_identifier_add = component.namespace
1028
+ target_for_translation_add = SubscriptionTarget(
1029
+ type="namespace_a2a_messages",
1030
+ identifier=effective_identifier_add,
1031
+ )
1032
+
1033
+ is_already_present = False
1034
+ for existing_target in current_abstract_targets:
1035
+ if existing_target.type == original_type_add and (
1036
+ original_type_add == "current_namespace_a2a_messages"
1037
+ or existing_target.identifier == effective_identifier_add
1038
+ ):
1039
+ is_already_present = True
1040
+ break
1041
+ if (
1042
+ original_type_add == "namespace_a2a_messages"
1043
+ and existing_target.type == "current_namespace_a2a_messages"
1044
+ and effective_identifier_add == component.namespace
1045
+ ):
1046
+ is_already_present = True
1047
+ break
1048
+
1049
+ if is_already_present:
1050
+ log.info(
1051
+ "%s Target %s (type: %s) effectively already subscribed. Skipping add.",
1052
+ log_id_prefix,
1053
+ effective_identifier_add,
1054
+ original_type_add,
1055
+ )
1056
+ continue
1057
+
1058
+ target_status = "denied_due_to_scope"
1059
+ required_scope = ""
1060
+
1061
+ identifier_for_spec = target_to_add_req.identifier
1062
+ if original_type_add == "current_namespace_a2a_messages":
1063
+ if target_to_add_req.identifier is None:
1064
+ identifier_for_spec = None
1065
+
1066
+ operation_spec = {
1067
+ "operation_type": "visualization_subscription_update",
1068
+ "target_type": original_type_add,
1069
+ "target_identifier": identifier_for_spec,
1070
+ }
1071
+
1072
+ validation_result = config_resolver.validate_operation_config(
1073
+ user_config, operation_spec, gateway_context_for_validation
1074
+ )
1075
+ has_permission = validation_result.get("valid", False)
1076
+
1077
+ if has_permission:
1078
+ solace_topics_for_target = _translate_target_to_solace_topics(
1079
+ target_for_translation_add, component.namespace
1080
+ )
1081
+ if not solace_topics_for_target:
1082
+ target_status = "error_translating_target"
1083
+ else:
1084
+ all_topics_added_successfully = True
1085
+ temp_solace_topics_added_for_this_target = set()
1086
+ for topic_str in solace_topics_for_target:
1087
+ if await component._add_visualization_subscription(
1088
+ topic_str, stream_id
1089
+ ):
1090
+ current_solace_topics.add(topic_str)
1091
+ temp_solace_topics_added_for_this_target.add(topic_str)
1092
+ else:
1093
+ all_topics_added_successfully = False
1094
+ log.error(
1095
+ "%s Failed to add subscription to Solace topic: %s for stream %s (target: %s)",
1096
+ log_id_prefix,
1097
+ topic_str,
1098
+ stream_id,
1099
+ effective_identifier_add,
1100
+ )
1101
+
1102
+ if all_topics_added_successfully:
1103
+ target_status = "subscribed"
1104
+ response_target_data = target_to_add_req.model_dump()
1105
+ if original_type_add == "current_namespace_a2a_messages":
1106
+ response_target_data["identifier"] = (
1107
+ effective_identifier_add
1108
+ )
1109
+ current_abstract_targets.append(
1110
+ ActualSubscribedTarget(
1111
+ **response_target_data, status=target_status
1112
+ )
1113
+ )
1114
+ else:
1115
+ target_status = "error_adding_subscription"
1116
+ for topic_str in temp_solace_topics_added_for_this_target:
1117
+ await component._remove_visualization_subscription_nolock(
1118
+ topic_str, stream_id
1119
+ )
1120
+ current_solace_topics.discard(topic_str)
1121
+ log.warning(
1122
+ "%s Rolled back Solace subscriptions (no-lock) for failed abstract target %s",
1123
+ log_id_prefix,
1124
+ effective_identifier_add,
1125
+ )
1126
+ else:
1127
+ log.warning(
1128
+ "%s User %s denied subscription to target %s (type: %s) due to missing scope: %s",
1129
+ log_id_prefix,
1130
+ user_id,
1131
+ effective_identifier_add,
1132
+ original_type_add,
1133
+ required_scope,
1134
+ )
1135
+
1136
+ if target_status not in ["subscribed", "denied_due_to_scope"]:
1137
+ failed_target_data = target_to_add_req.model_dump()
1138
+ if original_type_add == "current_namespace_a2a_messages":
1139
+ failed_target_data["identifier"] = effective_identifier_add
1140
+
1141
+ component._active_visualization_streams[stream_id][
1142
+ "abstract_targets"
1143
+ ] = current_abstract_targets
1144
+ component._active_visualization_streams[stream_id][
1145
+ "solace_topics"
1146
+ ] = current_solace_topics
1147
+
1148
+ log.info(
1149
+ "%s Stream %s configuration updated. Current abstract targets: %d, Solace topics: %d",
1150
+ log_id_prefix,
1151
+ stream_id,
1152
+ len(current_abstract_targets),
1153
+ len(current_solace_topics),
1154
+ )
1155
+ log.debug(
1156
+ "%s Released viz lock after updating stream config for %s",
1157
+ log_id_prefix,
1158
+ stream_id,
1159
+ )
1160
+
1161
+ return VisualizationConfigUpdateResponse(
1162
+ stream_id=stream_id,
1163
+ current_subscribed_targets=current_abstract_targets,
1164
+ )
1165
+
1166
+
1167
+ @router.delete("/{stream_id}/unsubscribe", status_code=status.HTTP_204_NO_CONTENT)
1168
+ async def unsubscribe_from_visualization_stream(
1169
+ stream_id: str,
1170
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
1171
+ sse_manager: SSEManager = Depends(get_sse_manager),
1172
+ user_id: str = Depends(get_user_id),
1173
+ ):
1174
+ """Terminates an active visualization stream."""
1175
+ log_id_prefix = f"{component.log_identifier}[DELETE /viz/{stream_id}]"
1176
+ log.info(
1177
+ "%s Request received from user %s to unsubscribe from stream.",
1178
+ log_id_prefix,
1179
+ user_id,
1180
+ )
1181
+
1182
+ log.debug(
1183
+ "%s Acquiring viz lock to unsubscribe from stream %s", log_id_prefix, stream_id
1184
+ )
1185
+ async with component._get_visualization_lock():
1186
+ stream_config = component._active_visualization_streams.get(stream_id)
1187
+ if not stream_config:
1188
+ log.info(
1189
+ "%s Stream %s not found, no action needed.", log_id_prefix, stream_id
1190
+ )
1191
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
1192
+
1193
+ if stream_config.get("user_id") != user_id:
1194
+ raise HTTPException(
1195
+ status_code=status.HTTP_403_FORBIDDEN,
1196
+ detail="User not authorized to unsubscribe from this stream.",
1197
+ )
1198
+
1199
+ topics_to_remove = list(stream_config.get("solace_topics", []))
1200
+ for topic_str in topics_to_remove:
1201
+ await component._remove_visualization_subscription_nolock(
1202
+ topic_str, stream_id
1203
+ )
1204
+
1205
+ sse_queue = stream_config.get("sse_queue")
1206
+ if sse_queue:
1207
+ await sse_manager.close_connection(stream_id, sse_queue)
1208
+
1209
+ component._active_visualization_streams.pop(stream_id, None)
1210
+ log.info("%s Stream %s unsubscribed and removed.", log_id_prefix, stream_id)
1211
+ log.debug(
1212
+ "%s Released viz lock after unsubscribing from stream %s",
1213
+ log_id_prefix,
1214
+ stream_id,
1215
+ )
1216
+
1217
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
1218
+
1219
+
1220
+ log.info("Initialized Router for A2A Message Visualization.")