solace-agent-mesh 1.6.1__py3-none-any.whl → 1.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (481) hide show
  1. solace_agent_mesh/agent/adk/alembic/README +74 -0
  2. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  3. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  4. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  5. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  6. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  7. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
  8. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
  9. solace_agent_mesh/agent/adk/callbacks.py +852 -109
  10. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
  11. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
  12. solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
  13. solace_agent_mesh/agent/adk/models/lite_llm.py +77 -21
  14. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
  15. solace_agent_mesh/agent/adk/runner.py +85 -20
  16. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  17. solace_agent_mesh/agent/adk/services.py +94 -18
  18. solace_agent_mesh/agent/adk/setup.py +281 -65
  19. solace_agent_mesh/agent/adk/stream_parser.py +231 -37
  20. solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
  21. solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
  22. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  23. solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
  24. solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
  25. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  26. solace_agent_mesh/agent/proxies/base/component.py +188 -22
  27. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
  28. solace_agent_mesh/agent/sac/app.py +91 -3
  29. solace_agent_mesh/agent/sac/component.py +591 -157
  30. solace_agent_mesh/agent/sac/patch_adk.py +8 -16
  31. solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
  32. solace_agent_mesh/agent/tools/__init__.py +3 -0
  33. solace_agent_mesh/agent/tools/audio_tools.py +3 -3
  34. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +710 -171
  35. solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
  36. solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
  37. solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
  38. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  39. solace_agent_mesh/agent/tools/tool_config_types.py +57 -2
  40. solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
  41. solace_agent_mesh/agent/tools/web_tools.py +125 -17
  42. solace_agent_mesh/agent/utils/artifact_helpers.py +248 -6
  43. solace_agent_mesh/agent/utils/context_helpers.py +17 -0
  44. solace_agent_mesh/assets/docs/404.html +6 -6
  45. solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
  46. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  51. solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
  52. solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
  53. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  54. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
  56. solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
  57. solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
  60. solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
  61. solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
  62. solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
  63. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  64. solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
  65. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
  66. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  67. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  68. solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
  69. solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
  70. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  71. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  72. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  73. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  74. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  75. solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
  76. solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
  77. solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
  78. solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
  79. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  80. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  81. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  82. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  83. solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
  84. solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
  85. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  86. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  95. solace_agent_mesh/assets/docs/assets/js/e04b235d.06d23db6.js +1 -0
  96. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
  101. solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
  102. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
  104. solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
  105. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
  106. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
  107. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
  108. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
  109. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +82 -68
  110. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
  111. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
  112. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
  113. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
  114. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
  115. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
  116. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
  117. solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
  118. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
  119. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  120. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  121. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
  122. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  123. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -49
  124. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
  125. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
  126. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
  127. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
  128. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +90 -0
  129. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
  130. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  131. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
  132. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +162 -171
  133. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
  134. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
  135. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
  136. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
  137. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
  138. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
  139. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
  140. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
  141. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
  142. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
  143. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
  144. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
  145. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
  146. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
  147. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  148. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  149. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  150. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
  151. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
  152. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  153. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +112 -87
  154. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  155. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +87 -64
  156. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  157. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
  158. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -37
  159. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
  160. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
  161. solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
  162. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +311 -0
  163. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
  164. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
  165. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
  166. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
  167. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
  168. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  169. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  170. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
  171. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
  172. solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
  173. solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
  174. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  175. solace_agent_mesh/assets/docs/search-doc-1768329217460.json +1 -0
  176. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  177. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  178. solace_agent_mesh/cli/__init__.py +1 -1
  179. solace_agent_mesh/cli/commands/add_cmd/__init__.py +3 -1
  180. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
  181. solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
  182. solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
  183. solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
  184. solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
  185. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
  186. solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
  187. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
  188. solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
  189. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
  190. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
  191. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
  192. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
  193. solace_agent_mesh/cli/commands/run_cmd.py +64 -49
  194. solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
  195. solace_agent_mesh/cli/main.py +15 -0
  196. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-KnKMP_vb.js} +1 -1
  197. solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
  198. solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
  199. solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
  200. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CGk8Suyh.js +565 -0
  201. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  202. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  203. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  204. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  205. solace_agent_mesh/common/a2a/events.py +2 -1
  206. solace_agent_mesh/common/a2a/protocol.py +5 -0
  207. solace_agent_mesh/common/a2a/types.py +2 -1
  208. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
  209. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  210. solace_agent_mesh/common/agent_registry.py +38 -11
  211. solace_agent_mesh/common/data_parts.py +144 -4
  212. solace_agent_mesh/common/error_handlers.py +83 -0
  213. solace_agent_mesh/common/exceptions.py +24 -0
  214. solace_agent_mesh/common/oauth/__init__.py +17 -0
  215. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  216. solace_agent_mesh/common/oauth/utils.py +50 -0
  217. solace_agent_mesh/common/rag_dto.py +156 -0
  218. solace_agent_mesh/common/sac/sam_component_base.py +97 -19
  219. solace_agent_mesh/common/sam_events/event_service.py +2 -2
  220. solace_agent_mesh/common/services/employee_service.py +1 -1
  221. solace_agent_mesh/common/utils/embeds/constants.py +1 -0
  222. solace_agent_mesh/common/utils/embeds/converter.py +1 -8
  223. solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
  224. solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
  225. solace_agent_mesh/common/utils/embeds/types.py +9 -0
  226. solace_agent_mesh/common/utils/log_formatters.py +20 -0
  227. solace_agent_mesh/common/utils/mime_helpers.py +12 -5
  228. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  229. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  230. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  231. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  232. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  233. solace_agent_mesh/config_portal/backend/common.py +12 -0
  234. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
  235. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
  236. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
  237. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
  238. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
  239. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
  240. solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
  241. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  242. solace_agent_mesh/core_a2a/service.py +3 -2
  243. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  244. solace_agent_mesh/gateway/adapter/base.py +170 -0
  245. solace_agent_mesh/gateway/adapter/types.py +230 -0
  246. solace_agent_mesh/gateway/base/app.py +39 -2
  247. solace_agent_mesh/gateway/base/auth_interface.py +103 -0
  248. solace_agent_mesh/gateway/base/component.py +1027 -151
  249. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  250. solace_agent_mesh/gateway/generic/app.py +50 -0
  251. solace_agent_mesh/gateway/generic/component.py +894 -0
  252. solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
  253. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  254. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  255. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  256. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  257. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  258. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  259. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  260. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  261. solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
  262. solace_agent_mesh/gateway/http_sse/app.py +40 -11
  263. solace_agent_mesh/gateway/http_sse/component.py +285 -160
  264. solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
  265. solace_agent_mesh/gateway/http_sse/main.py +68 -450
  266. solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
  267. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
  268. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  269. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  270. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +26 -3
  271. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
  272. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
  273. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
  274. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
  275. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  276. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  277. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  278. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +8 -2
  279. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
  280. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  281. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  282. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +177 -11
  283. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
  284. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
  285. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +256 -58
  286. solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
  287. solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
  288. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  289. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  290. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  291. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -1
  292. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
  293. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  294. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
  295. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  296. solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
  297. solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
  298. solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
  299. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
  300. solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
  301. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  302. solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
  303. solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
  304. solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
  305. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  306. solace_agent_mesh/gateway/http_sse/routers/visualization.py +92 -9
  307. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  308. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  309. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
  310. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
  311. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  312. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  313. solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
  314. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
  315. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  316. solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
  317. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  318. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
  319. solace_agent_mesh/services/__init__.py +0 -0
  320. solace_agent_mesh/services/platform/__init__.py +29 -0
  321. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  322. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  323. solace_agent_mesh/services/platform/alembic.ini +109 -0
  324. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  325. solace_agent_mesh/services/platform/api/dependencies.py +154 -0
  326. solace_agent_mesh/services/platform/api/main.py +314 -0
  327. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  328. solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
  329. solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
  330. solace_agent_mesh/services/platform/app.py +215 -0
  331. solace_agent_mesh/services/platform/component.py +777 -0
  332. solace_agent_mesh/shared/__init__.py +14 -0
  333. solace_agent_mesh/shared/api/__init__.py +42 -0
  334. solace_agent_mesh/shared/auth/__init__.py +26 -0
  335. solace_agent_mesh/shared/auth/dependencies.py +204 -0
  336. solace_agent_mesh/shared/auth/middleware.py +347 -0
  337. solace_agent_mesh/shared/database/__init__.py +20 -0
  338. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
  339. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
  340. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
  341. solace_agent_mesh/shared/exceptions/__init__.py +36 -0
  342. solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +19 -5
  343. solace_agent_mesh/shared/utils/__init__.py +21 -0
  344. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  345. solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
  346. solace_agent_mesh/templates/platform.yaml +49 -0
  347. solace_agent_mesh/templates/plugin_readme_template.md +3 -25
  348. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  349. solace_agent_mesh/templates/proxy_template.yaml +62 -0
  350. solace_agent_mesh/templates/webui.yaml +148 -6
  351. solace_agent_mesh/tools/web_search/__init__.py +18 -0
  352. solace_agent_mesh/tools/web_search/base.py +84 -0
  353. solace_agent_mesh/tools/web_search/google_search.py +247 -0
  354. solace_agent_mesh/tools/web_search/models.py +99 -0
  355. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
  356. solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
  357. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
  358. solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
  359. solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
  360. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
  361. solace_agent_mesh/agent/adk/models/models_llm.txt +0 -142
  362. solace_agent_mesh/agent/agent_llm.txt +0 -378
  363. solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
  364. solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
  365. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
  366. solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
  367. solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
  368. solace_agent_mesh/agent/testing/testing_llm.txt +0 -57
  369. solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
  370. solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
  371. solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
  372. solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
  373. solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
  374. solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
  375. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
  376. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  377. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
  378. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
  379. solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
  380. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
  381. solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
  382. solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
  383. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
  384. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  385. solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
  386. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
  387. solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
  388. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  389. solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
  390. solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
  391. solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
  392. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
  393. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  394. solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
  395. solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
  396. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +0 -1
  397. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
  398. solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
  399. solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
  400. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
  401. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
  402. solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
  403. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +0 -1
  404. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  405. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  406. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
  407. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  408. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  409. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  410. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  411. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
  412. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
  413. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
  414. solace_agent_mesh/client/webui/frontend/static/assets/client-CaY59VuC.js +0 -25
  415. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  416. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  417. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
  418. solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
  419. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
  420. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
  421. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
  422. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
  423. solace_agent_mesh/common/common_llm.txt +0 -251
  424. solace_agent_mesh/common/common_llm_detail.txt +0 -2562
  425. solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
  426. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
  427. solace_agent_mesh/common/sac/sac_llm.txt +0 -71
  428. solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
  429. solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
  430. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
  431. solace_agent_mesh/common/services/providers/providers_llm.txt +0 -80
  432. solace_agent_mesh/common/services/services_llm.txt +0 -363
  433. solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
  434. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
  435. solace_agent_mesh/common/utils/utils_llm.txt +0 -336
  436. solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
  437. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
  438. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
  439. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
  440. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
  441. solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
  442. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
  443. solace_agent_mesh/gateway/base/base_llm.txt +0 -224
  444. solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
  445. solace_agent_mesh/gateway/gateway_llm.txt +0 -373
  446. solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
  447. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
  448. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
  449. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
  450. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
  451. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
  452. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -263
  453. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
  454. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
  455. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
  456. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
  457. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
  458. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
  459. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
  460. solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
  461. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
  462. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
  463. solace_agent_mesh/llm.txt +0 -228
  464. solace_agent_mesh/llm_detail.txt +0 -2835
  465. solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
  466. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
  467. solace_agent_mesh/templates/logging_config_template.ini +0 -45
  468. solace_agent_mesh/templates/templates_llm.txt +0 -147
  469. solace_agent_mesh-1.6.1.dist-info/RECORD +0 -525
  470. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
  471. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
  472. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
  473. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
  474. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
  475. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
  476. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
  477. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
  478. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
  479. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
  480. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
  481. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,7 @@ Manages Server-Sent Event (SSE) connections for streaming task updates.
5
5
  import logging
6
6
  import asyncio
7
7
  import threading
8
- from typing import Dict, List, Any
8
+ from typing import Dict, List, Any, Callable, Optional
9
9
  import json
10
10
  import datetime
11
11
  import math
@@ -13,43 +13,28 @@ import math
13
13
  from .sse_event_buffer import SSEEventBuffer
14
14
 
15
15
  log = logging.getLogger(__name__)
16
+ trace_logger = logging.getLogger("sam_trace")
17
+
16
18
 
17
19
  class SSEManager:
18
20
  """
19
21
  Manages active SSE connections and distributes events based on task ID.
20
22
  Uses asyncio Queues for buffering events per connection.
23
+
24
+ Note: This manager uses a threading.Lock to ensure thread-safety across
25
+ different event loops (e.g., FastAPI event loop and SAC component event loop).
21
26
  """
22
27
 
23
- def __init__(self, max_queue_size: int, event_buffer: SSEEventBuffer):
28
+ def __init__(self, max_queue_size: int, event_buffer: SSEEventBuffer, session_factory: Optional[Callable] = None):
24
29
  self._connections: Dict[str, List[asyncio.Queue]] = {}
25
30
  self._event_buffer = event_buffer
26
- self._locks: Dict[asyncio.AbstractEventLoop, asyncio.Lock] = {}
27
- self._locks_lock = threading.Lock()
31
+ # Use a single threading lock for cross-event-loop synchronization
32
+ self._lock = threading.Lock()
28
33
  self.log_identifier = "[SSEManager]"
29
34
  self._max_queue_size = max_queue_size
30
-
31
- def _get_lock(self) -> asyncio.Lock:
32
- """Get or create a lock for the current event loop."""
33
- try:
34
- current_loop = asyncio.get_running_loop()
35
- except RuntimeError:
36
- log.error(
37
- "%s _get_lock must be called from within an async context.",
38
- self.log_identifier,
39
- )
40
- raise RuntimeError(
41
- "SSEManager methods must be called from within an async context"
42
- )
43
-
44
- with self._locks_lock:
45
- if current_loop not in self._locks:
46
- self._locks[current_loop] = asyncio.Lock()
47
- log.debug(
48
- "%s Created new lock for event loop %s",
49
- self.log_identifier,
50
- id(current_loop),
51
- )
52
- return self._locks[current_loop]
35
+ self._session_factory = session_factory
36
+ self._background_task_cache: Dict[str, bool] = {} # Cache to avoid repeated DB queries
37
+ self._tasks_with_prior_connection: set = set() # Track tasks that have had at least one SSE connection
53
38
 
54
39
  def _sanitize_json(self, obj):
55
40
  if isinstance(obj, dict):
@@ -77,27 +62,40 @@ class SSEManager:
77
62
  Returns:
78
63
  An asyncio.Queue that the SSE endpoint can consume from.
79
64
  """
80
- lock = self._get_lock()
81
- async with lock:
65
+ connection_queue = asyncio.Queue(maxsize=self._max_queue_size)
66
+ buffered_events = None
67
+
68
+ # Use threading lock for cross-event-loop synchronization
69
+ with self._lock:
82
70
  if task_id not in self._connections:
83
71
  self._connections[task_id] = []
84
72
 
85
- connection_queue = asyncio.Queue(maxsize=self._max_queue_size)
86
-
87
- # Flush any pending events from the buffer to the new connection
73
+ # Get buffered events atomically with adding the queue to connections
88
74
  buffered_events = self._event_buffer.get_and_remove_buffer(task_id)
89
- if buffered_events:
90
- for event in buffered_events:
91
- await connection_queue.put(event)
92
75
 
76
+ # Add queue to connections BEFORE releasing lock to ensure
77
+ # no events are buffered after we've retrieved the buffer
93
78
  self._connections[task_id].append(connection_queue)
79
+
80
+ # Mark this task as having had at least one connection
81
+ # This is used to distinguish "no connection yet" from "had connection but disconnected"
82
+ self._tasks_with_prior_connection.add(task_id)
83
+
94
84
  log.debug(
95
85
  "%s Created SSE connection queue for Task ID: %s. Total queues for task: %d",
96
86
  self.log_identifier,
97
87
  task_id,
98
88
  len(self._connections[task_id]),
99
89
  )
100
- return connection_queue
90
+
91
+ # Put buffered events into queue AFTER releasing the lock
92
+ # This is safe because the queue is already registered in _connections,
93
+ # so any new events will go directly to the queue via send_event
94
+ if buffered_events:
95
+ for event in buffered_events:
96
+ await connection_queue.put(event)
97
+
98
+ return connection_queue
101
99
 
102
100
  async def remove_sse_connection(
103
101
  self, task_id: str, connection_queue: asyncio.Queue
@@ -109,8 +107,7 @@ class SSEManager:
109
107
  task_id: The ID of the task.
110
108
  connection_queue: The specific queue instance to remove.
111
109
  """
112
- lock = self._get_lock()
113
- async with lock:
110
+ with self._lock:
114
111
  if task_id in self._connections:
115
112
  try:
116
113
  self._connections[task_id].remove(connection_queue)
@@ -140,6 +137,49 @@ class SSEManager:
140
137
  task_id,
141
138
  )
142
139
 
140
+ def _is_background_task(self, task_id: str) -> bool:
141
+ """
142
+ Check if a task is a background task by querying the database.
143
+ Uses caching to avoid repeated queries.
144
+
145
+ Args:
146
+ task_id: The ID of the task to check
147
+
148
+ Returns:
149
+ True if the task is a background task, False otherwise
150
+ """
151
+ # Check cache first
152
+ if task_id in self._background_task_cache:
153
+ return self._background_task_cache[task_id]
154
+
155
+ # If no session factory, assume not a background task
156
+ if not self._session_factory:
157
+ return False
158
+
159
+ try:
160
+ from .repository.task_repository import TaskRepository
161
+
162
+ db = self._session_factory()
163
+ try:
164
+ repo = TaskRepository()
165
+ task = repo.find_by_id(db, task_id)
166
+ is_background = task and task.background_execution_enabled
167
+
168
+ # Cache the result
169
+ self._background_task_cache[task_id] = is_background
170
+
171
+ return is_background
172
+ finally:
173
+ db.close()
174
+ except Exception as e:
175
+ log.warning(
176
+ "%s Failed to check if task %s is a background task: %s",
177
+ self.log_identifier,
178
+ task_id,
179
+ e,
180
+ )
181
+ return False
182
+
143
183
  async def send_event(
144
184
  self, task_id: str, event_data: Dict[str, Any], event_type: str = "message"
145
185
  ):
@@ -152,95 +192,130 @@ class SSEManager:
152
192
  event_data: The dictionary representing the A2A event (e.g., TaskStatusUpdateEvent).
153
193
  event_type: The type of the SSE event (default: "message").
154
194
  """
155
- lock = self._get_lock()
156
- async with lock:
157
- queues = self._connections.get(task_id)
195
+ # Serialize data outside the lock
196
+ try:
197
+ serialized_data = json.dumps(
198
+ self._sanitize_json(event_data), allow_nan=False
199
+ )
200
+ except Exception as json_err:
201
+ log.error(
202
+ "%s Failed to JSON serialize event data for Task ID %s: %s",
203
+ self.log_identifier,
204
+ task_id,
205
+ json_err,
206
+ )
207
+ return
158
208
 
159
- try:
160
- serialized_data = json.dumps(
161
- self._sanitize_json(event_data), allow_nan=False
162
- )
163
- except Exception as json_err:
164
- log.error(
165
- "%s Failed to JSON serialize event data for Task ID %s: %s",
166
- self.log_identifier,
167
- task_id,
168
- json_err,
169
- )
170
- return
209
+ sse_payload = {"event": event_type, "data": serialized_data}
171
210
 
172
- sse_payload = {"event": event_type, "data": serialized_data}
211
+ # Get queues and decide action under the lock
212
+ queues_copy = None
213
+
214
+ with self._lock:
215
+ queues = self._connections.get(task_id)
173
216
 
174
217
  if not queues:
175
- log.debug(
176
- "%s No active SSE connections for Task ID: %s. Buffering event.",
177
- self.log_identifier,
178
- task_id,
179
- )
180
- self._event_buffer.buffer_event(task_id, sse_payload)
181
- return
218
+ # Check if this is a background task (outside lock would be better,
219
+ # but we need the decision to be atomic with the buffering)
220
+ is_background_task = self._is_background_task(task_id)
182
221
 
183
- log.debug(
184
- "%s Prepared SSE payload for Task ID %s: %s",
185
- self.log_identifier,
186
- task_id,
187
- sse_payload,
188
- )
222
+ # Check if this task has ever had a connection
223
+ has_had_connection = task_id in self._tasks_with_prior_connection
189
224
 
190
- queues_to_remove = []
191
- for connection_queue in list(self._connections.get(task_id, [])):
192
- try:
193
- await asyncio.wait_for(
194
- connection_queue.put(sse_payload), timeout=0.1
195
- )
225
+ # Only drop events for background tasks that have HAD a connection before
226
+ # If no connection has ever been made, we must buffer so the first client gets the events
227
+ if is_background_task and has_had_connection:
228
+ # For background tasks where client disconnected, drop events to prevent buffer overflow
196
229
  log.debug(
197
- "%s Queued event for Task ID: %s to one connection.",
230
+ "%s No active SSE connections for background task %s (had prior connection). Dropping event to prevent buffer overflow.",
198
231
  self.log_identifier,
199
232
  task_id,
200
233
  )
201
- except asyncio.QueueFull:
202
- log.warning(
203
- "%s SSE connection queue full for Task ID: %s. Event dropped for one connection.",
204
- self.log_identifier,
205
- task_id,
206
- )
207
- queues_to_remove.append(connection_queue)
208
- except asyncio.TimeoutError:
209
- log.warning(
210
- "%s Timeout putting event onto SSE queue for Task ID: %s. Event dropped for one connection.",
211
- self.log_identifier,
212
- task_id,
213
- )
214
- queues_to_remove.append(connection_queue)
215
- except Exception as e:
216
- log.error(
217
- "%s Error putting event onto queue for Task ID %s: %s",
234
+ else:
235
+ log.debug(
236
+ "%s No active SSE connections for Task ID: %s. Buffering event.",
218
237
  self.log_identifier,
219
238
  task_id,
220
- e,
221
239
  )
222
- queues_to_remove.append(connection_queue)
223
-
224
- if queues_to_remove and task_id in self._connections:
225
- current_queues = self._connections[task_id]
226
- for q in queues_to_remove:
227
- try:
228
- current_queues.remove(q)
229
- log.warning(
230
- "%s Removed potentially broken/full SSE queue for Task ID: %s",
240
+ self._event_buffer.buffer_event(task_id, sse_payload)
241
+ return
242
+ else:
243
+ # Make a copy of queues to iterate outside the lock
244
+ queues_copy = list(queues)
245
+
246
+ # Log the payload outside the lock
247
+ if trace_logger.isEnabledFor(logging.DEBUG):
248
+ trace_logger.debug(
249
+ "%s Prepared SSE payload for Task ID %s: %s",
250
+ self.log_identifier,
251
+ task_id,
252
+ sse_payload,
253
+ )
254
+ else:
255
+ log.debug(
256
+ "%s Prepared SSE payload for Task ID %s",
257
+ self.log_identifier,
258
+ task_id,
259
+ )
260
+
261
+ # Send to queues outside the lock (async operations)
262
+ queues_to_remove = []
263
+ for connection_queue in queues_copy:
264
+ try:
265
+ await asyncio.wait_for(
266
+ connection_queue.put(sse_payload), timeout=0.1
267
+ )
268
+ log.debug(
269
+ "%s Queued event for Task ID: %s to one connection.",
270
+ self.log_identifier,
271
+ task_id,
272
+ )
273
+ except asyncio.QueueFull:
274
+ log.warning(
275
+ "%s SSE connection queue full for Task ID: %s. Event dropped for one connection.",
276
+ self.log_identifier,
277
+ task_id,
278
+ )
279
+ queues_to_remove.append(connection_queue)
280
+ except asyncio.TimeoutError:
281
+ log.warning(
282
+ "%s Timeout putting event onto SSE queue for Task ID: %s. Event dropped for one connection.",
283
+ self.log_identifier,
284
+ task_id,
285
+ )
286
+ queues_to_remove.append(connection_queue)
287
+ except Exception as e:
288
+ log.error(
289
+ "%s Error putting event onto queue for Task ID %s: %s",
290
+ self.log_identifier,
291
+ task_id,
292
+ e,
293
+ )
294
+ queues_to_remove.append(connection_queue)
295
+
296
+ # Remove broken queues under the lock
297
+ if queues_to_remove:
298
+ with self._lock:
299
+ if task_id in self._connections:
300
+ current_queues = self._connections[task_id]
301
+ for q in queues_to_remove:
302
+ try:
303
+ current_queues.remove(q)
304
+ log.warning(
305
+ "%s Removed potentially broken/full SSE queue for Task ID: %s",
306
+ self.log_identifier,
307
+ task_id,
308
+ )
309
+ except ValueError:
310
+ pass
311
+
312
+ if not current_queues:
313
+ del self._connections[task_id]
314
+ log.debug(
315
+ "%s Removed Task ID entry: %s after cleaning queues.",
231
316
  self.log_identifier,
232
317
  task_id,
233
318
  )
234
- except ValueError:
235
- pass
236
-
237
- if not current_queues:
238
- del self._connections[task_id]
239
- log.debug(
240
- "%s Removed Task ID entry: %s after cleaning queues.",
241
- self.log_identifier,
242
- task_id,
243
- )
244
319
 
245
320
  async def close_connection(self, task_id: str, connection_queue: asyncio.Queue):
246
321
  """
@@ -276,54 +351,58 @@ class SSEManager:
276
351
  finally:
277
352
  await self.remove_sse_connection(task_id, connection_queue)
278
353
 
354
+ async def drain_buffer_for_background_task(self, task_id: str):
355
+ """
356
+ Drains the event buffer for a background task when a client disconnects.
357
+ This prevents buffer overflow warnings when background tasks continue
358
+ generating events with no active consumers.
359
+
360
+ Args:
361
+ task_id: The ID of the background task
362
+ """
363
+ log.info(
364
+ "%s Draining event buffer for background task: %s",
365
+ self.log_identifier,
366
+ task_id,
367
+ )
368
+
369
+ # Remove any buffered events to prevent overflow
370
+ buffered_events = self._event_buffer.get_and_remove_buffer(task_id)
371
+ if buffered_events:
372
+ log.info(
373
+ "%s Drained %d buffered events for background task: %s",
374
+ self.log_identifier,
375
+ len(buffered_events),
376
+ task_id,
377
+ )
378
+ else:
379
+ log.debug(
380
+ "%s No buffered events to drain for background task: %s",
381
+ self.log_identifier,
382
+ task_id,
383
+ )
384
+
279
385
  async def close_all_for_task(self, task_id: str):
280
386
  """
281
387
  Closes all SSE connections associated with a specific task.
282
388
  If a connection existed, it also cleans up the event buffer.
283
389
  If no connection ever existed, the buffer is left for a late-connecting client.
284
390
  """
285
- lock = self._get_lock()
286
- async with lock:
391
+ queues_to_close = None
392
+ should_remove_buffer = False
393
+
394
+ with self._lock:
287
395
  if task_id in self._connections:
288
396
  # This is the "normal" case: a client is or was connected.
289
397
  # It's safe to clean up everything.
290
398
  queues_to_close = self._connections.pop(task_id)
399
+ should_remove_buffer = True
291
400
  log.debug(
292
401
  "%s Closing %d SSE connections for Task ID: %s and cleaning up buffer.",
293
402
  self.log_identifier,
294
403
  len(queues_to_close),
295
404
  task_id,
296
405
  )
297
- for q in queues_to_close:
298
- try:
299
- await asyncio.wait_for(q.put(None), timeout=0.1)
300
- except asyncio.QueueFull:
301
- log.warning(
302
- "%s Could not put None (close signal) on full queue during close_all for Task ID: %s.",
303
- self.log_identifier,
304
- task_id,
305
- )
306
- except asyncio.TimeoutError:
307
- log.warning(
308
- "%s Timeout putting None (close signal) on queue during close_all for Task ID: %s.",
309
- self.log_identifier,
310
- task_id,
311
- )
312
- except Exception as e:
313
- log.error(
314
- "%s Error putting None (close signal) on queue during close_all for Task ID %s: %s",
315
- self.log_identifier,
316
- task_id,
317
- e,
318
- )
319
-
320
- # Since a connection existed, the buffer is no longer needed.
321
- self._event_buffer.remove_buffer(task_id)
322
- log.debug(
323
- "%s Removed Task ID entry: %s and signaled queues to close.",
324
- self.log_identifier,
325
- task_id,
326
- )
327
406
  else:
328
407
  # This is the "race condition" case: no client has connected yet.
329
408
  # We MUST leave the buffer intact for the late-connecting client.
@@ -333,39 +412,80 @@ class SSEManager:
333
412
  task_id,
334
413
  )
335
414
 
336
- def cleanup_old_locks(self):
337
- """Remove locks for closed event loops to prevent memory leaks."""
338
- with self._locks_lock:
339
- closed_loops = [loop for loop in self._locks if loop.is_closed()]
340
- for loop in closed_loops:
341
- del self._locks[loop]
415
+ # Close queues outside the lock (async operations)
416
+ if queues_to_close:
417
+ for q in queues_to_close:
418
+ try:
419
+ await asyncio.wait_for(q.put(None), timeout=0.1)
420
+ except asyncio.QueueFull:
421
+ log.warning(
422
+ "%s Could not put None (close signal) on full queue during close_all for Task ID: %s.",
423
+ self.log_identifier,
424
+ task_id,
425
+ )
426
+ except asyncio.TimeoutError:
427
+ log.warning(
428
+ "%s Timeout putting None (close signal) on queue during close_all for Task ID: %s.",
429
+ self.log_identifier,
430
+ task_id,
431
+ )
432
+ except Exception as e:
433
+ log.error(
434
+ "%s Error putting None (close signal) on queue during close_all for Task ID %s: %s",
435
+ self.log_identifier,
436
+ task_id,
437
+ e,
438
+ )
439
+
440
+ # Since a connection existed, the buffer is no longer needed.
441
+ # This is safe to do without lock since we already removed the task from _connections
442
+ if should_remove_buffer:
443
+ self._event_buffer.remove_buffer(task_id)
444
+
445
+ # Clean up the connection tracking
446
+ with self._lock:
447
+ self._tasks_with_prior_connection.discard(task_id)
448
+
342
449
  log.debug(
343
- "%s Cleaned up lock for closed event loop %s",
450
+ "%s Removed Task ID entry: %s and signaled queues to close.",
344
451
  self.log_identifier,
345
- id(loop),
452
+ task_id,
346
453
  )
347
454
 
455
+ def cleanup_old_locks(self):
456
+ """Legacy method - no longer needed with single threading lock.
457
+ Kept for API compatibility but does nothing."""
458
+ pass
459
+
348
460
  async def close_all(self):
349
461
  """Closes all active SSE connections managed by this instance."""
350
462
  self.cleanup_old_locks()
351
- lock = self._get_lock()
352
- async with lock:
463
+
464
+ # Collect all queues to close under the lock
465
+ all_queues_to_close = []
466
+ all_task_ids = []
467
+
468
+ with self._lock:
353
469
  log.debug("%s Closing all active SSE connections...", self.log_identifier)
354
470
  all_task_ids = list(self._connections.keys())
355
- closed_count = 0
356
471
  for task_id in all_task_ids:
357
472
  if task_id in self._connections:
358
473
  queues = self._connections.pop(task_id)
359
- closed_count += len(queues)
360
- for q in queues:
361
- try:
362
- await asyncio.wait_for(q.put(None), timeout=0.1)
363
- except Exception:
364
- pass
365
- log.debug(
366
- "%s Closed %d connections for tasks: %s",
367
- self.log_identifier,
368
- closed_count,
369
- all_task_ids,
370
- )
474
+ all_queues_to_close.extend(queues)
371
475
  self._connections.clear()
476
+ self._tasks_with_prior_connection.clear()
477
+
478
+ # Close queues outside the lock (async operations)
479
+ closed_count = len(all_queues_to_close)
480
+ for q in all_queues_to_close:
481
+ try:
482
+ await asyncio.wait_for(q.put(None), timeout=0.1)
483
+ except Exception:
484
+ pass
485
+
486
+ log.debug(
487
+ "%s Closed %d connections for tasks: %s",
488
+ self.log_identifier,
489
+ closed_count,
490
+ all_task_ids,
491
+ )