solace-agent-mesh 1.11.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (624) hide show
  1. solace_agent_mesh/__init__.py +0 -0
  2. solace_agent_mesh/agent/__init__.py +0 -0
  3. solace_agent_mesh/agent/adk/__init__.py +0 -0
  4. solace_agent_mesh/agent/adk/adk_llm.txt +226 -0
  5. solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
  6. solace_agent_mesh/agent/adk/alembic/README +74 -0
  7. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  8. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  9. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  10. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  11. solace_agent_mesh/agent/adk/app_llm_agent.py +52 -0
  12. solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
  13. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
  14. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +545 -0
  15. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +609 -0
  16. solace_agent_mesh/agent/adk/callbacks.py +2318 -0
  17. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +406 -0
  18. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +415 -0
  19. solace_agent_mesh/agent/adk/mcp_content_processor.py +666 -0
  20. solace_agent_mesh/agent/adk/models/lite_llm.py +1026 -0
  21. solace_agent_mesh/agent/adk/models/models_llm.txt +189 -0
  22. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +132 -0
  23. solace_agent_mesh/agent/adk/runner.py +390 -0
  24. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  25. solace_agent_mesh/agent/adk/services.py +468 -0
  26. solace_agent_mesh/agent/adk/setup.py +1325 -0
  27. solace_agent_mesh/agent/adk/stream_parser.py +415 -0
  28. solace_agent_mesh/agent/adk/tool_wrapper.py +165 -0
  29. solace_agent_mesh/agent/agent_llm.txt +369 -0
  30. solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
  31. solace_agent_mesh/agent/protocol/__init__.py +0 -0
  32. solace_agent_mesh/agent/protocol/event_handlers.py +2041 -0
  33. solace_agent_mesh/agent/protocol/protocol_llm.txt +81 -0
  34. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
  35. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  36. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  37. solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
  38. solace_agent_mesh/agent/proxies/a2a/app.py +56 -0
  39. solace_agent_mesh/agent/proxies/a2a/component.py +1585 -0
  40. solace_agent_mesh/agent/proxies/a2a/config.py +216 -0
  41. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  42. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  43. solace_agent_mesh/agent/proxies/base/app.py +100 -0
  44. solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
  45. solace_agent_mesh/agent/proxies/base/component.py +816 -0
  46. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  47. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +19 -0
  48. solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
  49. solace_agent_mesh/agent/sac/__init__.py +0 -0
  50. solace_agent_mesh/agent/sac/app.py +595 -0
  51. solace_agent_mesh/agent/sac/component.py +3668 -0
  52. solace_agent_mesh/agent/sac/patch_adk.py +103 -0
  53. solace_agent_mesh/agent/sac/sac_llm.txt +189 -0
  54. solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
  55. solace_agent_mesh/agent/sac/task_execution_context.py +415 -0
  56. solace_agent_mesh/agent/testing/__init__.py +3 -0
  57. solace_agent_mesh/agent/testing/debug_utils.py +135 -0
  58. solace_agent_mesh/agent/testing/testing_llm.txt +58 -0
  59. solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
  60. solace_agent_mesh/agent/tools/__init__.py +16 -0
  61. solace_agent_mesh/agent/tools/audio_tools.py +1740 -0
  62. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +2500 -0
  63. solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +244 -0
  64. solace_agent_mesh/agent/tools/dynamic_tool.py +396 -0
  65. solace_agent_mesh/agent/tools/general_agent_tools.py +572 -0
  66. solace_agent_mesh/agent/tools/image_tools.py +1185 -0
  67. solace_agent_mesh/agent/tools/peer_agent_tool.py +363 -0
  68. solace_agent_mesh/agent/tools/registry.py +38 -0
  69. solace_agent_mesh/agent/tools/test_tools.py +136 -0
  70. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  71. solace_agent_mesh/agent/tools/tool_config_types.py +93 -0
  72. solace_agent_mesh/agent/tools/tool_definition.py +53 -0
  73. solace_agent_mesh/agent/tools/tools_llm.txt +276 -0
  74. solace_agent_mesh/agent/tools/tools_llm_detail.txt +275 -0
  75. solace_agent_mesh/agent/tools/web_tools.py +392 -0
  76. solace_agent_mesh/agent/utils/__init__.py +0 -0
  77. solace_agent_mesh/agent/utils/artifact_helpers.py +1353 -0
  78. solace_agent_mesh/agent/utils/config_parser.py +49 -0
  79. solace_agent_mesh/agent/utils/context_helpers.py +77 -0
  80. solace_agent_mesh/agent/utils/utils_llm.txt +152 -0
  81. solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
  82. solace_agent_mesh/assets/docs/404.html +16 -0
  83. solace_agent_mesh/assets/docs/assets/css/styles.8162edfb.css +1 -0
  84. solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
  85. solace_agent_mesh/assets/docs/assets/images/sam-enterprise-credentials-b269f095349473118b2b33bdfcc40122.png +0 -0
  86. solace_agent_mesh/assets/docs/assets/js/032c2d61.f3d37824.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
  95. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
  96. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  101. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js.LICENSE.txt +13 -0
  102. solace_agent_mesh/assets/docs/assets/js/2334.1cf50a20.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/240a0364.9ad94d1b.js +1 -0
  104. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  105. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
  106. solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
  107. solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
  108. solace_agent_mesh/assets/docs/assets/js/3624.0eaa1fd0.js +1 -0
  109. solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
  110. solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
  111. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
  112. solace_agent_mesh/assets/docs/assets/js/3ac1795d.28b7c67b.js +1 -0
  113. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2ddc75c0.js +1 -0
  114. solace_agent_mesh/assets/docs/assets/js/41adc471.48b12a4e.js +1 -0
  115. solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
  116. solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
  117. solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
  118. solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
  119. solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
  120. solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
  121. solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
  122. solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
  123. solace_agent_mesh/assets/docs/assets/js/509e993c.a1fbf45a.js +1 -0
  124. solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
  125. solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
  126. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  127. solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
  128. solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
  129. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.90a87880.js +1 -0
  130. solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
  131. solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
  132. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  133. solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
  134. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  135. solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
  136. solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
  137. solace_agent_mesh/assets/docs/assets/js/66d4869e.b77431fc.js +1 -0
  138. solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
  139. solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
  140. solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.js +1 -0
  141. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  142. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  143. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  144. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  145. solace_agent_mesh/assets/docs/assets/js/6fdfefc7.99de744e.js +1 -0
  146. solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
  147. solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
  148. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  149. solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
  150. solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
  151. solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
  152. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  153. solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
  154. solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
  155. solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
  156. solace_agent_mesh/assets/docs/assets/js/81a99df0.2484b8d9.js +1 -0
  157. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  158. solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
  159. solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
  160. solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
  161. solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
  162. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js +2 -0
  163. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js.LICENSE.txt +61 -0
  164. solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
  165. solace_agent_mesh/assets/docs/assets/js/8731.6c1dbf0c.js +1 -0
  166. solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
  167. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  168. solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
  169. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  170. solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
  171. solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
  172. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  173. solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
  174. solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
  175. solace_agent_mesh/assets/docs/assets/js/9bb13469.b2333011.js +1 -0
  176. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  177. solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
  178. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  179. solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +1 -0
  180. solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
  181. solace_agent_mesh/assets/docs/assets/js/ad71b5ed.af3ecfd1.js +1 -0
  182. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  183. solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.js +1 -0
  184. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  185. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  186. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  187. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  188. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  189. solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
  190. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  191. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  192. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  193. solace_agent_mesh/assets/docs/assets/js/de5f4c65.e8241890.js +1 -0
  194. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  195. solace_agent_mesh/assets/docs/assets/js/e04b235d.52cb25ed.js +1 -0
  196. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.b1068f9b.js +1 -0
  197. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  198. solace_agent_mesh/assets/docs/assets/js/e6f9706b.4488e34c.js +1 -0
  199. solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
  200. solace_agent_mesh/assets/docs/assets/js/f284c35a.250993bf.js +1 -0
  201. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  202. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js +2 -0
  203. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js.LICENSE.txt +81 -0
  204. solace_agent_mesh/assets/docs/assets/js/runtime~main.9e0813a2.js +1 -0
  205. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +154 -0
  206. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +99 -0
  207. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +90 -0
  208. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +107 -0
  209. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +166 -0
  210. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +101 -0
  211. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +219 -0
  212. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +92 -0
  213. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +29 -0
  214. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +55 -0
  215. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +110 -0
  216. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  217. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  218. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +345 -0
  219. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  220. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +83 -0
  221. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +84 -0
  222. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
  223. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
  224. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +85 -0
  225. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +60 -0
  226. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  227. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +144 -0
  228. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +191 -0
  229. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +128 -0
  230. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
  231. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  232. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +34 -0
  233. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
  234. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +267 -0
  235. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +142 -0
  236. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +116 -0
  237. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +86 -0
  238. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +164 -0
  239. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +140 -0
  240. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +57 -0
  241. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
  242. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +102 -0
  243. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  244. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  245. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  246. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +37 -0
  247. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +86 -0
  248. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  249. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +247 -0
  250. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  251. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +184 -0
  252. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  253. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +75 -0
  254. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +54 -0
  255. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +85 -0
  256. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
  257. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
  258. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +78 -0
  259. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +25 -0
  260. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +78 -0
  261. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +160 -0
  262. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -0
  263. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  264. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  265. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +100 -0
  266. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +52 -0
  267. solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
  268. solace_agent_mesh/assets/docs/img/logo.png +0 -0
  269. solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
  270. solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
  271. solace_agent_mesh/assets/docs/img/sam-enterprise-credentials.png +0 -0
  272. solace_agent_mesh/assets/docs/img/solace-logo-text.svg +18 -0
  273. solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
  274. solace_agent_mesh/assets/docs/lunr-index-1765810064709.json +1 -0
  275. solace_agent_mesh/assets/docs/lunr-index.json +1 -0
  276. solace_agent_mesh/assets/docs/search-doc-1765810064709.json +1 -0
  277. solace_agent_mesh/assets/docs/search-doc.json +1 -0
  278. solace_agent_mesh/assets/docs/sitemap.xml +1 -0
  279. solace_agent_mesh/cli/__init__.py +1 -0
  280. solace_agent_mesh/cli/commands/__init__.py +0 -0
  281. solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
  282. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
  283. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +729 -0
  284. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
  285. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +102 -0
  286. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +114 -0
  287. solace_agent_mesh/cli/commands/docs_cmd.py +60 -0
  288. solace_agent_mesh/cli/commands/eval_cmd.py +46 -0
  289. solace_agent_mesh/cli/commands/init_cmd/__init__.py +439 -0
  290. solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
  291. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  292. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
  293. solace_agent_mesh/cli/commands/init_cmd/env_step.py +238 -0
  294. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
  295. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +464 -0
  296. solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
  297. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +119 -0
  298. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +215 -0
  299. solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +20 -0
  300. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +137 -0
  301. solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
  302. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +144 -0
  303. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +306 -0
  304. solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
  305. solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +175 -0
  306. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
  307. solace_agent_mesh/cli/commands/run_cmd.py +215 -0
  308. solace_agent_mesh/cli/main.py +52 -0
  309. solace_agent_mesh/cli/utils.py +262 -0
  310. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-Dj3JtK42.js +1 -0
  311. solace_agent_mesh/client/webui/frontend/static/assets/client-ZKk9kEJ5.js +25 -0
  312. solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
  313. solace_agent_mesh/client/webui/frontend/static/assets/main-BcUaNZ-Q.css +1 -0
  314. solace_agent_mesh/client/webui/frontend/static/assets/main-vjch4RYc.js +435 -0
  315. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BNV4kZN0.js +535 -0
  316. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +15 -0
  317. solace_agent_mesh/client/webui/frontend/static/index.html +16 -0
  318. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  319. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  320. solace_agent_mesh/common/__init__.py +1 -0
  321. solace_agent_mesh/common/a2a/__init__.py +241 -0
  322. solace_agent_mesh/common/a2a/a2a_llm.txt +175 -0
  323. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
  324. solace_agent_mesh/common/a2a/artifact.py +368 -0
  325. solace_agent_mesh/common/a2a/events.py +213 -0
  326. solace_agent_mesh/common/a2a/message.py +375 -0
  327. solace_agent_mesh/common/a2a/protocol.py +689 -0
  328. solace_agent_mesh/common/a2a/task.py +127 -0
  329. solace_agent_mesh/common/a2a/translation.py +655 -0
  330. solace_agent_mesh/common/a2a/types.py +55 -0
  331. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  332. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +445 -0
  333. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
  334. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  335. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +48 -0
  336. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  337. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +41 -0
  338. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +330 -0
  339. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  340. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +48 -0
  341. solace_agent_mesh/common/agent_registry.py +122 -0
  342. solace_agent_mesh/common/common_llm.txt +230 -0
  343. solace_agent_mesh/common/common_llm_detail.txt +2562 -0
  344. solace_agent_mesh/common/constants.py +6 -0
  345. solace_agent_mesh/common/data_parts.py +150 -0
  346. solace_agent_mesh/common/exceptions.py +49 -0
  347. solace_agent_mesh/common/middleware/__init__.py +12 -0
  348. solace_agent_mesh/common/middleware/config_resolver.py +132 -0
  349. solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
  350. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
  351. solace_agent_mesh/common/middleware/registry.py +127 -0
  352. solace_agent_mesh/common/oauth/__init__.py +17 -0
  353. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  354. solace_agent_mesh/common/oauth/utils.py +50 -0
  355. solace_agent_mesh/common/sac/__init__.py +0 -0
  356. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  357. solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
  358. solace_agent_mesh/common/sac/sam_component_base.py +730 -0
  359. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  360. solace_agent_mesh/common/sam_events/event_service.py +208 -0
  361. solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
  362. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
  363. solace_agent_mesh/common/services/__init__.py +4 -0
  364. solace_agent_mesh/common/services/employee_service.py +164 -0
  365. solace_agent_mesh/common/services/identity_service.py +134 -0
  366. solace_agent_mesh/common/services/providers/__init__.py +4 -0
  367. solace_agent_mesh/common/services/providers/local_file_identity_service.py +151 -0
  368. solace_agent_mesh/common/services/providers/providers_llm.txt +81 -0
  369. solace_agent_mesh/common/services/services_llm.txt +368 -0
  370. solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
  371. solace_agent_mesh/common/utils/__init__.py +7 -0
  372. solace_agent_mesh/common/utils/artifact_utils.py +31 -0
  373. solace_agent_mesh/common/utils/asyncio_macos_fix.py +88 -0
  374. solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
  375. solace_agent_mesh/common/utils/embeds/constants.py +56 -0
  376. solace_agent_mesh/common/utils/embeds/converter.py +447 -0
  377. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +220 -0
  378. solace_agent_mesh/common/utils/embeds/evaluators.py +395 -0
  379. solace_agent_mesh/common/utils/embeds/modifiers.py +793 -0
  380. solace_agent_mesh/common/utils/embeds/resolver.py +967 -0
  381. solace_agent_mesh/common/utils/embeds/types.py +23 -0
  382. solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
  383. solace_agent_mesh/common/utils/initializer.py +52 -0
  384. solace_agent_mesh/common/utils/log_formatters.py +64 -0
  385. solace_agent_mesh/common/utils/message_utils.py +80 -0
  386. solace_agent_mesh/common/utils/mime_helpers.py +172 -0
  387. solace_agent_mesh/common/utils/push_notification_auth.py +135 -0
  388. solace_agent_mesh/common/utils/pydantic_utils.py +159 -0
  389. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  390. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  391. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  392. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  393. solace_agent_mesh/common/utils/type_utils.py +28 -0
  394. solace_agent_mesh/common/utils/utils_llm.txt +335 -0
  395. solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
  396. solace_agent_mesh/config_portal/__init__.py +0 -0
  397. solace_agent_mesh/config_portal/backend/__init__.py +0 -0
  398. solace_agent_mesh/config_portal/backend/common.py +77 -0
  399. solace_agent_mesh/config_portal/backend/plugin_catalog/__init__.py +0 -0
  400. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +24 -0
  401. solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
  402. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +166 -0
  403. solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +521 -0
  404. solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +217 -0
  405. solace_agent_mesh/config_portal/backend/server.py +644 -0
  406. solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
  407. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DiOiAjzL.js +103 -0
  408. solace_agent_mesh/config_portal/frontend/static/client/assets/components-Rk0n-9cK.js +140 -0
  409. solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-mvZjNKiz.js +19 -0
  410. solace_agent_mesh/config_portal/frontend/static/client/assets/index-DzNKzXrc.js +68 -0
  411. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-ba77705e.js +1 -0
  412. solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
  413. solace_agent_mesh/config_portal/frontend/static/client/assets/root-V2BeTIUc.js +10 -0
  414. solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
  415. solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
  416. solace_agent_mesh/core_a2a/__init__.py +1 -0
  417. solace_agent_mesh/core_a2a/core_a2a_llm.txt +90 -0
  418. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
  419. solace_agent_mesh/core_a2a/service.py +307 -0
  420. solace_agent_mesh/evaluation/__init__.py +0 -0
  421. solace_agent_mesh/evaluation/evaluator.py +691 -0
  422. solace_agent_mesh/evaluation/message_organizer.py +553 -0
  423. solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
  424. solace_agent_mesh/evaluation/report/chart_section.html +141 -0
  425. solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
  426. solace_agent_mesh/evaluation/report/modal.html +59 -0
  427. solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
  428. solace_agent_mesh/evaluation/report/modal_script.js +296 -0
  429. solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
  430. solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
  431. solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
  432. solace_agent_mesh/evaluation/report/templates/header.html +340 -0
  433. solace_agent_mesh/evaluation/report_data_processor.py +970 -0
  434. solace_agent_mesh/evaluation/report_generator.py +607 -0
  435. solace_agent_mesh/evaluation/run.py +954 -0
  436. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  437. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  438. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  439. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  440. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  441. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  442. solace_agent_mesh/evaluation/subscriber.py +776 -0
  443. solace_agent_mesh/evaluation/summary_builder.py +880 -0
  444. solace_agent_mesh/gateway/__init__.py +0 -0
  445. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  446. solace_agent_mesh/gateway/adapter/base.py +143 -0
  447. solace_agent_mesh/gateway/adapter/types.py +221 -0
  448. solace_agent_mesh/gateway/base/__init__.py +1 -0
  449. solace_agent_mesh/gateway/base/app.py +345 -0
  450. solace_agent_mesh/gateway/base/base_llm.txt +226 -0
  451. solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
  452. solace_agent_mesh/gateway/base/component.py +2030 -0
  453. solace_agent_mesh/gateway/base/task_context.py +75 -0
  454. solace_agent_mesh/gateway/gateway_llm.txt +369 -0
  455. solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
  456. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  457. solace_agent_mesh/gateway/generic/app.py +50 -0
  458. solace_agent_mesh/gateway/generic/component.py +727 -0
  459. solace_agent_mesh/gateway/http_sse/__init__.py +0 -0
  460. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +345 -0
  461. solace_agent_mesh/gateway/http_sse/alembic/env.py +87 -0
  462. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  463. solace_agent_mesh/gateway/http_sse/alembic/versions/20250910_d5b3f8f2e9a0_create_initial_database.py +58 -0
  464. solace_agent_mesh/gateway/http_sse/alembic/versions/20250911_b1c2d3e4f5g6_add_database_indexes.py +83 -0
  465. solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +412 -0
  466. solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
  467. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  468. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  469. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  470. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  471. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  472. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  473. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  474. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  475. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  476. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +161 -0
  477. solace_agent_mesh/gateway/http_sse/alembic.ini +109 -0
  478. solace_agent_mesh/gateway/http_sse/app.py +351 -0
  479. solace_agent_mesh/gateway/http_sse/component.py +2360 -0
  480. solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
  481. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +105 -0
  482. solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
  483. solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +110 -0
  484. solace_agent_mesh/gateway/http_sse/dependencies.py +653 -0
  485. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +299 -0
  486. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
  487. solace_agent_mesh/gateway/http_sse/main.py +789 -0
  488. solace_agent_mesh/gateway/http_sse/repository/__init__.py +46 -0
  489. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +102 -0
  490. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +11 -0
  491. solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
  492. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +221 -0
  493. solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
  494. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  495. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  496. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +66 -0
  497. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -0
  498. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +32 -0
  499. solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
  500. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +125 -0
  501. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +239 -0
  502. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +34 -0
  503. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  504. solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
  505. solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
  506. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +257 -0
  507. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  508. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  509. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  510. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +53 -0
  511. solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
  512. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +39 -0
  513. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  514. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  515. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +308 -0
  516. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +268 -0
  517. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +248 -0
  518. solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
  519. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +74 -0
  520. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1137 -0
  521. solace_agent_mesh/gateway/http_sse/routers/auth.py +311 -0
  522. solace_agent_mesh/gateway/http_sse/routers/config.py +371 -0
  523. solace_agent_mesh/gateway/http_sse/routers/dto/__init__.py +10 -0
  524. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +450 -0
  525. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  526. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  527. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +15 -0
  528. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  529. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +133 -0
  530. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +33 -0
  531. solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
  532. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +18 -0
  533. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
  534. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  535. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +123 -0
  536. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +33 -0
  537. solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
  538. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  539. solace_agent_mesh/gateway/http_sse/routers/feedback.py +168 -0
  540. solace_agent_mesh/gateway/http_sse/routers/people.py +38 -0
  541. solace_agent_mesh/gateway/http_sse/routers/projects.py +767 -0
  542. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1415 -0
  543. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +312 -0
  544. solace_agent_mesh/gateway/http_sse/routers/sessions.py +634 -0
  545. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  546. solace_agent_mesh/gateway/http_sse/routers/sse.py +230 -0
  547. solace_agent_mesh/gateway/http_sse/routers/tasks.py +1089 -0
  548. solace_agent_mesh/gateway/http_sse/routers/users.py +83 -0
  549. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  550. solace_agent_mesh/gateway/http_sse/routers/visualization.py +1220 -0
  551. solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
  552. solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +71 -0
  553. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  554. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  555. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +273 -0
  556. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +250 -0
  557. solace_agent_mesh/gateway/http_sse/services/people_service.py +78 -0
  558. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  559. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  560. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +303 -0
  561. solace_agent_mesh/gateway/http_sse/services/session_service.py +702 -0
  562. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +593 -0
  563. solace_agent_mesh/gateway/http_sse/services/task_service.py +119 -0
  564. solace_agent_mesh/gateway/http_sse/session_manager.py +219 -0
  565. solace_agent_mesh/gateway/http_sse/shared/__init__.py +146 -0
  566. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  567. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +252 -0
  568. solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
  569. solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
  570. solace_agent_mesh/gateway/http_sse/shared/enums.py +40 -0
  571. solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
  572. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +217 -0
  573. solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
  574. solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
  575. solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
  576. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +319 -0
  577. solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
  578. solace_agent_mesh/gateway/http_sse/shared/types.py +50 -0
  579. solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
  580. solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +88 -0
  581. solace_agent_mesh/gateway/http_sse/sse_manager.py +491 -0
  582. solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
  583. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  584. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +72 -0
  585. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
  586. solace_agent_mesh/llm.txt +228 -0
  587. solace_agent_mesh/llm_detail.txt +2835 -0
  588. solace_agent_mesh/services/__init__.py +0 -0
  589. solace_agent_mesh/services/platform/__init__.py +18 -0
  590. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  591. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  592. solace_agent_mesh/services/platform/alembic.ini +109 -0
  593. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  594. solace_agent_mesh/services/platform/api/dependencies.py +147 -0
  595. solace_agent_mesh/services/platform/api/main.py +280 -0
  596. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  597. solace_agent_mesh/services/platform/api/routers/__init__.py +24 -0
  598. solace_agent_mesh/services/platform/app.py +114 -0
  599. solace_agent_mesh/services/platform/component.py +235 -0
  600. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  601. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
  602. solace_agent_mesh/templates/agent_template.yaml +53 -0
  603. solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
  604. solace_agent_mesh/templates/gateway_app_template.py +75 -0
  605. solace_agent_mesh/templates/gateway_component_template.py +484 -0
  606. solace_agent_mesh/templates/gateway_config_template.yaml +38 -0
  607. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  608. solace_agent_mesh/templates/main_orchestrator.yaml +66 -0
  609. solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
  610. solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
  611. solace_agent_mesh/templates/plugin_custom_template.py +10 -0
  612. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +60 -0
  613. solace_agent_mesh/templates/plugin_pyproject_template.toml +32 -0
  614. solace_agent_mesh/templates/plugin_readme_template.md +12 -0
  615. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  616. solace_agent_mesh/templates/plugin_tools_template.py +224 -0
  617. solace_agent_mesh/templates/shared_config.yaml +112 -0
  618. solace_agent_mesh/templates/templates_llm.txt +147 -0
  619. solace_agent_mesh/templates/webui.yaml +177 -0
  620. solace_agent_mesh-1.11.2.dist-info/METADATA +504 -0
  621. solace_agent_mesh-1.11.2.dist-info/RECORD +624 -0
  622. solace_agent_mesh-1.11.2.dist-info/WHEEL +4 -0
  623. solace_agent_mesh-1.11.2.dist-info/entry_points.txt +3 -0
  624. solace_agent_mesh-1.11.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,1137 @@
1
+ """
2
+ FastAPI router for managing session-specific artifacts via REST endpoints.
3
+ """
4
+
5
+ import logging
6
+ from collections.abc import Callable
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ from fastapi import (
10
+ APIRouter,
11
+ Depends,
12
+ File,
13
+ Form,
14
+ HTTPException,
15
+ Path,
16
+ Query,
17
+ UploadFile,
18
+ status,
19
+ Request as FastAPIRequest,
20
+ )
21
+ from pydantic import BaseModel, Field
22
+ from fastapi.responses import Response, StreamingResponse
23
+
24
+ try:
25
+ from google.adk.artifacts import BaseArtifactService
26
+ except ImportError:
27
+
28
+ class BaseArtifactService:
29
+ pass
30
+
31
+
32
+ import io
33
+ import json
34
+ from datetime import datetime, timezone
35
+ from urllib.parse import parse_qs, quote, urlparse
36
+
37
+ from ....common.a2a.types import ArtifactInfo
38
+ from ....common.utils.embeds import (
39
+ LATE_EMBED_TYPES,
40
+ evaluate_embed,
41
+ resolve_embeds_recursively_in_string,
42
+ )
43
+ from ....common.utils.embeds.types import ResolutionMode
44
+ from ....common.utils.mime_helpers import is_text_based_mime_type
45
+ from ....common.utils.templates import resolve_template_blocks_in_string
46
+ from ..dependencies import (
47
+ get_project_service_optional,
48
+ ValidatedUserConfig,
49
+ get_sac_component,
50
+ get_session_validator,
51
+ get_shared_artifact_service,
52
+ get_user_id,
53
+ get_session_manager,
54
+ get_session_business_service_optional,
55
+ get_db_optional,
56
+ )
57
+ from ..services.project_service import ProjectService
58
+
59
+
60
+ from ..session_manager import SessionManager
61
+ from ..services.session_service import SessionService
62
+ from sqlalchemy.orm import Session
63
+
64
+ from ....agent.utils.artifact_helpers import (
65
+ get_artifact_info_list,
66
+ load_artifact_content_or_metadata,
67
+ process_artifact_upload,
68
+ )
69
+
70
+ if TYPE_CHECKING:
71
+ from ....gateway.http_sse.component import WebUIBackendComponent
72
+
73
+ log = logging.getLogger(__name__)
74
+
75
+ LOAD_FILE_CHUNK_SIZE = 1024 * 1024 # 1MB chunks
76
+
77
+ class ArtifactUploadResponse(BaseModel):
78
+ """Response model for artifact upload with camelCase fields."""
79
+
80
+ uri: str
81
+ session_id: str = Field(..., alias="sessionId")
82
+ filename: str
83
+ size: int
84
+ mime_type: str = Field(..., alias="mimeType")
85
+ metadata: dict[str, Any]
86
+ created_at: str = Field(..., alias="createdAt")
87
+
88
+ model_config = {"populate_by_name": True}
89
+
90
+
91
+ router = APIRouter()
92
+
93
+
94
+ def _resolve_storage_context(
95
+ session_id: str,
96
+ project_id: str | None,
97
+ user_id: str,
98
+ validate_session: Callable[[str, str], bool],
99
+ project_service: ProjectService | None,
100
+ log_prefix: str
101
+ ) -> tuple[str, str, str]:
102
+ """
103
+ Resolve storage context from session or project parameters.
104
+
105
+ Returns:
106
+ tuple: (storage_user_id, storage_session_id, context_type)
107
+
108
+ Raises:
109
+ HTTPException: If no valid context found
110
+ """
111
+ # Priority 1: Session context
112
+ if session_id and session_id.strip() and session_id not in ["null", "undefined"]:
113
+ if not validate_session(session_id, user_id):
114
+ log.warning("%s Session validation failed", log_prefix)
115
+ raise HTTPException(
116
+ status_code=status.HTTP_404_NOT_FOUND,
117
+ detail="Session not found or access denied.",
118
+ )
119
+ return user_id, session_id, "session"
120
+
121
+ # Priority 2: Project context (only if persistence is enabled)
122
+ elif project_id and project_id.strip() and project_id not in ["null", "undefined"]:
123
+ if project_service is None:
124
+ log.warning("%s Project context requested but persistence not enabled", log_prefix)
125
+ raise HTTPException(
126
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
127
+ detail="Project context requires database configuration.",
128
+ )
129
+
130
+ from ....gateway.http_sse.dependencies import SessionLocal
131
+
132
+ if SessionLocal is None:
133
+ log.warning("%s Project context requested but database not configured", log_prefix)
134
+ raise HTTPException(
135
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
136
+ detail="Project context requires database configuration.",
137
+ )
138
+
139
+ db = SessionLocal()
140
+ try:
141
+ project = project_service.get_project(db, project_id, user_id)
142
+ if not project:
143
+ log.warning("%s Project not found or access denied", log_prefix)
144
+ raise HTTPException(
145
+ status_code=status.HTTP_404_NOT_FOUND,
146
+ detail="Project not found or access denied.",
147
+ )
148
+ return project.user_id, f"project-{project_id}", "project"
149
+ except HTTPException:
150
+ raise
151
+ except Exception as e:
152
+ log.error("%s Error resolving project context: %s", log_prefix, e)
153
+ raise HTTPException(
154
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
155
+ detail="Failed to resolve project context"
156
+ )
157
+ finally:
158
+ db.close()
159
+
160
+ # No valid context
161
+ log.warning("%s No valid context found", log_prefix)
162
+ raise HTTPException(
163
+ status_code=status.HTTP_404_NOT_FOUND,
164
+ detail="No valid context provided.",
165
+ )
166
+
167
+
168
+ @router.post(
169
+ "/upload",
170
+ status_code=status.HTTP_201_CREATED,
171
+ response_model=ArtifactUploadResponse,
172
+ summary="Upload Artifact (Body-Based Session Management)",
173
+ description="Uploads file with sessionId and filename in request body. Creates session if sessionId is null/empty.",
174
+ )
175
+ async def upload_artifact_with_session(
176
+ request: FastAPIRequest,
177
+ upload_file: UploadFile = File(..., description="The file content to upload"),
178
+ sessionId: str | None = Form(
179
+ None,
180
+ description="Session ID (null/empty to create new session)",
181
+ alias="sessionId",
182
+ ),
183
+ filename: str = Form(..., description="The name of the artifact to create/update"),
184
+ metadata_json: str | None = Form(
185
+ None, description="JSON string of artifact metadata (e.g., description, source)"
186
+ ),
187
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
188
+ user_id: str = Depends(get_user_id),
189
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
190
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
191
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:create"])),
192
+ session_manager: SessionManager = Depends(get_session_manager),
193
+ session_service: SessionService | None = Depends(
194
+ get_session_business_service_optional
195
+ ),
196
+ db: Session | None = Depends(get_db_optional),
197
+ ):
198
+ """
199
+ Uploads a file to create a new version of the specified artifact.
200
+
201
+ Key features:
202
+ - Session ID and filename provided in request body (not URL)
203
+ - Automatically creates new session if session_id is null/empty
204
+ - Consistent with chat API patterns
205
+ """
206
+ log_prefix = f"[POST /artifacts/upload] User {user_id}: "
207
+
208
+ # Handle session creation logic (matching chat API pattern)
209
+ effective_session_id = None
210
+ is_new_session = False # Track if we created a new session
211
+
212
+ # Use session ID from request body (matching sessionId pattern in session APIs)
213
+ if sessionId and sessionId.strip():
214
+ effective_session_id = sessionId.strip()
215
+ log.info("%sUsing existing session: %s", log_prefix, effective_session_id)
216
+ else:
217
+ # Create new session when no sessionId provided (like chat does for new conversations)
218
+ effective_session_id = session_manager.create_new_session_id(request)
219
+ is_new_session = True # Mark that we created this session
220
+ log.info(
221
+ "%sCreated new session for file upload: %s",
222
+ log_prefix,
223
+ effective_session_id,
224
+ )
225
+
226
+ # Persist session in database if persistence is available (matching chat pattern)
227
+ if session_service and db:
228
+ try:
229
+ session_service.create_session(
230
+ db=db,
231
+ user_id=user_id,
232
+ session_id=effective_session_id,
233
+ agent_id=None, # Will be determined when first message is sent
234
+ name=None, # Will be set when first message is sent
235
+ )
236
+ db.commit()
237
+ log.info(
238
+ "%sSession created and committed to database: %s",
239
+ log_prefix,
240
+ effective_session_id,
241
+ )
242
+ except Exception as session_error:
243
+ db.rollback()
244
+ log.warning(
245
+ "%sSession persistence failed, continuing with in-memory session: %s",
246
+ log_prefix,
247
+ session_error,
248
+ )
249
+ else:
250
+ log.debug(
251
+ "%sNo persistence available - using in-memory session: %s",
252
+ log_prefix,
253
+ effective_session_id,
254
+ )
255
+
256
+ # Validate inputs
257
+ if not filename or not filename.strip():
258
+ raise HTTPException(
259
+ status_code=status.HTTP_400_BAD_REQUEST,
260
+ detail="Filename is required.",
261
+ )
262
+
263
+ if not upload_file.filename:
264
+ raise HTTPException(
265
+ status_code=status.HTTP_400_BAD_REQUEST,
266
+ detail="File upload is required.",
267
+ )
268
+
269
+ # Validate artifact service availability
270
+ if not artifact_service:
271
+ log.error("%sArtifact service is not configured.", log_prefix)
272
+ raise HTTPException(
273
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
274
+ detail="Artifact service is not configured.",
275
+ )
276
+
277
+ # Validate session (now that we have an effective_session_id)
278
+ # Skip validation if we just created the session to avoid race conditions
279
+ if not is_new_session and not validate_session(effective_session_id, user_id):
280
+ log.warning(
281
+ "%sSession validation failed for session: %s",
282
+ log_prefix,
283
+ effective_session_id,
284
+ )
285
+ raise HTTPException(
286
+ status_code=status.HTTP_403_FORBIDDEN,
287
+ detail="Invalid session or insufficient permissions.",
288
+ )
289
+
290
+ log.info(
291
+ "%sUploading file '%s' to session '%s'",
292
+ log_prefix,
293
+ filename.strip(),
294
+ effective_session_id,
295
+ )
296
+
297
+ try:
298
+ # ===== VALIDATE FILE SIZE BEFORE READING =====
299
+ max_upload_size = component.get_config("gateway_max_upload_size_bytes")
300
+
301
+ # Check Content-Length header first (if available)
302
+ content_length = request.headers.get("content-length")
303
+ if content_length:
304
+ try:
305
+ file_size = int(content_length)
306
+
307
+ if file_size > max_upload_size:
308
+ error_msg = (
309
+ f"File upload rejected: size {file_size:,} bytes "
310
+ f"exceeds maximum {max_upload_size:,} bytes "
311
+ f"({file_size / (1024*1024):.2f} MB > {max_upload_size / (1024*1024):.2f} MB)"
312
+ )
313
+ log.warning("%s %s", log_prefix, error_msg)
314
+
315
+ raise HTTPException(
316
+ status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
317
+ detail=error_msg # Use string instead of dict
318
+ )
319
+ except ValueError:
320
+ log.warning("%s Invalid Content-Length header: %s", log_prefix, content_length)
321
+
322
+ # Read file content in chunks with size validation
323
+ chunk_size = LOAD_FILE_CHUNK_SIZE
324
+ content_bytes = bytearray()
325
+ total_bytes_read = 0
326
+
327
+ try:
328
+ while True:
329
+ chunk = await upload_file.read(chunk_size)
330
+ if not chunk:
331
+ break # End of file
332
+
333
+ chunk_len = len(chunk)
334
+ total_bytes_read += chunk_len
335
+
336
+ # Validate size during reading (fail fast)
337
+ if total_bytes_read > max_upload_size:
338
+ error_msg = (
339
+ f"File '{upload_file.filename}' rejected: size exceeds maximum {max_upload_size:,} bytes "
340
+ f"(read {total_bytes_read:,} bytes so far, "
341
+ f"{total_bytes_read / (1024*1024):.2f} MB > {max_upload_size / (1024*1024):.2f} MB)"
342
+ )
343
+ log.warning("%s %s", log_prefix, error_msg)
344
+
345
+ raise HTTPException(
346
+ status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
347
+ detail=error_msg
348
+ )
349
+
350
+ content_bytes.extend(chunk)
351
+
352
+ # Convert to bytes for consistency with existing code
353
+ content_bytes = bytes(content_bytes)
354
+
355
+ log.debug(
356
+ "%s File read successfully in chunks: %d bytes total",
357
+ log_prefix,
358
+ total_bytes_read
359
+ )
360
+
361
+ except HTTPException:
362
+ # Re-raise HTTP exceptions (size limit exceeded)
363
+ raise
364
+ except Exception as read_error:
365
+ log.exception("%s Error reading uploaded file: %s", log_prefix, read_error)
366
+ raise HTTPException(
367
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
368
+ detail="Failed to read uploaded file"
369
+ )
370
+
371
+ mime_type = upload_file.content_type or "application/octet-stream"
372
+ filename_clean = filename.strip()
373
+
374
+ log.debug(
375
+ "%sProcessing file: %s (%d bytes, %s)",
376
+ log_prefix,
377
+ filename_clean,
378
+ len(content_bytes),
379
+ mime_type,
380
+ )
381
+
382
+ # Use the common upload helper
383
+ upload_result = await process_artifact_upload(
384
+ artifact_service=artifact_service,
385
+ component=component,
386
+ user_id=user_id,
387
+ session_id=effective_session_id,
388
+ filename=filename_clean,
389
+ content_bytes=content_bytes,
390
+ mime_type=mime_type,
391
+ metadata_json=metadata_json,
392
+ log_prefix=log_prefix,
393
+ )
394
+
395
+ if upload_result["status"] != "success":
396
+ error_msg = upload_result.get("message", "Failed to upload artifact")
397
+ error_type = upload_result.get("error", "unknown")
398
+
399
+ if error_type in ["invalid_filename", "empty_file"]:
400
+ status_code = status.HTTP_400_BAD_REQUEST
401
+ elif error_type == "file_too_large":
402
+ status_code = status.HTTP_413_REQUEST_ENTITY_TOO_LARGE
403
+ else:
404
+ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
405
+
406
+ log.error("%s%s", log_prefix, error_msg)
407
+ raise HTTPException(status_code=status_code, detail=error_msg)
408
+
409
+ artifact_uri = upload_result["artifact_uri"]
410
+ saved_version = upload_result["version"]
411
+
412
+ log.info(
413
+ "%sArtifact stored successfully: %s (%d bytes), version: %s",
414
+ log_prefix,
415
+ artifact_uri,
416
+ len(content_bytes),
417
+ saved_version,
418
+ )
419
+
420
+ # Get metadata from upload result (it was already parsed and validated)
421
+ metadata_dict = {}
422
+ if metadata_json and metadata_json.strip():
423
+ try:
424
+ metadata_dict = json.loads(metadata_json.strip())
425
+ if not isinstance(metadata_dict, dict):
426
+ metadata_dict = {}
427
+ except json.JSONDecodeError:
428
+ metadata_dict = {}
429
+
430
+ # Return standardized response using Pydantic model (ensures camelCase conversion)
431
+ return ArtifactUploadResponse(
432
+ uri=artifact_uri,
433
+ session_id=effective_session_id, # Will be returned as "sessionId" due to alias
434
+ filename=filename_clean,
435
+ size=len(content_bytes),
436
+ mime_type=mime_type, # Will be returned as "mimeType" due to alias
437
+ metadata=metadata_dict,
438
+ created_at=datetime.now(
439
+ timezone.utc
440
+ ).isoformat(), # Will be returned as "createdAt" due to alias
441
+ )
442
+
443
+ except HTTPException:
444
+ # Re-raise HTTP exceptions as-is
445
+ raise
446
+ except Exception as e:
447
+ log.exception("%sUnexpected error storing artifact: %s", log_prefix, e)
448
+ raise HTTPException(
449
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
450
+ detail="Failed to store artifact due to an internal error.",
451
+ )
452
+ finally:
453
+ # Ensure file is properly closed
454
+ try:
455
+ await upload_file.close()
456
+ except Exception as close_error:
457
+ log.warning("%sError closing upload file: %s", log_prefix, close_error)
458
+
459
+
460
+ @router.get(
461
+ "/{session_id}/{filename}/versions",
462
+ response_model=list[int],
463
+ summary="List Artifact Versions",
464
+ description="Retrieves a list of available version numbers for a specific artifact.",
465
+ )
466
+ async def list_artifact_versions(
467
+ session_id: str = Path(
468
+ ..., title="Session ID", description="The session ID to get artifacts from (or 'null' for project context)"
469
+ ),
470
+ filename: str = Path(..., title="Filename", description="The name of the artifact"),
471
+ project_id: Optional[str] = Query(None, description="Project ID for project context"),
472
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
473
+ user_id: str = Depends(get_user_id),
474
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
475
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
476
+ project_service: ProjectService | None = Depends(get_project_service_optional),
477
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:list"])),
478
+ ):
479
+ """
480
+ Lists the available integer versions for a given artifact filename
481
+ associated with the specified context (session or project).
482
+ """
483
+
484
+ log_prefix = f"[ArtifactRouter:ListVersions:{filename}] User={user_id}, Session={session_id} -"
485
+ log.info("%s Request received.", log_prefix)
486
+
487
+ # Resolve storage context
488
+ storage_user_id, storage_session_id, context_type = _resolve_storage_context(
489
+ session_id, project_id, user_id, validate_session, project_service, log_prefix
490
+ )
491
+
492
+ if artifact_service is None:
493
+ log.error("%s Artifact service not available.", log_prefix)
494
+ raise HTTPException(
495
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
496
+ detail="Artifact service is not configured.",
497
+ )
498
+
499
+ if not hasattr(artifact_service, "list_versions"):
500
+ log.warning(
501
+ "%s Configured artifact service (%s) does not support listing versions.",
502
+ log_prefix,
503
+ type(artifact_service).__name__,
504
+ )
505
+ raise HTTPException(
506
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
507
+ detail=f"Version listing not supported by the configured '{type(artifact_service).__name__}' artifact service.",
508
+ )
509
+
510
+ try:
511
+ app_name = component.get_config("name", "A2A_WebUI_App")
512
+
513
+ log.info("%s Using %s context: storage_user_id=%s, storage_session_id=%s",
514
+ log_prefix, context_type, storage_user_id, storage_session_id)
515
+
516
+ versions = await artifact_service.list_versions(
517
+ app_name=app_name,
518
+ user_id=storage_user_id,
519
+ session_id=storage_session_id,
520
+ filename=filename,
521
+ )
522
+ log.info("%s Found versions: %s", log_prefix, versions)
523
+ return versions
524
+ except FileNotFoundError:
525
+ log.warning("%s Artifact not found.", log_prefix)
526
+ raise HTTPException(
527
+ status_code=status.HTTP_404_NOT_FOUND,
528
+ detail=f"Artifact '{filename}' not found.",
529
+ )
530
+ except Exception as e:
531
+ log.exception("%s Error listing artifact versions: %s", log_prefix, e)
532
+ raise HTTPException(
533
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
534
+ detail=f"Failed to list artifact versions: {str(e)}",
535
+ )
536
+
537
+
538
+ @router.get(
539
+ "/{session_id}",
540
+ response_model=list[ArtifactInfo],
541
+ summary="List Artifact Information",
542
+ description="Retrieves detailed information for artifacts available for the specified user session.",
543
+ )
544
+ @router.get(
545
+ "/",
546
+ response_model=list[ArtifactInfo],
547
+ summary="List Artifact Information",
548
+ description="Retrieves detailed information for artifacts available for the current user session.",
549
+ )
550
+ async def list_artifacts(
551
+ session_id: str = Path(
552
+ ..., title="Session ID", description="The session ID to list artifacts for (or 'null' for project context)"
553
+ ),
554
+ project_id: Optional[str] = Query(None, description="Project ID for project context"),
555
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
556
+ user_id: str = Depends(get_user_id),
557
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
558
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
559
+ project_service: ProjectService | None = Depends(get_project_service_optional),
560
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:list"])),
561
+ ):
562
+ """
563
+ Lists detailed information (filename, size, type, modified date, uri)
564
+ for all artifacts associated with the specified context (session or project).
565
+ """
566
+
567
+ log_prefix = f"[ArtifactRouter:ListInfo] User={user_id}, Session={session_id} -"
568
+ log.info("%s Request received.", log_prefix)
569
+
570
+ # Resolve storage context (projects vs sessions). This allows for project artiacts
571
+ # to be listed before a session is created.
572
+ try:
573
+ storage_user_id, storage_session_id, context_type = _resolve_storage_context(
574
+ session_id, project_id, user_id, validate_session, project_service, log_prefix
575
+ )
576
+ except HTTPException:
577
+ log.info("%s No valid context found, returning empty list", log_prefix)
578
+ return []
579
+
580
+ if artifact_service is None:
581
+ log.error("%s Artifact service is not configured or available.", log_prefix)
582
+ raise HTTPException(
583
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
584
+ detail="Artifact service is not configured.",
585
+ )
586
+
587
+ try:
588
+ app_name = component.get_config("name", "A2A_WebUI_App")
589
+
590
+ log.info("%s Using %s context: storage_user_id=%s, storage_session_id=%s",
591
+ log_prefix, context_type, storage_user_id, storage_session_id)
592
+
593
+ artifact_info_list = await get_artifact_info_list(
594
+ artifact_service=artifact_service,
595
+ app_name=app_name,
596
+ user_id=storage_user_id,
597
+ session_id=storage_session_id,
598
+ )
599
+
600
+ log.info("%s Returning %d artifact details.", log_prefix, len(artifact_info_list))
601
+ return artifact_info_list
602
+
603
+ except Exception as e:
604
+ log.exception("%s Error retrieving artifact details: %s", log_prefix, e)
605
+ raise HTTPException(
606
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
607
+ detail=f"Failed to retrieve artifact details: {str(e)}",
608
+ )
609
+
610
+
611
+ @router.get(
612
+ "/{session_id}/{filename}",
613
+ summary="Get Latest Artifact Content",
614
+ description="Retrieves the content of the latest version of a specific artifact.",
615
+ )
616
+ async def get_latest_artifact(
617
+ session_id: str = Path(
618
+ ..., title="Session ID", description="The session ID to get artifacts from (or 'null' for project context)"
619
+ ),
620
+ filename: str = Path(..., title="Filename", description="The name of the artifact"),
621
+ project_id: Optional[str] = Query(None, description="Project ID for project context"),
622
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
623
+ user_id: str = Depends(get_user_id),
624
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
625
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
626
+ project_service: ProjectService | None = Depends(get_project_service_optional),
627
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
628
+ ):
629
+ """
630
+ Retrieves the content of the latest version of the specified artifact
631
+ associated with the specified context (session or project).
632
+ """
633
+ log_prefix = (
634
+ f"[ArtifactRouter:GetLatest:{filename}] User={user_id}, Session={session_id} -"
635
+ )
636
+ log.info("%s Request received.", log_prefix)
637
+
638
+ # Resolve storage context
639
+ storage_user_id, storage_session_id, context_type = _resolve_storage_context(
640
+ session_id, project_id, user_id, validate_session, project_service, log_prefix
641
+ )
642
+
643
+ if artifact_service is None:
644
+ log.error("%s Artifact service is not configured or available.", log_prefix)
645
+ raise HTTPException(
646
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
647
+ detail="Artifact service is not configured.",
648
+ )
649
+
650
+ try:
651
+ app_name = component.get_config("name", "A2A_WebUI_App")
652
+
653
+ log.info("%s Using %s context: storage_user_id=%s, storage_session_id=%s",
654
+ log_prefix, context_type, storage_user_id, storage_session_id)
655
+
656
+ artifact_part = await artifact_service.load_artifact(
657
+ app_name=app_name,
658
+ user_id=storage_user_id,
659
+ session_id=storage_session_id,
660
+ filename=filename,
661
+ )
662
+
663
+ if artifact_part is None or artifact_part.inline_data is None:
664
+ log.warning("%s Artifact not found or has no data.", log_prefix)
665
+ raise HTTPException(
666
+ status_code=status.HTTP_404_NOT_FOUND,
667
+ detail=f"Artifact '{filename}' not found or is empty.",
668
+ )
669
+
670
+ data_bytes = artifact_part.inline_data.data
671
+ mime_type = artifact_part.inline_data.mime_type or "application/octet-stream"
672
+ log.info(
673
+ "%s Artifact loaded successfully (%d bytes, %s).",
674
+ log_prefix,
675
+ len(data_bytes),
676
+ mime_type,
677
+ )
678
+
679
+ if is_text_based_mime_type(mime_type) and component.enable_embed_resolution:
680
+ log.info(
681
+ "%s Artifact is text-based. Attempting recursive embed resolution.",
682
+ log_prefix,
683
+ )
684
+ try:
685
+ original_content_string = data_bytes.decode("utf-8")
686
+
687
+ context_for_resolver = {
688
+ "artifact_service": artifact_service,
689
+ "session_context": {
690
+ "app_name": component.gateway_id,
691
+ "user_id": user_id,
692
+ "session_id": session_id,
693
+ },
694
+ }
695
+ config_for_resolver = {
696
+ "gateway_max_artifact_resolve_size_bytes": component.gateway_max_artifact_resolve_size_bytes,
697
+ "gateway_recursive_embed_depth": component.gateway_recursive_embed_depth,
698
+ }
699
+
700
+ resolved_content_string = await resolve_embeds_recursively_in_string(
701
+ text=original_content_string,
702
+ context=context_for_resolver,
703
+ resolver_func=evaluate_embed,
704
+ types_to_resolve=LATE_EMBED_TYPES,
705
+ resolution_mode=ResolutionMode.RECURSIVE_ARTIFACT_CONTENT,
706
+ log_identifier=f"{log_prefix}[RecursiveResolve]",
707
+ config=config_for_resolver,
708
+ max_depth=component.gateway_recursive_embed_depth,
709
+ max_total_size=component.gateway_max_artifact_resolve_size_bytes,
710
+ )
711
+ log.info(
712
+ "%s Recursive embed resolution complete. New size: %d bytes.",
713
+ log_prefix,
714
+ len(resolved_content_string),
715
+ )
716
+
717
+ # Also resolve any template blocks in the artifact
718
+ resolved_content_string = await resolve_template_blocks_in_string(
719
+ text=resolved_content_string,
720
+ artifact_service=artifact_service,
721
+ session_context=context_for_resolver["session_context"],
722
+ log_identifier=f"{log_prefix}[TemplateResolve]",
723
+ )
724
+ log.info(
725
+ "%s Template block resolution complete. Final size: %d bytes.",
726
+ log_prefix,
727
+ len(resolved_content_string),
728
+ )
729
+
730
+ data_bytes = resolved_content_string.encode("utf-8")
731
+ except UnicodeDecodeError as ude:
732
+ log.warning(
733
+ "%s Failed to decode artifact for recursive resolution: %s. Serving original content.",
734
+ log_prefix,
735
+ ude,
736
+ )
737
+ except Exception as resolve_err:
738
+ log.exception(
739
+ "%s Error during recursive embed resolution: %s. Serving original content.",
740
+ log_prefix,
741
+ resolve_err,
742
+ )
743
+ else:
744
+ log.info(
745
+ "%s Artifact is not text-based or embed resolution is disabled. Serving original content.",
746
+ log_prefix,
747
+ )
748
+
749
+ filename_encoded = quote(filename)
750
+ return StreamingResponse(
751
+ io.BytesIO(data_bytes),
752
+ media_type=mime_type,
753
+ headers={
754
+ "Content-Disposition": f"attachment; filename*=UTF-8''{filename_encoded}"
755
+ },
756
+ )
757
+
758
+ except FileNotFoundError:
759
+ log.warning("%s Artifact not found by service.", log_prefix)
760
+ raise HTTPException(
761
+ status_code=status.HTTP_404_NOT_FOUND,
762
+ detail=f"Artifact '{filename}' not found.",
763
+ )
764
+ except Exception as e:
765
+ log.exception("%s Error loading artifact: %s", log_prefix, e)
766
+ raise HTTPException(
767
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
768
+ detail=f"Failed to load artifact: {str(e)}",
769
+ )
770
+
771
+
772
+ @router.get(
773
+ "/{session_id}/{filename}/versions/{version}",
774
+ summary="Get Specific Artifact Version Content",
775
+ description="Retrieves the content of a specific version of an artifact.",
776
+ )
777
+ async def get_specific_artifact_version(
778
+ session_id: str = Path(
779
+ ..., title="Session ID", description="The session ID to get artifacts from (or 'null' for project context)"
780
+ ),
781
+ filename: str = Path(..., title="Filename", description="The name of the artifact"),
782
+ version: int | str = Path(
783
+ ...,
784
+ title="Version",
785
+ description="The specific version number to retrieve, or 'latest'",
786
+ ),
787
+ project_id: Optional[str] = Query(None, description="Project ID for project context"),
788
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
789
+ user_id: str = Depends(get_user_id),
790
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
791
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
792
+ project_service: ProjectService | None = Depends(get_project_service_optional),
793
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
794
+ ):
795
+ """
796
+ Retrieves the content of a specific version of the specified artifact
797
+ associated with the specified context (session or project).
798
+ """
799
+ log_prefix = f"[ArtifactRouter:GetVersion:{filename} v{version}] User={user_id}, Session={session_id} -"
800
+ log.info("%s Request received.", log_prefix)
801
+
802
+ # Resolve storage context
803
+ storage_user_id, storage_session_id, context_type = _resolve_storage_context(
804
+ session_id, project_id, user_id, validate_session, project_service, log_prefix
805
+ )
806
+
807
+ if artifact_service is None:
808
+ log.error("%s Artifact service is not configured or available.", log_prefix)
809
+ raise HTTPException(
810
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
811
+ detail="Artifact service is not configured.",
812
+ )
813
+
814
+ try:
815
+ app_name = component.get_config("name", "A2A_WebUI_App")
816
+
817
+ log.info("%s Using %s context: storage_user_id=%s, storage_session_id=%s",
818
+ log_prefix, context_type, storage_user_id, storage_session_id)
819
+
820
+ load_result = await load_artifact_content_or_metadata(
821
+ artifact_service=artifact_service,
822
+ app_name=app_name,
823
+ user_id=storage_user_id,
824
+ session_id=storage_session_id,
825
+ filename=filename,
826
+ version=version,
827
+ load_metadata_only=False,
828
+ return_raw_bytes=True,
829
+ log_identifier_prefix="[ArtifactRouter:GetVersion]",
830
+ )
831
+
832
+ if load_result.get("status") != "success":
833
+ error_message = load_result.get(
834
+ "message", f"Failed to load artifact '{filename}' version '{version}'."
835
+ )
836
+ log.warning("%s %s", log_prefix, error_message)
837
+ if (
838
+ "not found" in error_message.lower()
839
+ or "no versions available" in error_message.lower()
840
+ ):
841
+ status_code = status.HTTP_404_NOT_FOUND
842
+ elif "invalid version" in error_message.lower():
843
+ status_code = status.HTTP_400_BAD_REQUEST
844
+ else:
845
+ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
846
+ raise HTTPException(status_code=status_code, detail=error_message)
847
+
848
+ data_bytes = load_result.get("raw_bytes")
849
+ mime_type = load_result.get("mime_type", "application/octet-stream")
850
+ resolved_version_from_helper = load_result.get("version")
851
+ if data_bytes is None:
852
+ log.error(
853
+ "%s Helper (with return_raw_bytes=True) returned success but no raw_bytes for '%s' v%s (resolved to %s).",
854
+ log_prefix,
855
+ filename,
856
+ version,
857
+ resolved_version_from_helper,
858
+ )
859
+ raise HTTPException(
860
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
861
+ detail="Internal error retrieving artifact content.",
862
+ )
863
+
864
+ log.info(
865
+ "%s Artifact '%s' version %s (resolved to %s) loaded successfully (%d bytes, %s). Streaming content.",
866
+ log_prefix,
867
+ filename,
868
+ version,
869
+ resolved_version_from_helper,
870
+ len(data_bytes),
871
+ mime_type,
872
+ )
873
+
874
+ if is_text_based_mime_type(mime_type) and component.enable_embed_resolution:
875
+ log.info(
876
+ "%s Artifact is text-based. Attempting recursive embed resolution.",
877
+ log_prefix,
878
+ )
879
+ try:
880
+ original_content_string = data_bytes.decode("utf-8")
881
+
882
+ context_for_resolver = {
883
+ "artifact_service": artifact_service,
884
+ "session_context": {
885
+ "app_name": component.gateway_id,
886
+ "user_id": user_id,
887
+ "session_id": session_id,
888
+ },
889
+ }
890
+ config_for_resolver = {
891
+ "gateway_max_artifact_resolve_size_bytes": component.gateway_max_artifact_resolve_size_bytes,
892
+ "gateway_recursive_embed_depth": component.gateway_recursive_embed_depth,
893
+ }
894
+
895
+ resolved_content_string = await resolve_embeds_recursively_in_string(
896
+ text=original_content_string,
897
+ context=context_for_resolver,
898
+ resolver_func=evaluate_embed,
899
+ types_to_resolve=LATE_EMBED_TYPES,
900
+ resolution_mode=ResolutionMode.RECURSIVE_ARTIFACT_CONTENT,
901
+ log_identifier=f"{log_prefix}[RecursiveResolve]",
902
+ config=config_for_resolver,
903
+ max_depth=component.gateway_recursive_embed_depth,
904
+ max_total_size=component.gateway_max_artifact_resolve_size_bytes,
905
+ )
906
+ log.info(
907
+ "%s Recursive embed resolution complete. New size: %d bytes.",
908
+ log_prefix,
909
+ len(resolved_content_string),
910
+ )
911
+
912
+ # Also resolve any template blocks in the artifact
913
+ resolved_content_string = await resolve_template_blocks_in_string(
914
+ text=resolved_content_string,
915
+ artifact_service=artifact_service,
916
+ session_context=context_for_resolver["session_context"],
917
+ log_identifier=f"{log_prefix}[TemplateResolve]",
918
+ )
919
+ log.info(
920
+ "%s Template block resolution complete. Final size: %d bytes.",
921
+ log_prefix,
922
+ len(resolved_content_string),
923
+ )
924
+
925
+ data_bytes = resolved_content_string.encode("utf-8")
926
+ except UnicodeDecodeError as ude:
927
+ log.warning(
928
+ "%s Failed to decode artifact for recursive resolution: %s. Serving original content.",
929
+ log_prefix,
930
+ ude,
931
+ )
932
+ except Exception as resolve_err:
933
+ log.exception(
934
+ "%s Error during recursive embed resolution: %s. Serving original content.",
935
+ log_prefix,
936
+ resolve_err,
937
+ )
938
+ else:
939
+ log.info(
940
+ "%s Artifact is not text-based or embed resolution is disabled. Serving original content.",
941
+ log_prefix,
942
+ )
943
+
944
+ filename_encoded = quote(filename)
945
+ return StreamingResponse(
946
+ io.BytesIO(data_bytes),
947
+ media_type=mime_type,
948
+ headers={
949
+ "Content-Disposition": f"attachment; filename*=UTF-8''{filename_encoded}"
950
+ },
951
+ )
952
+
953
+ except FileNotFoundError:
954
+ log.warning("%s Artifact version not found by service.", log_prefix)
955
+ raise HTTPException(
956
+ status_code=status.HTTP_404_NOT_FOUND,
957
+ detail=f"Artifact '{filename}' version {version} not found.",
958
+ )
959
+ except ValueError as ve:
960
+ log.warning("%s Invalid request (e.g., version format): %s", log_prefix, ve)
961
+ raise HTTPException(
962
+ status_code=status.HTTP_400_BAD_REQUEST,
963
+ detail=f"Invalid request: {str(ve)}",
964
+ )
965
+ except Exception as e:
966
+ log.exception("%s Error loading artifact version: %s", log_prefix, e)
967
+ raise HTTPException(
968
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
969
+ detail=f"Failed to load artifact version: {str(e)}",
970
+ )
971
+
972
+
973
+ @router.get(
974
+ "/by-uri",
975
+ response_class=StreamingResponse,
976
+ summary="Get Artifact by URI",
977
+ description="Resolves a formal artifact:// URI and streams its content. This endpoint is secure and validates that the requesting user is authorized to access the specified artifact.",
978
+ )
979
+ async def get_artifact_by_uri(
980
+ uri: str,
981
+ requesting_user_id: str = Depends(get_user_id),
982
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
983
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
984
+ ):
985
+ """
986
+ Resolves an artifact:// URI and streams its content.
987
+ This allows fetching artifacts from any context, not just the current user's session,
988
+ after performing an authorization check.
989
+ """
990
+ log_id_prefix = "[ArtifactRouter:by-uri]"
991
+ log.info(
992
+ "%s Received request for URI: %s from user: %s",
993
+ log_id_prefix,
994
+ uri,
995
+ requesting_user_id,
996
+ )
997
+ artifact_service = component.get_shared_artifact_service()
998
+ if not artifact_service:
999
+ raise HTTPException(
1000
+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
1001
+ detail="Artifact service not available.",
1002
+ )
1003
+
1004
+ try:
1005
+ parsed_uri = urlparse(uri)
1006
+ if parsed_uri.scheme != "artifact":
1007
+ raise ValueError("Invalid URI scheme, must be 'artifact'.")
1008
+
1009
+ app_name = parsed_uri.netloc
1010
+ path_parts = parsed_uri.path.strip("/").split("/")
1011
+ if not app_name or len(path_parts) != 3:
1012
+ raise ValueError(
1013
+ "Invalid URI path structure. Expected artifact://app_name/user_id/session_id/filename"
1014
+ )
1015
+
1016
+ owner_user_id, session_id, filename = path_parts
1017
+
1018
+ query_params = parse_qs(parsed_uri.query)
1019
+ version_list = query_params.get("version")
1020
+ if not version_list or not version_list[0]:
1021
+ raise ValueError("Version query parameter is required.")
1022
+ version = version_list[0]
1023
+
1024
+ log.info(
1025
+ "%s Parsed URI: app=%s, owner=%s, session=%s, file=%s, version=%s",
1026
+ log_id_prefix,
1027
+ app_name,
1028
+ owner_user_id,
1029
+ session_id,
1030
+ filename,
1031
+ version,
1032
+ )
1033
+
1034
+ log.info(
1035
+ "%s User '%s' authorized to access artifact URI.",
1036
+ log_id_prefix,
1037
+ requesting_user_id,
1038
+ )
1039
+
1040
+ loaded_artifact = await load_artifact_content_or_metadata(
1041
+ artifact_service=artifact_service,
1042
+ app_name=app_name,
1043
+ user_id=owner_user_id,
1044
+ session_id=session_id,
1045
+ filename=filename,
1046
+ version=int(version),
1047
+ return_raw_bytes=True,
1048
+ log_identifier_prefix=log_id_prefix,
1049
+ component=component,
1050
+ )
1051
+
1052
+ if loaded_artifact.get("status") != "success":
1053
+ raise HTTPException(status_code=404, detail=loaded_artifact.get("message"))
1054
+
1055
+ content_bytes = loaded_artifact.get("raw_bytes")
1056
+ mime_type = loaded_artifact.get("mime_type", "application/octet-stream")
1057
+
1058
+ filename_encoded = quote(filename)
1059
+ return StreamingResponse(
1060
+ io.BytesIO(content_bytes),
1061
+ media_type=mime_type,
1062
+ headers={
1063
+ "Content-Disposition": f"attachment; filename*=UTF-8''{filename_encoded}"
1064
+ },
1065
+ )
1066
+
1067
+ except (ValueError, IndexError) as e:
1068
+ raise HTTPException(status_code=400, detail=f"Invalid artifact URI: {e}")
1069
+ except Exception as e:
1070
+ log.exception("%s Error fetching artifact by URI: %s", log_id_prefix, e)
1071
+ raise HTTPException(
1072
+ status_code=500, detail="Internal server error fetching artifact by URI"
1073
+ )
1074
+
1075
+
1076
+ @router.delete(
1077
+ "/{session_id}/{filename}",
1078
+ status_code=status.HTTP_204_NO_CONTENT,
1079
+ summary="Delete Artifact",
1080
+ description="Deletes an artifact and all its versions.",
1081
+ )
1082
+ async def delete_artifact(
1083
+ session_id: str = Path(
1084
+ ..., title="Session ID", description="The session ID to delete artifacts from"
1085
+ ),
1086
+ filename: str = Path(
1087
+ ..., title="Filename", description="The name of the artifact to delete"
1088
+ ),
1089
+ artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
1090
+ user_id: str = Depends(get_user_id),
1091
+ validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
1092
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
1093
+ user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:delete"])),
1094
+ ):
1095
+ """
1096
+ Deletes the specified artifact (including all its versions)
1097
+ associated with the current user and session ID.
1098
+ """
1099
+ log_prefix = (
1100
+ f"[ArtifactRouter:Delete:{filename}] User={user_id}, Session={session_id} -"
1101
+ )
1102
+ log.info("%s Request received.", log_prefix)
1103
+
1104
+ # Validate session exists and belongs to user
1105
+ if not validate_session(session_id, user_id):
1106
+ log.warning("%s Session validation failed or access denied.", log_prefix)
1107
+ raise HTTPException(
1108
+ status_code=status.HTTP_404_NOT_FOUND,
1109
+ detail="Session not found or access denied.",
1110
+ )
1111
+
1112
+ if artifact_service is None:
1113
+ log.error("%s Artifact service is not configured or available.", log_prefix)
1114
+ raise HTTPException(
1115
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
1116
+ detail="Artifact service is not configured.",
1117
+ )
1118
+
1119
+ try:
1120
+ app_name = component.get_config("name", "A2A_WebUI_App")
1121
+
1122
+ await artifact_service.delete_artifact(
1123
+ app_name=app_name,
1124
+ user_id=user_id,
1125
+ session_id=session_id,
1126
+ filename=filename,
1127
+ )
1128
+
1129
+ log.info("%s Artifact deletion request processed successfully.", log_prefix)
1130
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
1131
+
1132
+ except Exception as e:
1133
+ log.exception("%s Error deleting artifact: %s", log_prefix, e)
1134
+ raise HTTPException(
1135
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
1136
+ detail=f"Failed to delete artifact: {str(e)}",
1137
+ )