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

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

Potentially problematic release.


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

Files changed (481) hide show
  1. solace_agent_mesh/agent/adk/alembic/README +74 -0
  2. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  3. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  4. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  5. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  6. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  7. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
  8. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
  9. solace_agent_mesh/agent/adk/callbacks.py +852 -109
  10. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
  11. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
  12. solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
  13. solace_agent_mesh/agent/adk/models/lite_llm.py +77 -21
  14. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
  15. solace_agent_mesh/agent/adk/runner.py +85 -20
  16. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  17. solace_agent_mesh/agent/adk/services.py +94 -18
  18. solace_agent_mesh/agent/adk/setup.py +281 -65
  19. solace_agent_mesh/agent/adk/stream_parser.py +231 -37
  20. solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
  21. solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
  22. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  23. solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
  24. solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
  25. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  26. solace_agent_mesh/agent/proxies/base/component.py +188 -22
  27. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
  28. solace_agent_mesh/agent/sac/app.py +91 -3
  29. solace_agent_mesh/agent/sac/component.py +591 -157
  30. solace_agent_mesh/agent/sac/patch_adk.py +8 -16
  31. solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
  32. solace_agent_mesh/agent/tools/__init__.py +3 -0
  33. solace_agent_mesh/agent/tools/audio_tools.py +3 -3
  34. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +710 -171
  35. solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
  36. solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
  37. solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
  38. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  39. solace_agent_mesh/agent/tools/tool_config_types.py +57 -2
  40. solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
  41. solace_agent_mesh/agent/tools/web_tools.py +125 -17
  42. solace_agent_mesh/agent/utils/artifact_helpers.py +248 -6
  43. solace_agent_mesh/agent/utils/context_helpers.py +17 -0
  44. solace_agent_mesh/assets/docs/404.html +6 -6
  45. solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
  46. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  51. solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
  52. solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
  53. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  54. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
  56. solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
  57. solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
  60. solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
  61. solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
  62. solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
  63. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  64. solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
  65. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
  66. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  67. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  68. solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
  69. solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
  70. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  71. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  72. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  73. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  74. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  75. solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
  76. solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
  77. solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
  78. solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
  79. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  80. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  81. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  82. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  83. solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
  84. solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
  85. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  86. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  95. solace_agent_mesh/assets/docs/assets/js/e04b235d.06d23db6.js +1 -0
  96. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
  101. solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
  102. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
  104. solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
  105. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
  106. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
  107. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
  108. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
  109. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +82 -68
  110. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
  111. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
  112. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
  113. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
  114. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
  115. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
  116. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
  117. solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
  118. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
  119. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  120. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  121. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
  122. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  123. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -49
  124. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
  125. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
  126. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
  127. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
  128. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +90 -0
  129. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
  130. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  131. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
  132. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +162 -171
  133. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
  134. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
  135. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
  136. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
  137. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
  138. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
  139. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
  140. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
  141. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
  142. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
  143. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
  144. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
  145. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
  146. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
  147. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  148. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  149. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  150. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
  151. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
  152. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  153. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +112 -87
  154. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  155. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +87 -64
  156. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  157. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
  158. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -37
  159. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
  160. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
  161. solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
  162. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +311 -0
  163. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
  164. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
  165. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
  166. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
  167. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
  168. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  169. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  170. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
  171. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
  172. solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
  173. solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
  174. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  175. solace_agent_mesh/assets/docs/search-doc-1768329217460.json +1 -0
  176. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  177. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  178. solace_agent_mesh/cli/__init__.py +1 -1
  179. solace_agent_mesh/cli/commands/add_cmd/__init__.py +3 -1
  180. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
  181. solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
  182. solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
  183. solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
  184. solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
  185. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
  186. solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
  187. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
  188. solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
  189. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
  190. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
  191. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
  192. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
  193. solace_agent_mesh/cli/commands/run_cmd.py +64 -49
  194. solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
  195. solace_agent_mesh/cli/main.py +15 -0
  196. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-KnKMP_vb.js} +1 -1
  197. solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
  198. solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
  199. solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
  200. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CGk8Suyh.js +565 -0
  201. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  202. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  203. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  204. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  205. solace_agent_mesh/common/a2a/events.py +2 -1
  206. solace_agent_mesh/common/a2a/protocol.py +5 -0
  207. solace_agent_mesh/common/a2a/types.py +2 -1
  208. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
  209. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  210. solace_agent_mesh/common/agent_registry.py +38 -11
  211. solace_agent_mesh/common/data_parts.py +144 -4
  212. solace_agent_mesh/common/error_handlers.py +83 -0
  213. solace_agent_mesh/common/exceptions.py +24 -0
  214. solace_agent_mesh/common/oauth/__init__.py +17 -0
  215. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  216. solace_agent_mesh/common/oauth/utils.py +50 -0
  217. solace_agent_mesh/common/rag_dto.py +156 -0
  218. solace_agent_mesh/common/sac/sam_component_base.py +97 -19
  219. solace_agent_mesh/common/sam_events/event_service.py +2 -2
  220. solace_agent_mesh/common/services/employee_service.py +1 -1
  221. solace_agent_mesh/common/utils/embeds/constants.py +1 -0
  222. solace_agent_mesh/common/utils/embeds/converter.py +1 -8
  223. solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
  224. solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
  225. solace_agent_mesh/common/utils/embeds/types.py +9 -0
  226. solace_agent_mesh/common/utils/log_formatters.py +20 -0
  227. solace_agent_mesh/common/utils/mime_helpers.py +12 -5
  228. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  229. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  230. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  231. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  232. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  233. solace_agent_mesh/config_portal/backend/common.py +12 -0
  234. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
  235. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
  236. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
  237. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
  238. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
  239. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
  240. solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
  241. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  242. solace_agent_mesh/core_a2a/service.py +3 -2
  243. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  244. solace_agent_mesh/gateway/adapter/base.py +170 -0
  245. solace_agent_mesh/gateway/adapter/types.py +230 -0
  246. solace_agent_mesh/gateway/base/app.py +39 -2
  247. solace_agent_mesh/gateway/base/auth_interface.py +103 -0
  248. solace_agent_mesh/gateway/base/component.py +1027 -151
  249. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  250. solace_agent_mesh/gateway/generic/app.py +50 -0
  251. solace_agent_mesh/gateway/generic/component.py +894 -0
  252. solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
  253. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  254. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  255. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  256. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  257. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  258. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  259. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  260. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  261. solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
  262. solace_agent_mesh/gateway/http_sse/app.py +40 -11
  263. solace_agent_mesh/gateway/http_sse/component.py +285 -160
  264. solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
  265. solace_agent_mesh/gateway/http_sse/main.py +68 -450
  266. solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
  267. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
  268. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  269. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  270. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +26 -3
  271. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
  272. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
  273. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
  274. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
  275. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  276. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  277. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  278. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +8 -2
  279. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
  280. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  281. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  282. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +177 -11
  283. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
  284. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
  285. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +256 -58
  286. solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
  287. solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
  288. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  289. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  290. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  291. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -1
  292. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
  293. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  294. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
  295. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  296. solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
  297. solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
  298. solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
  299. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
  300. solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
  301. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  302. solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
  303. solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
  304. solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
  305. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  306. solace_agent_mesh/gateway/http_sse/routers/visualization.py +92 -9
  307. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  308. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  309. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
  310. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
  311. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  312. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  313. solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
  314. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
  315. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  316. solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
  317. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  318. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
  319. solace_agent_mesh/services/__init__.py +0 -0
  320. solace_agent_mesh/services/platform/__init__.py +29 -0
  321. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  322. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  323. solace_agent_mesh/services/platform/alembic.ini +109 -0
  324. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  325. solace_agent_mesh/services/platform/api/dependencies.py +154 -0
  326. solace_agent_mesh/services/platform/api/main.py +314 -0
  327. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  328. solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
  329. solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
  330. solace_agent_mesh/services/platform/app.py +215 -0
  331. solace_agent_mesh/services/platform/component.py +777 -0
  332. solace_agent_mesh/shared/__init__.py +14 -0
  333. solace_agent_mesh/shared/api/__init__.py +42 -0
  334. solace_agent_mesh/shared/auth/__init__.py +26 -0
  335. solace_agent_mesh/shared/auth/dependencies.py +204 -0
  336. solace_agent_mesh/shared/auth/middleware.py +347 -0
  337. solace_agent_mesh/shared/database/__init__.py +20 -0
  338. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
  339. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
  340. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
  341. solace_agent_mesh/shared/exceptions/__init__.py +36 -0
  342. solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +19 -5
  343. solace_agent_mesh/shared/utils/__init__.py +21 -0
  344. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  345. solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
  346. solace_agent_mesh/templates/platform.yaml +49 -0
  347. solace_agent_mesh/templates/plugin_readme_template.md +3 -25
  348. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  349. solace_agent_mesh/templates/proxy_template.yaml +62 -0
  350. solace_agent_mesh/templates/webui.yaml +148 -6
  351. solace_agent_mesh/tools/web_search/__init__.py +18 -0
  352. solace_agent_mesh/tools/web_search/base.py +84 -0
  353. solace_agent_mesh/tools/web_search/google_search.py +247 -0
  354. solace_agent_mesh/tools/web_search/models.py +99 -0
  355. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
  356. solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
  357. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
  358. solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
  359. solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
  360. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
  361. solace_agent_mesh/agent/adk/models/models_llm.txt +0 -142
  362. solace_agent_mesh/agent/agent_llm.txt +0 -378
  363. solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
  364. solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
  365. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
  366. solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
  367. solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
  368. solace_agent_mesh/agent/testing/testing_llm.txt +0 -57
  369. solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
  370. solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
  371. solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
  372. solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
  373. solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
  374. solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
  375. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
  376. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  377. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
  378. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
  379. solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
  380. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
  381. solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
  382. solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
  383. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
  384. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  385. solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
  386. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
  387. solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
  388. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  389. solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
  390. solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
  391. solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
  392. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
  393. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  394. solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
  395. solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
  396. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +0 -1
  397. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
  398. solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
  399. solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
  400. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
  401. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
  402. solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
  403. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +0 -1
  404. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  405. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  406. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
  407. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  408. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  409. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  410. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  411. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
  412. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
  413. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
  414. solace_agent_mesh/client/webui/frontend/static/assets/client-CaY59VuC.js +0 -25
  415. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  416. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  417. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
  418. solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
  419. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
  420. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
  421. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
  422. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
  423. solace_agent_mesh/common/common_llm.txt +0 -251
  424. solace_agent_mesh/common/common_llm_detail.txt +0 -2562
  425. solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
  426. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
  427. solace_agent_mesh/common/sac/sac_llm.txt +0 -71
  428. solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
  429. solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
  430. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
  431. solace_agent_mesh/common/services/providers/providers_llm.txt +0 -80
  432. solace_agent_mesh/common/services/services_llm.txt +0 -363
  433. solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
  434. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
  435. solace_agent_mesh/common/utils/utils_llm.txt +0 -336
  436. solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
  437. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
  438. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
  439. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
  440. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
  441. solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
  442. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
  443. solace_agent_mesh/gateway/base/base_llm.txt +0 -224
  444. solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
  445. solace_agent_mesh/gateway/gateway_llm.txt +0 -373
  446. solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
  447. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
  448. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
  449. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
  450. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
  451. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
  452. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -263
  453. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
  454. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
  455. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
  456. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
  457. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
  458. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
  459. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
  460. solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
  461. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
  462. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
  463. solace_agent_mesh/llm.txt +0 -228
  464. solace_agent_mesh/llm_detail.txt +0 -2835
  465. solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
  466. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
  467. solace_agent_mesh/templates/logging_config_template.ini +0 -45
  468. solace_agent_mesh/templates/templates_llm.txt +0 -147
  469. solace_agent_mesh-1.6.1.dist-info/RECORD +0 -525
  470. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
  471. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
  472. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
  473. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
  474. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
  475. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
  476. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
  477. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
  478. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
  479. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
  480. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
  481. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,894 @@
1
+ """
2
+ The GenericGatewayComponent, the engine that hosts and orchestrates GatewayAdapters.
3
+ """
4
+
5
+ import asyncio
6
+ import importlib
7
+ import logging
8
+ import uuid
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
11
+
12
+ from a2a.types import (
13
+ DataPart as A2ADataPart,
14
+ FilePart,
15
+ JSONRPCError,
16
+ Task,
17
+ TaskArtifactUpdateEvent,
18
+ TaskState,
19
+ TaskStatusUpdateEvent,
20
+ TextPart,
21
+ )
22
+
23
+ from ...common import a2a
24
+ from ...common.a2a.protocol import get_feedback_topic
25
+ from ...agent.utils.artifact_helpers import (
26
+ get_artifact_info_list,
27
+ load_artifact_content_or_metadata,
28
+ )
29
+ from ...common.a2a.types import ArtifactInfo
30
+ from ...common.utils.mime_helpers import is_text_based_mime_type
31
+ from ...common.utils.embeds import (
32
+ LATE_EMBED_TYPES,
33
+ evaluate_embed,
34
+ resolve_embeds_recursively_in_string,
35
+ )
36
+ from ...common.utils.embeds.types import ResolutionMode
37
+ from ..adapter.base import GatewayAdapter
38
+ from ..adapter.types import (
39
+ GatewayContext,
40
+ ResponseContext,
41
+ SamDataPart,
42
+ SamError,
43
+ SamFeedback,
44
+ SamFilePart,
45
+ SamTextPart,
46
+ SamUpdate,
47
+ )
48
+ from ..base.component import BaseGatewayComponent
49
+
50
+ # Try to import enterprise auth handler
51
+ try:
52
+ from solace_agent_mesh_enterprise.gateway.auth import SAMOAuth2Handler
53
+ ENTERPRISE_AUTH_AVAILABLE = True
54
+ except ImportError:
55
+ ENTERPRISE_AUTH_AVAILABLE = False
56
+ SAMOAuth2Handler = None
57
+
58
+
59
+ log = logging.getLogger(__name__)
60
+
61
+ info = {
62
+ "class_name": "GenericGatewayComponent",
63
+ "description": "A generic gateway component that hosts a pluggable GatewayAdapter.",
64
+ "config_parameters": [],
65
+ }
66
+
67
+
68
+ def _load_adapter_class(adapter_path: str) -> type[GatewayAdapter]:
69
+ """Dynamically loads the adapter class from a module path."""
70
+ try:
71
+ module_path, class_name = adapter_path.rsplit(".", 1)
72
+ module = importlib.import_module(module_path)
73
+ adapter_class = getattr(module, class_name)
74
+ if not issubclass(adapter_class, GatewayAdapter):
75
+ raise TypeError(
76
+ f"Class {adapter_path} is not a subclass of GatewayAdapter."
77
+ )
78
+ return adapter_class
79
+ except (ImportError, AttributeError, ValueError, TypeError) as e:
80
+ log.exception(f"Failed to load gateway adapter from path: {adapter_path}")
81
+ raise ImportError(
82
+ f"Could not load gateway adapter '{adapter_path}': {e}"
83
+ ) from e
84
+
85
+
86
+ class GenericGatewayComponent(BaseGatewayComponent, GatewayContext):
87
+ """
88
+ The engine that hosts and orchestrates a GatewayAdapter.
89
+
90
+ This component implements the `BaseGatewayComponent` abstract methods by
91
+ delegating platform-specific logic to a dynamically loaded adapter. It also
92
+ serves as the concrete implementation of the `GatewayContext` provided to
93
+ the adapter.
94
+ """
95
+
96
+ def __init__(self, **kwargs: Any):
97
+ component_config = kwargs.get("component_config", {})
98
+ app_config = component_config.get("app_config", {})
99
+ resolve_uris = app_config.get("resolve_artifact_uris_in_gateway", True)
100
+
101
+ # Generic gateway configuration:
102
+ # - supports_inline_artifact_resolution=True: Artifacts are converted to FileParts
103
+ # during embed resolution and can be rendered inline
104
+ # - filter_tool_data_parts=False: Gateway displays all parts including tool execution details
105
+ super().__init__(
106
+ resolve_artifact_uris_in_gateway=resolve_uris,
107
+ supports_inline_artifact_resolution=True,
108
+ filter_tool_data_parts=False,
109
+ **kwargs,
110
+ )
111
+ log.info("%s Initializing Generic Gateway Component...", self.log_identifier)
112
+
113
+ # --- Adapter Loading ---
114
+ adapter_path = self.get_config("gateway_adapter")
115
+ if not adapter_path:
116
+ raise ValueError("'gateway_adapter' path is not configured.")
117
+
118
+ log.info(
119
+ "%s Loading gateway adapter from: %s", self.log_identifier, adapter_path
120
+ )
121
+ AdapterClass = _load_adapter_class(adapter_path)
122
+ self.adapter: GatewayAdapter = AdapterClass()
123
+ log.info(
124
+ "%s Gateway adapter '%s' loaded successfully.",
125
+ self.log_identifier,
126
+ adapter_path,
127
+ )
128
+
129
+ # --- GatewayContext properties ---
130
+ adapter_config_dict = self.get_config("adapter_config", {})
131
+ if self.adapter.ConfigModel:
132
+ log.info(
133
+ "%s Validating adapter_config against %s...",
134
+ self.log_identifier,
135
+ self.adapter.ConfigModel.__name__,
136
+ )
137
+ self.adapter_config = self.adapter.ConfigModel(**adapter_config_dict)
138
+ else:
139
+ self.adapter_config = adapter_config_dict
140
+
141
+ self.artifact_service = self.shared_artifact_service
142
+ # `gateway_id`, `namespace`, `config` are available from base classes.
143
+
144
+ # --- Setup Authentication ---
145
+ self._setup_auth()
146
+
147
+ # --- Register Agent Registry Callbacks ---
148
+ # Wire up callbacks so the adapter is notified of agent changes
149
+ self.agent_registry.set_on_agent_added_callback(self._on_agent_added)
150
+ self.agent_registry.set_on_agent_removed_callback(self._on_agent_removed)
151
+ log.info(
152
+ "%s Agent registry callbacks registered for dynamic adapter updates.",
153
+ self.log_identifier,
154
+ )
155
+
156
+ def _setup_auth(self) -> None:
157
+ """
158
+ Setup authentication handler if enabled in config.
159
+
160
+ Uses enterprise SAMOAuth2Handler if available and auth is enabled.
161
+ Falls back gracefully if enterprise module is not installed.
162
+
163
+ Note: This method is called twice:
164
+ 1. During BaseGatewayComponent.__init__() (adapter_config not yet available)
165
+ 2. After adapter_config is set in GenericGatewayComponent.__init__()
166
+ """
167
+ # Early exit if adapter_config not yet set
168
+ if not hasattr(self, 'adapter_config'):
169
+ log.debug("%s _setup_auth() called before adapter_config set, skipping", self.log_identifier)
170
+ return
171
+
172
+ log.debug("%s adapter_config type: %s", self.log_identifier, type(self.adapter_config))
173
+
174
+ # Check enable_auth from adapter_config (not app_config)
175
+ # Handle both Pydantic models (use getattr) and plain dicts (use .get())
176
+ if isinstance(self.adapter_config, dict):
177
+ enable_auth = self.adapter_config.get('enable_auth', False)
178
+ log.debug("%s adapter_config is dict, keys: %s", self.log_identifier, list(self.adapter_config.keys()))
179
+ else:
180
+ # Pydantic model or other object
181
+ enable_auth = getattr(self.adapter_config, 'enable_auth', False)
182
+ log.debug("%s adapter_config is Pydantic model, enable_auth=%s", self.log_identifier, enable_auth)
183
+
184
+ log.info("%s Authentication check: enable_auth=%s (from adapter_config)", self.log_identifier, enable_auth)
185
+
186
+ if not enable_auth:
187
+ log.debug("%s Authentication disabled in config", self.log_identifier)
188
+ return
189
+
190
+ if not ENTERPRISE_AUTH_AVAILABLE:
191
+ log.warning(
192
+ "%s Authentication enabled but enterprise module not available. "
193
+ "Install solace-agent-mesh-enterprise to enable OAuth2 authentication.",
194
+ self.log_identifier
195
+ )
196
+ return
197
+
198
+ try:
199
+ # Build config dict for SAMOAuth2Handler from adapter_config
200
+ # Handler expects: oauth_proxy_url, external_auth_service_url, external_auth_provider, callback_url
201
+ auth_config = {}
202
+
203
+ # Try to get config from adapter_config (Pydantic model or dict)
204
+ if hasattr(self.adapter_config, '__dict__'):
205
+ # Pydantic model
206
+ auth_config = self.adapter_config.__dict__.copy()
207
+ elif isinstance(self.adapter_config, dict):
208
+ # Plain dict
209
+ auth_config = self.adapter_config.copy()
210
+
211
+ # Ensure callback_url is set (construct from host/port if not provided)
212
+ if 'callback_url' not in auth_config and 'callback_uri' not in auth_config:
213
+ # Try to construct from host and port
214
+ host = auth_config.get('host', 'localhost')
215
+ port = auth_config.get('port', 8080)
216
+ auth_config['callback_url'] = f"http://{host}:{port}/oauth/callback"
217
+ log.debug("%s Constructed callback_url: %s", self.log_identifier, auth_config['callback_url'])
218
+
219
+ # Initialize enterprise OAuth2 handler
220
+ self.auth_handler = SAMOAuth2Handler(auth_config)
221
+ log.info("%s OAuth2 authentication enabled via enterprise module", self.log_identifier)
222
+ except Exception as e:
223
+ log.error("%s Failed to initialize OAuth2 authentication: %s", self.log_identifier, e, exc_info=True)
224
+ self.auth_handler = None
225
+
226
+ def _on_agent_added(self, agent_card: Any) -> None:
227
+ """Called when a new agent is added to the registry."""
228
+ if self.adapter:
229
+ log.info(
230
+ "%s Registering new agent: %s",
231
+ self.log_identifier,
232
+ agent_card.name,
233
+ )
234
+ # Schedule the async call in the component's event loop
235
+ asyncio.run_coroutine_threadsafe(
236
+ self.adapter.handle_agent_registered(agent_card), self.get_async_loop()
237
+ )
238
+
239
+ def _on_agent_removed(self, agent_name: str) -> None:
240
+ """Called when an agent is removed from the registry."""
241
+ log.info(
242
+ "%s Deregistering agent: %s",
243
+ self.log_identifier,
244
+ agent_name,
245
+ )
246
+
247
+ if self.adapter:
248
+ # Schedule the async call in the component's event loop
249
+ asyncio.run_coroutine_threadsafe(
250
+ self.adapter.handle_agent_deregistered(agent_name), self.get_async_loop()
251
+ )
252
+
253
+ async def get_user_identity(
254
+ self, external_input: Any, endpoint_context: Optional[Dict[str, Any]] = None
255
+ ) -> Optional[Dict[str, Any]]:
256
+ """
257
+ Extracts the user identity from the external input via the adapter.
258
+
259
+ Returns:
260
+ A dictionary representing the user identity, or None if not available.
261
+ """
262
+ log_id_prefix = f"{self.log_identifier}[GetUserIdentity]"
263
+ user_identity = None
264
+ # 1. Authentication & Enrichment
265
+ # Try enterprise authentication first, fallback to adapter-based auth
266
+ try:
267
+ from solace_agent_mesh_enterprise.gateway.auth import authenticate_request
268
+
269
+ auth_claims = await authenticate_request(
270
+ adapter=self.adapter,
271
+ external_input=external_input,
272
+ endpoint_context=endpoint_context,
273
+ )
274
+ log.debug("%s Using enterprise authentication", log_id_prefix)
275
+ except ImportError:
276
+ # Enterprise package not available, use adapter-based auth
277
+ log.debug("%s Enterprise package not available, using adapter auth", log_id_prefix)
278
+ auth_claims = await self.adapter.extract_auth_claims(
279
+ external_input, endpoint_context
280
+ )
281
+
282
+ # The final user_identity is a dictionary, not the Pydantic model.
283
+ # It's built from claims and potentially enriched by an identity service.
284
+ if auth_claims:
285
+ if self.identity_service:
286
+ # Pass the rich claims object to the identity service
287
+ enriched_profile = await self.identity_service.get_user_profile(
288
+ auth_claims
289
+ )
290
+ if enriched_profile:
291
+ # Merge claims and profile, with profile taking precedence
292
+ user_identity = {
293
+ **auth_claims.model_dump(),
294
+ **enriched_profile,
295
+ }
296
+ else:
297
+ user_identity = auth_claims.model_dump()
298
+ else:
299
+ # No identity service, just use the claims from the adapter
300
+ user_identity = auth_claims.model_dump()
301
+ else:
302
+ # Fallback to default identity if no claims are extracted
303
+ default_identity = self.get_config("default_user_identity")
304
+ if default_identity:
305
+ user_identity = {"id": default_identity, "name": default_identity}
306
+ return user_identity
307
+
308
+ async def handle_external_input(
309
+ self, external_input: Any, endpoint_context: Optional[Dict[str, Any]] = None
310
+ ) -> str:
311
+ """
312
+ Processes an external input event through the full gateway flow.
313
+ Orchestrates auth, task preparation, and A2A submission.
314
+ """
315
+ log_id_prefix = f"{self.log_identifier}[HandleInput]"
316
+ user_identity = None
317
+ try:
318
+ user_identity = await self.get_user_identity(external_input, endpoint_context)
319
+ if not user_identity or not user_identity.get("id"):
320
+ raise PermissionError(
321
+ "Authentication failed: No identity could be determined."
322
+ )
323
+
324
+ log.info(
325
+ "%s Authenticated user: %s", log_id_prefix, user_identity.get("id")
326
+ )
327
+ # Add user_id to external_input so adapter can use it for permission checks.
328
+ external_input["user_id"] = user_identity.get("id")
329
+
330
+ # 2. Task Preparation
331
+ sam_task = await self.adapter.prepare_task(external_input, endpoint_context)
332
+ log.info(
333
+ "%s Adapter prepared task for agent '%s' with %d parts.",
334
+ log_id_prefix,
335
+ sam_task.target_agent,
336
+ len(sam_task.parts),
337
+ )
338
+
339
+ # 3. A2A Submission
340
+ a2a_parts = self._sam_parts_to_a2a_parts(sam_task.parts)
341
+
342
+ external_request_context = {
343
+ "a2a_session_id": sam_task.session_id,
344
+ "user_id_for_artifacts": user_identity.get("id"),
345
+ **sam_task.platform_context,
346
+ }
347
+
348
+ # Pass session_behavior if provided by adapter
349
+ if sam_task.session_behavior:
350
+ external_request_context["session_behavior"] = sam_task.session_behavior
351
+
352
+ task_id = await self.submit_a2a_task(
353
+ target_agent_name=sam_task.target_agent,
354
+ a2a_parts=a2a_parts,
355
+ external_request_context=external_request_context,
356
+ user_identity=user_identity,
357
+ is_streaming=sam_task.is_streaming,
358
+ )
359
+ return task_id
360
+
361
+ except Exception as e:
362
+ log.exception(
363
+ "%s Error during external input processing: %s", log_id_prefix, e
364
+ )
365
+ # Try to report error back to the platform if possible
366
+ if (
367
+ user_identity
368
+ and user_identity.get("id")
369
+ and isinstance(e, (ValueError, PermissionError))
370
+ ):
371
+ try:
372
+ # Create a dummy context to report the error
373
+ error_context = ResponseContext(
374
+ task_id="pre-task-error",
375
+ session_id=None,
376
+ conversation_id=None,
377
+ user_id=user_identity.get("id"),
378
+ platform_context={},
379
+ )
380
+ error = SamError(
381
+ message=str(e), code=-32001, category="GATEWAY_ERROR"
382
+ )
383
+ await self.adapter.handle_error(error, error_context)
384
+ except Exception as report_err:
385
+ log.error(
386
+ "%s Failed to report initial processing error to adapter: %s",
387
+ log_id_prefix,
388
+ report_err,
389
+ )
390
+ raise
391
+
392
+ async def cancel_task(self, task_id: str) -> None:
393
+ """Cancels an in-flight A2A task."""
394
+ log_id_prefix = f"{self.log_identifier}[CancelTask]"
395
+ context = self.task_context_manager.get_context(task_id)
396
+ if not context:
397
+ log.warning(
398
+ "%s Cannot cancel task %s: context not found.", log_id_prefix, task_id
399
+ )
400
+ return
401
+
402
+ target_agent_name = context.get("target_agent_name")
403
+ user_id = context.get("user_id_for_a2a")
404
+
405
+ if not target_agent_name:
406
+ log.error(
407
+ "%s Cannot cancel task %s: target_agent_name missing from context.",
408
+ log_id_prefix,
409
+ task_id,
410
+ )
411
+ return
412
+
413
+ log.info(
414
+ "%s Requesting cancellation for task %s on agent %s",
415
+ log_id_prefix,
416
+ task_id,
417
+ target_agent_name,
418
+ )
419
+ topic, payload, user_properties = self.core_a2a_service.cancel_task(
420
+ agent_name=target_agent_name,
421
+ task_id=task_id,
422
+ client_id=self.gateway_id,
423
+ user_id=user_id,
424
+ )
425
+ self.publish_a2a_message(
426
+ topic=topic, payload=payload, user_properties=user_properties
427
+ )
428
+
429
+ async def load_artifact_content(
430
+ self,
431
+ context: "ResponseContext",
432
+ filename: str,
433
+ version: Union[int, str] = "latest",
434
+ ) -> Optional[bytes]:
435
+ """Loads the raw byte content of an artifact using the shared service."""
436
+ log_id_prefix = f"{self.log_identifier}[LoadArtifact]"
437
+ if not self.artifact_service:
438
+ log.error("%s Artifact service is not configured.", log_id_prefix)
439
+ return None
440
+ try:
441
+ artifact_data = await load_artifact_content_or_metadata(
442
+ artifact_service=self.artifact_service,
443
+ app_name=self.gateway_id,
444
+ user_id=context.user_id,
445
+ session_id=context.session_id,
446
+ filename=filename,
447
+ version=version,
448
+ return_raw_bytes=True,
449
+ log_identifier_prefix=log_id_prefix,
450
+ )
451
+ if artifact_data.get("status") == "success":
452
+ content_bytes = artifact_data.get("raw_bytes")
453
+ mime_type = artifact_data.get("mime_type")
454
+
455
+ if content_bytes:
456
+ # For text-based artifacts, resolve templates and late embeds
457
+ if mime_type and is_text_based_mime_type(mime_type):
458
+ try:
459
+
460
+ content_str = content_bytes.decode("utf-8")
461
+
462
+ # Build context for resolution
463
+ context_for_resolver = {
464
+ "artifact_service": self.artifact_service,
465
+ "session_context": {
466
+ "app_name": self.gateway_id,
467
+ "user_id": context.user_id,
468
+ "session_id": context.session_id,
469
+ },
470
+ }
471
+
472
+ config_for_resolver = {
473
+ "gateway_max_artifact_resolve_size_bytes": (
474
+ self.gateway_max_artifact_resolve_size_bytes
475
+ if hasattr(
476
+ self, "gateway_max_artifact_resolve_size_bytes"
477
+ )
478
+ else -1
479
+ ),
480
+ "gateway_recursive_embed_depth": (
481
+ self.gateway_recursive_embed_depth
482
+ if hasattr(self, "gateway_recursive_embed_depth")
483
+ else 12
484
+ ),
485
+ }
486
+
487
+ log.debug(
488
+ "%s Text-based artifact. Resolving late embeds and templates.",
489
+ log_id_prefix,
490
+ )
491
+
492
+ # Resolve late embeds
493
+ resolved_content_str = await resolve_embeds_recursively_in_string(
494
+ text=content_str,
495
+ context=context_for_resolver,
496
+ resolver_func=evaluate_embed,
497
+ types_to_resolve=LATE_EMBED_TYPES,
498
+ resolution_mode=ResolutionMode.RECURSIVE_ARTIFACT_CONTENT,
499
+ log_identifier=f"{log_id_prefix}[RecursiveResolve]",
500
+ config=config_for_resolver,
501
+ max_depth=config_for_resolver[
502
+ "gateway_recursive_embed_depth"
503
+ ],
504
+ max_total_size=config_for_resolver[
505
+ "gateway_max_artifact_resolve_size_bytes"
506
+ ],
507
+ )
508
+
509
+ # Template blocks are automatically resolved by resolve_embeds_recursively_in_string
510
+ # when resolving late embeds. No need to call template resolution separately.
511
+
512
+ content_bytes = resolved_content_str.encode("utf-8")
513
+ log.info(
514
+ "%s Resolved embeds (including templates). Final size: %d bytes.",
515
+ log_id_prefix,
516
+ len(content_bytes),
517
+ )
518
+ except Exception as resolve_err:
519
+ log.warning(
520
+ "%s Failed to resolve embeds/templates: %s. Returning original content.",
521
+ log_id_prefix,
522
+ resolve_err,
523
+ )
524
+ # Fall through to return original content_bytes
525
+
526
+ log.info(
527
+ "%s Successfully loaded %d bytes for artifact '%s'.",
528
+ log_id_prefix,
529
+ len(content_bytes),
530
+ filename,
531
+ )
532
+ return content_bytes
533
+ else:
534
+ log.warning(
535
+ "%s Artifact '%s' (version: %s) loaded but has no content.",
536
+ log_id_prefix,
537
+ filename,
538
+ version,
539
+ )
540
+ return None
541
+ else:
542
+ log.warning(
543
+ "%s Failed to load artifact '%s' (version: %s). Status: %s",
544
+ log_id_prefix,
545
+ filename,
546
+ version,
547
+ artifact_data.get("status"),
548
+ )
549
+ return None
550
+ except Exception as e:
551
+ log.exception(
552
+ "%s Failed to load artifact '%s': %s", log_id_prefix, filename, e
553
+ )
554
+ return None
555
+
556
+ async def list_artifacts(
557
+ self, context: "ResponseContext"
558
+ ) -> List[ArtifactInfo]:
559
+ """Lists all artifacts available in the user's context."""
560
+ log_id_prefix = f"{self.log_identifier}[ListArtifacts]"
561
+ if not self.artifact_service:
562
+ log.error("%s Artifact service is not configured.", log_id_prefix)
563
+ return []
564
+ try:
565
+ artifact_infos = await get_artifact_info_list(
566
+ artifact_service=self.artifact_service,
567
+ app_name=self.gateway_id,
568
+ user_id=context.user_id,
569
+ session_id=context.session_id,
570
+ )
571
+ log.info(
572
+ "%s Found %d artifacts for user %s in session %s.",
573
+ log_id_prefix,
574
+ len(artifact_infos),
575
+ context.user_id,
576
+ context.session_id,
577
+ )
578
+ return artifact_infos
579
+ except Exception as e:
580
+ log.exception(
581
+ "%s Failed to list artifacts for user %s: %s",
582
+ log_id_prefix,
583
+ context.user_id,
584
+ e,
585
+ )
586
+ return []
587
+
588
+ def list_agents(self) -> List[Any]:
589
+ """Lists all agents currently registered in the agent registry."""
590
+ log_id_prefix = f"{self.log_identifier}[ListAgents]"
591
+ try:
592
+ agent_names = self.agent_registry.get_agent_names()
593
+ agents = []
594
+ for agent_name in agent_names:
595
+ agent_card = self.agent_registry.get_agent(agent_name)
596
+ if agent_card:
597
+ agents.append(agent_card)
598
+ log.info("%s Found %d registered agents.", log_id_prefix, len(agents))
599
+ return agents
600
+ except Exception as e:
601
+ log.exception("%s Failed to list agents: %s", log_id_prefix, e)
602
+ return []
603
+
604
+ async def submit_feedback(self, feedback: "SamFeedback") -> None:
605
+ """Handles feedback submission from an adapter."""
606
+ log_id_prefix = f"{self.log_identifier}[SubmitFeedback]"
607
+ feedback_config = self.get_config("feedback_publishing", {})
608
+
609
+ if not feedback_config.get("enabled", False):
610
+ log.debug("%s Feedback received but publishing is disabled.", log_id_prefix)
611
+ return
612
+
613
+ log.info(
614
+ "%s Received feedback for task %s: %s",
615
+ log_id_prefix,
616
+ feedback.task_id,
617
+ feedback.rating,
618
+ )
619
+
620
+ feedback_payload = {
621
+ "id": f"feedback-{uuid.uuid4().hex}",
622
+ "session_id": feedback.session_id,
623
+ "task_id": feedback.task_id,
624
+ "user_id": feedback.user_id,
625
+ "rating": feedback.rating,
626
+ "comment": feedback.comment,
627
+ "created_time": datetime.now(timezone.utc).isoformat(),
628
+ "gateway_id": self.gateway_id,
629
+ }
630
+
631
+ topic = get_feedback_topic(self.namespace)
632
+ self.publish_a2a_message(topic=topic, payload=feedback_payload)
633
+ log.info(
634
+ "%s Published feedback event for task %s to topic '%s'.",
635
+ log_id_prefix,
636
+ feedback.task_id,
637
+ topic,
638
+ )
639
+
640
+ def add_timer(
641
+ self, delay_ms: int, callback: Callable, interval_ms: Optional[int] = None
642
+ ) -> str:
643
+ timer_id = f"adapter-timer-{len(self.timer_manager.timers)}"
644
+ super().add_timer(delay_ms, timer_id, interval_ms, {"callback": callback})
645
+ return timer_id
646
+
647
+ def handle_timer_event(self, timer_data: Dict[str, Any]):
648
+ """Handles timer events and calls the adapter's callback."""
649
+ callback = timer_data.get("payload", {}).get("callback")
650
+ if callable(callback):
651
+ # Run async callback in the component's event loop
652
+ asyncio.run_coroutine_threadsafe(callback(), self.get_async_loop())
653
+ else:
654
+ log.warning("Timer fired but no valid callback found in payload.")
655
+
656
+ def get_task_state(self, task_id: str, key: str, default: Any = None) -> Any:
657
+ cache_key = f"task_state:{task_id}:{key}"
658
+ value = self.cache_service.get_data(cache_key)
659
+ return value if value is not None else default
660
+
661
+ def set_task_state(self, task_id: str, key: str, value: Any) -> None:
662
+ cache_key = f"task_state:{task_id}:{key}"
663
+ # Use a reasonable expiry to prevent orphaned state
664
+ self.cache_service.add_data(cache_key, value, expiry=3600) # 1 hour
665
+
666
+ def get_session_state(self, session_id: str, key: str, default: Any = None) -> Any:
667
+ cache_key = f"session_state:{session_id}:{key}"
668
+ value = self.cache_service.get_data(cache_key)
669
+ return value if value is not None else default
670
+
671
+ def set_session_state(self, session_id: str, key: str, value: Any) -> None:
672
+ cache_key = f"session_state:{session_id}:{key}"
673
+ # Use a longer expiry for session state
674
+ self.cache_service.add_data(cache_key, value, expiry=86400) # 24 hours
675
+
676
+ def process_sac_template(
677
+ self,
678
+ template: str,
679
+ payload: Any = None,
680
+ headers: Optional[Dict[str, str]] = None,
681
+ query_params: Optional[Dict[str, str]] = None,
682
+ user_data: Optional[Dict[str, Any]] = None,
683
+ ) -> str:
684
+ # This is a complex feature of SAC that requires careful implementation.
685
+ # For now, we raise an error.
686
+ raise NotImplementedError(
687
+ "process_sac_template is not yet implemented in GenericGatewayComponent."
688
+ )
689
+
690
+ # --- BaseGatewayComponent Abstract Method Implementations ---
691
+
692
+ def _start_listener(self) -> None:
693
+ """Starts the adapter's listener."""
694
+ log.info("%s Calling adapter.init()...", self.log_identifier)
695
+ # The adapter's init method is responsible for starting any listeners
696
+ # (e.g., an HTTP server, a websocket client).
697
+ # We run it in the component's event loop.
698
+ asyncio.run_coroutine_threadsafe(self.adapter.init(self), self.get_async_loop())
699
+
700
+ def _stop_listener(self) -> None:
701
+ """Stops the adapter's listener."""
702
+ log.info("%s Calling adapter.cleanup()...", self.log_identifier)
703
+ # The adapter's cleanup method should handle graceful shutdown.
704
+ if self.adapter:
705
+ future = asyncio.run_coroutine_threadsafe(
706
+ self.adapter.cleanup(), self.get_async_loop()
707
+ )
708
+ try:
709
+ future.result(timeout=10) # Wait for cleanup to finish
710
+ except Exception as e:
711
+ log.error("%s Error during adapter cleanup: %s", self.log_identifier, e)
712
+
713
+ async def _send_update_to_external(
714
+ self,
715
+ external_request_context: Dict[str, Any],
716
+ event_data: Union[TaskStatusUpdateEvent, TaskArtifactUpdateEvent],
717
+ is_final_chunk_of_update: bool,
718
+ ) -> None:
719
+ """Translates an A2A update event to SAM types and calls the adapter."""
720
+ response_context = self._create_response_context(external_request_context)
721
+ sam_update = SamUpdate(is_final=False)
722
+
723
+ parts: List[a2a.ContentPart] = []
724
+ if isinstance(event_data, TaskStatusUpdateEvent):
725
+ if event_data.status and event_data.status.message:
726
+ parts = a2a.get_parts_from_message(event_data.status.message)
727
+ elif isinstance(event_data, TaskArtifactUpdateEvent):
728
+ if event_data.artifact:
729
+ parts = a2a.get_parts_from_artifact(event_data.artifact)
730
+
731
+ sam_update.parts = self._a2a_parts_to_sam_parts(parts)
732
+ await self.adapter.handle_update(sam_update, response_context)
733
+
734
+ async def _send_final_response_to_external(
735
+ self, external_request_context: Dict[str, Any], task_data: Task
736
+ ) -> None:
737
+ """Translates a final A2A Task object to SAM types and calls the adapter."""
738
+ response_context = self._create_response_context(external_request_context)
739
+ sam_update = SamUpdate(is_final=True)
740
+
741
+ all_final_parts: List[a2a.ContentPart] = []
742
+ if task_data.status and task_data.status.message:
743
+ all_final_parts.extend(a2a.get_parts_from_message(task_data.status.message))
744
+ if task_data.artifacts:
745
+ for artifact in task_data.artifacts:
746
+ all_final_parts.extend(a2a.get_parts_from_artifact(artifact))
747
+
748
+ # If the original request was streaming, filter out text and file parts
749
+ # from the final response to avoid duplication, as they were already streamed.
750
+ was_streaming = external_request_context.get("is_streaming", False)
751
+ if was_streaming:
752
+ log.debug(
753
+ "%s Filtering final response parts for streaming task %s.",
754
+ self.log_identifier,
755
+ response_context.task_id,
756
+ )
757
+ filtered_parts = [
758
+ part
759
+ for part in all_final_parts
760
+ if not isinstance(part, (TextPart, FilePart))
761
+ ]
762
+ sam_update.parts = self._a2a_parts_to_sam_parts(filtered_parts)
763
+ else:
764
+ sam_update.parts = self._a2a_parts_to_sam_parts(all_final_parts)
765
+
766
+ # Send the final content update (which might be empty for streaming tasks)
767
+ await self.adapter.handle_update(sam_update, response_context)
768
+
769
+ # Then, signal completion
770
+ await self.adapter.handle_task_complete(response_context)
771
+
772
+ async def _send_error_to_external(
773
+ self, external_request_context: Dict[str, Any], error_data: JSONRPCError
774
+ ) -> None:
775
+ """Translates an A2A error to a SamError and calls the adapter."""
776
+ response_context = self._create_response_context(external_request_context)
777
+ sam_error = self._a2a_error_to_sam_error(error_data)
778
+
779
+ await self.adapter.handle_error(sam_error, response_context)
780
+
781
+ # Also signal task completion, as an error is a final state
782
+ await self.adapter.handle_task_complete(response_context)
783
+
784
+ # --- Unused BaseGatewayComponent Abstract Methods ---
785
+ # These are part of the old gateway pattern and are replaced by the adapter flow.
786
+
787
+ async def _extract_initial_claims(
788
+ self, external_event_data: Any
789
+ ) -> Optional[Dict[str, Any]]:
790
+ # This is now handled by `handle_external_input` calling the adapter directly.
791
+ # This method should not be called in the generic gateway flow.
792
+ log.warning(
793
+ "%s _extract_initial_claims called on GenericGatewayComponent. This should not happen.",
794
+ self.log_identifier,
795
+ )
796
+ return None
797
+
798
+ async def _translate_external_input(
799
+ self, external_event: Any
800
+ ) -> Tuple[str, List[a2a.ContentPart], Dict[str, Any]]:
801
+ # This is now handled by `handle_external_input` calling `adapter.prepare_task`.
802
+ # This method should not be called in the generic gateway flow.
803
+ log.warning(
804
+ "%s _translate_external_input called on GenericGatewayComponent. This should not happen.",
805
+ self.log_identifier,
806
+ )
807
+ raise NotImplementedError(
808
+ "_translate_external_input is not used in GenericGatewayComponent"
809
+ )
810
+
811
+ # --- Private Helper Methods ---
812
+
813
+ def _create_response_context(
814
+ self, external_request_context: Dict[str, Any]
815
+ ) -> ResponseContext:
816
+ """Builds a ResponseContext from the stored external request context."""
817
+ user_identity = external_request_context.get("user_identity", {})
818
+ return ResponseContext(
819
+ task_id=external_request_context.get("a2a_task_id_for_event"),
820
+ session_id=external_request_context.get("a2a_session_id"),
821
+ user_id=user_identity.get("id"),
822
+ platform_context=external_request_context,
823
+ )
824
+
825
+ def _sam_parts_to_a2a_parts(
826
+ self, sam_parts: List[Union[SamTextPart, SamFilePart, SamDataPart]]
827
+ ) -> List[a2a.ContentPart]:
828
+ """Converts a list of SAM parts to A2A parts."""
829
+ a2a_parts = []
830
+ for part in sam_parts:
831
+ if isinstance(part, SamTextPart):
832
+ a2a_parts.append(a2a.create_text_part(part.text))
833
+ elif isinstance(part, SamFilePart):
834
+ if part.content_bytes:
835
+ a2a_parts.append(
836
+ a2a.create_file_part_from_bytes(
837
+ content_bytes=part.content_bytes,
838
+ name=part.name,
839
+ mime_type=part.mime_type,
840
+ )
841
+ )
842
+ elif part.uri:
843
+ a2a_parts.append(
844
+ a2a.create_file_part_from_uri(
845
+ uri=part.uri,
846
+ name=part.name,
847
+ mime_type=part.mime_type,
848
+ )
849
+ )
850
+ elif isinstance(part, SamDataPart):
851
+ a2a_parts.append(a2a.create_data_part(part.data))
852
+ return a2a_parts
853
+
854
+ def _a2a_parts_to_sam_parts(
855
+ self, a2a_parts: List[a2a.ContentPart]
856
+ ) -> List[Union[SamTextPart, SamFilePart, SamDataPart]]:
857
+ """Converts a list of A2A parts to SAM parts."""
858
+ sam_parts = []
859
+ for part in a2a_parts:
860
+ if isinstance(part, TextPart):
861
+ sam_parts.append(SamTextPart(text=part.text))
862
+ elif isinstance(part, FilePart):
863
+ sam_parts.append(
864
+ SamFilePart(
865
+ name=a2a.get_filename_from_file_part(part),
866
+ content_bytes=a2a.get_bytes_from_file_part(part),
867
+ uri=a2a.get_uri_from_file_part(part),
868
+ mime_type=a2a.get_mimetype_from_file_part(part),
869
+ )
870
+ )
871
+ elif isinstance(part, A2ADataPart):
872
+ sam_parts.append(
873
+ SamDataPart(
874
+ data=a2a.get_data_from_data_part(part),
875
+ metadata=a2a.get_metadata_from_part(part),
876
+ )
877
+ )
878
+ return sam_parts
879
+
880
+ def _a2a_error_to_sam_error(self, error: JSONRPCError) -> SamError:
881
+ """Converts an A2A JSONRPCError to a SamError."""
882
+ category = "PROTOCOL_ERROR"
883
+ if isinstance(error.data, dict):
884
+ task_status = error.data.get("taskStatus")
885
+ if task_status == TaskState.failed:
886
+ category = "FAILED"
887
+ elif task_status == TaskState.canceled:
888
+ category = "CANCELED"
889
+
890
+ return SamError(
891
+ message=error.message,
892
+ code=error.code,
893
+ category=category,
894
+ )