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.
- solace_agent_mesh/__init__.py +0 -0
- solace_agent_mesh/agent/__init__.py +0 -0
- solace_agent_mesh/agent/adk/__init__.py +0 -0
- solace_agent_mesh/agent/adk/adk_llm.txt +226 -0
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
- solace_agent_mesh/agent/adk/alembic/README +74 -0
- solace_agent_mesh/agent/adk/alembic/env.py +77 -0
- solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
- solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
- solace_agent_mesh/agent/adk/alembic.ini +112 -0
- solace_agent_mesh/agent/adk/app_llm_agent.py +52 -0
- solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +545 -0
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +609 -0
- solace_agent_mesh/agent/adk/callbacks.py +2318 -0
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +406 -0
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +415 -0
- solace_agent_mesh/agent/adk/mcp_content_processor.py +666 -0
- solace_agent_mesh/agent/adk/models/lite_llm.py +1026 -0
- solace_agent_mesh/agent/adk/models/models_llm.txt +189 -0
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +132 -0
- solace_agent_mesh/agent/adk/runner.py +390 -0
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +468 -0
- solace_agent_mesh/agent/adk/setup.py +1325 -0
- solace_agent_mesh/agent/adk/stream_parser.py +415 -0
- solace_agent_mesh/agent/adk/tool_wrapper.py +165 -0
- solace_agent_mesh/agent/agent_llm.txt +369 -0
- solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
- solace_agent_mesh/agent/protocol/__init__.py +0 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +2041 -0
- solace_agent_mesh/agent/protocol/protocol_llm.txt +81 -0
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
- solace_agent_mesh/agent/proxies/__init__.py +0 -0
- solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
- solace_agent_mesh/agent/proxies/a2a/app.py +56 -0
- solace_agent_mesh/agent/proxies/a2a/component.py +1585 -0
- solace_agent_mesh/agent/proxies/a2a/config.py +216 -0
- solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
- solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/base/app.py +100 -0
- solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
- solace_agent_mesh/agent/proxies/base/component.py +816 -0
- solace_agent_mesh/agent/proxies/base/config.py +85 -0
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +19 -0
- solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
- solace_agent_mesh/agent/sac/__init__.py +0 -0
- solace_agent_mesh/agent/sac/app.py +595 -0
- solace_agent_mesh/agent/sac/component.py +3668 -0
- solace_agent_mesh/agent/sac/patch_adk.py +103 -0
- solace_agent_mesh/agent/sac/sac_llm.txt +189 -0
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
- solace_agent_mesh/agent/sac/task_execution_context.py +415 -0
- solace_agent_mesh/agent/testing/__init__.py +3 -0
- solace_agent_mesh/agent/testing/debug_utils.py +135 -0
- solace_agent_mesh/agent/testing/testing_llm.txt +58 -0
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
- solace_agent_mesh/agent/tools/__init__.py +16 -0
- solace_agent_mesh/agent/tools/audio_tools.py +1740 -0
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +2500 -0
- solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +244 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +396 -0
- solace_agent_mesh/agent/tools/general_agent_tools.py +572 -0
- solace_agent_mesh/agent/tools/image_tools.py +1185 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +363 -0
- solace_agent_mesh/agent/tools/registry.py +38 -0
- solace_agent_mesh/agent/tools/test_tools.py +136 -0
- solace_agent_mesh/agent/tools/time_tools.py +126 -0
- solace_agent_mesh/agent/tools/tool_config_types.py +93 -0
- solace_agent_mesh/agent/tools/tool_definition.py +53 -0
- solace_agent_mesh/agent/tools/tools_llm.txt +276 -0
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +275 -0
- solace_agent_mesh/agent/tools/web_tools.py +392 -0
- solace_agent_mesh/agent/utils/__init__.py +0 -0
- solace_agent_mesh/agent/utils/artifact_helpers.py +1353 -0
- solace_agent_mesh/agent/utils/config_parser.py +49 -0
- solace_agent_mesh/agent/utils/context_helpers.py +77 -0
- solace_agent_mesh/agent/utils/utils_llm.txt +152 -0
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
- solace_agent_mesh/assets/docs/404.html +16 -0
- solace_agent_mesh/assets/docs/assets/css/styles.8162edfb.css +1 -0
- solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
- solace_agent_mesh/assets/docs/assets/images/sam-enterprise-credentials-b269f095349473118b2b33bdfcc40122.png +0 -0
- solace_agent_mesh/assets/docs/assets/js/032c2d61.f3d37824.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js.LICENSE.txt +13 -0
- solace_agent_mesh/assets/docs/assets/js/2334.1cf50a20.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/240a0364.9ad94d1b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3624.0eaa1fd0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.28b7c67b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2ddc75c0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/41adc471.48b12a4e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/509e993c.a1fbf45a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.90a87880.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.b77431fc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6fdfefc7.99de744e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.2484b8d9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js.LICENSE.txt +61 -0
- solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8731.6c1dbf0c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.b2333011.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad71b5ed.af3ecfd1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de5f4c65.e8241890.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e04b235d.52cb25ed.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.b1068f9b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.4488e34c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.250993bf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js.LICENSE.txt +81 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.9e0813a2.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +154 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +99 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +90 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +107 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +166 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +101 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +219 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +92 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +29 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +110 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +345 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +83 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +84 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +85 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +60 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +144 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +191 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +128 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +34 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +267 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +142 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +116 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +164 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +140 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +57 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +102 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +37 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +247 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +184 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +75 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +85 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +78 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +78 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +160 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +100 -0
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +52 -0
- solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
- solace_agent_mesh/assets/docs/img/logo.png +0 -0
- solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
- solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
- solace_agent_mesh/assets/docs/img/sam-enterprise-credentials.png +0 -0
- solace_agent_mesh/assets/docs/img/solace-logo-text.svg +18 -0
- solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
- solace_agent_mesh/assets/docs/lunr-index-1765810064709.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -0
- solace_agent_mesh/assets/docs/search-doc-1765810064709.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -0
- solace_agent_mesh/assets/docs/sitemap.xml +1 -0
- solace_agent_mesh/cli/__init__.py +1 -0
- solace_agent_mesh/cli/commands/__init__.py +0 -0
- solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
- solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +729 -0
- solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
- solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +102 -0
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +114 -0
- solace_agent_mesh/cli/commands/docs_cmd.py +60 -0
- solace_agent_mesh/cli/commands/eval_cmd.py +46 -0
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +439 -0
- solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
- solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +238 -0
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +464 -0
- solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +119 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +215 -0
- solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +20 -0
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +137 -0
- solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +144 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +306 -0
- solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
- solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +175 -0
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
- solace_agent_mesh/cli/commands/run_cmd.py +215 -0
- solace_agent_mesh/cli/main.py +52 -0
- solace_agent_mesh/cli/utils.py +262 -0
- solace_agent_mesh/client/webui/frontend/static/assets/authCallback-Dj3JtK42.js +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/client-ZKk9kEJ5.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-BcUaNZ-Q.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-vjch4RYc.js +435 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-BNV4kZN0.js +535 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +15 -0
- solace_agent_mesh/client/webui/frontend/static/index.html +16 -0
- solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
- solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
- solace_agent_mesh/common/__init__.py +1 -0
- solace_agent_mesh/common/a2a/__init__.py +241 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +175 -0
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
- solace_agent_mesh/common/a2a/artifact.py +368 -0
- solace_agent_mesh/common/a2a/events.py +213 -0
- solace_agent_mesh/common/a2a/message.py +375 -0
- solace_agent_mesh/common/a2a/protocol.py +689 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +655 -0
- solace_agent_mesh/common/a2a/types.py +55 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +445 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
- solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +48 -0
- solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +41 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +330 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +48 -0
- solace_agent_mesh/common/agent_registry.py +122 -0
- solace_agent_mesh/common/common_llm.txt +230 -0
- solace_agent_mesh/common/common_llm_detail.txt +2562 -0
- solace_agent_mesh/common/constants.py +6 -0
- solace_agent_mesh/common/data_parts.py +150 -0
- solace_agent_mesh/common/exceptions.py +49 -0
- solace_agent_mesh/common/middleware/__init__.py +12 -0
- solace_agent_mesh/common/middleware/config_resolver.py +132 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
- solace_agent_mesh/common/middleware/registry.py +127 -0
- solace_agent_mesh/common/oauth/__init__.py +17 -0
- solace_agent_mesh/common/oauth/oauth_client.py +408 -0
- solace_agent_mesh/common/oauth/utils.py +50 -0
- solace_agent_mesh/common/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
- solace_agent_mesh/common/sac/sam_component_base.py +730 -0
- solace_agent_mesh/common/sam_events/__init__.py +9 -0
- solace_agent_mesh/common/sam_events/event_service.py +208 -0
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
- solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
- solace_agent_mesh/common/services/__init__.py +4 -0
- solace_agent_mesh/common/services/employee_service.py +164 -0
- solace_agent_mesh/common/services/identity_service.py +134 -0
- solace_agent_mesh/common/services/providers/__init__.py +4 -0
- solace_agent_mesh/common/services/providers/local_file_identity_service.py +151 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +81 -0
- solace_agent_mesh/common/services/services_llm.txt +368 -0
- solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
- solace_agent_mesh/common/utils/__init__.py +7 -0
- solace_agent_mesh/common/utils/artifact_utils.py +31 -0
- solace_agent_mesh/common/utils/asyncio_macos_fix.py +88 -0
- solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
- solace_agent_mesh/common/utils/embeds/constants.py +56 -0
- solace_agent_mesh/common/utils/embeds/converter.py +447 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +220 -0
- solace_agent_mesh/common/utils/embeds/evaluators.py +395 -0
- solace_agent_mesh/common/utils/embeds/modifiers.py +793 -0
- solace_agent_mesh/common/utils/embeds/resolver.py +967 -0
- solace_agent_mesh/common/utils/embeds/types.py +23 -0
- solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
- solace_agent_mesh/common/utils/initializer.py +52 -0
- solace_agent_mesh/common/utils/log_formatters.py +64 -0
- solace_agent_mesh/common/utils/message_utils.py +80 -0
- solace_agent_mesh/common/utils/mime_helpers.py +172 -0
- solace_agent_mesh/common/utils/push_notification_auth.py +135 -0
- solace_agent_mesh/common/utils/pydantic_utils.py +159 -0
- solace_agent_mesh/common/utils/rbac_utils.py +69 -0
- solace_agent_mesh/common/utils/templates/__init__.py +8 -0
- solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
- solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
- solace_agent_mesh/common/utils/type_utils.py +28 -0
- solace_agent_mesh/common/utils/utils_llm.txt +335 -0
- solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
- solace_agent_mesh/config_portal/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/common.py +77 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +24 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +166 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +521 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +217 -0
- solace_agent_mesh/config_portal/backend/server.py +644 -0
- solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DiOiAjzL.js +103 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/components-Rk0n-9cK.js +140 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-mvZjNKiz.js +19 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/index-DzNKzXrc.js +68 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-ba77705e.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-V2BeTIUc.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
- solace_agent_mesh/core_a2a/__init__.py +1 -0
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +90 -0
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
- solace_agent_mesh/core_a2a/service.py +307 -0
- solace_agent_mesh/evaluation/__init__.py +0 -0
- solace_agent_mesh/evaluation/evaluator.py +691 -0
- solace_agent_mesh/evaluation/message_organizer.py +553 -0
- solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
- solace_agent_mesh/evaluation/report/chart_section.html +141 -0
- solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
- solace_agent_mesh/evaluation/report/modal.html +59 -0
- solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
- solace_agent_mesh/evaluation/report/modal_script.js +296 -0
- solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
- solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
- solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
- solace_agent_mesh/evaluation/report/templates/header.html +340 -0
- solace_agent_mesh/evaluation/report_data_processor.py +970 -0
- solace_agent_mesh/evaluation/report_generator.py +607 -0
- solace_agent_mesh/evaluation/run.py +954 -0
- solace_agent_mesh/evaluation/shared/__init__.py +92 -0
- solace_agent_mesh/evaluation/shared/constants.py +47 -0
- solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
- solace_agent_mesh/evaluation/shared/helpers.py +35 -0
- solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
- solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
- solace_agent_mesh/evaluation/subscriber.py +776 -0
- solace_agent_mesh/evaluation/summary_builder.py +880 -0
- solace_agent_mesh/gateway/__init__.py +0 -0
- solace_agent_mesh/gateway/adapter/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +143 -0
- solace_agent_mesh/gateway/adapter/types.py +221 -0
- solace_agent_mesh/gateway/base/__init__.py +1 -0
- solace_agent_mesh/gateway/base/app.py +345 -0
- solace_agent_mesh/gateway/base/base_llm.txt +226 -0
- solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
- solace_agent_mesh/gateway/base/component.py +2030 -0
- solace_agent_mesh/gateway/base/task_context.py +75 -0
- solace_agent_mesh/gateway/gateway_llm.txt +369 -0
- solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
- solace_agent_mesh/gateway/generic/__init__.py +1 -0
- solace_agent_mesh/gateway/generic/app.py +50 -0
- solace_agent_mesh/gateway/generic/component.py +727 -0
- solace_agent_mesh/gateway/http_sse/__init__.py +0 -0
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +345 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +87 -0
- solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250910_d5b3f8f2e9a0_create_initial_database.py +58 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250911_b1c2d3e4f5g6_add_database_indexes.py +83 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +412 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +161 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +109 -0
- solace_agent_mesh/gateway/http_sse/app.py +351 -0
- solace_agent_mesh/gateway/http_sse/component.py +2360 -0
- solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +105 -0
- solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
- solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +110 -0
- solace_agent_mesh/gateway/http_sse/dependencies.py +653 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +299 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
- solace_agent_mesh/gateway/http_sse/main.py +789 -0
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +46 -0
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +102 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +11 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +221 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +66 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +32 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +125 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +239 -0
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +34 -0
- solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
- solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +257 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +53 -0
- solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +39 -0
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +308 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +268 -0
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +248 -0
- solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +74 -0
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1137 -0
- solace_agent_mesh/gateway/http_sse/routers/auth.py +311 -0
- solace_agent_mesh/gateway/http_sse/routers/config.py +371 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/__init__.py +10 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +450 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +15 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +133 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +33 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +18 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +123 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +33 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +168 -0
- solace_agent_mesh/gateway/http_sse/routers/people.py +38 -0
- solace_agent_mesh/gateway/http_sse/routers/projects.py +767 -0
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1415 -0
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +312 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +634 -0
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +230 -0
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +1089 -0
- solace_agent_mesh/gateway/http_sse/routers/users.py +83 -0
- solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +1220 -0
- solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +71 -0
- solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
- solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +273 -0
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +250 -0
- solace_agent_mesh/gateway/http_sse/services/people_service.py +78 -0
- solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
- solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +702 -0
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +593 -0
- solace_agent_mesh/gateway/http_sse/services/task_service.py +119 -0
- solace_agent_mesh/gateway/http_sse/session_manager.py +219 -0
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +146 -0
- solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +252 -0
- solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
- solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
- solace_agent_mesh/gateway/http_sse/shared/enums.py +40 -0
- solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +217 -0
- solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
- solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +319 -0
- solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
- solace_agent_mesh/gateway/http_sse/shared/types.py +50 -0
- solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
- solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +88 -0
- solace_agent_mesh/gateway/http_sse/sse_manager.py +491 -0
- solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
- solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +72 -0
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
- solace_agent_mesh/llm.txt +228 -0
- solace_agent_mesh/llm_detail.txt +2835 -0
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/platform/__init__.py +18 -0
- solace_agent_mesh/services/platform/alembic/env.py +85 -0
- solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
- solace_agent_mesh/services/platform/alembic.ini +109 -0
- solace_agent_mesh/services/platform/api/__init__.py +3 -0
- solace_agent_mesh/services/platform/api/dependencies.py +147 -0
- solace_agent_mesh/services/platform/api/main.py +280 -0
- solace_agent_mesh/services/platform/api/middleware.py +51 -0
- solace_agent_mesh/services/platform/api/routers/__init__.py +24 -0
- solace_agent_mesh/services/platform/app.py +114 -0
- solace_agent_mesh/services/platform/component.py +235 -0
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
- solace_agent_mesh/templates/agent_template.yaml +53 -0
- solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
- solace_agent_mesh/templates/gateway_app_template.py +75 -0
- solace_agent_mesh/templates/gateway_component_template.py +484 -0
- solace_agent_mesh/templates/gateway_config_template.yaml +38 -0
- solace_agent_mesh/templates/logging_config_template.yaml +48 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +66 -0
- solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
- solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
- solace_agent_mesh/templates/plugin_custom_template.py +10 -0
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +60 -0
- solace_agent_mesh/templates/plugin_pyproject_template.toml +32 -0
- solace_agent_mesh/templates/plugin_readme_template.md +12 -0
- solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
- solace_agent_mesh/templates/plugin_tools_template.py +224 -0
- solace_agent_mesh/templates/shared_config.yaml +112 -0
- solace_agent_mesh/templates/templates_llm.txt +147 -0
- solace_agent_mesh/templates/webui.yaml +177 -0
- solace_agent_mesh-1.11.2.dist-info/METADATA +504 -0
- solace_agent_mesh-1.11.2.dist-info/RECORD +624 -0
- solace_agent_mesh-1.11.2.dist-info/WHEEL +4 -0
- solace_agent_mesh-1.11.2.dist-info/entry_points.txt +3 -0
- 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
|
+
)
|