solace-agent-mesh 0.2.3__py3-none-any.whl → 1.0.1__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 (518) hide show
  1. solace_agent_mesh/agent/adk/adk_llm.txt +93 -0
  2. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  3. solace_agent_mesh/agent/adk/callbacks.py +1694 -0
  4. solace_agent_mesh/agent/adk/filesystem_artifact_service.py +381 -0
  5. solace_agent_mesh/agent/adk/invocation_monitor.py +295 -0
  6. solace_agent_mesh/agent/adk/models/lite_llm.py +872 -0
  7. solace_agent_mesh/agent/adk/models/models_llm.txt +94 -0
  8. solace_agent_mesh/agent/adk/runner.py +353 -0
  9. solace_agent_mesh/agent/adk/services.py +240 -0
  10. solace_agent_mesh/agent/adk/setup.py +751 -0
  11. solace_agent_mesh/agent/adk/stream_parser.py +214 -0
  12. solace_agent_mesh/agent/adk/tool_wrapper.py +139 -0
  13. solace_agent_mesh/agent/agent_llm.txt +41 -0
  14. solace_agent_mesh/agent/protocol/event_handlers.py +1469 -0
  15. solace_agent_mesh/agent/protocol/protocol_llm.txt +21 -0
  16. solace_agent_mesh/agent/sac/app.py +640 -0
  17. solace_agent_mesh/agent/sac/component.py +3388 -0
  18. solace_agent_mesh/agent/sac/patch_adk.py +111 -0
  19. solace_agent_mesh/agent/sac/sac_llm.txt +105 -0
  20. solace_agent_mesh/agent/sac/task_execution_context.py +176 -0
  21. solace_agent_mesh/agent/testing/__init__.py +3 -0
  22. solace_agent_mesh/agent/testing/debug_utils.py +135 -0
  23. solace_agent_mesh/agent/testing/testing_llm.txt +90 -0
  24. solace_agent_mesh/agent/tools/__init__.py +14 -0
  25. solace_agent_mesh/agent/tools/audio_tools.py +1622 -0
  26. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +1954 -0
  27. solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +238 -0
  28. solace_agent_mesh/agent/tools/general_agent_tools.py +569 -0
  29. solace_agent_mesh/agent/tools/image_tools.py +1184 -0
  30. solace_agent_mesh/agent/tools/peer_agent_tool.py +289 -0
  31. solace_agent_mesh/agent/tools/registry.py +36 -0
  32. solace_agent_mesh/agent/tools/test_tools.py +135 -0
  33. solace_agent_mesh/agent/tools/tool_definition.py +45 -0
  34. solace_agent_mesh/agent/tools/tools_llm.txt +104 -0
  35. solace_agent_mesh/agent/tools/web_tools.py +381 -0
  36. solace_agent_mesh/agent/utils/artifact_helpers.py +927 -0
  37. solace_agent_mesh/agent/utils/config_parser.py +47 -0
  38. solace_agent_mesh/agent/utils/context_helpers.py +60 -0
  39. solace_agent_mesh/agent/utils/utils_llm.txt +153 -0
  40. solace_agent_mesh/assets/docs/404.html +16 -0
  41. solace_agent_mesh/assets/docs/assets/css/styles.906a1503.css +1 -0
  42. solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
  43. solace_agent_mesh/assets/docs/assets/images/sac-flows-80d5b603c6aafd33e87945680ce0abf3.png +0 -0
  44. solace_agent_mesh/assets/docs/assets/images/sac_parts_of_a_component-cb3d0424b1d0c17734c5435cca6b4082.png +0 -0
  45. solace_agent_mesh/assets/docs/assets/js/04989206.674a8007.js +1 -0
  46. solace_agent_mesh/assets/docs/assets/js/0e682baa.79f0ab22.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/1023fc19.015679ca.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
  51. solace_agent_mesh/assets/docs/assets/js/1523c6b4.91c7bc01.js +1 -0
  52. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
  53. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
  54. solace_agent_mesh/assets/docs/assets/js/166ab619.7d97ccaf.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +2 -0
  56. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js.LICENSE.txt +7 -0
  57. solace_agent_mesh/assets/docs/assets/js/1c6e87d2.23bccffb.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/21ceee5f.614fa8dd.js +1 -0
  60. solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
  61. solace_agent_mesh/assets/docs/assets/js/2334.622a6395.js +1 -0
  62. solace_agent_mesh/assets/docs/assets/js/2a9cab12.8909df92.js +1 -0
  63. solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
  64. solace_agent_mesh/assets/docs/assets/js/332e10b5.7a103f42.js +1 -0
  65. solace_agent_mesh/assets/docs/assets/js/3624.b524e433.js +1 -0
  66. solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
  67. solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
  68. solace_agent_mesh/assets/docs/assets/js/3d406171.f722eaf5.js +1 -0
  69. solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
  70. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.36090198.js +1 -0
  71. solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
  72. solace_agent_mesh/assets/docs/assets/js/442a8107.5ba94b65.js +1 -0
  73. solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
  74. solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
  75. solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
  76. solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
  77. solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
  78. solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
  79. solace_agent_mesh/assets/docs/assets/js/4c2787c2.66ee00e9.js +1 -0
  80. solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
  81. solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.js +1 -0
  82. solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
  83. solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
  84. solace_agent_mesh/assets/docs/assets/js/5b4258a4.bda20761.js +1 -0
  85. solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
  86. solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/768e31b0.a12673db.js +1 -0
  95. solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
  96. solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/85387663.6bf41934.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
  101. solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
  102. solace_agent_mesh/assets/docs/assets/js/8591.d7c16be6.js +2 -0
  103. solace_agent_mesh/assets/docs/assets/js/8591.d7c16be6.js.LICENSE.txt +61 -0
  104. solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
  105. solace_agent_mesh/assets/docs/assets/js/8731.49e930c2.js +1 -0
  106. solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
  107. solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
  108. solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
  109. solace_agent_mesh/assets/docs/assets/js/945fb41e.74d728aa.js +1 -0
  110. solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
  111. solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
  112. solace_agent_mesh/assets/docs/assets/js/9eff14a2.1bf8f61c.js +1 -0
  113. solace_agent_mesh/assets/docs/assets/js/a3a92b25.26ca071f.js +1 -0
  114. solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
  115. solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +1 -0
  116. solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
  117. solace_agent_mesh/assets/docs/assets/js/aba87c2f.d3e2dcc3.js +1 -0
  118. solace_agent_mesh/assets/docs/assets/js/ae4415af.8e279b5d.js +1 -0
  119. solace_agent_mesh/assets/docs/assets/js/b7006a3a.40b10c9d.js +1 -0
  120. solace_agent_mesh/assets/docs/assets/js/bac0be12.f50d9bac.js +1 -0
  121. solace_agent_mesh/assets/docs/assets/js/bb2ef573.207e6990.js +1 -0
  122. solace_agent_mesh/assets/docs/assets/js/c2c06897.63b76e9e.js +1 -0
  123. solace_agent_mesh/assets/docs/assets/js/cc969b05.954186d4.js +1 -0
  124. solace_agent_mesh/assets/docs/assets/js/cd3d4052.ca6eed8c.js +1 -0
  125. solace_agent_mesh/assets/docs/assets/js/ced92a13.fb92e7ca.js +1 -0
  126. solace_agent_mesh/assets/docs/assets/js/cee5d587.f5b73ca1.js +1 -0
  127. solace_agent_mesh/assets/docs/assets/js/f284c35a.ecc3d195.js +1 -0
  128. solace_agent_mesh/assets/docs/assets/js/f897a61a.2c2e152c.js +1 -0
  129. solace_agent_mesh/assets/docs/assets/js/fbfa3e75.aca209c9.js +1 -0
  130. solace_agent_mesh/assets/docs/assets/js/main.7ed3319f.js +2 -0
  131. solace_agent_mesh/assets/docs/assets/js/main.7ed3319f.js.LICENSE.txt +81 -0
  132. solace_agent_mesh/assets/docs/assets/js/runtime~main.d9520ae2.js +1 -0
  133. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +128 -0
  134. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +91 -0
  135. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +201 -0
  136. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +91 -0
  137. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +55 -0
  138. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +82 -0
  139. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +60 -0
  140. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +48 -0
  141. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +54 -0
  142. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +17 -0
  143. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +45 -0
  144. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +76 -0
  145. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +150 -0
  146. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +54 -0
  147. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +267 -0
  148. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +136 -0
  149. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +116 -0
  150. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +80 -0
  151. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +164 -0
  152. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +57 -0
  153. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +72 -0
  154. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +102 -0
  155. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +99 -0
  156. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +90 -0
  157. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +107 -0
  158. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +152 -0
  159. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +103 -0
  160. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +170 -0
  161. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +200 -0
  162. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +54 -0
  163. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +69 -0
  164. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +59 -0
  165. solace_agent_mesh/assets/docs/img/Solace_AI_Framework_README.png +0 -0
  166. solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
  167. solace_agent_mesh/assets/docs/img/logo.png +0 -0
  168. solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
  169. solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
  170. solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
  171. solace_agent_mesh/assets/docs/lunr-index-1753813536522.json +1 -0
  172. solace_agent_mesh/assets/docs/lunr-index.json +1 -0
  173. solace_agent_mesh/assets/docs/search-doc-1753813536522.json +1 -0
  174. solace_agent_mesh/assets/docs/search-doc.json +1 -0
  175. solace_agent_mesh/assets/docs/sitemap.xml +1 -0
  176. solace_agent_mesh/cli/__init__.py +1 -1
  177. solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
  178. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
  179. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +659 -0
  180. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
  181. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +93 -0
  182. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +118 -0
  183. solace_agent_mesh/cli/commands/docs_cmd.py +57 -0
  184. solace_agent_mesh/cli/commands/eval_cmd.py +64 -0
  185. solace_agent_mesh/cli/commands/init_cmd/__init__.py +404 -0
  186. solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
  187. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
  188. solace_agent_mesh/cli/commands/init_cmd/env_step.py +197 -0
  189. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
  190. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +387 -0
  191. solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
  192. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +110 -0
  193. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +183 -0
  194. solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +18 -0
  195. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +372 -0
  196. solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
  197. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +138 -0
  198. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +309 -0
  199. solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +174 -0
  200. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
  201. solace_agent_mesh/cli/commands/run_cmd.py +158 -0
  202. solace_agent_mesh/cli/main.py +17 -294
  203. solace_agent_mesh/cli/utils.py +135 -204
  204. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-DvlO62me.js +1 -0
  205. solace_agent_mesh/client/webui/frontend/static/assets/client-bp6u3qVZ.js +49 -0
  206. solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
  207. solace_agent_mesh/client/webui/frontend/static/assets/main-An0a5j5k.js +663 -0
  208. solace_agent_mesh/client/webui/frontend/static/assets/main-Bu5-4Bac.css +1 -0
  209. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +14 -0
  210. solace_agent_mesh/client/webui/frontend/static/index.html +15 -0
  211. solace_agent_mesh/common/__init__.py +1 -0
  212. solace_agent_mesh/common/a2a_protocol.py +564 -0
  213. solace_agent_mesh/common/agent_registry.py +42 -0
  214. solace_agent_mesh/common/client/__init__.py +4 -0
  215. solace_agent_mesh/common/client/card_resolver.py +21 -0
  216. solace_agent_mesh/common/client/client.py +85 -0
  217. solace_agent_mesh/common/client/client_llm.txt +133 -0
  218. solace_agent_mesh/common/common_llm.txt +144 -0
  219. solace_agent_mesh/common/constants.py +1 -14
  220. solace_agent_mesh/common/middleware/__init__.py +12 -0
  221. solace_agent_mesh/common/middleware/config_resolver.py +130 -0
  222. solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
  223. solace_agent_mesh/common/middleware/registry.py +125 -0
  224. solace_agent_mesh/common/server/__init__.py +4 -0
  225. solace_agent_mesh/common/server/server.py +122 -0
  226. solace_agent_mesh/common/server/server_llm.txt +169 -0
  227. solace_agent_mesh/common/server/task_manager.py +291 -0
  228. solace_agent_mesh/common/server/utils.py +28 -0
  229. solace_agent_mesh/common/services/__init__.py +4 -0
  230. solace_agent_mesh/common/services/employee_service.py +162 -0
  231. solace_agent_mesh/common/services/identity_service.py +129 -0
  232. solace_agent_mesh/common/services/providers/__init__.py +4 -0
  233. solace_agent_mesh/common/services/providers/local_file_identity_service.py +148 -0
  234. solace_agent_mesh/common/services/providers/providers_llm.txt +113 -0
  235. solace_agent_mesh/common/services/services_llm.txt +132 -0
  236. solace_agent_mesh/common/types.py +411 -0
  237. solace_agent_mesh/common/utils/__init__.py +7 -0
  238. solace_agent_mesh/common/utils/asyncio_macos_fix.py +86 -0
  239. solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
  240. solace_agent_mesh/common/utils/embeds/constants.py +55 -0
  241. solace_agent_mesh/common/utils/embeds/converter.py +452 -0
  242. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +124 -0
  243. solace_agent_mesh/common/utils/embeds/evaluators.py +394 -0
  244. solace_agent_mesh/common/utils/embeds/modifiers.py +816 -0
  245. solace_agent_mesh/common/utils/embeds/resolver.py +865 -0
  246. solace_agent_mesh/common/utils/embeds/types.py +14 -0
  247. solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
  248. solace_agent_mesh/common/utils/log_formatters.py +44 -0
  249. solace_agent_mesh/common/utils/mime_helpers.py +106 -0
  250. solace_agent_mesh/common/utils/push_notification_auth.py +134 -0
  251. solace_agent_mesh/common/utils/utils_llm.txt +67 -0
  252. solace_agent_mesh/config_portal/backend/common.py +66 -24
  253. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +23 -0
  254. solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
  255. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +160 -0
  256. solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +525 -0
  257. solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +216 -0
  258. solace_agent_mesh/config_portal/backend/server.py +550 -140
  259. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DNxCwAGB.js +48 -0
  260. solace_agent_mesh/config_portal/frontend/static/client/assets/components-B7lKcHVY.js +140 -0
  261. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-DX1misIU.js → entry.client-CEumGClk.js} +3 -3
  262. solace_agent_mesh/config_portal/frontend/static/client/assets/index-DSo1AH_7.js +68 -0
  263. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d2b54a97.js +1 -0
  264. solace_agent_mesh/config_portal/frontend/static/client/assets/{root-BApq5dPK.js → root-C4XmHinv.js} +2 -2
  265. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +1 -0
  266. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  267. solace_agent_mesh/core_a2a/__init__.py +1 -0
  268. solace_agent_mesh/core_a2a/core_a2a_llm.txt +88 -0
  269. solace_agent_mesh/core_a2a/service.py +331 -0
  270. solace_agent_mesh/evaluation/config_loader.py +657 -0
  271. solace_agent_mesh/evaluation/evaluator.py +667 -0
  272. solace_agent_mesh/evaluation/message_organizer.py +568 -0
  273. solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
  274. solace_agent_mesh/evaluation/report/chart_section.html +141 -0
  275. solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
  276. solace_agent_mesh/evaluation/report/modal.html +59 -0
  277. solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
  278. solace_agent_mesh/evaluation/report/modal_script.js +296 -0
  279. solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
  280. solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
  281. solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
  282. solace_agent_mesh/evaluation/report/templates/header.html +340 -0
  283. solace_agent_mesh/evaluation/report_data_processor.py +972 -0
  284. solace_agent_mesh/evaluation/report_generator.py +613 -0
  285. solace_agent_mesh/evaluation/run.py +613 -0
  286. solace_agent_mesh/evaluation/subscriber.py +872 -0
  287. solace_agent_mesh/evaluation/summary_builder.py +775 -0
  288. solace_agent_mesh/evaluation/test_case_loader.py +714 -0
  289. solace_agent_mesh/gateway/base/__init__.py +1 -0
  290. solace_agent_mesh/gateway/base/app.py +266 -0
  291. solace_agent_mesh/gateway/base/base_llm.txt +119 -0
  292. solace_agent_mesh/gateway/base/component.py +1542 -0
  293. solace_agent_mesh/gateway/base/task_context.py +74 -0
  294. solace_agent_mesh/gateway/gateway_llm.txt +125 -0
  295. solace_agent_mesh/gateway/http_sse/app.py +190 -0
  296. solace_agent_mesh/gateway/http_sse/component.py +1602 -0
  297. solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
  298. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +65 -0
  299. solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +108 -0
  300. solace_agent_mesh/gateway/http_sse/dependencies.py +316 -0
  301. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +63 -0
  302. solace_agent_mesh/gateway/http_sse/main.py +442 -0
  303. solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
  304. solace_agent_mesh/gateway/http_sse/routers/agents.py +41 -0
  305. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +821 -0
  306. solace_agent_mesh/gateway/http_sse/routers/auth.py +212 -0
  307. solace_agent_mesh/gateway/http_sse/routers/config.py +55 -0
  308. solace_agent_mesh/gateway/http_sse/routers/people.py +69 -0
  309. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +37 -0
  310. solace_agent_mesh/gateway/http_sse/routers/sessions.py +80 -0
  311. solace_agent_mesh/gateway/http_sse/routers/sse.py +138 -0
  312. solace_agent_mesh/gateway/http_sse/routers/tasks.py +294 -0
  313. solace_agent_mesh/gateway/http_sse/routers/users.py +59 -0
  314. solace_agent_mesh/gateway/http_sse/routers/visualization.py +1131 -0
  315. solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
  316. solace_agent_mesh/gateway/http_sse/services/agent_service.py +69 -0
  317. solace_agent_mesh/gateway/http_sse/services/people_service.py +158 -0
  318. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +179 -0
  319. solace_agent_mesh/gateway/http_sse/services/task_service.py +121 -0
  320. solace_agent_mesh/gateway/http_sse/session_manager.py +187 -0
  321. solace_agent_mesh/gateway/http_sse/sse_manager.py +328 -0
  322. solace_agent_mesh/llm.txt +228 -0
  323. solace_agent_mesh/llm_detail.txt +2835 -0
  324. solace_agent_mesh/templates/agent_template.yaml +53 -0
  325. solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
  326. solace_agent_mesh/templates/gateway_app_template.py +73 -0
  327. solace_agent_mesh/templates/gateway_component_template.py +400 -0
  328. solace_agent_mesh/templates/gateway_config_template.yaml +43 -0
  329. solace_agent_mesh/templates/main_orchestrator.yaml +55 -0
  330. solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
  331. solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
  332. solace_agent_mesh/templates/plugin_custom_template.py +10 -0
  333. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +63 -0
  334. solace_agent_mesh/templates/plugin_pyproject_template.toml +33 -0
  335. solace_agent_mesh/templates/plugin_readme_template.md +34 -0
  336. solace_agent_mesh/templates/plugin_tools_template.py +224 -0
  337. solace_agent_mesh/templates/shared_config.yaml +66 -0
  338. solace_agent_mesh/templates/templates_llm.txt +147 -0
  339. solace_agent_mesh/templates/webui.yaml +53 -0
  340. solace_agent_mesh-1.0.1.dist-info/METADATA +432 -0
  341. solace_agent_mesh-1.0.1.dist-info/RECORD +359 -0
  342. solace_agent_mesh-1.0.1.dist-info/entry_points.txt +3 -0
  343. {solace_agent_mesh-0.2.3.dist-info → solace_agent_mesh-1.0.1.dist-info}/licenses/LICENSE +1 -1
  344. solace_agent_mesh/agents/base_agent_component.py +0 -226
  345. solace_agent_mesh/agents/global/actions/agent_state_change.py +0 -54
  346. solace_agent_mesh/agents/global/actions/clear_history.py +0 -32
  347. solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +0 -160
  348. solace_agent_mesh/agents/global/actions/create_file.py +0 -70
  349. solace_agent_mesh/agents/global/actions/error_action.py +0 -45
  350. solace_agent_mesh/agents/global/actions/plantuml_diagram.py +0 -163
  351. solace_agent_mesh/agents/global/actions/plotly_graph.py +0 -152
  352. solace_agent_mesh/agents/global/actions/retrieve_file.py +0 -51
  353. solace_agent_mesh/agents/global/global_agent_component.py +0 -38
  354. solace_agent_mesh/agents/image_processing/actions/create_image.py +0 -75
  355. solace_agent_mesh/agents/image_processing/actions/describe_image.py +0 -115
  356. solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +0 -23
  357. solace_agent_mesh/agents/slack/__init__.py +0 -1
  358. solace_agent_mesh/agents/slack/actions/__init__.py +0 -1
  359. solace_agent_mesh/agents/slack/actions/post_message.py +0 -177
  360. solace_agent_mesh/agents/slack/slack_agent_component.py +0 -59
  361. solace_agent_mesh/agents/web_request/actions/do_image_search.py +0 -84
  362. solace_agent_mesh/agents/web_request/actions/do_news_search.py +0 -47
  363. solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +0 -34
  364. solace_agent_mesh/agents/web_request/actions/do_web_request.py +0 -135
  365. solace_agent_mesh/agents/web_request/actions/download_file.py +0 -69
  366. solace_agent_mesh/agents/web_request/web_request_agent_component.py +0 -33
  367. solace_agent_mesh/assets/web-visualizer/assets/index-D0qORgkg.css +0 -1
  368. solace_agent_mesh/assets/web-visualizer/assets/index-DnDr1pnu.js +0 -109
  369. solace_agent_mesh/assets/web-visualizer/index.html +0 -14
  370. solace_agent_mesh/assets/web-visualizer/vite.svg +0 -1
  371. solace_agent_mesh/cli/commands/add/__init__.py +0 -3
  372. solace_agent_mesh/cli/commands/add/add.py +0 -88
  373. solace_agent_mesh/cli/commands/add/agent.py +0 -110
  374. solace_agent_mesh/cli/commands/add/copy_from_plugin.py +0 -92
  375. solace_agent_mesh/cli/commands/add/gateway.py +0 -374
  376. solace_agent_mesh/cli/commands/build.py +0 -670
  377. solace_agent_mesh/cli/commands/chat/__init__.py +0 -3
  378. solace_agent_mesh/cli/commands/chat/chat.py +0 -361
  379. solace_agent_mesh/cli/commands/config.py +0 -29
  380. solace_agent_mesh/cli/commands/init/__init__.py +0 -3
  381. solace_agent_mesh/cli/commands/init/ai_provider_step.py +0 -93
  382. solace_agent_mesh/cli/commands/init/broker_step.py +0 -99
  383. solace_agent_mesh/cli/commands/init/builtin_agent_step.py +0 -83
  384. solace_agent_mesh/cli/commands/init/check_if_already_done.py +0 -13
  385. solace_agent_mesh/cli/commands/init/create_config_file_step.py +0 -65
  386. solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +0 -147
  387. solace_agent_mesh/cli/commands/init/file_service_step.py +0 -73
  388. solace_agent_mesh/cli/commands/init/init.py +0 -92
  389. solace_agent_mesh/cli/commands/init/project_structure_step.py +0 -16
  390. solace_agent_mesh/cli/commands/init/web_init_step.py +0 -32
  391. solace_agent_mesh/cli/commands/plugin/__init__.py +0 -3
  392. solace_agent_mesh/cli/commands/plugin/add.py +0 -100
  393. solace_agent_mesh/cli/commands/plugin/build.py +0 -268
  394. solace_agent_mesh/cli/commands/plugin/create.py +0 -117
  395. solace_agent_mesh/cli/commands/plugin/plugin.py +0 -124
  396. solace_agent_mesh/cli/commands/plugin/remove.py +0 -73
  397. solace_agent_mesh/cli/commands/run.py +0 -68
  398. solace_agent_mesh/cli/commands/visualizer.py +0 -138
  399. solace_agent_mesh/cli/config.py +0 -85
  400. solace_agent_mesh/common/action.py +0 -91
  401. solace_agent_mesh/common/action_list.py +0 -37
  402. solace_agent_mesh/common/action_response.py +0 -340
  403. solace_agent_mesh/common/mysql_database.py +0 -40
  404. solace_agent_mesh/common/postgres_database.py +0 -85
  405. solace_agent_mesh/common/prompt_templates.py +0 -28
  406. solace_agent_mesh/common/stimulus_utils.py +0 -152
  407. solace_agent_mesh/common/time.py +0 -24
  408. solace_agent_mesh/common/utils.py +0 -712
  409. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DMmCawWe.js +0 -42
  410. solace_agent_mesh/config_portal/frontend/static/client/assets/components-ZIfdTbrV.js +0 -191
  411. solace_agent_mesh/config_portal/frontend/static/client/assets/index-BJHAE5s4.js +0 -17
  412. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-dd988f05.js +0 -1
  413. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DX4gQ516.css +0 -1
  414. solace_agent_mesh/configs/agent_global.yaml +0 -74
  415. solace_agent_mesh/configs/agent_image_processing.yaml +0 -82
  416. solace_agent_mesh/configs/agent_slack.yaml +0 -64
  417. solace_agent_mesh/configs/agent_web_request.yaml +0 -75
  418. solace_agent_mesh/configs/conversation_to_file.yaml +0 -56
  419. solace_agent_mesh/configs/error_catcher.yaml +0 -56
  420. solace_agent_mesh/configs/monitor.yaml +0 -0
  421. solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +0 -109
  422. solace_agent_mesh/configs/monitor_user_feedback.yaml +0 -58
  423. solace_agent_mesh/configs/orchestrator.yaml +0 -241
  424. solace_agent_mesh/configs/service_embedding.yaml +0 -81
  425. solace_agent_mesh/configs/service_llm.yaml +0 -265
  426. solace_agent_mesh/configs/visualize_websocket.yaml +0 -55
  427. solace_agent_mesh/gateway/components/gateway_base.py +0 -47
  428. solace_agent_mesh/gateway/components/gateway_input.py +0 -278
  429. solace_agent_mesh/gateway/components/gateway_output.py +0 -298
  430. solace_agent_mesh/gateway/identity/bamboohr_identity.py +0 -18
  431. solace_agent_mesh/gateway/identity/identity_base.py +0 -10
  432. solace_agent_mesh/gateway/identity/identity_provider.py +0 -60
  433. solace_agent_mesh/gateway/identity/no_identity.py +0 -9
  434. solace_agent_mesh/gateway/identity/passthru_identity.py +0 -9
  435. solace_agent_mesh/monitors/base_monitor_component.py +0 -26
  436. solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +0 -75
  437. solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +0 -560
  438. solace_agent_mesh/orchestrator/__init__.py +0 -0
  439. solace_agent_mesh/orchestrator/action_manager.py +0 -237
  440. solace_agent_mesh/orchestrator/components/__init__.py +0 -0
  441. solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +0 -58
  442. solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +0 -179
  443. solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +0 -107
  444. solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +0 -527
  445. solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +0 -260
  446. solace_agent_mesh/orchestrator/orchestrator_main.py +0 -172
  447. solace_agent_mesh/orchestrator/orchestrator_prompt.py +0 -539
  448. solace_agent_mesh/services/__init__.py +0 -0
  449. solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +0 -56
  450. solace_agent_mesh/services/bamboo_hr_service/__init__.py +0 -3
  451. solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +0 -182
  452. solace_agent_mesh/services/common/__init__.py +0 -4
  453. solace_agent_mesh/services/common/auto_expiry.py +0 -45
  454. solace_agent_mesh/services/common/singleton.py +0 -18
  455. solace_agent_mesh/services/file_service/__init__.py +0 -14
  456. solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
  457. solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +0 -149
  458. solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +0 -162
  459. solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +0 -64
  460. solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +0 -106
  461. solace_agent_mesh/services/file_service/file_service.py +0 -437
  462. solace_agent_mesh/services/file_service/file_service_constants.py +0 -54
  463. solace_agent_mesh/services/file_service/file_transformations.py +0 -141
  464. solace_agent_mesh/services/file_service/file_utils.py +0 -324
  465. solace_agent_mesh/services/file_service/transformers/__init__.py +0 -5
  466. solace_agent_mesh/services/history_service/__init__.py +0 -3
  467. solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
  468. solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +0 -54
  469. solace_agent_mesh/services/history_service/history_providers/file_history_provider.py +0 -74
  470. solace_agent_mesh/services/history_service/history_providers/index.py +0 -40
  471. solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +0 -33
  472. solace_agent_mesh/services/history_service/history_providers/mongodb_history_provider.py +0 -66
  473. solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +0 -66
  474. solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +0 -93
  475. solace_agent_mesh/services/history_service/history_service.py +0 -413
  476. solace_agent_mesh/services/history_service/long_term_memory/__init__.py +0 -0
  477. solace_agent_mesh/services/history_service/long_term_memory/long_term_memory.py +0 -399
  478. solace_agent_mesh/services/llm_service/components/llm_request_component.py +0 -340
  479. solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +0 -152
  480. solace_agent_mesh/services/middleware_service/__init__.py +0 -0
  481. solace_agent_mesh/services/middleware_service/middleware_service.py +0 -20
  482. solace_agent_mesh/templates/action.py +0 -38
  483. solace_agent_mesh/templates/agent.py +0 -29
  484. solace_agent_mesh/templates/agent.yaml +0 -70
  485. solace_agent_mesh/templates/gateway-config-template.yaml +0 -6
  486. solace_agent_mesh/templates/gateway-default-config.yaml +0 -28
  487. solace_agent_mesh/templates/gateway-flows.yaml +0 -78
  488. solace_agent_mesh/templates/gateway-header.yaml +0 -16
  489. solace_agent_mesh/templates/gateway_base.py +0 -15
  490. solace_agent_mesh/templates/gateway_input.py +0 -98
  491. solace_agent_mesh/templates/gateway_output.py +0 -71
  492. solace_agent_mesh/templates/plugin-gateway-default-config.yaml +0 -29
  493. solace_agent_mesh/templates/plugin-pyproject.toml +0 -30
  494. solace_agent_mesh/templates/rest-api-default-config.yaml +0 -31
  495. solace_agent_mesh/templates/rest-api-flows.yaml +0 -81
  496. solace_agent_mesh/templates/slack-default-config.yaml +0 -16
  497. solace_agent_mesh/templates/slack-flows.yaml +0 -81
  498. solace_agent_mesh/templates/solace-agent-mesh-default.yaml +0 -86
  499. solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +0 -8
  500. solace_agent_mesh/templates/web-default-config.yaml +0 -10
  501. solace_agent_mesh/templates/web-flows.yaml +0 -76
  502. solace_agent_mesh/tools/__init__.py +0 -0
  503. solace_agent_mesh/tools/components/__init__.py +0 -0
  504. solace_agent_mesh/tools/components/conversation_formatter.py +0 -111
  505. solace_agent_mesh/tools/components/file_resolver_component.py +0 -58
  506. solace_agent_mesh/tools/config/runtime_config.py +0 -26
  507. solace_agent_mesh-0.2.3.dist-info/METADATA +0 -172
  508. solace_agent_mesh-0.2.3.dist-info/RECORD +0 -193
  509. solace_agent_mesh-0.2.3.dist-info/entry_points.txt +0 -3
  510. /solace_agent_mesh/{agents → agent}/__init__.py +0 -0
  511. /solace_agent_mesh/{agents/global → agent/adk}/__init__.py +0 -0
  512. /solace_agent_mesh/{agents/global/actions → agent/protocol}/__init__.py +0 -0
  513. /solace_agent_mesh/{agents/image_processing → agent/sac}/__init__.py +0 -0
  514. /solace_agent_mesh/{agents/image_processing/actions → agent/utils}/__init__.py +0 -0
  515. /solace_agent_mesh/{agents/web_request → config_portal/backend/plugin_catalog}/__init__.py +0 -0
  516. /solace_agent_mesh/{agents/web_request/actions → evaluation}/__init__.py +0 -0
  517. /solace_agent_mesh/gateway/{components → http_sse}/__init__.py +0 -0
  518. {solace_agent_mesh-0.2.3.dist-info → solace_agent_mesh-1.0.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,872 @@
1
+ """
2
+ Refactored message subscriber with improved structure and readability.
3
+ This module handles Solace message subscription and processing for evaluation.
4
+ """
5
+
6
+ import os
7
+ import json
8
+ import threading
9
+ import time
10
+ from typing import Dict, List, Optional, Any, Set, Callable
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ import logging
14
+ from dotenv import load_dotenv
15
+
16
+ from solace.messaging.messaging_service import MessagingService
17
+ from solace.messaging.resources.topic_subscription import TopicSubscription
18
+ from solace.messaging.config.solace_properties import (
19
+ transport_layer_properties,
20
+ service_properties,
21
+ authentication_properties,
22
+ transport_layer_security_properties,
23
+ )
24
+
25
+ # Set up logging
26
+ logging.basicConfig(level=logging.INFO)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # Load environment variables
30
+ load_dotenv()
31
+
32
+
33
+ class ConnectionState(Enum):
34
+ """Represents the connection state of the subscriber."""
35
+
36
+ DISCONNECTED = "disconnected"
37
+ CONNECTING = "connecting"
38
+ CONNECTED = "connected"
39
+ DISCONNECTING = "disconnecting"
40
+ ERROR = "error"
41
+
42
+
43
+ class SubscriberError(Exception):
44
+ """Base exception for subscriber-related errors."""
45
+
46
+ pass
47
+
48
+
49
+ class BrokerConnectionError(SubscriberError):
50
+ """Raised when broker connection fails."""
51
+
52
+ pass
53
+
54
+
55
+ class MessageProcessingError(SubscriberError):
56
+ """Raised when message processing fails."""
57
+
58
+ pass
59
+
60
+
61
+ class ConfigurationError(SubscriberError):
62
+ """Raised when configuration is invalid."""
63
+
64
+ pass
65
+
66
+
67
+ @dataclass
68
+ class BrokerConfig:
69
+ """Broker connection configuration with validation."""
70
+
71
+ host: str
72
+ vpn_name: str
73
+ username: str
74
+ password: str
75
+ cert_validated: bool = False
76
+ connection_timeout: int = 30
77
+ reconnect_attempts: int = 3
78
+ reconnect_delay: float = 1.0
79
+
80
+ def __post_init__(self):
81
+ """Validate broker configuration after initialization."""
82
+ if not self.host or not self.host.strip():
83
+ raise ConfigurationError("Broker host cannot be empty")
84
+
85
+ if not self.vpn_name or not self.vpn_name.strip():
86
+ raise ConfigurationError("VPN name cannot be empty")
87
+
88
+ if not self.username or not self.username.strip():
89
+ raise ConfigurationError("Username cannot be empty")
90
+
91
+ if not self.password or not self.password.strip():
92
+ raise ConfigurationError("Password cannot be empty")
93
+
94
+ if self.connection_timeout <= 0:
95
+ raise ConfigurationError("Connection timeout must be positive")
96
+
97
+ if self.reconnect_attempts < 0:
98
+ raise ConfigurationError("Reconnect attempts cannot be negative")
99
+
100
+ def to_solace_properties(self) -> Dict[str, Any]:
101
+ """Convert to Solace messaging properties."""
102
+ return {
103
+ transport_layer_properties.HOST: self.host,
104
+ service_properties.VPN_NAME: self.vpn_name,
105
+ authentication_properties.SCHEME_BASIC_USER_NAME: self.username,
106
+ authentication_properties.SCHEME_BASIC_PASSWORD: self.password,
107
+ transport_layer_security_properties.CERT_VALIDATED: self.cert_validated,
108
+ }
109
+
110
+
111
+ @dataclass
112
+ class SubscriptionConfig:
113
+ """Subscription configuration and topic filters."""
114
+
115
+ namespace: str
116
+ allowed_topic_infixes: List[str] = field(
117
+ default_factory=lambda: [
118
+ "/agent/request/",
119
+ "/gateway/status/",
120
+ "/gateway/response/",
121
+ ]
122
+ )
123
+ message_timeout: int = 1000
124
+ filter_non_final_status: bool = True
125
+ remove_config_keys: bool = True
126
+
127
+ def __post_init__(self):
128
+ """Validate subscription configuration."""
129
+ if not self.namespace or not self.namespace.strip():
130
+ raise ConfigurationError("Namespace cannot be empty")
131
+
132
+ if not self.allowed_topic_infixes:
133
+ raise ConfigurationError("At least one topic infix must be allowed")
134
+
135
+ if self.message_timeout <= 0:
136
+ raise ConfigurationError("Message timeout must be positive")
137
+
138
+ @property
139
+ def topic_pattern(self) -> str:
140
+ """Get the topic subscription pattern."""
141
+ return f"{self.namespace}/a2a/v1/>"
142
+
143
+ def is_topic_allowed(self, topic: str) -> bool:
144
+ """Check if a topic is allowed based on configured infixes."""
145
+ return any(infix in topic for infix in self.allowed_topic_infixes)
146
+
147
+
148
+ @dataclass
149
+ class ProcessedMessage:
150
+ """Structured representation of a processed message."""
151
+
152
+ topic: str
153
+ payload: Any
154
+ timestamp: float = field(default_factory=time.time)
155
+ message_type: Optional[str] = None
156
+
157
+ def to_dict(self) -> Dict[str, Any]:
158
+ """Convert to dictionary for JSON serialization."""
159
+ return {
160
+ "topic": self.topic,
161
+ "payload": self.payload,
162
+ "timestamp": self.timestamp,
163
+ "message_type": self.message_type,
164
+ }
165
+
166
+
167
+ @dataclass
168
+ class TaskCompletionEvent:
169
+ """Represents a task completion event."""
170
+
171
+ task_id: str
172
+ topic: str
173
+ timestamp: float = field(default_factory=time.time)
174
+
175
+
176
+ class MessageSanitizer:
177
+ """Handles message sanitization and cleaning."""
178
+
179
+ @staticmethod
180
+ def remove_key_recursive(obj: Any, key_to_remove: str) -> None:
181
+ """
182
+ Recursively remove a key from nested dictionaries and lists.
183
+
184
+ Args:
185
+ obj: The object to process (dict, list, or other)
186
+ key_to_remove: The key to remove from dictionaries
187
+ """
188
+ try:
189
+ if isinstance(obj, dict):
190
+ # Create a list of keys to avoid modifying dict during iteration
191
+ keys_to_process = list(obj.keys())
192
+ for key in keys_to_process:
193
+ if key == key_to_remove:
194
+ del obj[key]
195
+ else:
196
+ MessageSanitizer.remove_key_recursive(obj[key], key_to_remove)
197
+ elif isinstance(obj, list):
198
+ for item in obj:
199
+ MessageSanitizer.remove_key_recursive(item, key_to_remove)
200
+ except Exception as e:
201
+ logger.warning(f"Error during key removal: {e}")
202
+
203
+ @staticmethod
204
+ def sanitize_message(payload: Any, remove_config: bool = True) -> Any:
205
+ """
206
+ Sanitize message payload by removing unwanted keys.
207
+
208
+ Args:
209
+ payload: The payload to sanitize
210
+ remove_config: Whether to remove 'config' keys
211
+
212
+ Returns:
213
+ Sanitized payload
214
+ """
215
+ if remove_config and isinstance(payload, (dict, list)):
216
+ # Work on a copy to avoid modifying the original
217
+ import copy
218
+
219
+ sanitized = copy.deepcopy(payload)
220
+ MessageSanitizer.remove_key_recursive(sanitized, "config")
221
+ return sanitized
222
+ return payload
223
+
224
+
225
+ class MessageProcessor:
226
+ """Processes and filters incoming messages."""
227
+
228
+ def __init__(self, config: SubscriptionConfig):
229
+ self.config = config
230
+ self.sanitizer = MessageSanitizer()
231
+ self.processed_count = 0
232
+ self.error_count = 0
233
+
234
+ def process_message(self, inbound_message) -> Optional[ProcessedMessage]:
235
+ """
236
+ Process an inbound message and return a ProcessedMessage if valid.
237
+
238
+ Args:
239
+ inbound_message: The inbound Solace message
240
+
241
+ Returns:
242
+ ProcessedMessage if the message should be kept, None otherwise
243
+ """
244
+ try:
245
+ topic = inbound_message.get_destination_name()
246
+
247
+ # Check if topic is allowed
248
+ if not self.config.is_topic_allowed(topic):
249
+ return None
250
+
251
+ # Extract and parse payload
252
+ payload = self._extract_payload(inbound_message)
253
+ if payload is None:
254
+ return None
255
+
256
+ # Filter status messages if configured
257
+ if self._should_filter_status_message(topic, payload):
258
+ return None
259
+
260
+ # Sanitize payload
261
+ if self.config.remove_config_keys:
262
+ payload = self.sanitizer.sanitize_message(payload)
263
+
264
+ # Determine message type
265
+ message_type = self._determine_message_type(topic)
266
+
267
+ self.processed_count += 1
268
+
269
+ return ProcessedMessage(
270
+ topic=topic, payload=payload, message_type=message_type
271
+ )
272
+
273
+ except Exception as e:
274
+ self.error_count += 1
275
+ logger.warning(f"Error processing message: {e}")
276
+ return None
277
+
278
+ def _extract_payload(self, inbound_message) -> Optional[Any]:
279
+ """Extract and parse payload from inbound message."""
280
+ try:
281
+ payload_bytes = inbound_message.get_payload_as_bytes()
282
+ if not payload_bytes:
283
+ return None
284
+
285
+ payload_str = payload_bytes.decode("utf-8", errors="ignore")
286
+
287
+ # Try to parse as JSON
288
+ try:
289
+ return json.loads(payload_str)
290
+ except json.JSONDecodeError:
291
+ # Return as string if not valid JSON
292
+ return payload_str
293
+
294
+ except Exception as e:
295
+ logger.warning(f"Error extracting payload: {e}")
296
+ return None
297
+
298
+ def _should_filter_status_message(self, topic: str, payload: Any) -> bool:
299
+ """Check if a status message should be filtered out."""
300
+ if not self.config.filter_non_final_status:
301
+ return False
302
+
303
+ if "/gateway/status/" not in topic:
304
+ return False
305
+
306
+ try:
307
+ if isinstance(payload, dict):
308
+ result = payload.get("result", {})
309
+ if isinstance(result, dict):
310
+ # Filter out non-final status messages
311
+ return not result.get("final", True)
312
+ except Exception:
313
+ pass
314
+
315
+ return False
316
+
317
+ def _determine_message_type(self, topic: str) -> str:
318
+ """Determine the type of message based on topic."""
319
+ if "/agent/request/" in topic:
320
+ return "agent_request"
321
+ elif "/gateway/status/" in topic:
322
+ return "gateway_status"
323
+ elif "/gateway/response/" in topic:
324
+ return "gateway_response"
325
+ else:
326
+ return "unknown"
327
+
328
+ def get_stats(self) -> Dict[str, int]:
329
+ """Get processing statistics."""
330
+ return {
331
+ "processed_count": self.processed_count,
332
+ "error_count": self.error_count,
333
+ }
334
+
335
+
336
+ class TaskTracker:
337
+ """Tracks task completion and manages active tasks."""
338
+
339
+ def __init__(
340
+ self, active_tasks: Set[str], wave_complete_event: Optional[threading.Event]
341
+ ):
342
+ self.active_tasks = active_tasks
343
+ self.wave_complete_event = wave_complete_event
344
+ self.completed_tasks: List[TaskCompletionEvent] = []
345
+ self._lock = threading.Lock()
346
+
347
+ def handle_task_completion(self, topic: str) -> Optional[TaskCompletionEvent]:
348
+ """
349
+ Handle task completion based on topic.
350
+
351
+ Args:
352
+ topic: The message topic
353
+
354
+ Returns:
355
+ TaskCompletionEvent if a task was completed, None otherwise
356
+ """
357
+ if "response" not in topic:
358
+ return None
359
+
360
+ try:
361
+ task_id = self._extract_task_id(topic)
362
+ if not task_id:
363
+ return None
364
+
365
+ with self._lock:
366
+ if task_id in self.active_tasks:
367
+ logger.info(f"Task {task_id} completed")
368
+ self.active_tasks.remove(task_id)
369
+
370
+ completion_event = TaskCompletionEvent(task_id=task_id, topic=topic)
371
+ self.completed_tasks.append(completion_event)
372
+
373
+ # Check if all tasks are complete
374
+ if not self.active_tasks and self.wave_complete_event:
375
+ logger.info("All tasks completed, setting wave complete event")
376
+ self.wave_complete_event.set()
377
+
378
+ return completion_event
379
+
380
+ except Exception as e:
381
+ logger.error(f"Error handling task completion: {e}")
382
+
383
+ return None
384
+
385
+ def _extract_task_id(self, topic: str) -> Optional[str]:
386
+ """Extract task ID from topic."""
387
+ try:
388
+ return topic.split("/")[-1]
389
+ except (IndexError, AttributeError):
390
+ return None
391
+
392
+ def get_active_task_count(self) -> int:
393
+ """Get the number of active tasks."""
394
+ with self._lock:
395
+ return len(self.active_tasks)
396
+
397
+ def get_completed_task_count(self) -> int:
398
+ """Get the number of completed tasks."""
399
+ with self._lock:
400
+ return len(self.completed_tasks)
401
+
402
+
403
+ class MessageStorage:
404
+ """Handles message storage and file operations."""
405
+
406
+ def __init__(self, results_path: str):
407
+ self.results_path = results_path
408
+ self.messages: List[ProcessedMessage] = []
409
+ self._lock = threading.Lock()
410
+
411
+ def add_message(self, message: ProcessedMessage) -> None:
412
+ """Add a message to storage."""
413
+ with self._lock:
414
+ self.messages.append(message)
415
+
416
+ def get_message_count(self) -> int:
417
+ """Get the number of stored messages."""
418
+ with self._lock:
419
+ return len(self.messages)
420
+
421
+ def save_messages(self, filename: str = "full_messages.json") -> str:
422
+ """
423
+ Save all messages to a JSON file.
424
+
425
+ Args:
426
+ filename: The filename to save to
427
+
428
+ Returns:
429
+ The full path to the saved file
430
+ """
431
+ output_file = os.path.join(self.results_path, filename)
432
+
433
+ try:
434
+ # Ensure directory exists
435
+ os.makedirs(os.path.dirname(output_file), exist_ok=True)
436
+
437
+ with self._lock:
438
+ # Convert messages to dictionaries for JSON serialization
439
+ message_dicts = [msg.to_dict() for msg in self.messages]
440
+
441
+ with open(output_file, "w") as f:
442
+ json.dump(message_dicts, f, indent=4)
443
+
444
+ logger.info(f"Saved {len(message_dicts)} messages to {output_file}")
445
+ return output_file
446
+
447
+ except Exception as e:
448
+ logger.error(f"Error saving messages: {e}")
449
+ raise MessageProcessingError(f"Failed to save messages: {e}")
450
+
451
+ def clear_messages(self) -> None:
452
+ """Clear all stored messages."""
453
+ with self._lock:
454
+ self.messages.clear()
455
+
456
+
457
+ class BrokerConnectionService:
458
+ """Handles Solace broker connection and lifecycle."""
459
+
460
+ def __init__(self, config: BrokerConfig):
461
+ self.config = config
462
+ self.messaging_service: Optional[MessagingService] = None
463
+ self.connection_state = ConnectionState.DISCONNECTED
464
+ self._connection_lock = threading.Lock()
465
+
466
+ def connect(self) -> None:
467
+ """Connect to the Solace broker."""
468
+ with self._connection_lock:
469
+ if self.connection_state == ConnectionState.CONNECTED:
470
+ logger.warning("Already connected to broker")
471
+ return
472
+
473
+ self.connection_state = ConnectionState.CONNECTING
474
+
475
+ try:
476
+ logger.info("Connecting to Solace PubSub+ Broker...")
477
+
478
+ broker_props = self.config.to_solace_properties()
479
+ self.messaging_service = (
480
+ MessagingService.builder().from_properties(broker_props).build()
481
+ )
482
+ self.messaging_service.connect()
483
+
484
+ self.connection_state = ConnectionState.CONNECTED
485
+ logger.info("Successfully connected to broker")
486
+
487
+ except Exception as e:
488
+ self.connection_state = ConnectionState.ERROR
489
+ logger.error(f"Failed to connect to broker: {e}")
490
+ raise BrokerConnectionError(f"Connection failed: {e}")
491
+
492
+ def disconnect(self) -> None:
493
+ """Disconnect from the Solace broker."""
494
+ with self._connection_lock:
495
+ if self.connection_state == ConnectionState.DISCONNECTED:
496
+ logger.warning("Already disconnected from broker")
497
+ return
498
+
499
+ self.connection_state = ConnectionState.DISCONNECTING
500
+
501
+ try:
502
+ if self.messaging_service:
503
+ logger.info("Disconnecting from broker...")
504
+ self.messaging_service.disconnect()
505
+ self.messaging_service = None
506
+
507
+ self.connection_state = ConnectionState.DISCONNECTED
508
+ logger.info("Successfully disconnected from broker")
509
+
510
+ except Exception as e:
511
+ self.connection_state = ConnectionState.ERROR
512
+ logger.error(f"Error during disconnect: {e}")
513
+ raise BrokerConnectionError(f"Disconnect failed: {e}")
514
+
515
+ def get_messaging_service(self) -> Optional[MessagingService]:
516
+ """Get the messaging service instance."""
517
+ return self.messaging_service
518
+
519
+ def is_connected(self) -> bool:
520
+ """Check if currently connected to broker."""
521
+ return self.connection_state == ConnectionState.CONNECTED
522
+
523
+ def get_connection_state(self) -> ConnectionState:
524
+ """Get the current connection state."""
525
+ return self.connection_state
526
+
527
+
528
+ class SubscriptionManager:
529
+ """Manages topic subscriptions and message receiving."""
530
+
531
+ def __init__(
532
+ self, connection_service: BrokerConnectionService, config: SubscriptionConfig
533
+ ):
534
+ self.connection_service = connection_service
535
+ self.config = config
536
+ self.message_receiver = None
537
+ self.subscription_active = False
538
+ self._receiver_lock = threading.Lock()
539
+
540
+ def start_subscription(
541
+ self, subscription_ready_event: Optional[threading.Event] = None
542
+ ) -> None:
543
+ """Start message subscription."""
544
+ with self._receiver_lock:
545
+ if self.subscription_active:
546
+ logger.warning("Subscription already active")
547
+ return
548
+
549
+ if not self.connection_service.is_connected():
550
+ raise BrokerConnectionError(
551
+ "Must be connected to broker before starting subscription"
552
+ )
553
+
554
+ try:
555
+ messaging_service = self.connection_service.get_messaging_service()
556
+ if not messaging_service:
557
+ raise BrokerConnectionError("No messaging service available")
558
+
559
+ # Create and start message receiver
560
+ self.message_receiver = (
561
+ messaging_service.create_direct_message_receiver_builder().build()
562
+ )
563
+ self.message_receiver.start()
564
+
565
+ # Add subscription
566
+ subscription = TopicSubscription.of(self.config.topic_pattern)
567
+ self.message_receiver.add_subscription(subscription)
568
+
569
+ self.subscription_active = True
570
+ logger.info(f"Started subscription to: {self.config.topic_pattern}")
571
+
572
+ # Signal that subscription is ready
573
+ if subscription_ready_event:
574
+ subscription_ready_event.set()
575
+
576
+ except Exception as e:
577
+ logger.error(f"Failed to start subscription: {e}")
578
+ raise BrokerConnectionError(f"Subscription failed: {e}")
579
+
580
+ def receive_message(self, timeout: Optional[int] = None):
581
+ """
582
+ Receive a message from the subscription.
583
+
584
+ Args:
585
+ timeout: Timeout in milliseconds, uses config default if None
586
+
587
+ Returns:
588
+ Received message or None if timeout
589
+ """
590
+ if not self.subscription_active or not self.message_receiver:
591
+ return None
592
+
593
+ timeout_ms = timeout or self.config.message_timeout
594
+
595
+ try:
596
+ return self.message_receiver.receive_message(timeout=timeout_ms)
597
+ except Exception as e:
598
+ logger.warning(f"Error receiving message: {e}")
599
+ return None
600
+
601
+ def stop_subscription(self) -> None:
602
+ """Stop message subscription."""
603
+ with self._receiver_lock:
604
+ if not self.subscription_active:
605
+ return
606
+
607
+ try:
608
+ if self.message_receiver:
609
+ logger.info("Stopping message receiver...")
610
+ self.message_receiver.terminate()
611
+ self.message_receiver = None
612
+
613
+ self.subscription_active = False
614
+ logger.info("Subscription stopped")
615
+
616
+ except Exception as e:
617
+ logger.error(f"Error stopping subscription: {e}")
618
+
619
+ def is_active(self) -> bool:
620
+ """Check if subscription is active."""
621
+ return self.subscription_active
622
+
623
+
624
+ class MessageSubscriber(threading.Thread):
625
+ """
626
+ Main message subscriber class that orchestrates all components.
627
+
628
+ This is the refactored version of the original Subscriber class,
629
+ maintaining the same interface while providing better structure.
630
+ """
631
+
632
+ def __init__(
633
+ self,
634
+ namespace: str,
635
+ active_tasks: Set[str],
636
+ wave_complete_event: Optional[threading.Event],
637
+ subscription_ready_event: Optional[threading.Event],
638
+ results_path: str,
639
+ ):
640
+ """
641
+ Initialize the message subscriber.
642
+
643
+ Args:
644
+ namespace: The namespace for topic subscription
645
+ active_tasks: Set of active task IDs to track
646
+ wave_complete_event: Event to set when all tasks complete
647
+ subscription_ready_event: Event to set when subscription is ready
648
+ results_path: Path to save results
649
+ """
650
+ super().__init__(name="MessageSubscriber")
651
+
652
+ # Initialize configuration
653
+ self.broker_config = self._create_broker_config()
654
+ self.subscription_config = SubscriptionConfig(namespace=namespace)
655
+
656
+ # Initialize services
657
+ self.connection_service = BrokerConnectionService(self.broker_config)
658
+ self.subscription_manager = SubscriptionManager(
659
+ self.connection_service, self.subscription_config
660
+ )
661
+ self.message_processor = MessageProcessor(self.subscription_config)
662
+ self.task_tracker = TaskTracker(active_tasks, wave_complete_event)
663
+ self.message_storage = MessageStorage(results_path)
664
+
665
+ # Thread control
666
+ self._running = False
667
+ self._subscription_ready_event = subscription_ready_event
668
+
669
+ # Statistics
670
+ self.start_time = time.time()
671
+ self.messages_received = 0
672
+ self.messages_processed = 0
673
+
674
+ def _create_broker_config(self) -> BrokerConfig:
675
+ """Create broker configuration from environment variables."""
676
+ try:
677
+ return BrokerConfig(
678
+ host=os.environ.get("SOLACE_BROKER_URL", ""),
679
+ vpn_name=os.environ.get("SOLACE_BROKER_VPN", ""),
680
+ username=os.environ.get("SOLACE_BROKER_USERNAME", ""),
681
+ password=os.environ.get("SOLACE_BROKER_PASSWORD", ""),
682
+ )
683
+ except ConfigurationError as e:
684
+ logger.error(f"Invalid broker configuration: {e}")
685
+ raise
686
+
687
+ def run(self) -> None:
688
+ """Main thread execution method."""
689
+ try:
690
+ self._running = True
691
+ logger.info("Starting message subscriber...")
692
+
693
+ # Connect to broker
694
+ self.connection_service.connect()
695
+
696
+ # Start subscription
697
+ self.subscription_manager.start_subscription(self._subscription_ready_event)
698
+
699
+ # Main message processing loop
700
+ self._message_processing_loop()
701
+
702
+ except Exception as e:
703
+ logger.error(f"Error in subscriber thread: {e}")
704
+ finally:
705
+ self._cleanup()
706
+
707
+ def _message_processing_loop(self) -> None:
708
+ """Main message processing loop."""
709
+ logger.info("Starting message processing loop...")
710
+
711
+ while self._running:
712
+ try:
713
+ # Receive message with timeout
714
+ inbound_message = self.subscription_manager.receive_message()
715
+
716
+ if inbound_message:
717
+ self.messages_received += 1
718
+ self._handle_inbound_message(inbound_message)
719
+
720
+ except Exception as e:
721
+ if self._running:
722
+ logger.error(f"Error in message processing loop: {e}")
723
+ # Continue processing other messages
724
+ continue
725
+
726
+ def _handle_inbound_message(self, inbound_message) -> None:
727
+ """Handle a single inbound message."""
728
+ try:
729
+ # Process the message
730
+ processed_message = self.message_processor.process_message(inbound_message)
731
+
732
+ if processed_message:
733
+ self.messages_processed += 1
734
+
735
+ # Store the message
736
+ self.message_storage.add_message(processed_message)
737
+
738
+ # Handle task completion if applicable
739
+ self.task_tracker.handle_task_completion(processed_message.topic)
740
+
741
+ except Exception as e:
742
+ logger.warning(f"Error handling message: {e}")
743
+
744
+ def stop(self) -> None:
745
+ """Stop the subscriber and clean up resources."""
746
+ logger.info("Stopping message subscriber...")
747
+ self._running = False
748
+
749
+ def _cleanup(self) -> None:
750
+ """Clean up all resources."""
751
+ try:
752
+ # Stop subscription
753
+ self.subscription_manager.stop_subscription()
754
+
755
+ # Disconnect from broker
756
+ self.connection_service.disconnect()
757
+
758
+ # Save messages
759
+ self.message_storage.save_messages()
760
+
761
+ # Log final statistics
762
+ self._log_final_statistics()
763
+
764
+ except Exception as e:
765
+ logger.error(f"Error during cleanup: {e}")
766
+
767
+ def _log_final_statistics(self) -> None:
768
+ """Log final processing statistics."""
769
+ runtime = time.time() - self.start_time
770
+ processor_stats = self.message_processor.get_stats()
771
+
772
+ logger.info("=== SUBSCRIBER STATISTICS ===")
773
+ logger.info(f"Runtime: {runtime:.2f} seconds")
774
+ logger.info(f"Messages received: {self.messages_received}")
775
+ logger.info(f"Messages processed: {self.messages_processed}")
776
+ logger.info(f"Messages stored: {self.message_storage.get_message_count()}")
777
+ logger.info(f"Processing errors: {processor_stats['error_count']}")
778
+ logger.info(
779
+ f"Active tasks remaining: {self.task_tracker.get_active_task_count()}"
780
+ )
781
+ logger.info(f"Tasks completed: {self.task_tracker.get_completed_task_count()}")
782
+ logger.info("=============================")
783
+
784
+ # Backward compatibility properties
785
+ @property
786
+ def active_tasks(self) -> Set[str]:
787
+ """Get active tasks set for backward compatibility."""
788
+ return self.task_tracker.active_tasks
789
+
790
+ @property
791
+ def messages(self) -> List[Dict[str, Any]]:
792
+ """Get messages list for backward compatibility."""
793
+ return [msg.to_dict() for msg in self.message_storage.messages]
794
+
795
+
796
+ # Backward compatibility alias
797
+ Subscriber = MessageSubscriber
798
+
799
+
800
+ def create_subscriber_from_env(
801
+ namespace: str,
802
+ active_tasks: Set[str],
803
+ wave_complete_event: Optional[threading.Event] = None,
804
+ subscription_ready_event: Optional[threading.Event] = None,
805
+ results_path: str = ".",
806
+ ) -> MessageSubscriber:
807
+ """
808
+ Factory function to create a subscriber with environment-based configuration.
809
+
810
+ Args:
811
+ namespace: The namespace for topic subscription
812
+ active_tasks: Set of active task IDs to track
813
+ wave_complete_event: Event to set when all tasks complete
814
+ subscription_ready_event: Event to set when subscription is ready
815
+ results_path: Path to save results
816
+
817
+ Returns:
818
+ Configured MessageSubscriber instance
819
+ """
820
+ return MessageSubscriber(
821
+ namespace=namespace,
822
+ active_tasks=active_tasks,
823
+ wave_complete_event=wave_complete_event,
824
+ subscription_ready_event=subscription_ready_event,
825
+ results_path=results_path,
826
+ )
827
+
828
+
829
+ def main():
830
+ """Main entry point for testing the subscriber."""
831
+ import signal
832
+ import sys
833
+
834
+ # Set up signal handling for graceful shutdown
835
+ def signal_handler(signum, frame):
836
+ print("\nShutting down subscriber...")
837
+ if "subscriber" in locals():
838
+ subscriber.stop()
839
+ subscriber.join()
840
+ sys.exit(0)
841
+
842
+ signal.signal(signal.SIGINT, signal_handler)
843
+ signal.signal(signal.SIGTERM, signal_handler)
844
+
845
+ # Create test subscriber
846
+ active_tasks = set()
847
+ subscription_ready = threading.Event()
848
+
849
+ try:
850
+ subscriber = create_subscriber_from_env(
851
+ namespace="test",
852
+ active_tasks=active_tasks,
853
+ subscription_ready_event=subscription_ready,
854
+ results_path=".",
855
+ )
856
+
857
+ subscriber.start()
858
+
859
+ # Wait for subscription to be ready
860
+ subscription_ready.wait(timeout=30)
861
+ print("Subscriber is ready and running...")
862
+
863
+ # Keep running until interrupted
864
+ subscriber.join()
865
+
866
+ except Exception as e:
867
+ print(f"Error running subscriber: {e}")
868
+ sys.exit(1)
869
+
870
+
871
+ if __name__ == "__main__":
872
+ main()