solace-agent-mesh 0.2.3__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agent/adk/adk_llm.txt +93 -0
- solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
- solace_agent_mesh/agent/adk/callbacks.py +1694 -0
- solace_agent_mesh/agent/adk/filesystem_artifact_service.py +381 -0
- solace_agent_mesh/agent/adk/invocation_monitor.py +295 -0
- solace_agent_mesh/agent/adk/models/lite_llm.py +872 -0
- solace_agent_mesh/agent/adk/models/models_llm.txt +94 -0
- solace_agent_mesh/agent/adk/runner.py +353 -0
- solace_agent_mesh/agent/adk/services.py +240 -0
- solace_agent_mesh/agent/adk/setup.py +751 -0
- solace_agent_mesh/agent/adk/stream_parser.py +214 -0
- solace_agent_mesh/agent/adk/tool_wrapper.py +139 -0
- solace_agent_mesh/agent/agent_llm.txt +41 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +1469 -0
- solace_agent_mesh/agent/protocol/protocol_llm.txt +21 -0
- solace_agent_mesh/agent/sac/app.py +640 -0
- solace_agent_mesh/agent/sac/component.py +3388 -0
- solace_agent_mesh/agent/sac/patch_adk.py +111 -0
- solace_agent_mesh/agent/sac/sac_llm.txt +105 -0
- solace_agent_mesh/agent/sac/task_execution_context.py +176 -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 +90 -0
- solace_agent_mesh/agent/tools/__init__.py +14 -0
- solace_agent_mesh/agent/tools/audio_tools.py +1622 -0
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +1954 -0
- solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +238 -0
- solace_agent_mesh/agent/tools/general_agent_tools.py +569 -0
- solace_agent_mesh/agent/tools/image_tools.py +1184 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +289 -0
- solace_agent_mesh/agent/tools/registry.py +36 -0
- solace_agent_mesh/agent/tools/test_tools.py +135 -0
- solace_agent_mesh/agent/tools/tool_definition.py +45 -0
- solace_agent_mesh/agent/tools/tools_llm.txt +104 -0
- solace_agent_mesh/agent/tools/web_tools.py +381 -0
- solace_agent_mesh/agent/utils/artifact_helpers.py +927 -0
- solace_agent_mesh/agent/utils/config_parser.py +47 -0
- solace_agent_mesh/agent/utils/context_helpers.py +60 -0
- solace_agent_mesh/agent/utils/utils_llm.txt +153 -0
- solace_agent_mesh/assets/docs/404.html +16 -0
- solace_agent_mesh/assets/docs/assets/css/styles.906a1503.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/sac-flows-80d5b603c6aafd33e87945680ce0abf3.png +0 -0
- solace_agent_mesh/assets/docs/assets/images/sac_parts_of_a_component-cb3d0424b1d0c17734c5435cca6b4082.png +0 -0
- solace_agent_mesh/assets/docs/assets/js/04989206.674a8007.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/0e682baa.79f0ab22.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1023fc19.015679ca.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/1523c6b4.91c7bc01.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/166ab619.7d97ccaf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js.LICENSE.txt +7 -0
- solace_agent_mesh/assets/docs/assets/js/1c6e87d2.23bccffb.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/21ceee5f.614fa8dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2334.622a6395.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2a9cab12.8909df92.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/332e10b5.7a103f42.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3624.b524e433.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/3d406171.f722eaf5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.36090198.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/442a8107.5ba94b65.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/4c2787c2.66ee00e9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.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/5b4258a4.bda20761.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.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/7040.cb436723.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/768e31b0.a12673db.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/8356.8a379c04.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/85387663.6bf41934.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.d7c16be6.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/8591.d7c16be6.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.49e930c2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/945fb41e.74d728aa.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/9eff14a2.1bf8f61c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.26ca071f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.d3e2dcc3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae4415af.8e279b5d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/b7006a3a.40b10c9d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/bac0be12.f50d9bac.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/bb2ef573.207e6990.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c2c06897.63b76e9e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cc969b05.954186d4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cd3d4052.ca6eed8c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ced92a13.fb92e7ca.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cee5d587.f5b73ca1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.ecc3d195.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f897a61a.2c2e152c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/fbfa3e75.aca209c9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.7ed3319f.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/main.7ed3319f.js.LICENSE.txt +81 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.d9520ae2.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +128 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +91 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +201 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +91 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +82 -0
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +60 -0
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +48 -0
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +17 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +45 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +76 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +150 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +267 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +136 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +116 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +80 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +164 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +57 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +72 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +102 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +99 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +90 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +107 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +152 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +103 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +170 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +200 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +69 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +59 -0
- solace_agent_mesh/assets/docs/img/Solace_AI_Framework_README.png +0 -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/solace-logo.png +0 -0
- solace_agent_mesh/assets/docs/lunr-index-1753813536522.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -0
- solace_agent_mesh/assets/docs/search-doc-1753813536522.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 -1
- 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 +659 -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 +93 -0
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +118 -0
- solace_agent_mesh/cli/commands/docs_cmd.py +57 -0
- solace_agent_mesh/cli/commands/eval_cmd.py +64 -0
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +404 -0
- solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +197 -0
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +387 -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 +110 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +183 -0
- solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +18 -0
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +372 -0
- solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +138 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +309 -0
- solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +174 -0
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
- solace_agent_mesh/cli/commands/run_cmd.py +158 -0
- solace_agent_mesh/cli/main.py +17 -294
- solace_agent_mesh/cli/utils.py +135 -204
- solace_agent_mesh/client/webui/frontend/static/assets/authCallback-DvlO62me.js +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/client-bp6u3qVZ.js +49 -0
- solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-An0a5j5k.js +663 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Bu5-4Bac.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +14 -0
- solace_agent_mesh/client/webui/frontend/static/index.html +15 -0
- solace_agent_mesh/common/__init__.py +1 -0
- solace_agent_mesh/common/a2a_protocol.py +564 -0
- solace_agent_mesh/common/agent_registry.py +42 -0
- solace_agent_mesh/common/client/__init__.py +4 -0
- solace_agent_mesh/common/client/card_resolver.py +21 -0
- solace_agent_mesh/common/client/client.py +85 -0
- solace_agent_mesh/common/client/client_llm.txt +133 -0
- solace_agent_mesh/common/common_llm.txt +144 -0
- solace_agent_mesh/common/constants.py +1 -14
- solace_agent_mesh/common/middleware/__init__.py +12 -0
- solace_agent_mesh/common/middleware/config_resolver.py +130 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
- solace_agent_mesh/common/middleware/registry.py +125 -0
- solace_agent_mesh/common/server/__init__.py +4 -0
- solace_agent_mesh/common/server/server.py +122 -0
- solace_agent_mesh/common/server/server_llm.txt +169 -0
- solace_agent_mesh/common/server/task_manager.py +291 -0
- solace_agent_mesh/common/server/utils.py +28 -0
- solace_agent_mesh/common/services/__init__.py +4 -0
- solace_agent_mesh/common/services/employee_service.py +162 -0
- solace_agent_mesh/common/services/identity_service.py +129 -0
- solace_agent_mesh/common/services/providers/__init__.py +4 -0
- solace_agent_mesh/common/services/providers/local_file_identity_service.py +148 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +113 -0
- solace_agent_mesh/common/services/services_llm.txt +132 -0
- solace_agent_mesh/common/types.py +411 -0
- solace_agent_mesh/common/utils/__init__.py +7 -0
- solace_agent_mesh/common/utils/asyncio_macos_fix.py +86 -0
- solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
- solace_agent_mesh/common/utils/embeds/constants.py +55 -0
- solace_agent_mesh/common/utils/embeds/converter.py +452 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +124 -0
- solace_agent_mesh/common/utils/embeds/evaluators.py +394 -0
- solace_agent_mesh/common/utils/embeds/modifiers.py +816 -0
- solace_agent_mesh/common/utils/embeds/resolver.py +865 -0
- solace_agent_mesh/common/utils/embeds/types.py +14 -0
- solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
- solace_agent_mesh/common/utils/log_formatters.py +44 -0
- solace_agent_mesh/common/utils/mime_helpers.py +106 -0
- solace_agent_mesh/common/utils/push_notification_auth.py +134 -0
- solace_agent_mesh/common/utils/utils_llm.txt +67 -0
- solace_agent_mesh/config_portal/backend/common.py +66 -24
- solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +23 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +160 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +525 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +216 -0
- solace_agent_mesh/config_portal/backend/server.py +550 -140
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DNxCwAGB.js +48 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/components-B7lKcHVY.js +140 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-DX1misIU.js → entry.client-CEumGClk.js} +3 -3
- solace_agent_mesh/config_portal/frontend/static/client/assets/index-DSo1AH_7.js +68 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d2b54a97.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{root-BApq5dPK.js → root-C4XmHinv.js} +2 -2
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/__init__.py +1 -0
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +88 -0
- solace_agent_mesh/core_a2a/service.py +331 -0
- solace_agent_mesh/evaluation/config_loader.py +657 -0
- solace_agent_mesh/evaluation/evaluator.py +667 -0
- solace_agent_mesh/evaluation/message_organizer.py +568 -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 +972 -0
- solace_agent_mesh/evaluation/report_generator.py +613 -0
- solace_agent_mesh/evaluation/run.py +613 -0
- solace_agent_mesh/evaluation/subscriber.py +872 -0
- solace_agent_mesh/evaluation/summary_builder.py +775 -0
- solace_agent_mesh/evaluation/test_case_loader.py +714 -0
- solace_agent_mesh/gateway/base/__init__.py +1 -0
- solace_agent_mesh/gateway/base/app.py +266 -0
- solace_agent_mesh/gateway/base/base_llm.txt +119 -0
- solace_agent_mesh/gateway/base/component.py +1542 -0
- solace_agent_mesh/gateway/base/task_context.py +74 -0
- solace_agent_mesh/gateway/gateway_llm.txt +125 -0
- solace_agent_mesh/gateway/http_sse/app.py +190 -0
- solace_agent_mesh/gateway/http_sse/component.py +1602 -0
- solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +65 -0
- solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +108 -0
- solace_agent_mesh/gateway/http_sse/dependencies.py +316 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +63 -0
- solace_agent_mesh/gateway/http_sse/main.py +442 -0
- solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/routers/agents.py +41 -0
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +821 -0
- solace_agent_mesh/gateway/http_sse/routers/auth.py +212 -0
- solace_agent_mesh/gateway/http_sse/routers/config.py +55 -0
- solace_agent_mesh/gateway/http_sse/routers/people.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +37 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +80 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +138 -0
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +294 -0
- solace_agent_mesh/gateway/http_sse/routers/users.py +59 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +1131 -0
- solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/services/agent_service.py +69 -0
- solace_agent_mesh/gateway/http_sse/services/people_service.py +158 -0
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +179 -0
- solace_agent_mesh/gateway/http_sse/services/task_service.py +121 -0
- solace_agent_mesh/gateway/http_sse/session_manager.py +187 -0
- solace_agent_mesh/gateway/http_sse/sse_manager.py +328 -0
- solace_agent_mesh/llm.txt +228 -0
- solace_agent_mesh/llm_detail.txt +2835 -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 +73 -0
- solace_agent_mesh/templates/gateway_component_template.py +400 -0
- solace_agent_mesh/templates/gateway_config_template.yaml +43 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +55 -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 +63 -0
- solace_agent_mesh/templates/plugin_pyproject_template.toml +33 -0
- solace_agent_mesh/templates/plugin_readme_template.md +34 -0
- solace_agent_mesh/templates/plugin_tools_template.py +224 -0
- solace_agent_mesh/templates/shared_config.yaml +66 -0
- solace_agent_mesh/templates/templates_llm.txt +147 -0
- solace_agent_mesh/templates/webui.yaml +53 -0
- solace_agent_mesh-1.0.1.dist-info/METADATA +432 -0
- solace_agent_mesh-1.0.1.dist-info/RECORD +359 -0
- solace_agent_mesh-1.0.1.dist-info/entry_points.txt +3 -0
- {solace_agent_mesh-0.2.3.dist-info → solace_agent_mesh-1.0.1.dist-info}/licenses/LICENSE +1 -1
- solace_agent_mesh/agents/base_agent_component.py +0 -226
- solace_agent_mesh/agents/global/actions/agent_state_change.py +0 -54
- solace_agent_mesh/agents/global/actions/clear_history.py +0 -32
- solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +0 -160
- solace_agent_mesh/agents/global/actions/create_file.py +0 -70
- solace_agent_mesh/agents/global/actions/error_action.py +0 -45
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +0 -163
- solace_agent_mesh/agents/global/actions/plotly_graph.py +0 -152
- solace_agent_mesh/agents/global/actions/retrieve_file.py +0 -51
- solace_agent_mesh/agents/global/global_agent_component.py +0 -38
- solace_agent_mesh/agents/image_processing/actions/create_image.py +0 -75
- solace_agent_mesh/agents/image_processing/actions/describe_image.py +0 -115
- solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +0 -23
- solace_agent_mesh/agents/slack/__init__.py +0 -1
- solace_agent_mesh/agents/slack/actions/__init__.py +0 -1
- solace_agent_mesh/agents/slack/actions/post_message.py +0 -177
- solace_agent_mesh/agents/slack/slack_agent_component.py +0 -59
- solace_agent_mesh/agents/web_request/actions/do_image_search.py +0 -84
- solace_agent_mesh/agents/web_request/actions/do_news_search.py +0 -47
- solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +0 -34
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +0 -135
- solace_agent_mesh/agents/web_request/actions/download_file.py +0 -69
- solace_agent_mesh/agents/web_request/web_request_agent_component.py +0 -33
- solace_agent_mesh/assets/web-visualizer/assets/index-D0qORgkg.css +0 -1
- solace_agent_mesh/assets/web-visualizer/assets/index-DnDr1pnu.js +0 -109
- solace_agent_mesh/assets/web-visualizer/index.html +0 -14
- solace_agent_mesh/assets/web-visualizer/vite.svg +0 -1
- solace_agent_mesh/cli/commands/add/__init__.py +0 -3
- solace_agent_mesh/cli/commands/add/add.py +0 -88
- solace_agent_mesh/cli/commands/add/agent.py +0 -110
- solace_agent_mesh/cli/commands/add/copy_from_plugin.py +0 -92
- solace_agent_mesh/cli/commands/add/gateway.py +0 -374
- solace_agent_mesh/cli/commands/build.py +0 -670
- solace_agent_mesh/cli/commands/chat/__init__.py +0 -3
- solace_agent_mesh/cli/commands/chat/chat.py +0 -361
- solace_agent_mesh/cli/commands/config.py +0 -29
- solace_agent_mesh/cli/commands/init/__init__.py +0 -3
- solace_agent_mesh/cli/commands/init/ai_provider_step.py +0 -93
- solace_agent_mesh/cli/commands/init/broker_step.py +0 -99
- solace_agent_mesh/cli/commands/init/builtin_agent_step.py +0 -83
- solace_agent_mesh/cli/commands/init/check_if_already_done.py +0 -13
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +0 -65
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +0 -147
- solace_agent_mesh/cli/commands/init/file_service_step.py +0 -73
- solace_agent_mesh/cli/commands/init/init.py +0 -92
- solace_agent_mesh/cli/commands/init/project_structure_step.py +0 -16
- solace_agent_mesh/cli/commands/init/web_init_step.py +0 -32
- solace_agent_mesh/cli/commands/plugin/__init__.py +0 -3
- solace_agent_mesh/cli/commands/plugin/add.py +0 -100
- solace_agent_mesh/cli/commands/plugin/build.py +0 -268
- solace_agent_mesh/cli/commands/plugin/create.py +0 -117
- solace_agent_mesh/cli/commands/plugin/plugin.py +0 -124
- solace_agent_mesh/cli/commands/plugin/remove.py +0 -73
- solace_agent_mesh/cli/commands/run.py +0 -68
- solace_agent_mesh/cli/commands/visualizer.py +0 -138
- solace_agent_mesh/cli/config.py +0 -85
- solace_agent_mesh/common/action.py +0 -91
- solace_agent_mesh/common/action_list.py +0 -37
- solace_agent_mesh/common/action_response.py +0 -340
- solace_agent_mesh/common/mysql_database.py +0 -40
- solace_agent_mesh/common/postgres_database.py +0 -85
- solace_agent_mesh/common/prompt_templates.py +0 -28
- solace_agent_mesh/common/stimulus_utils.py +0 -152
- solace_agent_mesh/common/time.py +0 -24
- solace_agent_mesh/common/utils.py +0 -712
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DMmCawWe.js +0 -42
- solace_agent_mesh/config_portal/frontend/static/client/assets/components-ZIfdTbrV.js +0 -191
- solace_agent_mesh/config_portal/frontend/static/client/assets/index-BJHAE5s4.js +0 -17
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-dd988f05.js +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DX4gQ516.css +0 -1
- solace_agent_mesh/configs/agent_global.yaml +0 -74
- solace_agent_mesh/configs/agent_image_processing.yaml +0 -82
- solace_agent_mesh/configs/agent_slack.yaml +0 -64
- solace_agent_mesh/configs/agent_web_request.yaml +0 -75
- solace_agent_mesh/configs/conversation_to_file.yaml +0 -56
- solace_agent_mesh/configs/error_catcher.yaml +0 -56
- solace_agent_mesh/configs/monitor.yaml +0 -0
- solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +0 -109
- solace_agent_mesh/configs/monitor_user_feedback.yaml +0 -58
- solace_agent_mesh/configs/orchestrator.yaml +0 -241
- solace_agent_mesh/configs/service_embedding.yaml +0 -81
- solace_agent_mesh/configs/service_llm.yaml +0 -265
- solace_agent_mesh/configs/visualize_websocket.yaml +0 -55
- solace_agent_mesh/gateway/components/gateway_base.py +0 -47
- solace_agent_mesh/gateway/components/gateway_input.py +0 -278
- solace_agent_mesh/gateway/components/gateway_output.py +0 -298
- solace_agent_mesh/gateway/identity/bamboohr_identity.py +0 -18
- solace_agent_mesh/gateway/identity/identity_base.py +0 -10
- solace_agent_mesh/gateway/identity/identity_provider.py +0 -60
- solace_agent_mesh/gateway/identity/no_identity.py +0 -9
- solace_agent_mesh/gateway/identity/passthru_identity.py +0 -9
- solace_agent_mesh/monitors/base_monitor_component.py +0 -26
- solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +0 -75
- solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +0 -560
- solace_agent_mesh/orchestrator/__init__.py +0 -0
- solace_agent_mesh/orchestrator/action_manager.py +0 -237
- solace_agent_mesh/orchestrator/components/__init__.py +0 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +0 -58
- solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +0 -179
- solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +0 -107
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +0 -527
- solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +0 -260
- solace_agent_mesh/orchestrator/orchestrator_main.py +0 -172
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +0 -539
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +0 -56
- solace_agent_mesh/services/bamboo_hr_service/__init__.py +0 -3
- solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +0 -182
- solace_agent_mesh/services/common/__init__.py +0 -4
- solace_agent_mesh/services/common/auto_expiry.py +0 -45
- solace_agent_mesh/services/common/singleton.py +0 -18
- solace_agent_mesh/services/file_service/__init__.py +0 -14
- solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
- solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +0 -149
- solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +0 -162
- solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +0 -64
- solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +0 -106
- solace_agent_mesh/services/file_service/file_service.py +0 -437
- solace_agent_mesh/services/file_service/file_service_constants.py +0 -54
- solace_agent_mesh/services/file_service/file_transformations.py +0 -141
- solace_agent_mesh/services/file_service/file_utils.py +0 -324
- solace_agent_mesh/services/file_service/transformers/__init__.py +0 -5
- solace_agent_mesh/services/history_service/__init__.py +0 -3
- solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +0 -54
- solace_agent_mesh/services/history_service/history_providers/file_history_provider.py +0 -74
- solace_agent_mesh/services/history_service/history_providers/index.py +0 -40
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +0 -33
- solace_agent_mesh/services/history_service/history_providers/mongodb_history_provider.py +0 -66
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +0 -66
- solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +0 -93
- solace_agent_mesh/services/history_service/history_service.py +0 -413
- solace_agent_mesh/services/history_service/long_term_memory/__init__.py +0 -0
- solace_agent_mesh/services/history_service/long_term_memory/long_term_memory.py +0 -399
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +0 -340
- solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +0 -152
- solace_agent_mesh/services/middleware_service/__init__.py +0 -0
- solace_agent_mesh/services/middleware_service/middleware_service.py +0 -20
- solace_agent_mesh/templates/action.py +0 -38
- solace_agent_mesh/templates/agent.py +0 -29
- solace_agent_mesh/templates/agent.yaml +0 -70
- solace_agent_mesh/templates/gateway-config-template.yaml +0 -6
- solace_agent_mesh/templates/gateway-default-config.yaml +0 -28
- solace_agent_mesh/templates/gateway-flows.yaml +0 -78
- solace_agent_mesh/templates/gateway-header.yaml +0 -16
- solace_agent_mesh/templates/gateway_base.py +0 -15
- solace_agent_mesh/templates/gateway_input.py +0 -98
- solace_agent_mesh/templates/gateway_output.py +0 -71
- solace_agent_mesh/templates/plugin-gateway-default-config.yaml +0 -29
- solace_agent_mesh/templates/plugin-pyproject.toml +0 -30
- solace_agent_mesh/templates/rest-api-default-config.yaml +0 -31
- solace_agent_mesh/templates/rest-api-flows.yaml +0 -81
- solace_agent_mesh/templates/slack-default-config.yaml +0 -16
- solace_agent_mesh/templates/slack-flows.yaml +0 -81
- solace_agent_mesh/templates/solace-agent-mesh-default.yaml +0 -86
- solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +0 -8
- solace_agent_mesh/templates/web-default-config.yaml +0 -10
- solace_agent_mesh/templates/web-flows.yaml +0 -76
- solace_agent_mesh/tools/__init__.py +0 -0
- solace_agent_mesh/tools/components/__init__.py +0 -0
- solace_agent_mesh/tools/components/conversation_formatter.py +0 -111
- solace_agent_mesh/tools/components/file_resolver_component.py +0 -58
- solace_agent_mesh/tools/config/runtime_config.py +0 -26
- solace_agent_mesh-0.2.3.dist-info/METADATA +0 -172
- solace_agent_mesh-0.2.3.dist-info/RECORD +0 -193
- solace_agent_mesh-0.2.3.dist-info/entry_points.txt +0 -3
- /solace_agent_mesh/{agents → agent}/__init__.py +0 -0
- /solace_agent_mesh/{agents/global → agent/adk}/__init__.py +0 -0
- /solace_agent_mesh/{agents/global/actions → agent/protocol}/__init__.py +0 -0
- /solace_agent_mesh/{agents/image_processing → agent/sac}/__init__.py +0 -0
- /solace_agent_mesh/{agents/image_processing/actions → agent/utils}/__init__.py +0 -0
- /solace_agent_mesh/{agents/web_request → config_portal/backend/plugin_catalog}/__init__.py +0 -0
- /solace_agent_mesh/{agents/web_request/actions → evaluation}/__init__.py +0 -0
- /solace_agent_mesh/gateway/{components → http_sse}/__init__.py +0 -0
- {solace_agent_mesh-0.2.3.dist-info → solace_agent_mesh-1.0.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,1469 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains event handling logic for the A2A_ADK_HostComponent.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import yaml
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Union, TYPE_CHECKING, List, Dict, Any
|
|
9
|
+
import fnmatch
|
|
10
|
+
from solace_ai_connector.common.log import log
|
|
11
|
+
from solace_ai_connector.common.message import Message as SolaceMessage
|
|
12
|
+
from solace_ai_connector.common.event import Event, EventType
|
|
13
|
+
from ...common.types import (
|
|
14
|
+
Message as A2AMessage,
|
|
15
|
+
SendTaskRequest,
|
|
16
|
+
SendTaskStreamingRequest,
|
|
17
|
+
CancelTaskRequest,
|
|
18
|
+
GetTaskRequest,
|
|
19
|
+
SetTaskPushNotificationRequest,
|
|
20
|
+
GetTaskPushNotificationRequest,
|
|
21
|
+
TaskResubscriptionRequest,
|
|
22
|
+
TaskIdParams,
|
|
23
|
+
JSONParseError,
|
|
24
|
+
InvalidRequestError,
|
|
25
|
+
InternalError,
|
|
26
|
+
JSONRPCResponse,
|
|
27
|
+
AgentCard,
|
|
28
|
+
AgentCapabilities,
|
|
29
|
+
Task,
|
|
30
|
+
TaskStatusUpdateEvent,
|
|
31
|
+
TaskArtifactUpdateEvent,
|
|
32
|
+
TaskStatus,
|
|
33
|
+
TaskState,
|
|
34
|
+
DataPart,
|
|
35
|
+
A2ARequest,
|
|
36
|
+
)
|
|
37
|
+
from ...common.a2a_protocol import (
|
|
38
|
+
get_agent_request_topic,
|
|
39
|
+
get_discovery_topic,
|
|
40
|
+
translate_a2a_to_adk_content,
|
|
41
|
+
get_client_response_topic,
|
|
42
|
+
get_agent_response_subscription_topic,
|
|
43
|
+
get_agent_status_subscription_topic,
|
|
44
|
+
_extract_text_from_parts,
|
|
45
|
+
)
|
|
46
|
+
from ...agent.utils.artifact_helpers import (
|
|
47
|
+
load_artifact_content_or_metadata,
|
|
48
|
+
)
|
|
49
|
+
from ...agent.adk.runner import run_adk_async_task_thread_wrapper
|
|
50
|
+
from ..sac.task_execution_context import TaskExecutionContext
|
|
51
|
+
from google.adk.agents import RunConfig
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from ..sac.component import SamAgentComponent
|
|
55
|
+
from google.adk.agents.run_config import StreamingMode
|
|
56
|
+
from google.adk.events import Event as ADKEvent
|
|
57
|
+
from google.genai import types as adk_types
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def _format_artifact_summary_from_manifest(
|
|
61
|
+
component: "SamAgentComponent",
|
|
62
|
+
produced_artifacts: List[Dict[str, Any]],
|
|
63
|
+
peer_agent_name: str,
|
|
64
|
+
correlation_data: Dict[str, Any],
|
|
65
|
+
) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Loads metadata for a list of produced artifacts and formats it into a
|
|
68
|
+
human-readable YAML summary string.
|
|
69
|
+
"""
|
|
70
|
+
if not produced_artifacts:
|
|
71
|
+
return ""
|
|
72
|
+
|
|
73
|
+
artifact_summary_lines = [
|
|
74
|
+
f"Peer agent `{peer_agent_name}` created {len(produced_artifacts)} artifact(s):"
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
original_task_context = correlation_data.get("original_task_context", {})
|
|
78
|
+
user_id = original_task_context.get("user_id")
|
|
79
|
+
session_id = original_task_context.get("session_id")
|
|
80
|
+
|
|
81
|
+
if not (component.artifact_service and user_id and session_id):
|
|
82
|
+
log.warning(
|
|
83
|
+
"%s Cannot load artifact metadata: missing artifact_service or context.",
|
|
84
|
+
component.log_identifier,
|
|
85
|
+
)
|
|
86
|
+
for artifact_ref in produced_artifacts:
|
|
87
|
+
artifact_summary_lines.append(
|
|
88
|
+
f"- `{artifact_ref.get('filename')}` (v{artifact_ref.get('version')})"
|
|
89
|
+
)
|
|
90
|
+
return "\n".join(artifact_summary_lines)
|
|
91
|
+
|
|
92
|
+
peer_agent_name_for_artifact = peer_agent_name
|
|
93
|
+
if (
|
|
94
|
+
not peer_agent_name_for_artifact
|
|
95
|
+
or peer_agent_name_for_artifact == "A peer agent"
|
|
96
|
+
):
|
|
97
|
+
log.warning(
|
|
98
|
+
"%s Peer agent name not in task metadata, using self agent name for artifact loading.",
|
|
99
|
+
component.log_identifier,
|
|
100
|
+
)
|
|
101
|
+
peer_agent_name_for_artifact = component.agent_name
|
|
102
|
+
|
|
103
|
+
for artifact_ref in produced_artifacts:
|
|
104
|
+
filename = artifact_ref.get("filename")
|
|
105
|
+
version = artifact_ref.get("version")
|
|
106
|
+
if not filename or version is None:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
metadata_result = await load_artifact_content_or_metadata(
|
|
111
|
+
artifact_service=component.artifact_service,
|
|
112
|
+
app_name=peer_agent_name_for_artifact,
|
|
113
|
+
user_id=user_id,
|
|
114
|
+
session_id=session_id,
|
|
115
|
+
filename=filename,
|
|
116
|
+
version=version,
|
|
117
|
+
load_metadata_only=True,
|
|
118
|
+
)
|
|
119
|
+
if metadata_result.get("status") == "success":
|
|
120
|
+
metadata = metadata_result.get("metadata", {})
|
|
121
|
+
TRUNCATION_LIMIT_BYTES = 1024
|
|
122
|
+
TRUNCATION_MESSAGE = "\n... [truncated] ..."
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
formatted_metadata_str = yaml.safe_dump(
|
|
126
|
+
metadata,
|
|
127
|
+
default_flow_style=False,
|
|
128
|
+
sort_keys=False,
|
|
129
|
+
allow_unicode=True,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
len(formatted_metadata_str.encode("utf-8"))
|
|
134
|
+
> TRUNCATION_LIMIT_BYTES
|
|
135
|
+
):
|
|
136
|
+
cutoff = TRUNCATION_LIMIT_BYTES - len(
|
|
137
|
+
TRUNCATION_MESSAGE.encode("utf-8")
|
|
138
|
+
)
|
|
139
|
+
formatted_metadata_str = (
|
|
140
|
+
formatted_metadata_str[:cutoff] + TRUNCATION_MESSAGE
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
summary_line = f"- `{filename}` (v{version}):\n ```yaml\n{formatted_metadata_str}\n ```"
|
|
144
|
+
artifact_summary_lines.append(summary_line)
|
|
145
|
+
except Exception as e_format:
|
|
146
|
+
log.error(
|
|
147
|
+
"Error formatting metadata for %s v%s: %s",
|
|
148
|
+
filename,
|
|
149
|
+
version,
|
|
150
|
+
e_format,
|
|
151
|
+
)
|
|
152
|
+
artifact_summary_lines.append(
|
|
153
|
+
f"- `{filename}` (v{version}): Error formatting metadata."
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
artifact_summary_lines.append(
|
|
157
|
+
f"- `{filename}` (v{version}): Could not load metadata."
|
|
158
|
+
)
|
|
159
|
+
except Exception as e_meta:
|
|
160
|
+
log.error(
|
|
161
|
+
"Error loading metadata for %s v%s: %s", filename, version, e_meta
|
|
162
|
+
)
|
|
163
|
+
artifact_summary_lines.append(
|
|
164
|
+
f"- `{filename}` (v{version}): Error loading metadata."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return "\n".join(artifact_summary_lines)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _register_peer_artifacts_in_parent_context(
|
|
171
|
+
parent_task_context: "TaskExecutionContext",
|
|
172
|
+
peer_task_object: Task,
|
|
173
|
+
log_identifier: str,
|
|
174
|
+
):
|
|
175
|
+
"""
|
|
176
|
+
Registers artifacts produced by a peer agent in the parent agent's
|
|
177
|
+
task execution context, allowing them to be "bubbled up".
|
|
178
|
+
"""
|
|
179
|
+
if not parent_task_context:
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
if peer_task_object.metadata and "produced_artifacts" in peer_task_object.metadata:
|
|
183
|
+
peer_artifacts = peer_task_object.metadata.get("produced_artifacts", [])
|
|
184
|
+
if not peer_artifacts:
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
log.debug(
|
|
188
|
+
"%s Registering %d artifacts from peer response into parent task context.",
|
|
189
|
+
log_identifier,
|
|
190
|
+
len(peer_artifacts),
|
|
191
|
+
)
|
|
192
|
+
for artifact_ref in peer_artifacts:
|
|
193
|
+
filename = artifact_ref.get("filename")
|
|
194
|
+
version = artifact_ref.get("version")
|
|
195
|
+
if filename and version is not None:
|
|
196
|
+
parent_task_context.register_produced_artifact(
|
|
197
|
+
filename=filename,
|
|
198
|
+
version=version,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def process_event(component, event: Event):
|
|
203
|
+
"""
|
|
204
|
+
Processes incoming events (Messages, Timers, etc.). Routes to specific handlers.
|
|
205
|
+
Args:
|
|
206
|
+
component: The A2A_ADK_HostComponent instance.
|
|
207
|
+
event: The event object received from the SAC framework.
|
|
208
|
+
"""
|
|
209
|
+
try:
|
|
210
|
+
if event.event_type == EventType.MESSAGE:
|
|
211
|
+
message = event.data
|
|
212
|
+
topic = message.get_topic()
|
|
213
|
+
if not topic:
|
|
214
|
+
log.warning(
|
|
215
|
+
"%s Received message without topic. Ignoring.",
|
|
216
|
+
component.log_identifier,
|
|
217
|
+
)
|
|
218
|
+
return
|
|
219
|
+
if component.invocation_monitor:
|
|
220
|
+
component.invocation_monitor.log_message_event(
|
|
221
|
+
direction="RECEIVED",
|
|
222
|
+
topic=topic,
|
|
223
|
+
payload=message.get_payload(),
|
|
224
|
+
component_identifier=component.log_identifier,
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
log.warning(
|
|
228
|
+
f"{component.log_identifier} InvocationMonitor not available in component for event on topic {topic}"
|
|
229
|
+
)
|
|
230
|
+
namespace = component.get_config("namespace")
|
|
231
|
+
agent_name = component.get_config("agent_name")
|
|
232
|
+
agent_request_topic = get_agent_request_topic(namespace, agent_name)
|
|
233
|
+
discovery_topic = get_discovery_topic(namespace)
|
|
234
|
+
agent_response_sub_prefix = (
|
|
235
|
+
get_agent_response_subscription_topic(namespace, agent_name)[:-2] + "/"
|
|
236
|
+
)
|
|
237
|
+
agent_status_sub_prefix = (
|
|
238
|
+
get_agent_status_subscription_topic(namespace, agent_name)[:-2] + "/"
|
|
239
|
+
)
|
|
240
|
+
if topic == agent_request_topic:
|
|
241
|
+
await handle_a2a_request(component, message)
|
|
242
|
+
elif topic == discovery_topic:
|
|
243
|
+
payload = message.get_payload()
|
|
244
|
+
if isinstance(payload, dict) and payload.get("name") != agent_name:
|
|
245
|
+
handle_agent_card_message(component, message)
|
|
246
|
+
else:
|
|
247
|
+
message.call_acknowledgements()
|
|
248
|
+
elif topic.startswith(agent_response_sub_prefix) or topic.startswith(
|
|
249
|
+
agent_status_sub_prefix
|
|
250
|
+
):
|
|
251
|
+
handle_a2a_response(component, message)
|
|
252
|
+
else:
|
|
253
|
+
log.warning(
|
|
254
|
+
"%s Received message on unhandled topic: %s",
|
|
255
|
+
component.log_identifier,
|
|
256
|
+
topic,
|
|
257
|
+
)
|
|
258
|
+
message.call_acknowledgements()
|
|
259
|
+
elif event.event_type == EventType.TIMER:
|
|
260
|
+
timer_data = event.data
|
|
261
|
+
log.debug(
|
|
262
|
+
"%s Received timer event: %s", component.log_identifier, timer_data
|
|
263
|
+
)
|
|
264
|
+
if timer_data.get("timer_id") == component._card_publish_timer_id:
|
|
265
|
+
publish_agent_card(component)
|
|
266
|
+
elif event.event_type == EventType.CACHE_EXPIRY:
|
|
267
|
+
cache_data = event.data
|
|
268
|
+
log.info(
|
|
269
|
+
"%s Received cache expiry event: %s",
|
|
270
|
+
component.log_identifier,
|
|
271
|
+
cache_data,
|
|
272
|
+
)
|
|
273
|
+
sub_task_id = cache_data.get("key")
|
|
274
|
+
if sub_task_id and sub_task_id.startswith(
|
|
275
|
+
component.CORRELATION_DATA_PREFIX
|
|
276
|
+
):
|
|
277
|
+
expired_data = cache_data.get("expired_data")
|
|
278
|
+
if expired_data:
|
|
279
|
+
await component._handle_peer_timeout(sub_task_id, expired_data)
|
|
280
|
+
else:
|
|
281
|
+
log.error(
|
|
282
|
+
"%s Missing expired_data in cache expiry event for sub-task %s. Cannot process timeout.",
|
|
283
|
+
component.log_identifier,
|
|
284
|
+
sub_task_id,
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
log.debug(
|
|
288
|
+
"%s Cache expiry for key '%s' is not a peer sub-task timeout.",
|
|
289
|
+
component.log_identifier,
|
|
290
|
+
sub_task_id,
|
|
291
|
+
)
|
|
292
|
+
else:
|
|
293
|
+
log.warning(
|
|
294
|
+
"%s Received unknown event type: %s",
|
|
295
|
+
component.log_identifier,
|
|
296
|
+
event.event_type,
|
|
297
|
+
)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
log.exception(
|
|
300
|
+
"%s Unhandled error in process_event: %s", component.log_identifier, e
|
|
301
|
+
)
|
|
302
|
+
if event.event_type == EventType.MESSAGE:
|
|
303
|
+
try:
|
|
304
|
+
event.data.call_negative_acknowledgements()
|
|
305
|
+
log.warning(
|
|
306
|
+
"%s NACKed message due to error in process_event.",
|
|
307
|
+
component.log_identifier,
|
|
308
|
+
)
|
|
309
|
+
except Exception as nack_e:
|
|
310
|
+
log.error(
|
|
311
|
+
"%s Failed to NACK message after error in process_event: %s",
|
|
312
|
+
component.log_identifier,
|
|
313
|
+
nack_e,
|
|
314
|
+
)
|
|
315
|
+
component.handle_error(e, event)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
async def handle_a2a_request(component, message: SolaceMessage):
|
|
319
|
+
"""
|
|
320
|
+
Handles an incoming A2A request message.
|
|
321
|
+
Starts the ADK runner for SendTask/SendTaskStreaming requests.
|
|
322
|
+
Handles CancelTask requests directly.
|
|
323
|
+
Stores the original SolaceMessage in context for the ADK runner to ACK/NACK.
|
|
324
|
+
"""
|
|
325
|
+
log.info(
|
|
326
|
+
"%s Received new A2A request on topic: %s",
|
|
327
|
+
component.log_identifier,
|
|
328
|
+
message.get_topic(),
|
|
329
|
+
)
|
|
330
|
+
a2a_context = {}
|
|
331
|
+
adk_session = None
|
|
332
|
+
jsonrpc_request_id = None
|
|
333
|
+
logical_task_id = None
|
|
334
|
+
client_id = message.get_user_properties().get("clientId", "default_client")
|
|
335
|
+
status_topic_from_peer = message.get_user_properties().get("a2aStatusTopic")
|
|
336
|
+
reply_topic_from_peer = message.get_user_properties().get("replyTo")
|
|
337
|
+
namespace = component.get_config("namespace")
|
|
338
|
+
a2a_user_config = message.get_user_properties().get("a2aUserConfig", {})
|
|
339
|
+
if not isinstance(a2a_user_config, dict):
|
|
340
|
+
log.warning(
|
|
341
|
+
"%s 'a2aUserConfig' user property is not a dictionary, received: %s. Defaulting to empty dict.",
|
|
342
|
+
component.log_identifier,
|
|
343
|
+
type(a2a_user_config),
|
|
344
|
+
)
|
|
345
|
+
a2a_user_config = {}
|
|
346
|
+
log.debug(
|
|
347
|
+
"%s Extracted 'a2aUserConfig': %s",
|
|
348
|
+
component.log_identifier,
|
|
349
|
+
a2a_user_config,
|
|
350
|
+
)
|
|
351
|
+
try:
|
|
352
|
+
payload_dict = message.get_payload()
|
|
353
|
+
if not isinstance(payload_dict, dict):
|
|
354
|
+
raise ValueError("Payload is not a dictionary.")
|
|
355
|
+
jsonrpc_request_id = payload_dict.get("id")
|
|
356
|
+
a2a_request: Union[
|
|
357
|
+
SendTaskRequest,
|
|
358
|
+
SendTaskStreamingRequest,
|
|
359
|
+
CancelTaskRequest,
|
|
360
|
+
GetTaskRequest,
|
|
361
|
+
SetTaskPushNotificationRequest,
|
|
362
|
+
GetTaskPushNotificationRequest,
|
|
363
|
+
TaskResubscriptionRequest,
|
|
364
|
+
] = A2ARequest.validate_python(payload_dict)
|
|
365
|
+
jsonrpc_request_id = a2a_request.id
|
|
366
|
+
logical_task_id = a2a_request.params.id
|
|
367
|
+
if isinstance(a2a_request, CancelTaskRequest):
|
|
368
|
+
log.info(
|
|
369
|
+
"%s Received CancelTaskRequest for Task ID: %s.",
|
|
370
|
+
component.log_identifier,
|
|
371
|
+
logical_task_id,
|
|
372
|
+
)
|
|
373
|
+
task_context = None
|
|
374
|
+
with component.active_tasks_lock:
|
|
375
|
+
task_context = component.active_tasks.get(logical_task_id)
|
|
376
|
+
|
|
377
|
+
if task_context:
|
|
378
|
+
task_context.cancel()
|
|
379
|
+
log.info(
|
|
380
|
+
"%s Sent cancellation signal to ADK task %s.",
|
|
381
|
+
component.log_identifier,
|
|
382
|
+
logical_task_id,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
peer_sub_tasks = task_context.peer_sub_tasks
|
|
386
|
+
if peer_sub_tasks:
|
|
387
|
+
for sub_task_info in peer_sub_tasks:
|
|
388
|
+
sub_task_id = sub_task_info.get("sub_task_id")
|
|
389
|
+
target_peer_agent_name = sub_task_info.get("peer_agent_name")
|
|
390
|
+
if sub_task_id and target_peer_agent_name:
|
|
391
|
+
log.info(
|
|
392
|
+
"%s Attempting to cancel peer sub-task %s for agent %s (main task %s).",
|
|
393
|
+
component.log_identifier,
|
|
394
|
+
sub_task_id,
|
|
395
|
+
target_peer_agent_name,
|
|
396
|
+
logical_task_id,
|
|
397
|
+
)
|
|
398
|
+
try:
|
|
399
|
+
peer_cancel_params = TaskIdParams(id=sub_task_id)
|
|
400
|
+
peer_cancel_request = CancelTaskRequest(
|
|
401
|
+
params=peer_cancel_params
|
|
402
|
+
)
|
|
403
|
+
peer_cancel_user_props = {
|
|
404
|
+
"clientId": component.agent_name
|
|
405
|
+
}
|
|
406
|
+
component._publish_a2a_message(
|
|
407
|
+
payload=peer_cancel_request.model_dump(
|
|
408
|
+
exclude_none=True
|
|
409
|
+
),
|
|
410
|
+
topic=component._get_agent_request_topic(
|
|
411
|
+
target_peer_agent_name
|
|
412
|
+
),
|
|
413
|
+
user_properties=peer_cancel_user_props,
|
|
414
|
+
)
|
|
415
|
+
log.info(
|
|
416
|
+
"%s Sent CancelTaskRequest to peer %s for sub-task %s.",
|
|
417
|
+
component.log_identifier,
|
|
418
|
+
target_peer_agent_name,
|
|
419
|
+
sub_task_id,
|
|
420
|
+
)
|
|
421
|
+
except Exception as e_peer_cancel:
|
|
422
|
+
log.error(
|
|
423
|
+
"%s Failed to send CancelTaskRequest to peer %s for sub-task %s: %s",
|
|
424
|
+
component.log_identifier,
|
|
425
|
+
target_peer_agent_name,
|
|
426
|
+
sub_task_id,
|
|
427
|
+
e_peer_cancel,
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
log.warning(
|
|
431
|
+
"%s Peer info for main task %s incomplete, cannot cancel peer task. Info: %s",
|
|
432
|
+
component.log_identifier,
|
|
433
|
+
logical_task_id,
|
|
434
|
+
sub_task_info,
|
|
435
|
+
)
|
|
436
|
+
else:
|
|
437
|
+
log.info(
|
|
438
|
+
"%s No active task found for cancellation (ID: %s) or task already completed. Ignoring signal.",
|
|
439
|
+
component.log_identifier,
|
|
440
|
+
logical_task_id,
|
|
441
|
+
)
|
|
442
|
+
try:
|
|
443
|
+
message.call_acknowledgements()
|
|
444
|
+
log.debug(
|
|
445
|
+
"%s ACKed CancelTaskRequest for Task ID: %s.",
|
|
446
|
+
component.log_identifier,
|
|
447
|
+
logical_task_id,
|
|
448
|
+
)
|
|
449
|
+
except Exception as ack_e:
|
|
450
|
+
log.error(
|
|
451
|
+
"%s Failed to ACK CancelTaskRequest for Task ID %s: %s",
|
|
452
|
+
component.log_identifier,
|
|
453
|
+
logical_task_id,
|
|
454
|
+
ack_e,
|
|
455
|
+
)
|
|
456
|
+
return None
|
|
457
|
+
elif isinstance(a2a_request, (SendTaskRequest, SendTaskStreamingRequest)):
|
|
458
|
+
original_session_id = a2a_request.params.sessionId
|
|
459
|
+
task_id = a2a_request.params.id
|
|
460
|
+
task_metadata = a2a_request.params.metadata or {}
|
|
461
|
+
system_purpose = task_metadata.get("system_purpose")
|
|
462
|
+
response_format = task_metadata.get("response_format")
|
|
463
|
+
session_behavior_from_meta = task_metadata.get("sessionBehavior")
|
|
464
|
+
if session_behavior_from_meta:
|
|
465
|
+
session_behavior = str(session_behavior_from_meta).upper()
|
|
466
|
+
if session_behavior not in ["PERSISTENT", "RUN_BASED"]:
|
|
467
|
+
log.warning(
|
|
468
|
+
"%s Invalid 'sessionBehavior' in task metadata: '%s'. Using component default: '%s'.",
|
|
469
|
+
component.log_identifier,
|
|
470
|
+
session_behavior,
|
|
471
|
+
component.default_session_behavior,
|
|
472
|
+
)
|
|
473
|
+
session_behavior = component.default_session_behavior
|
|
474
|
+
else:
|
|
475
|
+
log.info(
|
|
476
|
+
"%s Using 'sessionBehavior' from task metadata: '%s'.",
|
|
477
|
+
component.log_identifier,
|
|
478
|
+
session_behavior,
|
|
479
|
+
)
|
|
480
|
+
else:
|
|
481
|
+
session_behavior = component.default_session_behavior
|
|
482
|
+
log.info(
|
|
483
|
+
"%s No 'sessionBehavior' in task metadata. Using component default: '%s'.",
|
|
484
|
+
component.log_identifier,
|
|
485
|
+
session_behavior,
|
|
486
|
+
)
|
|
487
|
+
user_id = message.get_user_properties().get("userId", "default_user")
|
|
488
|
+
agent_name = component.get_config("agent_name")
|
|
489
|
+
is_streaming_request = isinstance(a2a_request, SendTaskStreamingRequest)
|
|
490
|
+
host_supports_streaming = component.get_config("supports_streaming", False)
|
|
491
|
+
if is_streaming_request and not host_supports_streaming:
|
|
492
|
+
raise ValueError(
|
|
493
|
+
"Host does not support streaming (tasks/sendSubscribe) requests."
|
|
494
|
+
)
|
|
495
|
+
effective_session_id = original_session_id
|
|
496
|
+
is_run_based_session = False
|
|
497
|
+
temporary_run_session_id_for_cleanup = None
|
|
498
|
+
if session_behavior == "RUN_BASED":
|
|
499
|
+
is_run_based_session = True
|
|
500
|
+
effective_session_id = f"{original_session_id}:{task_id}:run"
|
|
501
|
+
temporary_run_session_id_for_cleanup = effective_session_id
|
|
502
|
+
log.info(
|
|
503
|
+
"%s Session behavior is RUN_BASED. OriginalID='%s', EffectiveID for this run='%s', TaskID='%s'.",
|
|
504
|
+
component.log_identifier,
|
|
505
|
+
original_session_id,
|
|
506
|
+
effective_session_id,
|
|
507
|
+
task_id,
|
|
508
|
+
)
|
|
509
|
+
else:
|
|
510
|
+
is_run_based_session = False
|
|
511
|
+
effective_session_id = original_session_id
|
|
512
|
+
temporary_run_session_id_for_cleanup = None
|
|
513
|
+
log.info(
|
|
514
|
+
"%s Session behavior is PERSISTENT. EffectiveID='%s' for TaskID='%s'.",
|
|
515
|
+
component.log_identifier,
|
|
516
|
+
effective_session_id,
|
|
517
|
+
task_id,
|
|
518
|
+
)
|
|
519
|
+
adk_session_for_run = await component.session_service.get_session(
|
|
520
|
+
app_name=agent_name, user_id=user_id, session_id=effective_session_id
|
|
521
|
+
)
|
|
522
|
+
if adk_session_for_run is None:
|
|
523
|
+
adk_session_for_run = await component.session_service.create_session(
|
|
524
|
+
app_name=agent_name,
|
|
525
|
+
user_id=user_id,
|
|
526
|
+
session_id=effective_session_id,
|
|
527
|
+
)
|
|
528
|
+
log.info(
|
|
529
|
+
"%s Created new ADK session '%s' for task '%s'.",
|
|
530
|
+
component.log_identifier,
|
|
531
|
+
effective_session_id,
|
|
532
|
+
task_id,
|
|
533
|
+
)
|
|
534
|
+
else:
|
|
535
|
+
log.info(
|
|
536
|
+
"%s Reusing existing ADK session '%s' for task '%s'.",
|
|
537
|
+
component.log_identifier,
|
|
538
|
+
effective_session_id,
|
|
539
|
+
task_id,
|
|
540
|
+
)
|
|
541
|
+
if is_run_based_session:
|
|
542
|
+
try:
|
|
543
|
+
original_adk_session_data = (
|
|
544
|
+
await component.session_service.get_session(
|
|
545
|
+
app_name=agent_name,
|
|
546
|
+
user_id=user_id,
|
|
547
|
+
session_id=original_session_id,
|
|
548
|
+
)
|
|
549
|
+
)
|
|
550
|
+
if original_adk_session_data and hasattr(
|
|
551
|
+
original_adk_session_data, "history"
|
|
552
|
+
):
|
|
553
|
+
original_history_events = original_adk_session_data.history
|
|
554
|
+
if original_history_events:
|
|
555
|
+
log.debug(
|
|
556
|
+
"%s Copying %d events from original session '%s' to run-based session '%s'.",
|
|
557
|
+
component.log_identifier,
|
|
558
|
+
len(original_history_events),
|
|
559
|
+
original_session_id,
|
|
560
|
+
effective_session_id,
|
|
561
|
+
)
|
|
562
|
+
run_based_adk_session_for_copy = (
|
|
563
|
+
await component.session_service.create_session(
|
|
564
|
+
app_name=agent_name,
|
|
565
|
+
user_id=user_id,
|
|
566
|
+
session_id=effective_session_id,
|
|
567
|
+
)
|
|
568
|
+
)
|
|
569
|
+
for event_to_copy in original_history_events:
|
|
570
|
+
await component.session_service.append_event(
|
|
571
|
+
session=run_based_adk_session_for_copy,
|
|
572
|
+
event=event_to_copy,
|
|
573
|
+
)
|
|
574
|
+
else:
|
|
575
|
+
log.debug(
|
|
576
|
+
"%s No history to copy from original session '%s' for run-based task '%s'.",
|
|
577
|
+
component.log_identifier,
|
|
578
|
+
original_session_id,
|
|
579
|
+
task_id,
|
|
580
|
+
)
|
|
581
|
+
else:
|
|
582
|
+
log.debug(
|
|
583
|
+
"%s Original session '%s' not found or has no history, cannot copy for run-based task '%s'.",
|
|
584
|
+
component.log_identifier,
|
|
585
|
+
original_session_id,
|
|
586
|
+
task_id,
|
|
587
|
+
)
|
|
588
|
+
except Exception as e_copy:
|
|
589
|
+
log.error(
|
|
590
|
+
"%s Error copying history for run-based session '%s' (task '%s'): %s. Proceeding with empty session.",
|
|
591
|
+
component.log_identifier,
|
|
592
|
+
effective_session_id,
|
|
593
|
+
task_id,
|
|
594
|
+
e_copy,
|
|
595
|
+
)
|
|
596
|
+
a2a_context = {
|
|
597
|
+
"jsonrpc_request_id": jsonrpc_request_id,
|
|
598
|
+
"logical_task_id": logical_task_id,
|
|
599
|
+
"session_id": original_session_id,
|
|
600
|
+
"user_id": user_id,
|
|
601
|
+
"client_id": client_id,
|
|
602
|
+
"is_streaming": is_streaming_request,
|
|
603
|
+
"statusTopic": status_topic_from_peer,
|
|
604
|
+
"replyToTopic": reply_topic_from_peer,
|
|
605
|
+
"original_solace_message": message,
|
|
606
|
+
"a2a_user_config": a2a_user_config,
|
|
607
|
+
"effective_session_id": effective_session_id,
|
|
608
|
+
"is_run_based_session": is_run_based_session,
|
|
609
|
+
"temporary_run_session_id_for_cleanup": temporary_run_session_id_for_cleanup,
|
|
610
|
+
"agent_name_for_session": (
|
|
611
|
+
agent_name if is_run_based_session else None
|
|
612
|
+
),
|
|
613
|
+
"user_id_for_session": user_id if is_run_based_session else None,
|
|
614
|
+
"system_purpose": system_purpose,
|
|
615
|
+
"response_format": response_format,
|
|
616
|
+
"host_agent_name": agent_name,
|
|
617
|
+
}
|
|
618
|
+
log.debug(
|
|
619
|
+
"%s A2A Context (shared service model): %s",
|
|
620
|
+
component.log_identifier,
|
|
621
|
+
a2a_context,
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
# Create and store the execution context for this task
|
|
625
|
+
task_context = TaskExecutionContext(
|
|
626
|
+
task_id=logical_task_id, a2a_context=a2a_context
|
|
627
|
+
)
|
|
628
|
+
with component.active_tasks_lock:
|
|
629
|
+
component.active_tasks[logical_task_id] = task_context
|
|
630
|
+
log.info(
|
|
631
|
+
"%s Created and stored new TaskExecutionContext for task %s.",
|
|
632
|
+
component.log_identifier,
|
|
633
|
+
logical_task_id,
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
adk_content = translate_a2a_to_adk_content(
|
|
637
|
+
a2a_request.params.message, component.log_identifier
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
adk_session = await component.session_service.get_session(
|
|
641
|
+
app_name=agent_name, user_id=user_id, session_id=effective_session_id
|
|
642
|
+
)
|
|
643
|
+
if adk_session is None:
|
|
644
|
+
log.info(
|
|
645
|
+
"%s ADK session '%s' not found in component.session_service, creating new one.",
|
|
646
|
+
component.log_identifier,
|
|
647
|
+
effective_session_id,
|
|
648
|
+
)
|
|
649
|
+
adk_session = await component.session_service.create_session(
|
|
650
|
+
app_name=agent_name,
|
|
651
|
+
user_id=user_id,
|
|
652
|
+
session_id=effective_session_id,
|
|
653
|
+
)
|
|
654
|
+
else:
|
|
655
|
+
log.info(
|
|
656
|
+
"%s Reusing existing ADK session '%s' from component.session_service.",
|
|
657
|
+
component.log_identifier,
|
|
658
|
+
effective_session_id,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
# Always use SSE streaming mode for the ADK runner.
|
|
662
|
+
# This ensures that real-time callbacks (e.g., for fenced artifact
|
|
663
|
+
# progress) can function correctly for all task types. The component's
|
|
664
|
+
# internal logic uses the 'is_run_based_session' flag to differentiate
|
|
665
|
+
# between aggregating a final response and streaming partial updates.
|
|
666
|
+
streaming_mode = StreamingMode.SSE
|
|
667
|
+
|
|
668
|
+
max_llm_calls_per_task = component.get_config("max_llm_calls_per_task", 20)
|
|
669
|
+
log.info(
|
|
670
|
+
"%s Using max_llm_calls_per_task: %s",
|
|
671
|
+
component.log_identifier,
|
|
672
|
+
max_llm_calls_per_task,
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
run_config = RunConfig(
|
|
676
|
+
streaming_mode=streaming_mode, max_llm_calls=max_llm_calls_per_task
|
|
677
|
+
)
|
|
678
|
+
log.info(
|
|
679
|
+
"%s Setting ADK RunConfig streaming_mode to: %s, max_llm_calls to: %s",
|
|
680
|
+
component.log_identifier,
|
|
681
|
+
streaming_mode,
|
|
682
|
+
max_llm_calls_per_task,
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
log.info(
|
|
686
|
+
"%s Starting ADK runner task for request %s (Task ID: %s)",
|
|
687
|
+
component.log_identifier,
|
|
688
|
+
jsonrpc_request_id,
|
|
689
|
+
logical_task_id,
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
await run_adk_async_task_thread_wrapper(
|
|
693
|
+
component,
|
|
694
|
+
adk_session,
|
|
695
|
+
adk_content,
|
|
696
|
+
run_config,
|
|
697
|
+
a2a_context,
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
log.info(
|
|
701
|
+
"%s ADK task execution awaited for Task ID %s.",
|
|
702
|
+
component.log_identifier,
|
|
703
|
+
logical_task_id,
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
else:
|
|
707
|
+
log.warning(
|
|
708
|
+
"%s Received unhandled A2A request type: %s. Acknowledging.",
|
|
709
|
+
component.log_identifier,
|
|
710
|
+
type(a2a_request).__name__,
|
|
711
|
+
)
|
|
712
|
+
try:
|
|
713
|
+
message.call_acknowledgements()
|
|
714
|
+
except Exception as ack_e:
|
|
715
|
+
log.error(
|
|
716
|
+
"%s Failed to ACK unhandled request type %s: %s",
|
|
717
|
+
component.log_identifier,
|
|
718
|
+
type(a2a_request).__name__,
|
|
719
|
+
ack_e,
|
|
720
|
+
)
|
|
721
|
+
return None
|
|
722
|
+
|
|
723
|
+
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
|
724
|
+
log.error(
|
|
725
|
+
"%s Failed to parse, validate, or start ADK task for A2A request: %s",
|
|
726
|
+
component.log_identifier,
|
|
727
|
+
e,
|
|
728
|
+
)
|
|
729
|
+
error_data = {"taskId": logical_task_id} if logical_task_id else None
|
|
730
|
+
if isinstance(e, ValueError):
|
|
731
|
+
error_response = JSONRPCResponse(
|
|
732
|
+
id=jsonrpc_request_id,
|
|
733
|
+
error=InvalidRequestError(message=str(e), data=error_data),
|
|
734
|
+
)
|
|
735
|
+
else:
|
|
736
|
+
error_response = JSONRPCResponse(
|
|
737
|
+
id=jsonrpc_request_id,
|
|
738
|
+
error=JSONParseError(message=str(e), data=error_data),
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
target_topic = reply_topic_from_peer or (
|
|
742
|
+
get_client_response_topic(namespace, client_id) if client_id else None
|
|
743
|
+
)
|
|
744
|
+
if target_topic:
|
|
745
|
+
component._publish_a2a_message(
|
|
746
|
+
error_response.model_dump(exclude_none=True),
|
|
747
|
+
target_topic,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
try:
|
|
751
|
+
message.call_negative_acknowledgements()
|
|
752
|
+
log.warning(
|
|
753
|
+
"%s NACKed original A2A request due to parsing/validation/start error.",
|
|
754
|
+
component.log_identifier,
|
|
755
|
+
)
|
|
756
|
+
except Exception as nack_e:
|
|
757
|
+
log.error(
|
|
758
|
+
"%s Failed to NACK message after pre-start error: %s",
|
|
759
|
+
component.log_identifier,
|
|
760
|
+
nack_e,
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
component.handle_error(e, Event(EventType.MESSAGE, message))
|
|
764
|
+
return None
|
|
765
|
+
|
|
766
|
+
except Exception as e:
|
|
767
|
+
log.exception(
|
|
768
|
+
"%s Unexpected error handling A2A request: %s", component.log_identifier, e
|
|
769
|
+
)
|
|
770
|
+
error_response = JSONRPCResponse(
|
|
771
|
+
id=jsonrpc_request_id,
|
|
772
|
+
error=InternalError(
|
|
773
|
+
message=f"Unexpected server error: {e}",
|
|
774
|
+
data={"taskId": logical_task_id},
|
|
775
|
+
),
|
|
776
|
+
)
|
|
777
|
+
target_topic = reply_topic_from_peer or (
|
|
778
|
+
get_client_response_topic(namespace, client_id) if client_id else None
|
|
779
|
+
)
|
|
780
|
+
if target_topic:
|
|
781
|
+
component._publish_a2a_message(
|
|
782
|
+
error_response.model_dump(exclude_none=True),
|
|
783
|
+
target_topic,
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
try:
|
|
787
|
+
message.call_negative_acknowledgements()
|
|
788
|
+
log.warning(
|
|
789
|
+
"%s NACKed original A2A request due to unexpected error.",
|
|
790
|
+
component.log_identifier,
|
|
791
|
+
)
|
|
792
|
+
except Exception as nack_e:
|
|
793
|
+
log.error(
|
|
794
|
+
"%s Failed to NACK message after unexpected error: %s",
|
|
795
|
+
component.log_identifier,
|
|
796
|
+
nack_e,
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
component.handle_error(e, Event(EventType.MESSAGE, message))
|
|
800
|
+
return None
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def handle_agent_card_message(component, message: SolaceMessage):
|
|
804
|
+
"""Handles incoming Agent Card messages."""
|
|
805
|
+
try:
|
|
806
|
+
payload = message.get_payload()
|
|
807
|
+
if not isinstance(payload, dict):
|
|
808
|
+
log.warning(
|
|
809
|
+
"%s Received agent card with non-dict payload. Ignoring.",
|
|
810
|
+
component.log_identifier,
|
|
811
|
+
)
|
|
812
|
+
message.call_acknowledgements()
|
|
813
|
+
return
|
|
814
|
+
|
|
815
|
+
agent_card = AgentCard(**payload)
|
|
816
|
+
agent_name = agent_card.name
|
|
817
|
+
self_agent_name = component.get_config("agent_name")
|
|
818
|
+
|
|
819
|
+
if agent_name == self_agent_name:
|
|
820
|
+
message.call_acknowledgements()
|
|
821
|
+
return
|
|
822
|
+
|
|
823
|
+
agent_discovery = component.get_config("agent_discovery", {})
|
|
824
|
+
if agent_discovery.get("enabled", False) is False:
|
|
825
|
+
message.call_acknowledgements()
|
|
826
|
+
return
|
|
827
|
+
|
|
828
|
+
inter_agent_config = component.get_config("inter_agent_communication", {})
|
|
829
|
+
allow_list = inter_agent_config.get("allow_list", ["*"])
|
|
830
|
+
deny_list = inter_agent_config.get("deny_list", [])
|
|
831
|
+
is_allowed = False
|
|
832
|
+
for pattern in allow_list:
|
|
833
|
+
if fnmatch.fnmatch(agent_name, pattern):
|
|
834
|
+
is_allowed = True
|
|
835
|
+
break
|
|
836
|
+
|
|
837
|
+
if is_allowed:
|
|
838
|
+
for pattern in deny_list:
|
|
839
|
+
if fnmatch.fnmatch(agent_name, pattern):
|
|
840
|
+
is_allowed = False
|
|
841
|
+
break
|
|
842
|
+
|
|
843
|
+
if is_allowed:
|
|
844
|
+
agent_card.peer_agents = {}
|
|
845
|
+
component.peer_agents[agent_name] = agent_card
|
|
846
|
+
|
|
847
|
+
message.call_acknowledgements()
|
|
848
|
+
|
|
849
|
+
except Exception as e:
|
|
850
|
+
log.exception(
|
|
851
|
+
"%s Error processing agent card message: %s", component.log_identifier, e
|
|
852
|
+
)
|
|
853
|
+
message.call_acknowledgements()
|
|
854
|
+
component.handle_error(e, Event(EventType.MESSAGE, message))
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def handle_a2a_response(component, message: SolaceMessage):
|
|
858
|
+
"""Handles incoming responses/status updates from peer agents."""
|
|
859
|
+
sub_task_id = None
|
|
860
|
+
agent_name = component.get_config("agent_name")
|
|
861
|
+
payload_to_queue = None
|
|
862
|
+
is_final_response = False
|
|
863
|
+
|
|
864
|
+
try:
|
|
865
|
+
topic = message.get_topic()
|
|
866
|
+
topic_parts = topic.split("/")
|
|
867
|
+
if len(topic_parts) > 0:
|
|
868
|
+
sub_task_id = topic_parts[-1]
|
|
869
|
+
if not sub_task_id.startswith(component.CORRELATION_DATA_PREFIX):
|
|
870
|
+
log.warning(
|
|
871
|
+
"%s Topic %s does not end with expected sub-task ID format. Ignoring.",
|
|
872
|
+
component.log_identifier,
|
|
873
|
+
topic,
|
|
874
|
+
)
|
|
875
|
+
message.call_acknowledgements()
|
|
876
|
+
return
|
|
877
|
+
else:
|
|
878
|
+
log.error(
|
|
879
|
+
"%s Could not extract sub-task ID from topic: %s",
|
|
880
|
+
component.log_identifier,
|
|
881
|
+
topic,
|
|
882
|
+
)
|
|
883
|
+
message.call_negative_acknowledgements()
|
|
884
|
+
return
|
|
885
|
+
|
|
886
|
+
log.debug("%s Extracted sub-task ID: %s", component.log_identifier, sub_task_id)
|
|
887
|
+
|
|
888
|
+
payload_dict = message.get_payload()
|
|
889
|
+
if not isinstance(payload_dict, dict):
|
|
890
|
+
log.error(
|
|
891
|
+
"%s Received non-dict payload for sub-task %s. Payload: %s",
|
|
892
|
+
component.log_identifier,
|
|
893
|
+
sub_task_id,
|
|
894
|
+
payload_dict,
|
|
895
|
+
)
|
|
896
|
+
payload_to_queue = {
|
|
897
|
+
"error": "Received invalid payload format from peer.",
|
|
898
|
+
"code": "PEER_PAYLOAD_ERROR",
|
|
899
|
+
}
|
|
900
|
+
is_final_response = True
|
|
901
|
+
else:
|
|
902
|
+
try:
|
|
903
|
+
a2a_response = JSONRPCResponse(**payload_dict)
|
|
904
|
+
|
|
905
|
+
if a2a_response.result and isinstance(a2a_response.result, dict):
|
|
906
|
+
payload_data = a2a_response.result
|
|
907
|
+
parsed_successfully = False
|
|
908
|
+
is_final_response = False
|
|
909
|
+
payload_to_queue = None
|
|
910
|
+
|
|
911
|
+
if (
|
|
912
|
+
"final" in payload_data
|
|
913
|
+
and "status" in payload_data
|
|
914
|
+
and isinstance(payload_data.get("final"), bool)
|
|
915
|
+
):
|
|
916
|
+
try:
|
|
917
|
+
status_event = TaskStatusUpdateEvent(**payload_data)
|
|
918
|
+
|
|
919
|
+
if (
|
|
920
|
+
status_event.status
|
|
921
|
+
and status_event.status.message
|
|
922
|
+
and status_event.status.message.parts
|
|
923
|
+
):
|
|
924
|
+
for part_from_peer in status_event.status.message.parts:
|
|
925
|
+
if (
|
|
926
|
+
isinstance(part_from_peer, DataPart)
|
|
927
|
+
and part_from_peer.data.get("a2a_signal_type")
|
|
928
|
+
== "agent_status_message"
|
|
929
|
+
):
|
|
930
|
+
log.info(
|
|
931
|
+
"%s Received agent_status_message signal from peer for sub-task %s.",
|
|
932
|
+
component.log_identifier,
|
|
933
|
+
sub_task_id,
|
|
934
|
+
)
|
|
935
|
+
correlation_data = (
|
|
936
|
+
component.cache_service.get_data(
|
|
937
|
+
sub_task_id
|
|
938
|
+
)
|
|
939
|
+
)
|
|
940
|
+
if not correlation_data:
|
|
941
|
+
log.warning(
|
|
942
|
+
"%s Correlation data not found for sub-task %s. Cannot forward status signal.",
|
|
943
|
+
component.log_identifier,
|
|
944
|
+
sub_task_id,
|
|
945
|
+
)
|
|
946
|
+
message.call_acknowledgements()
|
|
947
|
+
return
|
|
948
|
+
|
|
949
|
+
original_task_context = correlation_data.get(
|
|
950
|
+
"original_task_context"
|
|
951
|
+
)
|
|
952
|
+
if not original_task_context:
|
|
953
|
+
log.warning(
|
|
954
|
+
"%s original_task_context not found in correlation data for sub-task %s. Cannot forward status signal.",
|
|
955
|
+
component.log_identifier,
|
|
956
|
+
sub_task_id,
|
|
957
|
+
)
|
|
958
|
+
message.call_acknowledgements()
|
|
959
|
+
return
|
|
960
|
+
|
|
961
|
+
main_logical_task_id = (
|
|
962
|
+
original_task_context.get("logical_task_id")
|
|
963
|
+
)
|
|
964
|
+
original_jsonrpc_request_id = (
|
|
965
|
+
original_task_context.get(
|
|
966
|
+
"jsonrpc_request_id"
|
|
967
|
+
)
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
target_topic_for_forward = (
|
|
971
|
+
original_task_context.get("statusTopic")
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
if (
|
|
975
|
+
not main_logical_task_id
|
|
976
|
+
or not original_jsonrpc_request_id
|
|
977
|
+
or not target_topic_for_forward
|
|
978
|
+
):
|
|
979
|
+
log.error(
|
|
980
|
+
"%s Missing critical info (main_task_id, original_rpc_id, or target_status_topic) in context for sub-task %s. Cannot forward. Context: %s",
|
|
981
|
+
component.log_identifier,
|
|
982
|
+
sub_task_id,
|
|
983
|
+
original_task_context,
|
|
984
|
+
)
|
|
985
|
+
message.call_acknowledgements()
|
|
986
|
+
return
|
|
987
|
+
|
|
988
|
+
peer_agent_name = (
|
|
989
|
+
status_event.metadata.get(
|
|
990
|
+
"agent_name", "UnknownPeer"
|
|
991
|
+
)
|
|
992
|
+
if status_event.metadata
|
|
993
|
+
else "UnknownPeer"
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
forwarded_message = A2AMessage(
|
|
997
|
+
role="agent",
|
|
998
|
+
parts=[part_from_peer],
|
|
999
|
+
metadata={
|
|
1000
|
+
"agent_name": component.agent_name,
|
|
1001
|
+
"forwarded_from_peer": peer_agent_name,
|
|
1002
|
+
"original_peer_event_id": status_event.id,
|
|
1003
|
+
"original_peer_event_timestamp": (
|
|
1004
|
+
status_event.status.timestamp.isoformat()
|
|
1005
|
+
if status_event.status
|
|
1006
|
+
and status_event.status.timestamp
|
|
1007
|
+
else None
|
|
1008
|
+
),
|
|
1009
|
+
"function_call_id": correlation_data.get(
|
|
1010
|
+
"adk_function_call_id", None
|
|
1011
|
+
),
|
|
1012
|
+
},
|
|
1013
|
+
)
|
|
1014
|
+
forwarded_status = TaskStatus(
|
|
1015
|
+
state=TaskState.WORKING,
|
|
1016
|
+
message=forwarded_message,
|
|
1017
|
+
timestamp=status_event.status.timestamp,
|
|
1018
|
+
)
|
|
1019
|
+
forwarded_event = TaskStatusUpdateEvent(
|
|
1020
|
+
id=main_logical_task_id,
|
|
1021
|
+
status=forwarded_status,
|
|
1022
|
+
final=False,
|
|
1023
|
+
)
|
|
1024
|
+
forwarded_rpc_response = JSONRPCResponse(
|
|
1025
|
+
id=original_jsonrpc_request_id,
|
|
1026
|
+
result=forwarded_event,
|
|
1027
|
+
)
|
|
1028
|
+
payload_to_publish = (
|
|
1029
|
+
forwarded_rpc_response.model_dump(
|
|
1030
|
+
exclude_none=True
|
|
1031
|
+
)
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
try:
|
|
1035
|
+
component._publish_a2a_message(
|
|
1036
|
+
payload_to_publish,
|
|
1037
|
+
target_topic_for_forward,
|
|
1038
|
+
)
|
|
1039
|
+
log.info(
|
|
1040
|
+
"%s Forwarded agent_status_message signal for main task %s (from peer %s) to %s.",
|
|
1041
|
+
component.log_identifier,
|
|
1042
|
+
main_logical_task_id,
|
|
1043
|
+
peer_agent_name,
|
|
1044
|
+
target_topic_for_forward,
|
|
1045
|
+
)
|
|
1046
|
+
except Exception as pub_err:
|
|
1047
|
+
log.exception(
|
|
1048
|
+
"%s Failed to publish forwarded status signal for main task %s: %s",
|
|
1049
|
+
component.log_identifier,
|
|
1050
|
+
main_logical_task_id,
|
|
1051
|
+
pub_err,
|
|
1052
|
+
)
|
|
1053
|
+
message.call_acknowledgements()
|
|
1054
|
+
return
|
|
1055
|
+
|
|
1056
|
+
payload_to_queue = status_event.model_dump(
|
|
1057
|
+
exclude_none=True
|
|
1058
|
+
)
|
|
1059
|
+
if status_event.final:
|
|
1060
|
+
log.debug(
|
|
1061
|
+
"%s Parsed TaskStatusUpdateEvent(final=True) from peer for sub-task %s. This is an intermediate update for PeerAgentTool.",
|
|
1062
|
+
component.log_identifier,
|
|
1063
|
+
sub_task_id,
|
|
1064
|
+
)
|
|
1065
|
+
|
|
1066
|
+
if (
|
|
1067
|
+
status_event.status
|
|
1068
|
+
and status_event.status.message
|
|
1069
|
+
and status_event.status.message.parts
|
|
1070
|
+
):
|
|
1071
|
+
response_parts_data = []
|
|
1072
|
+
for part in status_event.status.message.parts:
|
|
1073
|
+
if (
|
|
1074
|
+
hasattr(part, "text")
|
|
1075
|
+
and part.text is not None
|
|
1076
|
+
):
|
|
1077
|
+
response_parts_data.append(str(part.text))
|
|
1078
|
+
elif (
|
|
1079
|
+
hasattr(part, "data")
|
|
1080
|
+
and part.data is not None
|
|
1081
|
+
):
|
|
1082
|
+
try:
|
|
1083
|
+
response_parts_data.append(
|
|
1084
|
+
json.dumps(part.data)
|
|
1085
|
+
)
|
|
1086
|
+
except TypeError:
|
|
1087
|
+
response_parts_data.append(
|
|
1088
|
+
str(part.data)
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
payload_to_queue = {
|
|
1092
|
+
"result": "\n".join(response_parts_data)
|
|
1093
|
+
}
|
|
1094
|
+
log.debug(
|
|
1095
|
+
"%s Extracted content for TaskStatusUpdateEvent(final=True) for sub-task %s: %s",
|
|
1096
|
+
component.log_identifier,
|
|
1097
|
+
sub_task_id,
|
|
1098
|
+
payload_to_queue,
|
|
1099
|
+
)
|
|
1100
|
+
else:
|
|
1101
|
+
log.debug(
|
|
1102
|
+
"%s TaskStatusUpdateEvent(final=True) for sub-task %s has no message parts to extract. Sending event object.",
|
|
1103
|
+
component.log_identifier,
|
|
1104
|
+
sub_task_id,
|
|
1105
|
+
)
|
|
1106
|
+
else:
|
|
1107
|
+
log.debug(
|
|
1108
|
+
"%s Parsed TaskStatusUpdateEvent(final=False) from peer for sub-task %s. This is an intermediate update.",
|
|
1109
|
+
component.log_identifier,
|
|
1110
|
+
sub_task_id,
|
|
1111
|
+
)
|
|
1112
|
+
parsed_successfully = True
|
|
1113
|
+
except Exception as e:
|
|
1114
|
+
log.warning(
|
|
1115
|
+
"%s Failed to parse payload as TaskStatusUpdateEvent for sub-task %s. Payload: %s. Error: %s",
|
|
1116
|
+
component.log_identifier,
|
|
1117
|
+
sub_task_id,
|
|
1118
|
+
payload_data,
|
|
1119
|
+
e,
|
|
1120
|
+
)
|
|
1121
|
+
payload_to_queue = None
|
|
1122
|
+
|
|
1123
|
+
if (
|
|
1124
|
+
not parsed_successfully
|
|
1125
|
+
and "artifact" in payload_data
|
|
1126
|
+
and isinstance(payload_data.get("artifact"), dict)
|
|
1127
|
+
):
|
|
1128
|
+
try:
|
|
1129
|
+
artifact_event = TaskArtifactUpdateEvent(**payload_data)
|
|
1130
|
+
payload_to_queue = artifact_event.model_dump(
|
|
1131
|
+
exclude_none=True
|
|
1132
|
+
)
|
|
1133
|
+
is_final_response = False
|
|
1134
|
+
log.debug(
|
|
1135
|
+
"%s Parsed TaskArtifactUpdateEvent from peer for sub-task %s. This is an intermediate update.",
|
|
1136
|
+
component.log_identifier,
|
|
1137
|
+
sub_task_id,
|
|
1138
|
+
)
|
|
1139
|
+
parsed_successfully = True
|
|
1140
|
+
except Exception as e:
|
|
1141
|
+
log.warning(
|
|
1142
|
+
"%s Failed to parse payload as TaskArtifactUpdateEvent for sub-task %s. Payload: %s. Error: %s",
|
|
1143
|
+
component.log_identifier,
|
|
1144
|
+
sub_task_id,
|
|
1145
|
+
payload_data,
|
|
1146
|
+
e,
|
|
1147
|
+
)
|
|
1148
|
+
payload_to_queue = None
|
|
1149
|
+
|
|
1150
|
+
if not parsed_successfully:
|
|
1151
|
+
try:
|
|
1152
|
+
final_task = Task(**payload_data)
|
|
1153
|
+
payload_to_queue = final_task.model_dump(exclude_none=True)
|
|
1154
|
+
is_final_response = True
|
|
1155
|
+
log.debug(
|
|
1156
|
+
"%s Parsed final Task object from peer for sub-task %s.",
|
|
1157
|
+
component.log_identifier,
|
|
1158
|
+
sub_task_id,
|
|
1159
|
+
)
|
|
1160
|
+
parsed_successfully = True
|
|
1161
|
+
except Exception as task_parse_error:
|
|
1162
|
+
log.error(
|
|
1163
|
+
"%s Failed to parse peer response for sub-task %s as any known type. Payload: %s. Error: %s",
|
|
1164
|
+
component.log_identifier,
|
|
1165
|
+
sub_task_id,
|
|
1166
|
+
payload_data,
|
|
1167
|
+
task_parse_error,
|
|
1168
|
+
)
|
|
1169
|
+
if not a2a_response.error:
|
|
1170
|
+
a2a_response.error = InternalError(
|
|
1171
|
+
message=f"Failed to parse response from peer agent for sub-task {sub_task_id}",
|
|
1172
|
+
data={
|
|
1173
|
+
"original_payload": payload_data,
|
|
1174
|
+
"error": str(task_parse_error),
|
|
1175
|
+
},
|
|
1176
|
+
)
|
|
1177
|
+
payload_to_queue = None
|
|
1178
|
+
is_final_response = True
|
|
1179
|
+
|
|
1180
|
+
if (
|
|
1181
|
+
not parsed_successfully
|
|
1182
|
+
and not a2a_response.error
|
|
1183
|
+
and payload_to_queue is None
|
|
1184
|
+
):
|
|
1185
|
+
log.error(
|
|
1186
|
+
"%s Unhandled payload structure from peer for sub-task %s: %s.",
|
|
1187
|
+
component.log_identifier,
|
|
1188
|
+
sub_task_id,
|
|
1189
|
+
payload_data,
|
|
1190
|
+
)
|
|
1191
|
+
a2a_response.error = InternalError(
|
|
1192
|
+
message=f"Unknown response structure from peer agent for sub-task {sub_task_id}",
|
|
1193
|
+
data={"original_payload": payload_data},
|
|
1194
|
+
)
|
|
1195
|
+
is_final_response = True
|
|
1196
|
+
|
|
1197
|
+
elif a2a_response.error:
|
|
1198
|
+
log.warning(
|
|
1199
|
+
"%s Received error response from peer for sub-task %s: %s",
|
|
1200
|
+
component.log_identifier,
|
|
1201
|
+
sub_task_id,
|
|
1202
|
+
a2a_response.error,
|
|
1203
|
+
)
|
|
1204
|
+
payload_to_queue = {
|
|
1205
|
+
"error": a2a_response.error.message,
|
|
1206
|
+
"code": a2a_response.error.code,
|
|
1207
|
+
"data": a2a_response.error.data,
|
|
1208
|
+
}
|
|
1209
|
+
is_final_response = True
|
|
1210
|
+
else:
|
|
1211
|
+
log.warning(
|
|
1212
|
+
"%s Received JSONRPCResponse with no result or error for sub-task %s.",
|
|
1213
|
+
component.log_identifier,
|
|
1214
|
+
sub_task_id,
|
|
1215
|
+
)
|
|
1216
|
+
payload_to_queue = {"result": "Peer responded with empty message."}
|
|
1217
|
+
is_final_response = True
|
|
1218
|
+
|
|
1219
|
+
except Exception as parse_error:
|
|
1220
|
+
log.error(
|
|
1221
|
+
"%s Failed to parse A2A response payload for sub-task %s: %s",
|
|
1222
|
+
component.log_identifier,
|
|
1223
|
+
sub_task_id,
|
|
1224
|
+
parse_error,
|
|
1225
|
+
)
|
|
1226
|
+
payload_to_queue = {
|
|
1227
|
+
"error": f"Failed to parse response from peer: {parse_error}",
|
|
1228
|
+
"code": "PEER_PARSE_ERROR",
|
|
1229
|
+
}
|
|
1230
|
+
is_final_response = True
|
|
1231
|
+
|
|
1232
|
+
if not is_final_response:
|
|
1233
|
+
# This is an intermediate status update for monitoring.
|
|
1234
|
+
# Log it, acknowledge it, but do not aggregate its content.
|
|
1235
|
+
log.debug(
|
|
1236
|
+
"%s Received and ignored intermediate status update from peer for sub-task %s.",
|
|
1237
|
+
component.log_identifier,
|
|
1238
|
+
sub_task_id,
|
|
1239
|
+
)
|
|
1240
|
+
message.call_acknowledgements()
|
|
1241
|
+
return
|
|
1242
|
+
|
|
1243
|
+
correlation_data = component.cache_service.get_data(sub_task_id)
|
|
1244
|
+
if not correlation_data:
|
|
1245
|
+
log.warning(
|
|
1246
|
+
"%s No correlation data found for sub-task %s. Cannot process response. Ignoring.",
|
|
1247
|
+
component.log_identifier,
|
|
1248
|
+
sub_task_id,
|
|
1249
|
+
)
|
|
1250
|
+
message.call_acknowledgements()
|
|
1251
|
+
return
|
|
1252
|
+
|
|
1253
|
+
async def _handle_final_peer_response():
|
|
1254
|
+
"""
|
|
1255
|
+
Handles a final peer response by updating the completion counter and,
|
|
1256
|
+
if all peer tasks are complete, calling the re-trigger logic.
|
|
1257
|
+
"""
|
|
1258
|
+
logical_task_id = correlation_data.get("logical_task_id")
|
|
1259
|
+
invocation_id = correlation_data.get("invocation_id")
|
|
1260
|
+
|
|
1261
|
+
if not logical_task_id or not invocation_id:
|
|
1262
|
+
log.error(
|
|
1263
|
+
"%s 'logical_task_id' or 'invocation_id' not found in correlation data for sub-task %s. Cannot proceed.",
|
|
1264
|
+
component.log_identifier,
|
|
1265
|
+
sub_task_id,
|
|
1266
|
+
)
|
|
1267
|
+
return
|
|
1268
|
+
|
|
1269
|
+
log_retrigger = (
|
|
1270
|
+
f"{component.log_identifier}[RetriggerManager:{logical_task_id}]"
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
with component.active_tasks_lock:
|
|
1274
|
+
task_context = component.active_tasks.get(logical_task_id)
|
|
1275
|
+
|
|
1276
|
+
if not task_context:
|
|
1277
|
+
log.error(
|
|
1278
|
+
"%s TaskExecutionContext not found for task %s. Cannot process final peer response.",
|
|
1279
|
+
log_retrigger,
|
|
1280
|
+
logical_task_id,
|
|
1281
|
+
)
|
|
1282
|
+
return
|
|
1283
|
+
|
|
1284
|
+
final_text = ""
|
|
1285
|
+
artifact_summary = ""
|
|
1286
|
+
if isinstance(payload_to_queue, dict):
|
|
1287
|
+
if "result" in payload_to_queue:
|
|
1288
|
+
final_text = payload_to_queue["result"]
|
|
1289
|
+
elif "error" in payload_to_queue:
|
|
1290
|
+
final_text = (
|
|
1291
|
+
f"Peer agent returned an error: {payload_to_queue['error']}"
|
|
1292
|
+
)
|
|
1293
|
+
elif "status" in payload_to_queue: # It's a Task object
|
|
1294
|
+
try:
|
|
1295
|
+
task_obj = Task(**payload_to_queue)
|
|
1296
|
+
if task_obj.status and task_obj.status.message:
|
|
1297
|
+
final_text = _extract_text_from_parts(
|
|
1298
|
+
task_obj.status.message.parts
|
|
1299
|
+
)
|
|
1300
|
+
|
|
1301
|
+
if (
|
|
1302
|
+
task_obj.metadata
|
|
1303
|
+
and "produced_artifacts" in task_obj.metadata
|
|
1304
|
+
):
|
|
1305
|
+
produced_artifacts = task_obj.metadata.get(
|
|
1306
|
+
"produced_artifacts", []
|
|
1307
|
+
)
|
|
1308
|
+
if produced_artifacts:
|
|
1309
|
+
peer_agent_name = task_obj.metadata.get(
|
|
1310
|
+
"agent_name", "A peer agent"
|
|
1311
|
+
)
|
|
1312
|
+
artifact_summary = (
|
|
1313
|
+
await _format_artifact_summary_from_manifest(
|
|
1314
|
+
component,
|
|
1315
|
+
produced_artifacts,
|
|
1316
|
+
peer_agent_name,
|
|
1317
|
+
correlation_data,
|
|
1318
|
+
)
|
|
1319
|
+
)
|
|
1320
|
+
# Bubble up the peer's artifacts to the parent context
|
|
1321
|
+
_register_peer_artifacts_in_parent_context(
|
|
1322
|
+
task_context, task_obj, log_retrigger
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
except Exception:
|
|
1326
|
+
final_text = json.dumps(payload_to_queue)
|
|
1327
|
+
else:
|
|
1328
|
+
final_text = json.dumps(payload_to_queue)
|
|
1329
|
+
elif isinstance(payload_to_queue, str):
|
|
1330
|
+
final_text = payload_to_queue
|
|
1331
|
+
else:
|
|
1332
|
+
final_text = str(payload_to_queue)
|
|
1333
|
+
|
|
1334
|
+
full_response_text = final_text
|
|
1335
|
+
if artifact_summary:
|
|
1336
|
+
full_response_text = f"{artifact_summary}\n\n{full_response_text}"
|
|
1337
|
+
|
|
1338
|
+
current_result = {
|
|
1339
|
+
"adk_function_call_id": correlation_data.get("adk_function_call_id"),
|
|
1340
|
+
"peer_tool_name": correlation_data.get("peer_tool_name"),
|
|
1341
|
+
"payload": {"result": full_response_text},
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
all_sub_tasks_completed = task_context.record_parallel_result(
|
|
1345
|
+
current_result, invocation_id
|
|
1346
|
+
)
|
|
1347
|
+
log.info(
|
|
1348
|
+
"%s Updated parallel counter for task %s: %s",
|
|
1349
|
+
log_retrigger,
|
|
1350
|
+
logical_task_id,
|
|
1351
|
+
task_context.parallel_tool_calls.get(invocation_id),
|
|
1352
|
+
)
|
|
1353
|
+
|
|
1354
|
+
if not all_sub_tasks_completed:
|
|
1355
|
+
log.info(
|
|
1356
|
+
"%s Waiting for more peer responses for task %s.",
|
|
1357
|
+
log_retrigger,
|
|
1358
|
+
logical_task_id,
|
|
1359
|
+
)
|
|
1360
|
+
return
|
|
1361
|
+
|
|
1362
|
+
log.info(
|
|
1363
|
+
"%s All peer responses received for task %s. Retriggering agent.",
|
|
1364
|
+
log_retrigger,
|
|
1365
|
+
logical_task_id,
|
|
1366
|
+
)
|
|
1367
|
+
results_to_inject = task_context.parallel_tool_calls.get(
|
|
1368
|
+
invocation_id, {}
|
|
1369
|
+
).get("results", [])
|
|
1370
|
+
|
|
1371
|
+
await component._retrigger_agent_with_peer_responses(
|
|
1372
|
+
results_to_inject, correlation_data, task_context
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
loop = component.get_async_loop()
|
|
1376
|
+
if loop and loop.is_running():
|
|
1377
|
+
asyncio.run_coroutine_threadsafe(_handle_final_peer_response(), loop)
|
|
1378
|
+
else:
|
|
1379
|
+
log.error(
|
|
1380
|
+
"%s Async loop not available. Cannot handle final peer response for sub-task %s.",
|
|
1381
|
+
component.log_identifier,
|
|
1382
|
+
sub_task_id,
|
|
1383
|
+
)
|
|
1384
|
+
|
|
1385
|
+
message.call_acknowledgements()
|
|
1386
|
+
log.info(
|
|
1387
|
+
"%s Acknowledged final peer response message for sub-task %s.",
|
|
1388
|
+
component.log_identifier,
|
|
1389
|
+
sub_task_id,
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
except Exception as e:
|
|
1393
|
+
log.exception(
|
|
1394
|
+
"%s Unexpected error handling A2A response for sub-task %s: %s",
|
|
1395
|
+
component.log_identifier,
|
|
1396
|
+
sub_task_id,
|
|
1397
|
+
e,
|
|
1398
|
+
)
|
|
1399
|
+
try:
|
|
1400
|
+
message.call_negative_acknowledgements()
|
|
1401
|
+
log.warning(
|
|
1402
|
+
"%s NACKed peer response message for sub-task %s due to unexpected error.",
|
|
1403
|
+
component.log_identifier,
|
|
1404
|
+
sub_task_id,
|
|
1405
|
+
)
|
|
1406
|
+
except Exception as nack_e:
|
|
1407
|
+
log.error(
|
|
1408
|
+
"%s Failed to NACK peer response message for sub-task %s after error: %s",
|
|
1409
|
+
component.log_identifier,
|
|
1410
|
+
sub_task_id,
|
|
1411
|
+
nack_e,
|
|
1412
|
+
)
|
|
1413
|
+
component.handle_error(e, Event(EventType.MESSAGE, message))
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def publish_agent_card(component):
|
|
1417
|
+
"""Publishes the agent's card to the discovery topic."""
|
|
1418
|
+
try:
|
|
1419
|
+
card_config = component.get_config("agent_card", {})
|
|
1420
|
+
agent_name = component.get_config("agent_name")
|
|
1421
|
+
display_name = component.get_config("display_name")
|
|
1422
|
+
namespace = component.get_config("namespace")
|
|
1423
|
+
supports_streaming = component.get_config("supports_streaming", False)
|
|
1424
|
+
peer_agents = component.peer_agents
|
|
1425
|
+
|
|
1426
|
+
agent_request_topic = get_agent_request_topic(namespace, agent_name)
|
|
1427
|
+
dynamic_url = f"solace:{agent_request_topic}"
|
|
1428
|
+
|
|
1429
|
+
capabilities = AgentCapabilities(
|
|
1430
|
+
streaming=supports_streaming,
|
|
1431
|
+
pushNotifications=False,
|
|
1432
|
+
stateTransitionHistory=False,
|
|
1433
|
+
)
|
|
1434
|
+
|
|
1435
|
+
skills = card_config.get("skills", [])
|
|
1436
|
+
dynamic_tools = getattr(component, "agent_card_tool_manifest", [])
|
|
1437
|
+
|
|
1438
|
+
agent_card = AgentCard(
|
|
1439
|
+
name=agent_name,
|
|
1440
|
+
display_name=display_name,
|
|
1441
|
+
version=component.HOST_COMPONENT_VERSION,
|
|
1442
|
+
url=dynamic_url,
|
|
1443
|
+
capabilities=capabilities,
|
|
1444
|
+
description=card_config.get("description", ""),
|
|
1445
|
+
skills=skills,
|
|
1446
|
+
tools=dynamic_tools,
|
|
1447
|
+
defaultInputModes=card_config.get("defaultInputModes", ["text"]),
|
|
1448
|
+
defaultOutputModes=card_config.get("defaultOutputModes", ["text"]),
|
|
1449
|
+
documentationUrl=card_config.get("documentationUrl"),
|
|
1450
|
+
provider=card_config.get("provider"),
|
|
1451
|
+
peer_agents=peer_agents,
|
|
1452
|
+
)
|
|
1453
|
+
|
|
1454
|
+
discovery_topic = get_discovery_topic(namespace)
|
|
1455
|
+
|
|
1456
|
+
component._publish_a2a_message(
|
|
1457
|
+
agent_card.model_dump(exclude_none=True), discovery_topic
|
|
1458
|
+
)
|
|
1459
|
+
log.debug(
|
|
1460
|
+
"%s Successfully published Agent Card to %s",
|
|
1461
|
+
component.log_identifier,
|
|
1462
|
+
discovery_topic,
|
|
1463
|
+
)
|
|
1464
|
+
|
|
1465
|
+
except Exception as e:
|
|
1466
|
+
log.exception(
|
|
1467
|
+
"%s Failed to publish Agent Card: %s", component.log_identifier, e
|
|
1468
|
+
)
|
|
1469
|
+
component.handle_error(e, None)
|