solace-agent-mesh 1.7.1__py3-none-any.whl → 1.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- 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/artifacts/filesystem_artifact_service.py +164 -0
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
- solace_agent_mesh/agent/adk/callbacks.py +752 -127
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +99 -7
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
- solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
- solace_agent_mesh/agent/adk/models/lite_llm.py +34 -16
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
- solace_agent_mesh/agent/adk/runner.py +66 -8
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +41 -1
- solace_agent_mesh/agent/adk/setup.py +220 -32
- solace_agent_mesh/agent/adk/stream_parser.py +229 -40
- solace_agent_mesh/agent/protocol/event_handlers.py +219 -33
- solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
- solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
- solace_agent_mesh/agent/proxies/base/component.py +188 -22
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
- solace_agent_mesh/agent/sac/app.py +37 -12
- solace_agent_mesh/agent/sac/component.py +322 -52
- solace_agent_mesh/agent/sac/patch_adk.py +8 -16
- solace_agent_mesh/agent/sac/task_execution_context.py +90 -0
- solace_agent_mesh/agent/tools/__init__.py +3 -0
- solace_agent_mesh/agent/tools/audio_tools.py +3 -3
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +698 -24
- solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
- solace_agent_mesh/agent/tools/time_tools.py +126 -0
- solace_agent_mesh/agent/tools/tool_config_types.py +54 -2
- solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
- solace_agent_mesh/agent/tools/web_tools.py +125 -17
- solace_agent_mesh/agent/utils/artifact_helpers.py +243 -5
- solace_agent_mesh/agent/utils/context_helpers.py +17 -0
- solace_agent_mesh/assets/docs/404.html +6 -6
- solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
- solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
- solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.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/e04b235d.06d23db6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +75 -75
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
- solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +98 -112
- solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
- solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -28
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -29
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +67 -53
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -17
- 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 +38 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +87 -87
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
- 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 +50 -23
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +29 -24
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +21 -21
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
- 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 +96 -66
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +181 -181
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +75 -75
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +27 -27
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -38
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +135 -114
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +37 -37
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +112 -112
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +28 -28
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
- solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
- solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1768329217460.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add_cmd/__init__.py +3 -1
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
- solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
- solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
- solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
- solace_agent_mesh/cli/commands/run_cmd.py +64 -49
- solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
- solace_agent_mesh/cli/main.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-tcIFZLis.js → authCallback-KnKMP_vb.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CINwxvwV.js → vendor-CGk8Suyh.js} +189 -94
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- 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/a2a/types.py +1 -1
- solace_agent_mesh/common/agent_registry.py +38 -11
- solace_agent_mesh/common/data_parts.py +124 -0
- solace_agent_mesh/common/error_handlers.py +83 -0
- solace_agent_mesh/common/exceptions.py +24 -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/rag_dto.py +156 -0
- solace_agent_mesh/common/sac/sam_component_base.py +73 -1
- solace_agent_mesh/common/sam_events/event_service.py +2 -2
- solace_agent_mesh/common/utils/embeds/converter.py +1 -8
- solace_agent_mesh/common/utils/embeds/modifiers.py +2 -27
- solace_agent_mesh/common/utils/embeds/resolver.py +94 -25
- solace_agent_mesh/common/utils/embeds/types.py +1 -0
- solace_agent_mesh/common/utils/log_formatters.py +20 -0
- solace_agent_mesh/common/utils/mime_helpers.py +12 -5
- 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/config_portal/backend/common.py +12 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/service.py +3 -2
- solace_agent_mesh/gateway/adapter/base.py +28 -1
- solace_agent_mesh/gateway/adapter/types.py +9 -0
- solace_agent_mesh/gateway/base/app.py +10 -0
- solace_agent_mesh/gateway/base/auth_interface.py +103 -0
- solace_agent_mesh/gateway/base/component.py +451 -10
- solace_agent_mesh/gateway/generic/component.py +274 -30
- solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +2 -43
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +2 -2
- 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.ini +0 -36
- solace_agent_mesh/gateway/http_sse/app.py +23 -6
- solace_agent_mesh/gateway/http_sse/component.py +158 -73
- solace_agent_mesh/gateway/http_sse/dependencies.py +50 -57
- solace_agent_mesh/gateway/http_sse/main.py +58 -482
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +3 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +5 -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 +1 -1
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +12 -107
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +113 -7
- solace_agent_mesh/gateway/http_sse/routers/auth.py +69 -132
- solace_agent_mesh/gateway/http_sse/routers/config.py +235 -10
- 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/session_requests.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +1 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +3 -2
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +250 -24
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +14 -5
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +117 -4
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +509 -149
- solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +2 -1
- 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 +1 -1
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/project_service.py +539 -12
- solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +198 -21
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
- solace_agent_mesh/gateway/http_sse/sse_manager.py +280 -169
- solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/platform/__init__.py +29 -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 +154 -0
- solace_agent_mesh/services/platform/api/main.py +314 -0
- solace_agent_mesh/services/platform/api/middleware.py +51 -0
- solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
- solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
- solace_agent_mesh/services/platform/app.py +215 -0
- solace_agent_mesh/services/platform/component.py +777 -0
- solace_agent_mesh/shared/__init__.py +14 -0
- solace_agent_mesh/shared/api/__init__.py +42 -0
- solace_agent_mesh/shared/auth/__init__.py +26 -0
- solace_agent_mesh/shared/auth/dependencies.py +204 -0
- solace_agent_mesh/shared/auth/middleware.py +347 -0
- solace_agent_mesh/shared/database/__init__.py +20 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
- solace_agent_mesh/shared/exceptions/__init__.py +36 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +1 -1
- solace_agent_mesh/shared/utils/__init__.py +21 -0
- solace_agent_mesh/templates/logging_config_template.yaml +48 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
- solace_agent_mesh/templates/platform.yaml +49 -0
- solace_agent_mesh/templates/plugin_readme_template.md +3 -25
- solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
- solace_agent_mesh/templates/proxy_template.yaml +62 -0
- solace_agent_mesh/templates/webui.yaml +148 -6
- solace_agent_mesh/tools/web_search/__init__.py +18 -0
- solace_agent_mesh/tools/web_search/base.py +84 -0
- solace_agent_mesh/tools/web_search/google_search.py +247 -0
- solace_agent_mesh/tools/web_search/models.py +99 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +29 -8
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/RECORD +334 -313
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
- solace_agent_mesh/agent/adk/adk_llm.txt +0 -226
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
- solace_agent_mesh/agent/adk/models/models_llm.txt +0 -189
- solace_agent_mesh/agent/agent_llm.txt +0 -369
- solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
- solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +0 -190
- solace_agent_mesh/agent/proxies/base/base_llm.txt +0 -148
- solace_agent_mesh/agent/proxies/proxies_llm.txt +0 -283
- solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
- solace_agent_mesh/agent/testing/testing_llm.txt +0 -58
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
- solace_agent_mesh/agent/tools/tools_llm.txt +0 -276
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -275
- solace_agent_mesh/agent/utils/utils_llm.txt +0 -152
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
- solace_agent_mesh/assets/docs/assets/js/05749d90.c70b2be9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/240a0364.c39f8388.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.e4870a49.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.b63ee53a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.45b32c2b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.09ed9234.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.f902fad8.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e04b235d.c9c50c7b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.d11c67a7.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.045d0fa1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.f213fe0c.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +0 -1
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +0 -47
- solace_agent_mesh/assets/docs/lunr-index-1762283454666.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1762283454666.json +0 -1
- solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
- solace_agent_mesh/client/webui/frontend/static/assets/client-CRYdKo2Q.js +0 -25
- solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +0 -353
- solace_agent_mesh/common/a2a/a2a_llm.txt +0 -175
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -445
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -330
- solace_agent_mesh/common/common_llm.txt +0 -230
- solace_agent_mesh/common/common_llm_detail.txt +0 -2562
- solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
- solace_agent_mesh/common/sac/sac_llm.txt +0 -71
- solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
- solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
- solace_agent_mesh/common/services/providers/providers_llm.txt +0 -81
- solace_agent_mesh/common/services/services_llm.txt +0 -368
- solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
- solace_agent_mesh/common/utils/utils_llm.txt +0 -335
- solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
- solace_agent_mesh/gateway/base/base_llm.txt +0 -226
- solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
- solace_agent_mesh/gateway/gateway_llm.txt +0 -369
- solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -345
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +0 -92
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -161
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -221
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -257
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -308
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -450
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -133
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -123
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -312
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -303
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -319
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
- solace_agent_mesh/llm.txt +0 -228
- solace_agent_mesh/llm_detail.txt +0 -2835
- solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
- solace_agent_mesh/templates/logging_config_template.ini +0 -45
- solace_agent_mesh/templates/templates_llm.txt +0 -147
- /solace_agent_mesh/assets/docs/assets/js/{main.f213fe0c.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -27,6 +27,8 @@ from a2a.types import (
|
|
|
27
27
|
AgentCard,
|
|
28
28
|
Artifact,
|
|
29
29
|
CancelTaskRequest,
|
|
30
|
+
DataPart,
|
|
31
|
+
InternalError,
|
|
30
32
|
Message,
|
|
31
33
|
SendMessageRequest,
|
|
32
34
|
SendStreamingMessageRequest,
|
|
@@ -44,6 +46,8 @@ from solace_ai_connector.common.log import log
|
|
|
44
46
|
from datetime import datetime, timezone
|
|
45
47
|
|
|
46
48
|
from ....common import a2a
|
|
49
|
+
from ....common.oauth import OAuth2Client, validate_https_url
|
|
50
|
+
from ....common.data_parts import AgentProgressUpdateData
|
|
47
51
|
from ....agent.utils.artifact_helpers import format_artifact_uri
|
|
48
52
|
from ..base.component import BaseProxyComponent
|
|
49
53
|
|
|
@@ -80,11 +84,29 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
80
84
|
# when multiple concurrent requests target the same agent
|
|
81
85
|
self._oauth_token_cache: OAuth2TokenCache = OAuth2TokenCache()
|
|
82
86
|
|
|
87
|
+
# OAuth 2.0 client for protocol operations (no retry for A2A)
|
|
88
|
+
self._oauth_client = OAuth2Client()
|
|
89
|
+
|
|
83
90
|
# Index agent configs by name for O(1) lookup (performance optimization)
|
|
84
91
|
self._agent_config_by_name: Dict[str, Dict[str, Any]] = {
|
|
85
92
|
agent["name"]: agent for agent in self.proxied_agents_config
|
|
86
93
|
}
|
|
87
94
|
|
|
95
|
+
# NEW: OAuth 2.0 authorization code support (enterprise feature)
|
|
96
|
+
# Stores paused tasks waiting for user authorization
|
|
97
|
+
self._paused_a2a_oauth2_tasks: Dict[str, Dict[str, Any]] = {}
|
|
98
|
+
# Caches CredentialManagerWithDiscovery instances per agent
|
|
99
|
+
self._a2a_oauth2_credential_managers: Dict[str, Any] = {}
|
|
100
|
+
|
|
101
|
+
# NEW: Initialize enterprise features for OAuth2 support
|
|
102
|
+
try:
|
|
103
|
+
from solace_agent_mesh_enterprise.init_enterprise_component import (
|
|
104
|
+
init_enterprise_proxy_features
|
|
105
|
+
)
|
|
106
|
+
init_enterprise_proxy_features(self)
|
|
107
|
+
except ImportError:
|
|
108
|
+
pass # Enterprise not installed
|
|
109
|
+
|
|
88
110
|
# OAuth 2.0 configuration is now validated by Pydantic models at app initialization
|
|
89
111
|
# No need for separate _validate_oauth_config() method
|
|
90
112
|
|
|
@@ -100,11 +122,82 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
100
122
|
"""
|
|
101
123
|
return self._agent_config_by_name.get(agent_name)
|
|
102
124
|
|
|
125
|
+
async def _build_headers(
|
|
126
|
+
self,
|
|
127
|
+
agent_name: str,
|
|
128
|
+
agent_config: Dict[str, Any],
|
|
129
|
+
custom_headers_key: str,
|
|
130
|
+
use_auth: bool = True,
|
|
131
|
+
) -> Dict[str, str]:
|
|
132
|
+
"""
|
|
133
|
+
Builds HTTP headers for requests, applying authentication and custom headers.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
agent_name: The name of the agent.
|
|
137
|
+
agent_config: The agent configuration dictionary.
|
|
138
|
+
custom_headers_key: Key to look up custom headers in config ('agent_card_headers' or 'task_headers').
|
|
139
|
+
use_auth: Whether to apply authentication headers.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Dictionary of HTTP headers. Custom headers are applied after auth headers.
|
|
143
|
+
Note: For task invocations, the A2A SDK's AuthInterceptor may further
|
|
144
|
+
modify authentication headers after these are set.
|
|
145
|
+
"""
|
|
146
|
+
headers: Dict[str, str] = {}
|
|
147
|
+
|
|
148
|
+
# Step 1: Add authentication headers if requested
|
|
149
|
+
if use_auth:
|
|
150
|
+
auth_config = agent_config.get("authentication")
|
|
151
|
+
if auth_config:
|
|
152
|
+
auth_type = auth_config.get("type")
|
|
153
|
+
|
|
154
|
+
# Determine auth type (with backward compatibility)
|
|
155
|
+
if not auth_type:
|
|
156
|
+
scheme = auth_config.get("scheme", "bearer")
|
|
157
|
+
auth_type = "static_bearer" if scheme == "bearer" else "static_apikey"
|
|
158
|
+
|
|
159
|
+
# Apply authentication based on type
|
|
160
|
+
if auth_type == "static_bearer":
|
|
161
|
+
token = auth_config.get("token")
|
|
162
|
+
if token:
|
|
163
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
164
|
+
elif auth_type == "static_apikey":
|
|
165
|
+
token = auth_config.get("token")
|
|
166
|
+
if token:
|
|
167
|
+
headers["X-API-Key"] = token
|
|
168
|
+
elif auth_type == "oauth2_client_credentials":
|
|
169
|
+
# Fetch OAuth token
|
|
170
|
+
try:
|
|
171
|
+
access_token = await self._fetch_oauth2_token(agent_name, auth_config)
|
|
172
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
173
|
+
except Exception as e:
|
|
174
|
+
log.error(
|
|
175
|
+
"%s Failed to obtain OAuth 2.0 token for headers: %s",
|
|
176
|
+
self.log_identifier,
|
|
177
|
+
e,
|
|
178
|
+
)
|
|
179
|
+
# Continue without auth header - let the request fail downstream
|
|
180
|
+
|
|
181
|
+
# Step 2: Add custom headers (these override auth headers)
|
|
182
|
+
custom_headers_list = agent_config.get(custom_headers_key)
|
|
183
|
+
if custom_headers_list:
|
|
184
|
+
for header_config in custom_headers_list:
|
|
185
|
+
header_name = header_config.get("name")
|
|
186
|
+
header_value = header_config.get("value")
|
|
187
|
+
if header_name and header_value:
|
|
188
|
+
headers[header_name] = header_value
|
|
189
|
+
|
|
190
|
+
return headers
|
|
191
|
+
|
|
103
192
|
async def _fetch_agent_card(
|
|
104
193
|
self, agent_config: Dict[str, Any]
|
|
105
194
|
) -> Optional[AgentCard]:
|
|
106
195
|
"""
|
|
107
196
|
Fetches the AgentCard from a downstream A2A agent via HTTPS.
|
|
197
|
+
|
|
198
|
+
Applies authentication and custom headers based on configuration:
|
|
199
|
+
- If use_auth_for_agent_card=true, applies the configured authentication
|
|
200
|
+
- Custom agent_card_headers override authentication headers
|
|
108
201
|
"""
|
|
109
202
|
agent_name = agent_config.get("name")
|
|
110
203
|
agent_url = agent_config.get("url")
|
|
@@ -116,8 +209,27 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
116
209
|
return None
|
|
117
210
|
|
|
118
211
|
try:
|
|
212
|
+
# Build headers based on configuration
|
|
213
|
+
use_auth = agent_config.get("use_auth_for_agent_card", False)
|
|
214
|
+
headers = await self._build_headers(
|
|
215
|
+
agent_name=agent_name,
|
|
216
|
+
agent_config=agent_config,
|
|
217
|
+
custom_headers_key="agent_card_headers",
|
|
218
|
+
use_auth=use_auth,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if headers:
|
|
222
|
+
log.debug(
|
|
223
|
+
"%s Fetching agent card with %d custom header(s) (auth=%s)",
|
|
224
|
+
log_identifier,
|
|
225
|
+
len(headers),
|
|
226
|
+
use_auth,
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
log.debug("%s Fetching agent card without authentication", log_identifier)
|
|
230
|
+
|
|
119
231
|
log.info("%s Fetching agent card from %s", log_identifier, agent_url)
|
|
120
|
-
async with httpx.AsyncClient() as client:
|
|
232
|
+
async with httpx.AsyncClient(headers=headers) as client:
|
|
121
233
|
resolver = A2ACardResolver(httpx_client=client, base_url=agent_url)
|
|
122
234
|
agent_card = await resolver.get_agent_card()
|
|
123
235
|
return agent_card
|
|
@@ -152,6 +264,9 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
152
264
|
f"{self.log_identifier}[ForwardRequest:{task_context.task_id}:{agent_name}]"
|
|
153
265
|
)
|
|
154
266
|
|
|
267
|
+
# Store original request for potential resumption (OAuth2 authorization code flow)
|
|
268
|
+
task_context.original_request = request
|
|
269
|
+
|
|
155
270
|
# Step 1: Initialize retry counter
|
|
156
271
|
# Why only retry once: Prevents infinite loops on persistent auth failures.
|
|
157
272
|
# First 401 may be due to token expiration between cache check and request;
|
|
@@ -159,7 +274,57 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
159
274
|
max_auth_retries: int = 1
|
|
160
275
|
auth_retry_count: int = 0
|
|
161
276
|
|
|
162
|
-
# Step 2:
|
|
277
|
+
# Step 2: Check for OAuth2 authorization code flow
|
|
278
|
+
# This auth type requires user interaction and can pause the task,
|
|
279
|
+
# so we check it before attempting normal request flow
|
|
280
|
+
agent_config = self._get_agent_config(agent_name)
|
|
281
|
+
auth_config = agent_config.get("authentication") if agent_config else None
|
|
282
|
+
auth_type = auth_config.get("type") if auth_config else None
|
|
283
|
+
|
|
284
|
+
if auth_type == "oauth2_authorization_code":
|
|
285
|
+
try:
|
|
286
|
+
from solace_agent_mesh_enterprise.auth.a2a import (
|
|
287
|
+
check_authorization_required,
|
|
288
|
+
request_authorization,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Check if user authorization is needed
|
|
292
|
+
needs_auth = await check_authorization_required(
|
|
293
|
+
component=self,
|
|
294
|
+
agent_name=agent_name,
|
|
295
|
+
task_context=task_context,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
if needs_auth:
|
|
299
|
+
# Pause task and request authorization
|
|
300
|
+
log.info(
|
|
301
|
+
"%s User authorization required for agent '%s'. Pausing task.",
|
|
302
|
+
log_identifier,
|
|
303
|
+
agent_name,
|
|
304
|
+
)
|
|
305
|
+
await request_authorization(
|
|
306
|
+
component=self,
|
|
307
|
+
agent_name=agent_name,
|
|
308
|
+
task_context=task_context,
|
|
309
|
+
)
|
|
310
|
+
return # Exit - task paused, will resume after OAuth callback
|
|
311
|
+
|
|
312
|
+
except ImportError:
|
|
313
|
+
log.error(
|
|
314
|
+
"%s Agent '%s' requires OAuth2 authorization code flow, "
|
|
315
|
+
"but solace-agent-mesh-enterprise is not installed.",
|
|
316
|
+
log_identifier,
|
|
317
|
+
agent_name,
|
|
318
|
+
)
|
|
319
|
+
raise ValueError(
|
|
320
|
+
f"Agent '{agent_name}' requires OAuth2 authorization code flow, "
|
|
321
|
+
"but solace-agent-mesh-enterprise is not installed."
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Step 3: Normal request flow for all other auth types
|
|
325
|
+
# (static_bearer, static_apikey, oauth2_client_credentials, or authorized oauth2_authorization_code)
|
|
326
|
+
received_final_task = False
|
|
327
|
+
|
|
163
328
|
while auth_retry_count <= max_auth_retries:
|
|
164
329
|
try:
|
|
165
330
|
# Get or create A2AClient
|
|
@@ -184,6 +349,24 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
184
349
|
# Extract the Message from the request params
|
|
185
350
|
message_to_send = request.params.message
|
|
186
351
|
|
|
352
|
+
# Check if this is a RUN_BASED request by inspecting message metadata
|
|
353
|
+
# For RUN_BASED requests, omit context_id to indicate independent tasks
|
|
354
|
+
if message_to_send.metadata:
|
|
355
|
+
session_behavior = message_to_send.metadata.get("sessionBehavior")
|
|
356
|
+
if session_behavior:
|
|
357
|
+
session_behavior = str(session_behavior).upper()
|
|
358
|
+
if session_behavior == "RUN_BASED" and message_to_send.context_id:
|
|
359
|
+
# For RUN_BASED requests, omit context_id entirely
|
|
360
|
+
# Each request is independent with no logical grouping
|
|
361
|
+
log.debug(
|
|
362
|
+
"%s RUN_BASED request detected. Omitting context_id "
|
|
363
|
+
"(independent task)",
|
|
364
|
+
log_identifier,
|
|
365
|
+
)
|
|
366
|
+
message_to_send = message_to_send.model_copy(
|
|
367
|
+
update={"context_id": None}
|
|
368
|
+
)
|
|
369
|
+
|
|
187
370
|
# WORKAROUND: The A2A SDK has a bug in ClientTaskManager that breaks streaming.
|
|
188
371
|
# For streaming requests, we bypass the Client.send_message() method and call
|
|
189
372
|
# the transport directly to avoid the buggy ClientTaskManager.
|
|
@@ -202,6 +385,10 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
202
385
|
await self._process_downstream_response(
|
|
203
386
|
raw_event, task_context, client, agent_name
|
|
204
387
|
)
|
|
388
|
+
# Check if this is a final task
|
|
389
|
+
if isinstance(raw_event, Task) and raw_event.status:
|
|
390
|
+
if raw_event.status.state in [TaskState.completed, TaskState.failed, TaskState.canceled]:
|
|
391
|
+
received_final_task = True
|
|
205
392
|
else:
|
|
206
393
|
# Non-streaming: use normal client method (works fine)
|
|
207
394
|
log.debug(
|
|
@@ -214,21 +401,51 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
214
401
|
await self._process_downstream_response(
|
|
215
402
|
event, task_context, client, agent_name
|
|
216
403
|
)
|
|
404
|
+
# Check if this is a final task (event is tuple of (Task, Optional[UpdateEvent]))
|
|
405
|
+
if isinstance(event, tuple) and len(event) > 0:
|
|
406
|
+
task = event[0]
|
|
407
|
+
if isinstance(task, Task) and task.status:
|
|
408
|
+
if task.status.state in [TaskState.completed, TaskState.failed, TaskState.canceled]:
|
|
409
|
+
received_final_task = True
|
|
217
410
|
elif isinstance(request, CancelTaskRequest):
|
|
218
|
-
# Forward cancel request to downstream agent
|
|
411
|
+
# Forward cancel request to downstream agent using the downstream task ID
|
|
412
|
+
# The request.params.id contains SAM's task ID, but we need to send
|
|
413
|
+
# the downstream agent's task ID for the cancel to work
|
|
414
|
+
|
|
415
|
+
if not task_context.downstream_task_id:
|
|
416
|
+
log.error(
|
|
417
|
+
"%s Cannot forward cancel request: downstream task ID not yet captured for SAM task %s",
|
|
418
|
+
log_identifier,
|
|
419
|
+
task_context.task_id,
|
|
420
|
+
)
|
|
421
|
+
# Create an error response
|
|
422
|
+
from a2a.types import InvalidRequestError
|
|
423
|
+
error = InvalidRequestError(
|
|
424
|
+
message=f"Cannot cancel task {task_context.task_id}: downstream task ID not available",
|
|
425
|
+
data={"taskId": task_context.task_id}
|
|
426
|
+
)
|
|
427
|
+
# Publish error response
|
|
428
|
+
await self._publish_error_response(error, task_context.a2a_context)
|
|
429
|
+
break
|
|
430
|
+
|
|
219
431
|
log.info(
|
|
220
|
-
"%s Forwarding cancel request for task %s to downstream agent.",
|
|
432
|
+
"%s Forwarding cancel request for task %s (SAM ID: %s, downstream ID: %s) to downstream agent.",
|
|
221
433
|
log_identifier,
|
|
222
|
-
|
|
434
|
+
task_context.downstream_task_id,
|
|
435
|
+
task_context.task_id,
|
|
436
|
+
task_context.downstream_task_id,
|
|
223
437
|
)
|
|
224
|
-
|
|
225
|
-
#
|
|
226
|
-
|
|
438
|
+
|
|
439
|
+
# Create new params with the downstream task ID
|
|
440
|
+
from a2a.types import TaskIdParams
|
|
441
|
+
downstream_params = TaskIdParams(id=task_context.downstream_task_id)
|
|
442
|
+
|
|
443
|
+
# Use the modern client's cancel_task method with the downstream task ID
|
|
227
444
|
result = await client.cancel_task(
|
|
228
|
-
|
|
445
|
+
downstream_params, context=call_context
|
|
229
446
|
)
|
|
230
447
|
# Publish the canceled task response
|
|
231
|
-
await self.
|
|
448
|
+
await self._publish_task_response(result, task_context.a2a_context)
|
|
232
449
|
else:
|
|
233
450
|
log.warning(
|
|
234
451
|
"%s Unhandled request type for forwarding: %s",
|
|
@@ -262,6 +479,22 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
262
479
|
|
|
263
480
|
except A2AClientJSONRPCError as e:
|
|
264
481
|
# Handle JSON-RPC protocol errors
|
|
482
|
+
|
|
483
|
+
# Special case: Task already in terminal state (canceled/completed/failed)
|
|
484
|
+
# This is not a fatal error - the cancellation is effectively a no-op
|
|
485
|
+
if (e.error.code == -32002 and
|
|
486
|
+
"cannot be canceled" in e.error.message.lower() and
|
|
487
|
+
isinstance(request, CancelTaskRequest)):
|
|
488
|
+
log.warning(
|
|
489
|
+
"%s Task %s is already in terminal state: %s. Treating as successful cancellation.",
|
|
490
|
+
log_identifier,
|
|
491
|
+
task_context.downstream_task_id,
|
|
492
|
+
e.error.message,
|
|
493
|
+
)
|
|
494
|
+
# Task is already done - return success (cancellation is effectively complete)
|
|
495
|
+
# We don't need to publish a response because the task already sent its final response
|
|
496
|
+
break
|
|
497
|
+
|
|
265
498
|
log.error(
|
|
266
499
|
"%s JSON-RPC error from agent '%s': %s",
|
|
267
500
|
log_identifier,
|
|
@@ -335,6 +568,20 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
335
568
|
# and publish an error response.
|
|
336
569
|
raise
|
|
337
570
|
|
|
571
|
+
# After retry loop completes - check if we received a final task
|
|
572
|
+
# This detects cases where the stream closes without error but also without final response
|
|
573
|
+
if not received_final_task and isinstance(request, (SendStreamingMessageRequest, SendMessageRequest)):
|
|
574
|
+
from ....common.a2a import create_error_response
|
|
575
|
+
|
|
576
|
+
error_msg = f"Remote agent '{agent_name}' disconnected without completing the task. Agent may have crashed."
|
|
577
|
+
log.error("%s %s", log_identifier, error_msg)
|
|
578
|
+
|
|
579
|
+
error = InternalError(message=error_msg, data={"agent_name": agent_name})
|
|
580
|
+
reply_topic = task_context.a2a_context.get("reply_to_topic")
|
|
581
|
+
if reply_topic:
|
|
582
|
+
response = create_error_response(error=error, request_id=task_context.a2a_context.get("jsonrpc_request_id"))
|
|
583
|
+
self._publish_a2a_message(response.model_dump(exclude_none=True), reply_topic)
|
|
584
|
+
|
|
338
585
|
async def _handle_auth_error(
|
|
339
586
|
self, agent_name: str, task_context: ProxyTaskContext
|
|
340
587
|
) -> bool:
|
|
@@ -486,18 +733,8 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
486
733
|
"'token_url', 'client_id', and 'client_secret'."
|
|
487
734
|
)
|
|
488
735
|
|
|
489
|
-
# SECURITY: Enforce HTTPS for token URL
|
|
490
|
-
|
|
491
|
-
if parsed_url.scheme != "https":
|
|
492
|
-
log.error(
|
|
493
|
-
"%s OAuth 2.0 token_url must use HTTPS for security. Got scheme: '%s'",
|
|
494
|
-
log_identifier,
|
|
495
|
-
parsed_url.scheme,
|
|
496
|
-
)
|
|
497
|
-
raise ValueError(
|
|
498
|
-
f"{log_identifier} OAuth 2.0 token_url must use HTTPS for security. "
|
|
499
|
-
f"Got: {parsed_url.scheme}://"
|
|
500
|
-
)
|
|
736
|
+
# SECURITY: Enforce HTTPS for token URL using common utility
|
|
737
|
+
validate_https_url(token_url)
|
|
501
738
|
|
|
502
739
|
# Step 3: Extract optional parameters
|
|
503
740
|
scope = auth_config.get("scope", "")
|
|
@@ -515,49 +752,32 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
515
752
|
)
|
|
516
753
|
|
|
517
754
|
try:
|
|
518
|
-
# Step 5:
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
"client_secret": client_secret,
|
|
528
|
-
"scope": scope,
|
|
529
|
-
},
|
|
530
|
-
headers={
|
|
531
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
532
|
-
"Accept": "application/json",
|
|
533
|
-
},
|
|
534
|
-
)
|
|
535
|
-
response.raise_for_status()
|
|
536
|
-
|
|
537
|
-
# Step 7: Parse response
|
|
538
|
-
token_response = response.json()
|
|
539
|
-
access_token = token_response.get("access_token")
|
|
755
|
+
# Step 5: Fetch token using common OAuth client (no retry for A2A)
|
|
756
|
+
token_data = await self._oauth_client.fetch_client_credentials_token(
|
|
757
|
+
token_url=token_url,
|
|
758
|
+
client_id=client_id,
|
|
759
|
+
client_secret=client_secret,
|
|
760
|
+
scope=scope,
|
|
761
|
+
verify=True,
|
|
762
|
+
timeout=30.0,
|
|
763
|
+
)
|
|
540
764
|
|
|
541
|
-
|
|
542
|
-
raise ValueError(
|
|
543
|
-
f"{log_identifier} Token response missing 'access_token' field. "
|
|
544
|
-
f"Response keys: {list(token_response.keys())}"
|
|
545
|
-
)
|
|
765
|
+
access_token = token_data["access_token"]
|
|
546
766
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
767
|
+
# Step 6: Cache the token
|
|
768
|
+
await self._oauth_token_cache.set(
|
|
769
|
+
agent_name, access_token, cache_duration
|
|
770
|
+
)
|
|
551
771
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
772
|
+
# Step 7: Log success
|
|
773
|
+
log.info(
|
|
774
|
+
"%s Successfully obtained OAuth 2.0 token (cached for %ds)",
|
|
775
|
+
log_identifier,
|
|
776
|
+
cache_duration,
|
|
777
|
+
)
|
|
558
778
|
|
|
559
|
-
|
|
560
|
-
|
|
779
|
+
# Step 8: Return access token
|
|
780
|
+
return access_token
|
|
561
781
|
|
|
562
782
|
except httpx.HTTPStatusError as e:
|
|
563
783
|
log.error(
|
|
@@ -597,6 +817,7 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
597
817
|
- static_bearer: Static bearer token authentication
|
|
598
818
|
- static_apikey: Static API key authentication
|
|
599
819
|
- oauth2_client_credentials: OAuth 2.0 Client Credentials flow with automatic token refresh
|
|
820
|
+
- oauth2_authorization_code: OAuth 2.0 Authorization Code flow
|
|
600
821
|
|
|
601
822
|
For backward compatibility, legacy configurations without a 'type' field
|
|
602
823
|
will have their type inferred from the 'scheme' field.
|
|
@@ -622,6 +843,20 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
622
843
|
log.error(f"Agent card not found for '{agent_name}' in registry.")
|
|
623
844
|
return None
|
|
624
845
|
|
|
846
|
+
# Check if we should use the configured URL or the agent card URL
|
|
847
|
+
use_agent_card_url = agent_config.get("use_agent_card_url", True)
|
|
848
|
+
if not use_agent_card_url:
|
|
849
|
+
# Override the agent card URL with the configured URL
|
|
850
|
+
configured_url = agent_config.get("url")
|
|
851
|
+
log.info(
|
|
852
|
+
"%s Overriding agent card URL with configured URL for agent '%s': %s",
|
|
853
|
+
self.log_identifier,
|
|
854
|
+
agent_name,
|
|
855
|
+
configured_url,
|
|
856
|
+
)
|
|
857
|
+
# Create a modified copy of the agent card with the configured URL
|
|
858
|
+
agent_card = agent_card.model_copy(update={"url": configured_url})
|
|
859
|
+
|
|
625
860
|
# Resolve timeout - ensure we always have a valid timeout value
|
|
626
861
|
default_timeout = self.get_config("default_request_timeout_seconds", 300)
|
|
627
862
|
agent_timeout = agent_config.get("request_timeout_seconds")
|
|
@@ -629,7 +864,18 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
629
864
|
agent_timeout = default_timeout
|
|
630
865
|
log.info("Using timeout of %ss for agent '%s'.", agent_timeout, agent_name)
|
|
631
866
|
|
|
632
|
-
#
|
|
867
|
+
# Build custom headers for task invocation
|
|
868
|
+
# Note: We build headers here but apply them via the httpx client
|
|
869
|
+
# The A2A SDK's AuthInterceptor will add auth headers via the credential store,
|
|
870
|
+
# but we want custom headers to override those, so we apply them at the client level
|
|
871
|
+
task_headers = await self._build_headers(
|
|
872
|
+
agent_name=agent_name,
|
|
873
|
+
agent_config=agent_config,
|
|
874
|
+
custom_headers_key="task_headers",
|
|
875
|
+
use_auth=False, # Auth will be handled by AuthInterceptor below
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
# Create a new httpx client with the specific timeout and custom headers for this agent
|
|
633
879
|
# httpx.Timeout requires explicit values for connect, read, write, and pool
|
|
634
880
|
httpx_client_for_agent = httpx.AsyncClient(
|
|
635
881
|
timeout=httpx.Timeout(
|
|
@@ -637,9 +883,18 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
637
883
|
read=agent_timeout,
|
|
638
884
|
write=agent_timeout,
|
|
639
885
|
pool=agent_timeout,
|
|
640
|
-
)
|
|
886
|
+
),
|
|
887
|
+
headers=task_headers if task_headers else None,
|
|
641
888
|
)
|
|
642
889
|
|
|
890
|
+
if task_headers:
|
|
891
|
+
log.info(
|
|
892
|
+
"%s Applied %d custom task header(s) for agent '%s'",
|
|
893
|
+
self.log_identifier,
|
|
894
|
+
len(task_headers),
|
|
895
|
+
agent_name,
|
|
896
|
+
)
|
|
897
|
+
|
|
643
898
|
# Setup authentication if configured
|
|
644
899
|
auth_config = agent_config.get("authentication")
|
|
645
900
|
if auth_config:
|
|
@@ -712,10 +967,63 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
712
967
|
)
|
|
713
968
|
raise
|
|
714
969
|
|
|
970
|
+
elif auth_type == "oauth2_authorization_code":
|
|
971
|
+
# NEW: OAuth 2.0 Authorization Code Flow (enterprise feature)
|
|
972
|
+
# At this point, user has already authorized (checked in _forward_request)
|
|
973
|
+
# We just need to get the access token from enterprise helpers
|
|
974
|
+
try:
|
|
975
|
+
from solace_agent_mesh_enterprise.auth.a2a import get_access_token
|
|
976
|
+
|
|
977
|
+
# Get access token (enterprise handles refresh if needed)
|
|
978
|
+
access_token = await get_access_token(
|
|
979
|
+
component=self,
|
|
980
|
+
agent_name=agent_name,
|
|
981
|
+
task_context=task_context,
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
if not access_token:
|
|
985
|
+
raise ValueError(
|
|
986
|
+
f"No OAuth2 credential found for agent '{agent_name}'. "
|
|
987
|
+
"User authorization should have completed in _forward_request()."
|
|
988
|
+
)
|
|
989
|
+
|
|
990
|
+
# Find the OAuth2 authorization code scheme name from agent card
|
|
991
|
+
oauth_scheme_name = None
|
|
992
|
+
if agent_card and agent_card.security_schemes:
|
|
993
|
+
for scheme_name, scheme_wrapper in agent_card.security_schemes.items():
|
|
994
|
+
scheme = scheme_wrapper.root
|
|
995
|
+
if (hasattr(scheme, 'type') and scheme.type.lower() == 'oauth2' and
|
|
996
|
+
hasattr(scheme, 'flows') and scheme.flows and
|
|
997
|
+
scheme.flows.authorization_code):
|
|
998
|
+
oauth_scheme_name = scheme_name
|
|
999
|
+
break
|
|
1000
|
+
|
|
1001
|
+
# Fallback if not found
|
|
1002
|
+
if not oauth_scheme_name:
|
|
1003
|
+
oauth_scheme_name = "oauth2_authorization_code"
|
|
1004
|
+
log.warning(
|
|
1005
|
+
"%s No OAuth2 authorization code scheme found in agent card, using default name",
|
|
1006
|
+
self.log_identifier
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
# Store in credential store for AuthInterceptor
|
|
1010
|
+
await self._credential_store.set_credentials(
|
|
1011
|
+
session_id, oauth_scheme_name, access_token
|
|
1012
|
+
)
|
|
1013
|
+
|
|
1014
|
+
except ImportError:
|
|
1015
|
+
log.error(
|
|
1016
|
+
"%s OAuth2 authorization code requires solace-agent-mesh-enterprise package",
|
|
1017
|
+
self.log_identifier,
|
|
1018
|
+
)
|
|
1019
|
+
raise ValueError(
|
|
1020
|
+
"OAuth2 authorization code requires solace-agent-mesh-enterprise package"
|
|
1021
|
+
)
|
|
1022
|
+
|
|
715
1023
|
else:
|
|
716
1024
|
raise ValueError(
|
|
717
1025
|
f"Unsupported authentication type '{auth_type}' for agent '{agent_name}'. "
|
|
718
|
-
f"Supported types: static_bearer, static_apikey, oauth2_client_credentials."
|
|
1026
|
+
f"Supported types: static_bearer, static_apikey, oauth2_client_credentials, oauth2_authorization_code."
|
|
719
1027
|
)
|
|
720
1028
|
|
|
721
1029
|
# Create ClientConfig for the modern client
|
|
@@ -981,6 +1289,79 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
981
1289
|
type(event_payload).__name__,
|
|
982
1290
|
)
|
|
983
1291
|
|
|
1292
|
+
# Convert TextParts to AgentProgressUpdateData for intermediate status updates if configured
|
|
1293
|
+
# Only convert non-final status updates; final status updates are used to construct the final Task
|
|
1294
|
+
if isinstance(event_payload, TaskStatusUpdateEvent) and not event_payload.final:
|
|
1295
|
+
agent_config = self._get_agent_config(agent_name)
|
|
1296
|
+
convert_progress = agent_config.get("convert_progress_updates", True) if agent_config else True
|
|
1297
|
+
|
|
1298
|
+
# DEBUG: Log config lookup results
|
|
1299
|
+
log.info(
|
|
1300
|
+
"%s DEBUG convert_progress_updates: agent_name='%s', agent_config_name='%s', agent_config_keys=%s, convert_progress_value=%s, convert_progress=%s",
|
|
1301
|
+
log_identifier,
|
|
1302
|
+
agent_name,
|
|
1303
|
+
agent_config.get('name') if agent_config else None,
|
|
1304
|
+
list(agent_config.keys()) if agent_config else None,
|
|
1305
|
+
agent_config.get("convert_progress_updates") if agent_config else None,
|
|
1306
|
+
convert_progress,
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
if convert_progress and event_payload.status and event_payload.status.message:
|
|
1310
|
+
message = event_payload.status.message
|
|
1311
|
+
original_parts = a2a.get_parts_from_message(message)
|
|
1312
|
+
|
|
1313
|
+
if original_parts:
|
|
1314
|
+
converted_parts = []
|
|
1315
|
+
text_parts_converted = 0
|
|
1316
|
+
|
|
1317
|
+
for part in original_parts:
|
|
1318
|
+
if isinstance(part, TextPart) and part.text:
|
|
1319
|
+
# Convert TextPart to DataPart with AgentProgressUpdateData
|
|
1320
|
+
progress_data = AgentProgressUpdateData(
|
|
1321
|
+
type="agent_progress_update",
|
|
1322
|
+
status_text=part.text
|
|
1323
|
+
)
|
|
1324
|
+
data_part = DataPart(
|
|
1325
|
+
kind="data",
|
|
1326
|
+
data=progress_data.model_dump(),
|
|
1327
|
+
metadata=part.metadata
|
|
1328
|
+
)
|
|
1329
|
+
converted_parts.append(data_part)
|
|
1330
|
+
text_parts_converted += 1
|
|
1331
|
+
else:
|
|
1332
|
+
# Keep non-text parts as-is
|
|
1333
|
+
converted_parts.append(part)
|
|
1334
|
+
|
|
1335
|
+
if text_parts_converted > 0:
|
|
1336
|
+
# Update the message with converted parts
|
|
1337
|
+
event_payload.status.message = a2a.update_message_parts(
|
|
1338
|
+
message, converted_parts
|
|
1339
|
+
)
|
|
1340
|
+
log.debug(
|
|
1341
|
+
"%s Converted %d TextPart(s) to AgentProgressUpdateData in status update",
|
|
1342
|
+
log_identifier,
|
|
1343
|
+
text_parts_converted,
|
|
1344
|
+
)
|
|
1345
|
+
|
|
1346
|
+
# Capture the downstream task ID before we replace it
|
|
1347
|
+
# This is needed for forwarding cancellation requests to the downstream agent
|
|
1348
|
+
downstream_id = None
|
|
1349
|
+
if hasattr(event_payload, "task_id") and event_payload.task_id:
|
|
1350
|
+
downstream_id = event_payload.task_id
|
|
1351
|
+
elif hasattr(event_payload, "id") and event_payload.id:
|
|
1352
|
+
downstream_id = event_payload.id
|
|
1353
|
+
|
|
1354
|
+
# Store the downstream task ID in the context if we haven't already
|
|
1355
|
+
if downstream_id and not task_context.downstream_task_id:
|
|
1356
|
+
task_context.downstream_task_id = downstream_id
|
|
1357
|
+
log.debug(
|
|
1358
|
+
"%s Captured downstream task ID: %s (SAM task ID: %s)",
|
|
1359
|
+
log_identifier,
|
|
1360
|
+
downstream_id,
|
|
1361
|
+
task_context.task_id,
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
# Replace the downstream task ID with SAM's task ID for upstream responses
|
|
984
1365
|
original_task_id = task_context.task_id
|
|
985
1366
|
if hasattr(event_payload, "task_id") and event_payload.task_id:
|
|
986
1367
|
event_payload.task_id = original_task_id
|
|
@@ -1031,20 +1412,123 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
1031
1412
|
Part(root=summary_message_part)
|
|
1032
1413
|
)
|
|
1033
1414
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1415
|
+
# Convert text-only TaskArtifactUpdateEvents to TaskStatusUpdateEvents
|
|
1416
|
+
# Some A2A agents send text content as artifacts, which SAM expects as status updates
|
|
1417
|
+
if isinstance(event_payload, TaskArtifactUpdateEvent):
|
|
1418
|
+
artifact = event_payload.artifact
|
|
1419
|
+
if a2a.is_text_only_artifact(artifact):
|
|
1420
|
+
log.info(
|
|
1421
|
+
"%s Converting text-only artifact to status update",
|
|
1422
|
+
log_identifier,
|
|
1423
|
+
)
|
|
1424
|
+
# Extract text from text-only artifact
|
|
1425
|
+
text_content = "\n".join(a2a.get_text_content_from_artifact(artifact))
|
|
1426
|
+
|
|
1427
|
+
# Convert to status update
|
|
1428
|
+
text_message = a2a.create_agent_text_message(
|
|
1429
|
+
text=text_content,
|
|
1430
|
+
task_id=event_payload.task_id,
|
|
1431
|
+
context_id=event_payload.context_id,
|
|
1432
|
+
)
|
|
1433
|
+
|
|
1434
|
+
status_event = TaskStatusUpdateEvent(
|
|
1435
|
+
task_id=event_payload.task_id,
|
|
1436
|
+
context_id=event_payload.context_id,
|
|
1437
|
+
kind="status-update",
|
|
1438
|
+
status=TaskStatus(state=TaskState.working, message=text_message),
|
|
1439
|
+
final=False,
|
|
1440
|
+
metadata=event_payload.metadata,
|
|
1038
1441
|
)
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1442
|
+
|
|
1443
|
+
# Replace event_payload with the converted status update
|
|
1444
|
+
event_payload = status_event
|
|
1445
|
+
log.info(
|
|
1446
|
+
"%s Converted text-only artifact (length: %d bytes) to status update",
|
|
1447
|
+
log_identifier,
|
|
1448
|
+
len(text_content.encode("utf-8")),
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
# Determine if this is a terminal event requiring cleanup
|
|
1452
|
+
should_cleanup_task = False
|
|
1453
|
+
|
|
1454
|
+
# Route based on event type
|
|
1455
|
+
if isinstance(event_payload, Task):
|
|
1456
|
+
# Discard initial Task events (non-completed states)
|
|
1457
|
+
# The final Task will be constructed from the final status update
|
|
1458
|
+
if event_payload.status.state != TaskState.completed:
|
|
1459
|
+
log.debug(
|
|
1460
|
+
"%s Discarding Task event with state=%s (not completed). Final Task will be constructed from final status update.",
|
|
1461
|
+
log_identifier,
|
|
1462
|
+
event_payload.status.state,
|
|
1463
|
+
)
|
|
1464
|
+
# Don't publish, don't cleanup - wait for final status update
|
|
1465
|
+
return
|
|
1466
|
+
|
|
1467
|
+
# Forward completed Task to reply topic
|
|
1468
|
+
await self._publish_task_response(event_payload, task_context.a2a_context)
|
|
1469
|
+
|
|
1470
|
+
# Completed Task is terminal - cleanup
|
|
1471
|
+
should_cleanup_task = True
|
|
1472
|
+
log.debug(
|
|
1473
|
+
"%s Task in terminal state: %s",
|
|
1474
|
+
log_identifier,
|
|
1475
|
+
event_payload.status.state,
|
|
1476
|
+
)
|
|
1477
|
+
|
|
1478
|
+
elif isinstance(event_payload, TaskStatusUpdateEvent):
|
|
1479
|
+
# Forward status update to status topic
|
|
1480
|
+
await self._publish_status_update(event_payload, task_context.a2a_context)
|
|
1481
|
+
|
|
1482
|
+
# Check if final event - construct and send Task
|
|
1483
|
+
if event_payload.final:
|
|
1484
|
+
log.info(
|
|
1485
|
+
"%s Received final status update (final=true). Constructing completed Task.",
|
|
1486
|
+
log_identifier,
|
|
1042
1487
|
)
|
|
1488
|
+
|
|
1489
|
+
# Construct Task from final status update
|
|
1490
|
+
# Copy the status but ensure state is "completed"
|
|
1491
|
+
final_task_status = TaskStatus(
|
|
1492
|
+
state=TaskState.completed,
|
|
1493
|
+
message=event_payload.status.message if event_payload.status else None,
|
|
1494
|
+
)
|
|
1495
|
+
|
|
1496
|
+
final_task = Task(
|
|
1497
|
+
id=event_payload.task_id,
|
|
1498
|
+
context_id=event_payload.context_id,
|
|
1499
|
+
status=final_task_status,
|
|
1500
|
+
artifacts=None, # Artifacts come via separate events
|
|
1501
|
+
metadata=event_payload.metadata,
|
|
1502
|
+
)
|
|
1503
|
+
|
|
1504
|
+
# Add produced_artifacts metadata if any artifacts were processed
|
|
1505
|
+
if produced_artifacts:
|
|
1506
|
+
if not final_task.metadata:
|
|
1507
|
+
final_task.metadata = {}
|
|
1508
|
+
final_task.metadata["produced_artifacts"] = produced_artifacts
|
|
1509
|
+
log.info(
|
|
1510
|
+
"%s Added manifest of %d produced artifacts to final Task metadata.",
|
|
1511
|
+
log_identifier,
|
|
1512
|
+
len(produced_artifacts),
|
|
1513
|
+
)
|
|
1514
|
+
|
|
1515
|
+
# Publish the constructed Task
|
|
1516
|
+
await self._publish_task_response(final_task, task_context.a2a_context)
|
|
1517
|
+
|
|
1518
|
+
should_cleanup_task = True
|
|
1519
|
+
log.debug(
|
|
1520
|
+
"%s Published final Task constructed from status update",
|
|
1521
|
+
log_identifier,
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1043
1524
|
elif isinstance(event_payload, TaskArtifactUpdateEvent):
|
|
1525
|
+
# Forward artifact update to status topic
|
|
1044
1526
|
await self._publish_artifact_update(event_payload, task_context.a2a_context)
|
|
1527
|
+
|
|
1045
1528
|
elif isinstance(event_payload, Message):
|
|
1529
|
+
# Wrap Message in Task for gateway compatibility
|
|
1046
1530
|
log.info(
|
|
1047
|
-
"%s Received
|
|
1531
|
+
"%s Received direct Message response. Wrapping in completed Task.",
|
|
1048
1532
|
log_identifier,
|
|
1049
1533
|
)
|
|
1050
1534
|
final_task = Task(
|
|
@@ -1053,7 +1537,7 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
1053
1537
|
status=TaskStatus(state=TaskState.completed, message=event_payload),
|
|
1054
1538
|
)
|
|
1055
1539
|
|
|
1056
|
-
# Add produced_artifacts metadata
|
|
1540
|
+
# Add produced_artifacts metadata if any artifacts were processed
|
|
1057
1541
|
if produced_artifacts:
|
|
1058
1542
|
final_task.metadata = {"produced_artifacts": produced_artifacts}
|
|
1059
1543
|
log.info(
|
|
@@ -1062,11 +1546,24 @@ class A2AProxyComponent(BaseProxyComponent):
|
|
|
1062
1546
|
len(produced_artifacts),
|
|
1063
1547
|
)
|
|
1064
1548
|
|
|
1065
|
-
await self.
|
|
1549
|
+
await self._publish_task_response(final_task, task_context.a2a_context)
|
|
1550
|
+
should_cleanup_task = True
|
|
1551
|
+
|
|
1066
1552
|
else:
|
|
1067
1553
|
log.warning(
|
|
1068
|
-
|
|
1554
|
+
"%s Received unhandled response payload type: %s",
|
|
1555
|
+
log_identifier,
|
|
1556
|
+
type(event_payload).__name__,
|
|
1557
|
+
)
|
|
1558
|
+
|
|
1559
|
+
# Cleanup task state if terminal event detected
|
|
1560
|
+
if should_cleanup_task:
|
|
1561
|
+
log.info(
|
|
1562
|
+
"%s Terminal event detected for task %s. Cleaning up state.",
|
|
1563
|
+
log_identifier,
|
|
1564
|
+
task_context.task_id,
|
|
1069
1565
|
)
|
|
1566
|
+
self._cleanup_task_state(task_context.task_id)
|
|
1070
1567
|
|
|
1071
1568
|
def clear_client_cache(self):
|
|
1072
1569
|
"""
|