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,1220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API Router for managing A2A message visualization streams.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import asyncio
|
|
7
|
+
import uuid
|
|
8
|
+
from fastapi import (
|
|
9
|
+
APIRouter,
|
|
10
|
+
Depends,
|
|
11
|
+
HTTPException,
|
|
12
|
+
Request as FastAPIRequest,
|
|
13
|
+
Response,
|
|
14
|
+
status,
|
|
15
|
+
)
|
|
16
|
+
from pydantic import BaseModel, Field
|
|
17
|
+
import json
|
|
18
|
+
from typing import List, Optional, Dict, Any, Set
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
from ....gateway.http_sse.dependencies import (
|
|
22
|
+
get_sac_component,
|
|
23
|
+
get_user_id,
|
|
24
|
+
get_sse_manager,
|
|
25
|
+
)
|
|
26
|
+
from ....gateway.http_sse.sse_manager import SSEManager
|
|
27
|
+
from ....common.middleware.registry import MiddlewareRegistry
|
|
28
|
+
|
|
29
|
+
from typing import TYPE_CHECKING
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ....gateway.http_sse.component import WebUIBackendComponent
|
|
33
|
+
|
|
34
|
+
log = logging.getLogger(__name__)
|
|
35
|
+
trace_logger = logging.getLogger("sam_trace")
|
|
36
|
+
|
|
37
|
+
router = APIRouter()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SubscriptionTarget(BaseModel):
|
|
41
|
+
"""Defines an abstract target for A2A message visualization."""
|
|
42
|
+
|
|
43
|
+
type: str = Field(
|
|
44
|
+
...,
|
|
45
|
+
description="Type of the target to monitor.",
|
|
46
|
+
examples=[
|
|
47
|
+
"my_a2a_messages",
|
|
48
|
+
"current_namespace_a2a_messages",
|
|
49
|
+
"namespace_a2a_messages",
|
|
50
|
+
"agent_a2a_messages",
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
identifier: Optional[str] = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
description="Identifier for the target (e.g., namespace string or agent name). Not required if type is 'current_namespace_a2a_messages'.",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ActualSubscribedTarget(SubscriptionTarget):
|
|
60
|
+
"""Represents an abstract target that the gateway attempted to subscribe to, including its status."""
|
|
61
|
+
|
|
62
|
+
status: str = Field(
|
|
63
|
+
...,
|
|
64
|
+
description="Status of the subscription attempt for this target.",
|
|
65
|
+
examples=["subscribed", "denied_due_to_scope", "error_translating_target"],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class VisualizationSubscribeRequest(BaseModel):
|
|
70
|
+
"""Request body for initiating a visualization stream."""
|
|
71
|
+
|
|
72
|
+
subscription_targets: Optional[List[SubscriptionTarget]] = Field(
|
|
73
|
+
default_factory=list,
|
|
74
|
+
description="Optional list of abstract targets to monitor.",
|
|
75
|
+
)
|
|
76
|
+
client_stream_id: Optional[str] = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
description="Optional client-generated ID for idempotency or re-association. If not provided, a new one is generated.",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class VisualizationSubscribeResponse(BaseModel):
|
|
83
|
+
"""Response body for a successful visualization subscription."""
|
|
84
|
+
|
|
85
|
+
stream_id: str = Field(..., description="Unique ID for the visualization stream.")
|
|
86
|
+
sse_endpoint_url: str = Field(..., description="URL for the SSE event stream.")
|
|
87
|
+
actual_subscribed_targets: List[ActualSubscribedTarget] = Field(
|
|
88
|
+
default_factory=list,
|
|
89
|
+
description="List of abstract targets processed, with their subscription status.",
|
|
90
|
+
)
|
|
91
|
+
message: str = "Visualization stream initiated. Connect to the SSE endpoint."
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class VisualizationConfigUpdateRequest(BaseModel):
|
|
95
|
+
"""Request body for updating an active visualization stream's configuration."""
|
|
96
|
+
|
|
97
|
+
subscription_targets_to_add: Optional[List[SubscriptionTarget]] = Field(
|
|
98
|
+
default=None,
|
|
99
|
+
description="List of new abstract targets to add to the subscription.",
|
|
100
|
+
)
|
|
101
|
+
subscription_targets_to_remove: Optional[List[SubscriptionTarget]] = Field(
|
|
102
|
+
default=None,
|
|
103
|
+
description="List of abstract targets to remove from the subscription.",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class VisualizationConfigUpdateResponse(BaseModel):
|
|
108
|
+
"""Response body for a successful visualization configuration update."""
|
|
109
|
+
|
|
110
|
+
stream_id: str = Field(..., description="ID of the updated visualization stream.")
|
|
111
|
+
message: str = "Visualization stream configuration updated successfully."
|
|
112
|
+
current_subscribed_targets: List[ActualSubscribedTarget] = Field(
|
|
113
|
+
default_factory=list,
|
|
114
|
+
description="Current list of active abstract targets for this stream, with their status.",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class VisualizationSubscriptionError(BaseModel):
|
|
119
|
+
"""Error response for subscription failures."""
|
|
120
|
+
|
|
121
|
+
message: str = Field(..., description="Human-readable error message")
|
|
122
|
+
failed_targets: List[ActualSubscribedTarget] = Field(
|
|
123
|
+
..., description="List of targets that failed to subscribe"
|
|
124
|
+
)
|
|
125
|
+
error_type: str = Field(
|
|
126
|
+
...,
|
|
127
|
+
description="Type of error: 'authorization_failure' or 'subscription_failure'",
|
|
128
|
+
)
|
|
129
|
+
suggested_action: Optional[str] = Field(
|
|
130
|
+
default=None, description="Suggested action for the user"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
from sse_starlette.sse import EventSourceResponse
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _generate_sse_url(fastapi_request: FastAPIRequest, stream_id: str) -> str:
|
|
138
|
+
"""
|
|
139
|
+
Generate SSE endpoint URL with proper scheme and host detection for reverse proxy scenarios.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
fastapi_request: The FastAPI request object
|
|
143
|
+
stream_id: The stream ID for the SSE endpoint
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Complete SSE URL with correct scheme (http/https) and host.
|
|
147
|
+
"""
|
|
148
|
+
base_url = fastapi_request.url_for(
|
|
149
|
+
"get_visualization_stream_events", stream_id=stream_id
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
forwarded_proto = fastapi_request.headers.get("x-forwarded-proto")
|
|
153
|
+
forwarded_host = fastapi_request.headers.get("x-forwarded-host")
|
|
154
|
+
|
|
155
|
+
if forwarded_proto and forwarded_host:
|
|
156
|
+
# In a reverse proxy environment like GitHub Codespaces, reconstruct the URL
|
|
157
|
+
# using the forwarded headers to ensure it's publicly accessible.
|
|
158
|
+
return str(base_url.replace(scheme=forwarded_proto, netloc=forwarded_host))
|
|
159
|
+
elif forwarded_proto:
|
|
160
|
+
# Handle cases with only a forwarded protocol (standard reverse proxy)
|
|
161
|
+
return str(base_url.replace(scheme=forwarded_proto))
|
|
162
|
+
else:
|
|
163
|
+
# Default behavior when not behind a reverse proxy
|
|
164
|
+
return str(base_url)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _translate_target_to_solace_topics(
|
|
168
|
+
target: SubscriptionTarget, component_namespace: str
|
|
169
|
+
) -> List[str]:
|
|
170
|
+
"""Translates an abstract SubscriptionTarget to a list of Solace topic strings."""
|
|
171
|
+
topics = []
|
|
172
|
+
target_identifier = target.identifier.strip("/") if target.identifier else ""
|
|
173
|
+
component_namespace_formatted = component_namespace.strip("/")
|
|
174
|
+
if target.type == "current_namespace_a2a_messages":
|
|
175
|
+
topics.append(f"{component_namespace_formatted}/a2a/>")
|
|
176
|
+
elif target.type == "namespace_a2a_messages":
|
|
177
|
+
if not target.identifier:
|
|
178
|
+
log.warning(f"Identifier missing for target type {target.type}")
|
|
179
|
+
return []
|
|
180
|
+
topics.append(f"{target_identifier}/a2a/>")
|
|
181
|
+
elif target.type == "agent_a2a_messages":
|
|
182
|
+
if not target.identifier:
|
|
183
|
+
log.warning(f"Identifier missing for target type {target.type}")
|
|
184
|
+
return []
|
|
185
|
+
base_agent_topic = f"{component_namespace_formatted}/a2a/v1/agent"
|
|
186
|
+
topics.append(f"{base_agent_topic}/request/{target_identifier}/>")
|
|
187
|
+
topics.append(f"{base_agent_topic}/response/{target_identifier}/>")
|
|
188
|
+
topics.append(f"{base_agent_topic}/status/{target_identifier}/>")
|
|
189
|
+
else:
|
|
190
|
+
log.warning(f"Unknown subscription target type: {target.type}")
|
|
191
|
+
return topics
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _resolve_user_identity_for_authorization(
|
|
195
|
+
component: "WebUIBackendComponent", raw_user_id: str
|
|
196
|
+
) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Applies the same user identity resolution logic as BaseGatewayComponent.submit_a2a_task().
|
|
199
|
+
This ensures visualization authorization uses the same identity resolution as task submission.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
component: The WebUIBackendComponent instance
|
|
203
|
+
raw_user_id: The raw user ID from the session (e.g., web-client-xxxxx)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
The resolved user identity to use for authorization
|
|
207
|
+
"""
|
|
208
|
+
log_id_prefix = f"{component.log_identifier}[ResolveUserIdentity]"
|
|
209
|
+
user_identity = raw_user_id
|
|
210
|
+
|
|
211
|
+
force_identity = component.get_config("force_user_identity")
|
|
212
|
+
if force_identity:
|
|
213
|
+
original_identity = user_identity
|
|
214
|
+
user_identity = force_identity
|
|
215
|
+
log.info(
|
|
216
|
+
"%s DEVELOPMENT MODE: Forcing user_identity from '%s' to '%s' for visualization",
|
|
217
|
+
log_id_prefix,
|
|
218
|
+
original_identity,
|
|
219
|
+
user_identity,
|
|
220
|
+
)
|
|
221
|
+
return user_identity
|
|
222
|
+
|
|
223
|
+
if not user_identity:
|
|
224
|
+
use_authorization = component.get_config("frontend_use_authorization", False)
|
|
225
|
+
if not use_authorization:
|
|
226
|
+
user_identity = "sam_dev_user"
|
|
227
|
+
log.info(
|
|
228
|
+
"%s No user_identity provided and auth is disabled, using sam_dev_user for visualization",
|
|
229
|
+
log_id_prefix,
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
log.error(
|
|
233
|
+
"%s No user_identity provided but authorization is enabled. This should not happen.",
|
|
234
|
+
log_id_prefix,
|
|
235
|
+
)
|
|
236
|
+
raise ValueError(
|
|
237
|
+
"No user identity available when authorization is required"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return user_identity
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _include_for_visualization(event_payload: Dict[str, Any]) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Check if an event should be included for visualization based on metadata.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
event_payload: The event payload containing event data
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
False if the event should be excluded from visualization (when visualization is False),
|
|
252
|
+
True otherwise (include by default)
|
|
253
|
+
"""
|
|
254
|
+
try:
|
|
255
|
+
# Get the data field from the event payload
|
|
256
|
+
data_str = event_payload.get("data")
|
|
257
|
+
if not data_str:
|
|
258
|
+
return True # Include by default if no data
|
|
259
|
+
|
|
260
|
+
# Parse the JSON data
|
|
261
|
+
try:
|
|
262
|
+
data = json.loads(data_str)
|
|
263
|
+
except (json.JSONDecodeError, TypeError):
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
# Look for the full_payload in the data
|
|
267
|
+
full_payload = data.get("full_payload")
|
|
268
|
+
if not full_payload:
|
|
269
|
+
return True
|
|
270
|
+
|
|
271
|
+
# Check if full_payload has params
|
|
272
|
+
params = full_payload.get("params")
|
|
273
|
+
if not params:
|
|
274
|
+
return True
|
|
275
|
+
|
|
276
|
+
# Check if params has message
|
|
277
|
+
message = params.get("message")
|
|
278
|
+
if not message:
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
# Check if message has metadata
|
|
282
|
+
metadata = message.get("metadata")
|
|
283
|
+
if not metadata:
|
|
284
|
+
return True
|
|
285
|
+
|
|
286
|
+
# Check the visualization setting in metadata
|
|
287
|
+
visualization_setting = metadata.get("visualization")
|
|
288
|
+
if visualization_setting is not None and (
|
|
289
|
+
(
|
|
290
|
+
isinstance(visualization_setting, str)
|
|
291
|
+
and visualization_setting.lower() == "false"
|
|
292
|
+
)
|
|
293
|
+
or (
|
|
294
|
+
isinstance(visualization_setting, bool)
|
|
295
|
+
and visualization_setting is False
|
|
296
|
+
)
|
|
297
|
+
):
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
log.warning("Error checking visualization filter for event: %s", e)
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@router.post(
|
|
308
|
+
"/subscribe",
|
|
309
|
+
response_model=VisualizationSubscribeResponse,
|
|
310
|
+
status_code=status.HTTP_201_CREATED,
|
|
311
|
+
)
|
|
312
|
+
async def subscribe_to_visualization_stream(
|
|
313
|
+
request_data: VisualizationSubscribeRequest,
|
|
314
|
+
fastapi_request: FastAPIRequest,
|
|
315
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
316
|
+
user_id: str = Depends(get_user_id),
|
|
317
|
+
sse_manager: SSEManager = Depends(get_sse_manager),
|
|
318
|
+
):
|
|
319
|
+
"""Initiates a new A2A message visualization stream using abstract targets."""
|
|
320
|
+
log_id_prefix = f"{component.log_identifier}[POST /viz/subscribe]"
|
|
321
|
+
log.info(
|
|
322
|
+
"%s Request received from user %s. Client Stream ID: %s",
|
|
323
|
+
log_id_prefix,
|
|
324
|
+
user_id,
|
|
325
|
+
request_data.client_stream_id,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
component._ensure_visualization_flow_is_running()
|
|
330
|
+
except Exception as e:
|
|
331
|
+
log.exception(
|
|
332
|
+
"%s Failed to ensure visualization flow is running: %s", log_id_prefix, e
|
|
333
|
+
)
|
|
334
|
+
raise HTTPException(
|
|
335
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
336
|
+
detail="Failed to initialize visualization backend.",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
stream_id = request_data.client_stream_id or f"viz-stream-{uuid.uuid4().hex}"
|
|
340
|
+
|
|
341
|
+
log.debug(
|
|
342
|
+
"%s Acquiring viz lock to check for existing stream %s",
|
|
343
|
+
log_id_prefix,
|
|
344
|
+
stream_id,
|
|
345
|
+
)
|
|
346
|
+
async with component._get_visualization_lock():
|
|
347
|
+
if stream_id in component._active_visualization_streams:
|
|
348
|
+
existing_stream_data = component._active_visualization_streams[stream_id]
|
|
349
|
+
if existing_stream_data.get("user_id") != user_id:
|
|
350
|
+
raise HTTPException(
|
|
351
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
352
|
+
detail="Client stream ID already in use by another user.",
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
log.warning(
|
|
356
|
+
"%s Stream ID %s (client-provided) already exists. Returning existing info.",
|
|
357
|
+
log_id_prefix,
|
|
358
|
+
stream_id,
|
|
359
|
+
)
|
|
360
|
+
sse_url = _generate_sse_url(fastapi_request, stream_id)
|
|
361
|
+
return VisualizationSubscribeResponse(
|
|
362
|
+
stream_id=stream_id,
|
|
363
|
+
sse_endpoint_url=sse_url,
|
|
364
|
+
actual_subscribed_targets=existing_stream_data.get(
|
|
365
|
+
"abstract_targets", []
|
|
366
|
+
),
|
|
367
|
+
message="Visualization stream with this client_stream_id already exists and is active.",
|
|
368
|
+
)
|
|
369
|
+
log.debug(
|
|
370
|
+
"%s Released viz lock after checking for existing stream %s",
|
|
371
|
+
log_id_prefix,
|
|
372
|
+
stream_id,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
try:
|
|
376
|
+
sse_queue = await sse_manager.create_sse_connection(stream_id)
|
|
377
|
+
except Exception as e:
|
|
378
|
+
log.exception(
|
|
379
|
+
"%s Failed to create SSE connection queue for stream %s: %s",
|
|
380
|
+
log_id_prefix,
|
|
381
|
+
stream_id,
|
|
382
|
+
e,
|
|
383
|
+
)
|
|
384
|
+
raise HTTPException(
|
|
385
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
386
|
+
detail="Failed to establish SSE infrastructure.",
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
resolved_user_identity = _resolve_user_identity_for_authorization(
|
|
390
|
+
component, user_id
|
|
391
|
+
)
|
|
392
|
+
log.debug(
|
|
393
|
+
"%s Resolved user identity for authorization: '%s' (from raw user_id: '%s')",
|
|
394
|
+
log_id_prefix,
|
|
395
|
+
resolved_user_identity,
|
|
396
|
+
user_id,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
config_resolver = MiddlewareRegistry.get_config_resolver()
|
|
400
|
+
gateway_context = {
|
|
401
|
+
"gateway_id": component.gateway_id,
|
|
402
|
+
"request": fastapi_request,
|
|
403
|
+
"gateway_namespace": component.namespace,
|
|
404
|
+
}
|
|
405
|
+
user_config: Dict[str, Any] = {}
|
|
406
|
+
try:
|
|
407
|
+
user_config = await config_resolver.resolve_user_config(
|
|
408
|
+
resolved_user_identity, gateway_context, {}
|
|
409
|
+
)
|
|
410
|
+
log.debug(
|
|
411
|
+
"%s Resolved user_config for resolved_user_identity '%s': %s",
|
|
412
|
+
log_id_prefix,
|
|
413
|
+
resolved_user_identity,
|
|
414
|
+
{k: v for k, v in user_config.items() if not k.startswith("_")},
|
|
415
|
+
)
|
|
416
|
+
except Exception as config_err:
|
|
417
|
+
log.exception(
|
|
418
|
+
"%s Error resolving user_config for user %s: %s. Proceeding with empty config.",
|
|
419
|
+
log_id_prefix,
|
|
420
|
+
resolved_user_identity,
|
|
421
|
+
config_err,
|
|
422
|
+
)
|
|
423
|
+
user_config = {}
|
|
424
|
+
processed_targets_for_response: List[ActualSubscribedTarget] = []
|
|
425
|
+
current_solace_topics_for_stream: Set[str] = set()
|
|
426
|
+
current_abstract_targets_for_stream: List[ActualSubscribedTarget] = []
|
|
427
|
+
|
|
428
|
+
initial_stream_config = {
|
|
429
|
+
"user_id": user_id,
|
|
430
|
+
"user_config": user_config,
|
|
431
|
+
"solace_topics": current_solace_topics_for_stream,
|
|
432
|
+
"abstract_targets": current_abstract_targets_for_stream,
|
|
433
|
+
"sse_queue": sse_queue,
|
|
434
|
+
"client_stream_id": request_data.client_stream_id,
|
|
435
|
+
}
|
|
436
|
+
log.debug(
|
|
437
|
+
"%s Acquiring viz lock to add initial stream config for %s",
|
|
438
|
+
log_id_prefix,
|
|
439
|
+
stream_id,
|
|
440
|
+
)
|
|
441
|
+
async with component._get_visualization_lock():
|
|
442
|
+
component._active_visualization_streams[stream_id] = initial_stream_config
|
|
443
|
+
log.debug(
|
|
444
|
+
"%s Released viz lock after adding initial stream config for %s",
|
|
445
|
+
log_id_prefix,
|
|
446
|
+
stream_id,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
targets_to_process = request_data.subscription_targets
|
|
450
|
+
if not targets_to_process:
|
|
451
|
+
log.info(
|
|
452
|
+
"%s No subscription targets provided, defaulting to current namespace.",
|
|
453
|
+
log_id_prefix,
|
|
454
|
+
)
|
|
455
|
+
targets_to_process = [SubscriptionTarget(type="current_namespace_a2a_messages")]
|
|
456
|
+
|
|
457
|
+
log.debug(
|
|
458
|
+
"%s Starting to process %d subscription targets.",
|
|
459
|
+
log_id_prefix,
|
|
460
|
+
len(targets_to_process),
|
|
461
|
+
)
|
|
462
|
+
for target_request_idx, target_request in enumerate(targets_to_process):
|
|
463
|
+
log.debug(
|
|
464
|
+
"%s Processing target %d/%d: %s",
|
|
465
|
+
log_id_prefix,
|
|
466
|
+
target_request_idx + 1,
|
|
467
|
+
len(targets_to_process),
|
|
468
|
+
target_request.model_dump(),
|
|
469
|
+
)
|
|
470
|
+
target_status = "denied_due_to_scope"
|
|
471
|
+
required_scope = ""
|
|
472
|
+
effective_identifier = target_request.identifier
|
|
473
|
+
|
|
474
|
+
if target_request.type == "current_namespace_a2a_messages":
|
|
475
|
+
effective_identifier = component.namespace
|
|
476
|
+
required_scope = (
|
|
477
|
+
f"monitor/namespace/{effective_identifier}:a2a_messages:subscribe"
|
|
478
|
+
)
|
|
479
|
+
elif target_request.type == "namespace_a2a_messages":
|
|
480
|
+
if not effective_identifier:
|
|
481
|
+
log.warning(
|
|
482
|
+
"%s Identifier missing for target type 'namespace_a2a_messages'",
|
|
483
|
+
log_id_prefix,
|
|
484
|
+
)
|
|
485
|
+
target_status = "error_missing_identifier"
|
|
486
|
+
processed_targets_for_response.append(
|
|
487
|
+
ActualSubscribedTarget(
|
|
488
|
+
**target_request.model_dump(), status=target_status
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
continue
|
|
492
|
+
required_scope = (
|
|
493
|
+
f"monitor/namespace/{effective_identifier}:a2a_messages:subscribe"
|
|
494
|
+
)
|
|
495
|
+
elif target_request.type == "agent_a2a_messages":
|
|
496
|
+
if not effective_identifier:
|
|
497
|
+
log.warning(
|
|
498
|
+
"%s Identifier missing for target type 'agent_a2a_messages'",
|
|
499
|
+
log_id_prefix,
|
|
500
|
+
)
|
|
501
|
+
target_status = "error_missing_identifier"
|
|
502
|
+
processed_targets_for_response.append(
|
|
503
|
+
ActualSubscribedTarget(
|
|
504
|
+
**target_request.model_dump(), status=target_status
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
continue
|
|
508
|
+
|
|
509
|
+
pass
|
|
510
|
+
elif target_request.type == "my_a2a_messages":
|
|
511
|
+
operation_spec = {
|
|
512
|
+
"operation_type": "visualization_subscription",
|
|
513
|
+
"target_type": "my_a2a_messages",
|
|
514
|
+
}
|
|
515
|
+
validation_result = config_resolver.validate_operation_config(
|
|
516
|
+
user_config, operation_spec, gateway_context
|
|
517
|
+
)
|
|
518
|
+
has_permission = validation_result.get("valid", False)
|
|
519
|
+
|
|
520
|
+
if has_permission:
|
|
521
|
+
target_status = "subscribed"
|
|
522
|
+
response_target_data = target_request.model_dump()
|
|
523
|
+
current_abstract_targets_for_stream.append(
|
|
524
|
+
ActualSubscribedTarget(**response_target_data, status=target_status)
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
firehose_topic = f"{component.namespace.strip('/')}/a2a/>"
|
|
528
|
+
log.debug(
|
|
529
|
+
"%s Adding firehose subscription '%s' for my_a2a_messages stream.",
|
|
530
|
+
log_id_prefix,
|
|
531
|
+
firehose_topic,
|
|
532
|
+
)
|
|
533
|
+
if not await component._add_visualization_subscription(
|
|
534
|
+
firehose_topic, stream_id
|
|
535
|
+
):
|
|
536
|
+
log.error(
|
|
537
|
+
"%s Failed to add required firehose subscription for my_a2a_messages.",
|
|
538
|
+
log_id_prefix,
|
|
539
|
+
)
|
|
540
|
+
target_status = "error_adding_subscription"
|
|
541
|
+
current_abstract_targets_for_stream.pop()
|
|
542
|
+
|
|
543
|
+
else:
|
|
544
|
+
log.warning(
|
|
545
|
+
"%s User %s denied subscription to 'my_a2a_messages' due to missing scope.",
|
|
546
|
+
log_id_prefix,
|
|
547
|
+
resolved_user_identity,
|
|
548
|
+
)
|
|
549
|
+
processed_targets_for_response.append(
|
|
550
|
+
ActualSubscribedTarget(
|
|
551
|
+
**target_request.model_dump(), status=target_status
|
|
552
|
+
)
|
|
553
|
+
)
|
|
554
|
+
continue
|
|
555
|
+
else:
|
|
556
|
+
log.warning(
|
|
557
|
+
"%s Unknown subscription target type: %s for identifier %s",
|
|
558
|
+
log_id_prefix,
|
|
559
|
+
target_request.type,
|
|
560
|
+
effective_identifier,
|
|
561
|
+
)
|
|
562
|
+
target_status = "error_unknown_target_type"
|
|
563
|
+
processed_targets_for_response.append(
|
|
564
|
+
ActualSubscribedTarget(
|
|
565
|
+
**target_request.model_dump(), status=target_status
|
|
566
|
+
)
|
|
567
|
+
)
|
|
568
|
+
continue
|
|
569
|
+
|
|
570
|
+
identifier_for_spec = target_request.identifier
|
|
571
|
+
if (
|
|
572
|
+
target_request.type == "current_namespace_a2a_messages"
|
|
573
|
+
and target_request.identifier is None
|
|
574
|
+
):
|
|
575
|
+
identifier_for_spec = None
|
|
576
|
+
|
|
577
|
+
operation_spec = {
|
|
578
|
+
"operation_type": "visualization_subscription",
|
|
579
|
+
"target_type": target_request.type,
|
|
580
|
+
"target_identifier": identifier_for_spec,
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
validation_result = config_resolver.validate_operation_config(
|
|
584
|
+
user_config, operation_spec, gateway_context
|
|
585
|
+
)
|
|
586
|
+
has_permission = validation_result.get("valid", False)
|
|
587
|
+
|
|
588
|
+
if has_permission:
|
|
589
|
+
target_for_translation = target_request
|
|
590
|
+
if target_request.type == "current_namespace_a2a_messages":
|
|
591
|
+
target_for_translation = SubscriptionTarget(
|
|
592
|
+
type="namespace_a2a_messages", identifier=effective_identifier
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
solace_topics_for_target = _translate_target_to_solace_topics(
|
|
596
|
+
target_for_translation, component.namespace
|
|
597
|
+
)
|
|
598
|
+
if not solace_topics_for_target:
|
|
599
|
+
log.warning(
|
|
600
|
+
"%s No Solace topics derived for target: %s",
|
|
601
|
+
log_id_prefix,
|
|
602
|
+
target_request.model_dump(),
|
|
603
|
+
)
|
|
604
|
+
target_status = "error_translating_target"
|
|
605
|
+
else:
|
|
606
|
+
all_topics_added_successfully = True
|
|
607
|
+
for topic_str in solace_topics_for_target:
|
|
608
|
+
success = await component._add_visualization_subscription(
|
|
609
|
+
topic_str, stream_id
|
|
610
|
+
)
|
|
611
|
+
if success:
|
|
612
|
+
current_solace_topics_for_stream.add(topic_str)
|
|
613
|
+
else:
|
|
614
|
+
all_topics_added_successfully = False
|
|
615
|
+
log.error(
|
|
616
|
+
"%s Failed to add subscription to Solace topic: %s for stream %s (target: %s)",
|
|
617
|
+
log_id_prefix,
|
|
618
|
+
topic_str,
|
|
619
|
+
stream_id,
|
|
620
|
+
effective_identifier,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
if all_topics_added_successfully:
|
|
624
|
+
target_status = "subscribed"
|
|
625
|
+
response_target_data = target_request.model_dump()
|
|
626
|
+
if target_request.type == "current_namespace_a2a_messages":
|
|
627
|
+
response_target_data["identifier"] = effective_identifier
|
|
628
|
+
current_abstract_targets_for_stream.append(
|
|
629
|
+
ActualSubscribedTarget(
|
|
630
|
+
**response_target_data, status=target_status
|
|
631
|
+
)
|
|
632
|
+
)
|
|
633
|
+
else:
|
|
634
|
+
target_status = "error_adding_subscription"
|
|
635
|
+
else:
|
|
636
|
+
log.warning(
|
|
637
|
+
"%s User %s denied subscription to target %s (type: %s) due to missing scope: %s",
|
|
638
|
+
log_id_prefix,
|
|
639
|
+
resolved_user_identity,
|
|
640
|
+
effective_identifier,
|
|
641
|
+
target_request.type,
|
|
642
|
+
required_scope,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
response_target_data_for_processed_list = target_request.model_dump()
|
|
646
|
+
if (
|
|
647
|
+
target_request.type == "current_namespace_a2a_messages"
|
|
648
|
+
and target_status == "subscribed"
|
|
649
|
+
):
|
|
650
|
+
response_target_data_for_processed_list["identifier"] = effective_identifier
|
|
651
|
+
|
|
652
|
+
processed_targets_for_response.append(
|
|
653
|
+
ActualSubscribedTarget(
|
|
654
|
+
**response_target_data_for_processed_list, status=target_status
|
|
655
|
+
)
|
|
656
|
+
)
|
|
657
|
+
log.debug("%s Finished processing all subscription targets.", log_id_prefix)
|
|
658
|
+
|
|
659
|
+
successful_subscriptions = [
|
|
660
|
+
target
|
|
661
|
+
for target in processed_targets_for_response
|
|
662
|
+
if target.status == "subscribed"
|
|
663
|
+
]
|
|
664
|
+
|
|
665
|
+
if not successful_subscriptions:
|
|
666
|
+
log.warning(
|
|
667
|
+
"%s All subscription targets failed for user %s. Cleaning up stream %s.",
|
|
668
|
+
log_id_prefix,
|
|
669
|
+
user_id,
|
|
670
|
+
stream_id,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
try:
|
|
674
|
+
await sse_manager.close_all_for_task(stream_id)
|
|
675
|
+
except Exception as cleanup_error:
|
|
676
|
+
log.warning(
|
|
677
|
+
"%s Failed to cleanup SSE connection for stream %s: %s",
|
|
678
|
+
log_id_prefix,
|
|
679
|
+
stream_id,
|
|
680
|
+
cleanup_error,
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
log.debug(
|
|
684
|
+
"%s Acquiring viz lock to clean up failed stream %s",
|
|
685
|
+
log_id_prefix,
|
|
686
|
+
stream_id,
|
|
687
|
+
)
|
|
688
|
+
async with component._get_visualization_lock():
|
|
689
|
+
component._active_visualization_streams.pop(stream_id, None)
|
|
690
|
+
log.debug(
|
|
691
|
+
"%s Released viz lock after cleaning up failed stream %s",
|
|
692
|
+
log_id_prefix,
|
|
693
|
+
stream_id,
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
denied_targets = [
|
|
697
|
+
target
|
|
698
|
+
for target in processed_targets_for_response
|
|
699
|
+
if target.status == "denied_due_to_scope"
|
|
700
|
+
]
|
|
701
|
+
|
|
702
|
+
if denied_targets:
|
|
703
|
+
raise HTTPException(
|
|
704
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
705
|
+
detail={
|
|
706
|
+
"message": "Access denied: insufficient permissions for all requested targets",
|
|
707
|
+
"failed_targets": [
|
|
708
|
+
target.model_dump() for target in processed_targets_for_response
|
|
709
|
+
],
|
|
710
|
+
"error_type": "authorization_failure",
|
|
711
|
+
"suggested_action": "Please check your permissions or contact your administrator",
|
|
712
|
+
},
|
|
713
|
+
)
|
|
714
|
+
else:
|
|
715
|
+
raise HTTPException(
|
|
716
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
717
|
+
detail={
|
|
718
|
+
"message": "All subscription targets failed to process",
|
|
719
|
+
"failed_targets": [
|
|
720
|
+
target.model_dump() for target in processed_targets_for_response
|
|
721
|
+
],
|
|
722
|
+
"error_type": "subscription_failure",
|
|
723
|
+
"suggested_action": "Please check your target specifications and try again",
|
|
724
|
+
},
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
failed_targets = [
|
|
728
|
+
target
|
|
729
|
+
for target in processed_targets_for_response
|
|
730
|
+
if target.status != "subscribed"
|
|
731
|
+
]
|
|
732
|
+
|
|
733
|
+
response_message = "Visualization stream initiated. Connect to the SSE endpoint."
|
|
734
|
+
if failed_targets:
|
|
735
|
+
response_message = f"Visualization stream initiated with {len(successful_subscriptions)} successful and {len(failed_targets)} failed subscriptions."
|
|
736
|
+
log.warning(
|
|
737
|
+
"%s Partial subscription success for user %s: %d successful, %d failed",
|
|
738
|
+
log_id_prefix,
|
|
739
|
+
user_id,
|
|
740
|
+
len(successful_subscriptions),
|
|
741
|
+
len(failed_targets),
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
sse_url = _generate_sse_url(fastapi_request, stream_id)
|
|
745
|
+
log.info(
|
|
746
|
+
"%s Visualization stream %s initiated for user %s. SSE URL: %s. Processed Targets: %s",
|
|
747
|
+
log_id_prefix,
|
|
748
|
+
stream_id,
|
|
749
|
+
user_id,
|
|
750
|
+
sse_url,
|
|
751
|
+
processed_targets_for_response,
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
return VisualizationSubscribeResponse(
|
|
755
|
+
stream_id=stream_id,
|
|
756
|
+
sse_endpoint_url=sse_url,
|
|
757
|
+
actual_subscribed_targets=processed_targets_for_response,
|
|
758
|
+
message=response_message,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
@router.get("/{stream_id}/events")
|
|
763
|
+
async def get_visualization_stream_events(
|
|
764
|
+
stream_id: str,
|
|
765
|
+
fastapi_request: FastAPIRequest,
|
|
766
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
767
|
+
sse_manager: SSEManager = Depends(get_sse_manager),
|
|
768
|
+
user_id: str = Depends(get_user_id),
|
|
769
|
+
):
|
|
770
|
+
"""Establishes an SSE connection for receiving filtered A2A messages for a specific stream."""
|
|
771
|
+
log_id_prefix = f"{component.log_identifier}[GET /viz/{stream_id}/events]"
|
|
772
|
+
log.info("%s Client %s requesting SSE connection.", log_id_prefix, user_id)
|
|
773
|
+
|
|
774
|
+
stream_config: Optional[Dict[str, Any]] = None
|
|
775
|
+
log.debug(
|
|
776
|
+
"%s Acquiring viz lock to get stream config for %s", log_id_prefix, stream_id
|
|
777
|
+
)
|
|
778
|
+
async with component._get_visualization_lock():
|
|
779
|
+
stream_config = component._active_visualization_streams.get(stream_id)
|
|
780
|
+
log.debug(
|
|
781
|
+
"%s Released viz lock after getting stream config for %s",
|
|
782
|
+
log_id_prefix,
|
|
783
|
+
stream_id,
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
if not stream_config:
|
|
787
|
+
log.warning("%s Stream ID %s not found.", log_id_prefix, stream_id)
|
|
788
|
+
raise HTTPException(
|
|
789
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
790
|
+
detail="Visualization stream not found.",
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
stream_owner_id = stream_config.get("user_id")
|
|
794
|
+
resolved_stream_owner = _resolve_user_identity_for_authorization(
|
|
795
|
+
component, stream_owner_id
|
|
796
|
+
)
|
|
797
|
+
resolved_requester = _resolve_user_identity_for_authorization(component, user_id)
|
|
798
|
+
|
|
799
|
+
if resolved_stream_owner != resolved_requester:
|
|
800
|
+
log.warning(
|
|
801
|
+
"%s User %s (resolved: %s) forbidden to access stream %s owned by %s (resolved: %s).",
|
|
802
|
+
log_id_prefix,
|
|
803
|
+
user_id,
|
|
804
|
+
resolved_requester,
|
|
805
|
+
stream_id,
|
|
806
|
+
stream_owner_id,
|
|
807
|
+
resolved_stream_owner,
|
|
808
|
+
)
|
|
809
|
+
raise HTTPException(
|
|
810
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
811
|
+
detail="Access to this visualization stream is forbidden.",
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
sse_queue: Optional[asyncio.Queue] = stream_config.get("sse_queue")
|
|
815
|
+
if not sse_queue:
|
|
816
|
+
log.error(
|
|
817
|
+
"%s SSE queue not found for stream ID %s, though stream config exists.",
|
|
818
|
+
log_id_prefix,
|
|
819
|
+
stream_id,
|
|
820
|
+
)
|
|
821
|
+
raise HTTPException(
|
|
822
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
823
|
+
detail="Internal error: SSE queue missing for stream.",
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
async def event_generator():
|
|
827
|
+
log.debug(
|
|
828
|
+
"%s SSE event generator started for stream %s.", log_id_prefix, stream_id
|
|
829
|
+
)
|
|
830
|
+
try:
|
|
831
|
+
yield {
|
|
832
|
+
"comment": f"SSE connection established for visualization stream {stream_id}"
|
|
833
|
+
}
|
|
834
|
+
while True:
|
|
835
|
+
if await fastapi_request.is_disconnected():
|
|
836
|
+
log.info(
|
|
837
|
+
"%s Client disconnected from stream %s.",
|
|
838
|
+
log_id_prefix,
|
|
839
|
+
stream_id,
|
|
840
|
+
)
|
|
841
|
+
break
|
|
842
|
+
try:
|
|
843
|
+
event_payload = await asyncio.wait_for(sse_queue.get(), timeout=30)
|
|
844
|
+
if event_payload is None:
|
|
845
|
+
log.info(
|
|
846
|
+
"%s SSE queue for stream %s received None sentinel. Closing connection.",
|
|
847
|
+
log_id_prefix,
|
|
848
|
+
stream_id,
|
|
849
|
+
)
|
|
850
|
+
break
|
|
851
|
+
if _include_for_visualization(event_payload):
|
|
852
|
+
if trace_logger.isEnabledFor(logging.DEBUG):
|
|
853
|
+
trace_logger.debug(
|
|
854
|
+
"%s Yielding event for stream %s: %s",
|
|
855
|
+
log_id_prefix,
|
|
856
|
+
stream_id,
|
|
857
|
+
event_payload,
|
|
858
|
+
)
|
|
859
|
+
else:
|
|
860
|
+
log.debug(
|
|
861
|
+
"%s Yielding event for stream %s",
|
|
862
|
+
log_id_prefix,
|
|
863
|
+
stream_id,
|
|
864
|
+
)
|
|
865
|
+
yield event_payload
|
|
866
|
+
sse_queue.task_done()
|
|
867
|
+
except asyncio.TimeoutError:
|
|
868
|
+
yield {"comment": "keep-alive"}
|
|
869
|
+
continue
|
|
870
|
+
except asyncio.CancelledError:
|
|
871
|
+
log.debug(
|
|
872
|
+
"%s SSE event generator for stream %s cancelled.",
|
|
873
|
+
log_id_prefix,
|
|
874
|
+
stream_id,
|
|
875
|
+
)
|
|
876
|
+
break
|
|
877
|
+
except Exception as e:
|
|
878
|
+
log.exception(
|
|
879
|
+
"%s Error in SSE event generator for stream %s: %s",
|
|
880
|
+
log_id_prefix,
|
|
881
|
+
stream_id,
|
|
882
|
+
e,
|
|
883
|
+
)
|
|
884
|
+
finally:
|
|
885
|
+
log.debug(
|
|
886
|
+
"%s SSE event generator for stream %s finished.",
|
|
887
|
+
log_id_prefix,
|
|
888
|
+
stream_id,
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
return EventSourceResponse(event_generator())
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
@router.put("/{stream_id}/config", response_model=VisualizationConfigUpdateResponse)
|
|
895
|
+
async def update_visualization_stream_config(
|
|
896
|
+
stream_id: str,
|
|
897
|
+
update_request: VisualizationConfigUpdateRequest,
|
|
898
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
899
|
+
user_id: str = Depends(get_user_id),
|
|
900
|
+
):
|
|
901
|
+
"""Modifies the configuration of an active visualization stream."""
|
|
902
|
+
log_id_prefix = f"{component.log_identifier}[PUT /viz/{stream_id}/config]"
|
|
903
|
+
log.info(
|
|
904
|
+
"%s Request received from user %s to update stream.", log_id_prefix, user_id
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
log.debug(
|
|
908
|
+
"%s Acquiring viz lock to update stream config for %s", log_id_prefix, stream_id
|
|
909
|
+
)
|
|
910
|
+
async with component._get_visualization_lock():
|
|
911
|
+
stream_config = component._active_visualization_streams.get(stream_id)
|
|
912
|
+
if not stream_config:
|
|
913
|
+
raise HTTPException(
|
|
914
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
915
|
+
detail="Visualization stream not found.",
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
if stream_config.get("user_id") != user_id:
|
|
919
|
+
raise HTTPException(
|
|
920
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
921
|
+
detail="User not authorized to modify this stream.",
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
user_config = stream_config.get("user_config", {})
|
|
925
|
+
config_resolver = MiddlewareRegistry.get_config_resolver()
|
|
926
|
+
gateway_context_for_validation = {
|
|
927
|
+
"gateway_id": component.gateway_id,
|
|
928
|
+
"gateway_namespace": component.namespace,
|
|
929
|
+
}
|
|
930
|
+
current_abstract_targets: List[ActualSubscribedTarget] = [
|
|
931
|
+
ActualSubscribedTarget(**t.model_dump())
|
|
932
|
+
for t in stream_config.get("abstract_targets", [])
|
|
933
|
+
]
|
|
934
|
+
current_solace_topics: Set[str] = stream_config.get(
|
|
935
|
+
"solace_topics", set()
|
|
936
|
+
).copy()
|
|
937
|
+
|
|
938
|
+
if update_request.subscription_targets_to_remove:
|
|
939
|
+
targets_actually_removed_abstract = []
|
|
940
|
+
for target_to_remove_req in update_request.subscription_targets_to_remove:
|
|
941
|
+
effective_identifier_remove = target_to_remove_req.identifier
|
|
942
|
+
target_for_translation_remove = target_to_remove_req
|
|
943
|
+
|
|
944
|
+
if target_to_remove_req.type == "current_namespace_a2a_messages":
|
|
945
|
+
effective_identifier_remove = component.namespace
|
|
946
|
+
target_for_translation_remove = SubscriptionTarget(
|
|
947
|
+
type="namespace_a2a_messages",
|
|
948
|
+
identifier=effective_identifier_remove,
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
if (
|
|
952
|
+
not effective_identifier_remove
|
|
953
|
+
and target_to_remove_req.type != "current_namespace_a2a_messages"
|
|
954
|
+
):
|
|
955
|
+
log.warning(
|
|
956
|
+
"%s Identifier missing for removal target type %s. Skipping.",
|
|
957
|
+
log_id_prefix,
|
|
958
|
+
target_to_remove_req.type,
|
|
959
|
+
)
|
|
960
|
+
continue
|
|
961
|
+
|
|
962
|
+
solace_topics_for_removal = _translate_target_to_solace_topics(
|
|
963
|
+
target_for_translation_remove, component.namespace
|
|
964
|
+
)
|
|
965
|
+
removed_any_solace_topic_for_this_abstract_target = False
|
|
966
|
+
for topic_str in solace_topics_for_removal:
|
|
967
|
+
if topic_str in current_solace_topics:
|
|
968
|
+
if await component._remove_visualization_subscription_nolock(
|
|
969
|
+
topic_str, stream_id
|
|
970
|
+
):
|
|
971
|
+
log.info(
|
|
972
|
+
"%s Unsubscribed (no-lock) from Solace topic: %s for stream %s (due to removal of %s)",
|
|
973
|
+
log_id_prefix,
|
|
974
|
+
topic_str,
|
|
975
|
+
stream_id,
|
|
976
|
+
effective_identifier_remove,
|
|
977
|
+
)
|
|
978
|
+
current_solace_topics.remove(topic_str)
|
|
979
|
+
removed_any_solace_topic_for_this_abstract_target = True
|
|
980
|
+
else:
|
|
981
|
+
log.error(
|
|
982
|
+
"%s Failed to unsubscribe from Solace topic: %s for stream %s",
|
|
983
|
+
log_id_prefix,
|
|
984
|
+
topic_str,
|
|
985
|
+
stream_id,
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
if removed_any_solace_topic_for_this_abstract_target:
|
|
989
|
+
if target_to_remove_req.type == "current_namespace_a2a_messages":
|
|
990
|
+
current_abstract_targets = [
|
|
991
|
+
t
|
|
992
|
+
for t in current_abstract_targets
|
|
993
|
+
if not (
|
|
994
|
+
t.type == target_to_remove_req.type
|
|
995
|
+
or (
|
|
996
|
+
t.type == "namespace_a2a_messages"
|
|
997
|
+
and t.identifier == effective_identifier_remove
|
|
998
|
+
)
|
|
999
|
+
)
|
|
1000
|
+
]
|
|
1001
|
+
else:
|
|
1002
|
+
current_abstract_targets = [
|
|
1003
|
+
t
|
|
1004
|
+
for t in current_abstract_targets
|
|
1005
|
+
if not (
|
|
1006
|
+
t.type == target_to_remove_req.type
|
|
1007
|
+
and t.identifier == effective_identifier_remove
|
|
1008
|
+
)
|
|
1009
|
+
]
|
|
1010
|
+
targets_actually_removed_abstract.append(
|
|
1011
|
+
effective_identifier_remove
|
|
1012
|
+
)
|
|
1013
|
+
|
|
1014
|
+
log.info(
|
|
1015
|
+
"%s Processed removals. Abstract targets effectively removed identifiers: %s",
|
|
1016
|
+
log_id_prefix,
|
|
1017
|
+
targets_actually_removed_abstract,
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
if update_request.subscription_targets_to_add:
|
|
1021
|
+
for target_to_add_req in update_request.subscription_targets_to_add:
|
|
1022
|
+
effective_identifier_add = target_to_add_req.identifier
|
|
1023
|
+
target_for_translation_add = target_to_add_req
|
|
1024
|
+
original_type_add = target_to_add_req.type
|
|
1025
|
+
|
|
1026
|
+
if target_to_add_req.type == "current_namespace_a2a_messages":
|
|
1027
|
+
effective_identifier_add = component.namespace
|
|
1028
|
+
target_for_translation_add = SubscriptionTarget(
|
|
1029
|
+
type="namespace_a2a_messages",
|
|
1030
|
+
identifier=effective_identifier_add,
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
is_already_present = False
|
|
1034
|
+
for existing_target in current_abstract_targets:
|
|
1035
|
+
if existing_target.type == original_type_add and (
|
|
1036
|
+
original_type_add == "current_namespace_a2a_messages"
|
|
1037
|
+
or existing_target.identifier == effective_identifier_add
|
|
1038
|
+
):
|
|
1039
|
+
is_already_present = True
|
|
1040
|
+
break
|
|
1041
|
+
if (
|
|
1042
|
+
original_type_add == "namespace_a2a_messages"
|
|
1043
|
+
and existing_target.type == "current_namespace_a2a_messages"
|
|
1044
|
+
and effective_identifier_add == component.namespace
|
|
1045
|
+
):
|
|
1046
|
+
is_already_present = True
|
|
1047
|
+
break
|
|
1048
|
+
|
|
1049
|
+
if is_already_present:
|
|
1050
|
+
log.info(
|
|
1051
|
+
"%s Target %s (type: %s) effectively already subscribed. Skipping add.",
|
|
1052
|
+
log_id_prefix,
|
|
1053
|
+
effective_identifier_add,
|
|
1054
|
+
original_type_add,
|
|
1055
|
+
)
|
|
1056
|
+
continue
|
|
1057
|
+
|
|
1058
|
+
target_status = "denied_due_to_scope"
|
|
1059
|
+
required_scope = ""
|
|
1060
|
+
|
|
1061
|
+
identifier_for_spec = target_to_add_req.identifier
|
|
1062
|
+
if original_type_add == "current_namespace_a2a_messages":
|
|
1063
|
+
if target_to_add_req.identifier is None:
|
|
1064
|
+
identifier_for_spec = None
|
|
1065
|
+
|
|
1066
|
+
operation_spec = {
|
|
1067
|
+
"operation_type": "visualization_subscription_update",
|
|
1068
|
+
"target_type": original_type_add,
|
|
1069
|
+
"target_identifier": identifier_for_spec,
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
validation_result = config_resolver.validate_operation_config(
|
|
1073
|
+
user_config, operation_spec, gateway_context_for_validation
|
|
1074
|
+
)
|
|
1075
|
+
has_permission = validation_result.get("valid", False)
|
|
1076
|
+
|
|
1077
|
+
if has_permission:
|
|
1078
|
+
solace_topics_for_target = _translate_target_to_solace_topics(
|
|
1079
|
+
target_for_translation_add, component.namespace
|
|
1080
|
+
)
|
|
1081
|
+
if not solace_topics_for_target:
|
|
1082
|
+
target_status = "error_translating_target"
|
|
1083
|
+
else:
|
|
1084
|
+
all_topics_added_successfully = True
|
|
1085
|
+
temp_solace_topics_added_for_this_target = set()
|
|
1086
|
+
for topic_str in solace_topics_for_target:
|
|
1087
|
+
if await component._add_visualization_subscription(
|
|
1088
|
+
topic_str, stream_id
|
|
1089
|
+
):
|
|
1090
|
+
current_solace_topics.add(topic_str)
|
|
1091
|
+
temp_solace_topics_added_for_this_target.add(topic_str)
|
|
1092
|
+
else:
|
|
1093
|
+
all_topics_added_successfully = False
|
|
1094
|
+
log.error(
|
|
1095
|
+
"%s Failed to add subscription to Solace topic: %s for stream %s (target: %s)",
|
|
1096
|
+
log_id_prefix,
|
|
1097
|
+
topic_str,
|
|
1098
|
+
stream_id,
|
|
1099
|
+
effective_identifier_add,
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
if all_topics_added_successfully:
|
|
1103
|
+
target_status = "subscribed"
|
|
1104
|
+
response_target_data = target_to_add_req.model_dump()
|
|
1105
|
+
if original_type_add == "current_namespace_a2a_messages":
|
|
1106
|
+
response_target_data["identifier"] = (
|
|
1107
|
+
effective_identifier_add
|
|
1108
|
+
)
|
|
1109
|
+
current_abstract_targets.append(
|
|
1110
|
+
ActualSubscribedTarget(
|
|
1111
|
+
**response_target_data, status=target_status
|
|
1112
|
+
)
|
|
1113
|
+
)
|
|
1114
|
+
else:
|
|
1115
|
+
target_status = "error_adding_subscription"
|
|
1116
|
+
for topic_str in temp_solace_topics_added_for_this_target:
|
|
1117
|
+
await component._remove_visualization_subscription_nolock(
|
|
1118
|
+
topic_str, stream_id
|
|
1119
|
+
)
|
|
1120
|
+
current_solace_topics.discard(topic_str)
|
|
1121
|
+
log.warning(
|
|
1122
|
+
"%s Rolled back Solace subscriptions (no-lock) for failed abstract target %s",
|
|
1123
|
+
log_id_prefix,
|
|
1124
|
+
effective_identifier_add,
|
|
1125
|
+
)
|
|
1126
|
+
else:
|
|
1127
|
+
log.warning(
|
|
1128
|
+
"%s User %s denied subscription to target %s (type: %s) due to missing scope: %s",
|
|
1129
|
+
log_id_prefix,
|
|
1130
|
+
user_id,
|
|
1131
|
+
effective_identifier_add,
|
|
1132
|
+
original_type_add,
|
|
1133
|
+
required_scope,
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
if target_status not in ["subscribed", "denied_due_to_scope"]:
|
|
1137
|
+
failed_target_data = target_to_add_req.model_dump()
|
|
1138
|
+
if original_type_add == "current_namespace_a2a_messages":
|
|
1139
|
+
failed_target_data["identifier"] = effective_identifier_add
|
|
1140
|
+
|
|
1141
|
+
component._active_visualization_streams[stream_id][
|
|
1142
|
+
"abstract_targets"
|
|
1143
|
+
] = current_abstract_targets
|
|
1144
|
+
component._active_visualization_streams[stream_id][
|
|
1145
|
+
"solace_topics"
|
|
1146
|
+
] = current_solace_topics
|
|
1147
|
+
|
|
1148
|
+
log.info(
|
|
1149
|
+
"%s Stream %s configuration updated. Current abstract targets: %d, Solace topics: %d",
|
|
1150
|
+
log_id_prefix,
|
|
1151
|
+
stream_id,
|
|
1152
|
+
len(current_abstract_targets),
|
|
1153
|
+
len(current_solace_topics),
|
|
1154
|
+
)
|
|
1155
|
+
log.debug(
|
|
1156
|
+
"%s Released viz lock after updating stream config for %s",
|
|
1157
|
+
log_id_prefix,
|
|
1158
|
+
stream_id,
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
return VisualizationConfigUpdateResponse(
|
|
1162
|
+
stream_id=stream_id,
|
|
1163
|
+
current_subscribed_targets=current_abstract_targets,
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
@router.delete("/{stream_id}/unsubscribe", status_code=status.HTTP_204_NO_CONTENT)
|
|
1168
|
+
async def unsubscribe_from_visualization_stream(
|
|
1169
|
+
stream_id: str,
|
|
1170
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
1171
|
+
sse_manager: SSEManager = Depends(get_sse_manager),
|
|
1172
|
+
user_id: str = Depends(get_user_id),
|
|
1173
|
+
):
|
|
1174
|
+
"""Terminates an active visualization stream."""
|
|
1175
|
+
log_id_prefix = f"{component.log_identifier}[DELETE /viz/{stream_id}]"
|
|
1176
|
+
log.info(
|
|
1177
|
+
"%s Request received from user %s to unsubscribe from stream.",
|
|
1178
|
+
log_id_prefix,
|
|
1179
|
+
user_id,
|
|
1180
|
+
)
|
|
1181
|
+
|
|
1182
|
+
log.debug(
|
|
1183
|
+
"%s Acquiring viz lock to unsubscribe from stream %s", log_id_prefix, stream_id
|
|
1184
|
+
)
|
|
1185
|
+
async with component._get_visualization_lock():
|
|
1186
|
+
stream_config = component._active_visualization_streams.get(stream_id)
|
|
1187
|
+
if not stream_config:
|
|
1188
|
+
log.info(
|
|
1189
|
+
"%s Stream %s not found, no action needed.", log_id_prefix, stream_id
|
|
1190
|
+
)
|
|
1191
|
+
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
1192
|
+
|
|
1193
|
+
if stream_config.get("user_id") != user_id:
|
|
1194
|
+
raise HTTPException(
|
|
1195
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
1196
|
+
detail="User not authorized to unsubscribe from this stream.",
|
|
1197
|
+
)
|
|
1198
|
+
|
|
1199
|
+
topics_to_remove = list(stream_config.get("solace_topics", []))
|
|
1200
|
+
for topic_str in topics_to_remove:
|
|
1201
|
+
await component._remove_visualization_subscription_nolock(
|
|
1202
|
+
topic_str, stream_id
|
|
1203
|
+
)
|
|
1204
|
+
|
|
1205
|
+
sse_queue = stream_config.get("sse_queue")
|
|
1206
|
+
if sse_queue:
|
|
1207
|
+
await sse_manager.close_connection(stream_id, sse_queue)
|
|
1208
|
+
|
|
1209
|
+
component._active_visualization_streams.pop(stream_id, None)
|
|
1210
|
+
log.info("%s Stream %s unsubscribed and removed.", log_id_prefix, stream_id)
|
|
1211
|
+
log.debug(
|
|
1212
|
+
"%s Released viz lock after unsubscribing from stream %s",
|
|
1213
|
+
log_id_prefix,
|
|
1214
|
+
stream_id,
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
log.info("Initialized Router for A2A Message Visualization.")
|