solace-agent-mesh 1.6.3__py3-none-any.whl → 1.7.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 +12 -18
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +1 -1
- solace_agent_mesh/agent/adk/callbacks.py +138 -20
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +2 -0
- solace_agent_mesh/agent/adk/models/lite_llm.py +38 -5
- solace_agent_mesh/agent/adk/models/models_llm.txt +82 -35
- solace_agent_mesh/agent/adk/runner.py +9 -0
- solace_agent_mesh/agent/adk/stream_parser.py +6 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
- solace_agent_mesh/agent/agent_llm.txt +61 -70
- solace_agent_mesh/agent/protocol/event_handlers.py +29 -1
- solace_agent_mesh/agent/protocol/protocol_llm.txt +1 -1
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
- solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
- solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
- solace_agent_mesh/agent/sac/app.py +22 -0
- solace_agent_mesh/agent/sac/component.py +76 -40
- solace_agent_mesh/agent/sac/sac_llm.txt +1 -1
- solace_agent_mesh/agent/sac/task_execution_context.py +21 -0
- solace_agent_mesh/agent/testing/testing_llm.txt +2 -1
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +13 -148
- solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
- solace_agent_mesh/agent/tools/tools_llm.txt +93 -80
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +3 -2
- solace_agent_mesh/agent/utils/artifact_helpers.py +4 -0
- solace_agent_mesh/agent/utils/utils_llm.txt +16 -2
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/05749d90.c70b2be9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.e4870a49.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.b63ee53a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.45b32c2b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.09ed9234.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{ab9708a8.3e6dd091.js → ab9708a8.245ae0ef.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.f902fad8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e04b235d.c9c50c7b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.d11c67a7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{e6f9706b.e74a984d.js → e6f9706b.045d0fa1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.f213fe0c.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +18 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +196 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +6 -7
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +160 -169
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +59 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +10 -6
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +27 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +5 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1762283454666.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1762283454666.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/docs_cmd.py +4 -1
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-D4_RMYRh.js → authCallback-tcIFZLis.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-UZ3qU6Bq.js → client-CRYdKo2Q.js} +3 -3
- solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +353 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-CINwxvwV.js +470 -0
- 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/common/a2a/a2a_llm.txt +13 -20
- solace_agent_mesh/common/a2a/protocol.py +5 -0
- solace_agent_mesh/common/a2a/types.py +1 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +49 -11
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
- solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +26 -9
- solace_agent_mesh/common/common_llm.txt +13 -34
- solace_agent_mesh/common/data_parts.py +20 -4
- solace_agent_mesh/common/middleware/middleware_llm.txt +1 -1
- solace_agent_mesh/common/sac/sac_llm.txt +1 -1
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +1 -1
- solace_agent_mesh/common/services/employee_service.py +1 -1
- solace_agent_mesh/common/services/providers/providers_llm.txt +3 -2
- solace_agent_mesh/common/services/services_llm.txt +9 -4
- solace_agent_mesh/common/utils/embeds/constants.py +1 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +1 -1
- solace_agent_mesh/common/utils/embeds/modifiers.py +2 -1
- solace_agent_mesh/common/utils/embeds/resolver.py +58 -6
- solace_agent_mesh/common/utils/embeds/types.py +8 -0
- solace_agent_mesh/common/utils/utils_llm.txt +5 -6
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +1 -1
- solace_agent_mesh/gateway/adapter/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +143 -0
- solace_agent_mesh/gateway/adapter/types.py +221 -0
- solace_agent_mesh/gateway/base/app.py +29 -2
- solace_agent_mesh/gateway/base/base_llm.txt +10 -8
- solace_agent_mesh/gateway/base/component.py +573 -142
- solace_agent_mesh/gateway/gateway_llm.txt +55 -59
- solace_agent_mesh/gateway/generic/__init__.py +1 -0
- solace_agent_mesh/gateway/generic/app.py +50 -0
- solace_agent_mesh/gateway/generic/component.py +650 -0
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +99 -49
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +92 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +150 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +26 -20
- solace_agent_mesh/gateway/http_sse/app.py +0 -14
- solace_agent_mesh/gateway/http_sse/component.py +17 -56
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +1 -1
- solace_agent_mesh/gateway/http_sse/dependencies.py +21 -3
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +8 -8
- solace_agent_mesh/gateway/http_sse/main.py +23 -5
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +56 -98
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +23 -1
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +112 -4
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -1
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +51 -60
- solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +7 -1
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +125 -157
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +269 -8
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +143 -51
- solace_agent_mesh/gateway/http_sse/routers/config.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +198 -94
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +68 -18
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +13 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +30 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +51 -35
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +2 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +542 -0
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +9 -11
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +154 -3
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +296 -4
- solace_agent_mesh/gateway/http_sse/services/project_service.py +403 -0
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +16 -10
- solace_agent_mesh/gateway/http_sse/services/session_service.py +178 -6
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +2 -3
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +48 -14
- solace_agent_mesh/solace_agent_mesh_llm.txt +1 -1
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.1.dist-info}/METADATA +3 -5
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.1.dist-info}/RECORD +218 -175
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.3b883666.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.ed05b14d.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.a8a75e0b.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1761744323675.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1761744323675.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +0 -342
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-DSqhjwq_.js +0 -405
- /solace_agent_mesh/assets/docs/assets/js/{main.ed05b14d.js.LICENSE.txt → main.f213fe0c.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.1.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.1.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ Base Component class for Gateway implementations in the Solace AI Connector.
|
|
|
5
5
|
import logging
|
|
6
6
|
import asyncio
|
|
7
7
|
import queue
|
|
8
|
-
import
|
|
8
|
+
import re
|
|
9
9
|
import uuid
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
11
|
from typing import Any, Dict, Optional, List, Tuple, Union
|
|
@@ -31,19 +31,21 @@ from a2a.types import (
|
|
|
31
31
|
TaskArtifactUpdateEvent,
|
|
32
32
|
JSONRPCError,
|
|
33
33
|
TextPart,
|
|
34
|
+
DataPart,
|
|
34
35
|
FilePart,
|
|
35
|
-
FileWithBytes,
|
|
36
36
|
Artifact as A2AArtifact,
|
|
37
37
|
)
|
|
38
38
|
from ...common import a2a
|
|
39
|
-
from ...common.utils import is_text_based_mime_type
|
|
40
39
|
from ...common.utils.embeds import (
|
|
41
40
|
resolve_embeds_in_string,
|
|
42
|
-
resolve_embeds_recursively_in_string,
|
|
43
41
|
evaluate_embed,
|
|
44
42
|
LATE_EMBED_TYPES,
|
|
45
43
|
EARLY_EMBED_TYPES,
|
|
46
|
-
|
|
44
|
+
)
|
|
45
|
+
from ...common.utils.embeds.types import ResolutionMode
|
|
46
|
+
from ...agent.utils.artifact_helpers import (
|
|
47
|
+
load_artifact_content_or_metadata,
|
|
48
|
+
format_artifact_uri,
|
|
47
49
|
)
|
|
48
50
|
from solace_ai_connector.common.message import (
|
|
49
51
|
Message as SolaceMessage,
|
|
@@ -97,9 +99,32 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
97
99
|
|
|
98
100
|
return super().get_config(key, default)
|
|
99
101
|
|
|
100
|
-
def __init__(
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
resolve_artifact_uris_in_gateway: bool = True,
|
|
105
|
+
supports_inline_artifact_resolution: bool = False,
|
|
106
|
+
filter_tool_data_parts: bool = True,
|
|
107
|
+
**kwargs: Any
|
|
108
|
+
):
|
|
109
|
+
"""
|
|
110
|
+
Initialize the BaseGatewayComponent.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
resolve_artifact_uris_in_gateway: If True, resolves artifact URIs before sending to external.
|
|
114
|
+
supports_inline_artifact_resolution: If True, SIGNAL_ARTIFACT_RETURN embeds are converted
|
|
115
|
+
to FileParts during embed resolution. If False (default), signals are passed through
|
|
116
|
+
for the gateway to handle manually. Use False for legacy gateways (e.g., Slack),
|
|
117
|
+
True for modern gateways that support inline artifact rendering (e.g., HTTP SSE).
|
|
118
|
+
filter_tool_data_parts: If True (default), filters out tool-related DataParts (tool_call,
|
|
119
|
+
tool_result, etc.) from final Task messages before sending to gateway. Use True for
|
|
120
|
+
gateways that don't want to display internal tool execution details (e.g., Slack),
|
|
121
|
+
False for gateways that display all parts (e.g., HTTP SSE Web UI).
|
|
122
|
+
**kwargs: Additional arguments passed to parent class.
|
|
123
|
+
"""
|
|
101
124
|
super().__init__(info, **kwargs)
|
|
102
125
|
self.resolve_artifact_uris_in_gateway = resolve_artifact_uris_in_gateway
|
|
126
|
+
self.supports_inline_artifact_resolution = supports_inline_artifact_resolution
|
|
127
|
+
self.filter_tool_data_parts = filter_tool_data_parts
|
|
103
128
|
log.info("%s Initializing Base Gateway Component...", self.log_identifier)
|
|
104
129
|
|
|
105
130
|
try:
|
|
@@ -263,10 +288,12 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
263
288
|
external_request_context["user_identity"] = user_identity
|
|
264
289
|
external_request_context["a2a_user_config"] = user_config
|
|
265
290
|
external_request_context["api_version"] = api_version
|
|
291
|
+
external_request_context["is_streaming"] = is_streaming
|
|
266
292
|
log.debug(
|
|
267
|
-
"%s Stored user_identity, configuration, and
|
|
293
|
+
"%s Stored user_identity, configuration, api_version (%s), and is_streaming (%s) in external_request_context.",
|
|
268
294
|
log_id_prefix,
|
|
269
295
|
api_version,
|
|
296
|
+
is_streaming,
|
|
270
297
|
)
|
|
271
298
|
|
|
272
299
|
now = datetime.now(timezone.utc)
|
|
@@ -458,7 +485,7 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
458
485
|
async def _handle_resolved_signals(
|
|
459
486
|
self,
|
|
460
487
|
external_request_context: Dict,
|
|
461
|
-
signals: List[Tuple[
|
|
488
|
+
signals: List[Tuple[None, str, Any]],
|
|
462
489
|
original_rpc_id: Optional[str],
|
|
463
490
|
is_finalizing_context: bool = False,
|
|
464
491
|
):
|
|
@@ -466,7 +493,7 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
466
493
|
if not signals:
|
|
467
494
|
return
|
|
468
495
|
|
|
469
|
-
for
|
|
496
|
+
for signal_tuple in signals:
|
|
470
497
|
if (
|
|
471
498
|
isinstance(signal_tuple, tuple)
|
|
472
499
|
and len(signal_tuple) == 3
|
|
@@ -526,6 +553,245 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
526
553
|
log.exception(
|
|
527
554
|
"%s Error sending status signal: %s", log_id_prefix, e
|
|
528
555
|
)
|
|
556
|
+
elif signal_type == "SIGNAL_ARTIFACT_RETURN":
|
|
557
|
+
# Handle artifact return signal for legacy gateways
|
|
558
|
+
# During finalizing context (final Task), suppress this to avoid duplicates
|
|
559
|
+
# since the same signal might appear in both streaming and final responses
|
|
560
|
+
if is_finalizing_context:
|
|
561
|
+
log.debug(
|
|
562
|
+
"%s Suppressing SIGNAL_ARTIFACT_RETURN during finalizing context to avoid duplicate: %s",
|
|
563
|
+
log_id_prefix,
|
|
564
|
+
signal_data,
|
|
565
|
+
)
|
|
566
|
+
continue
|
|
567
|
+
|
|
568
|
+
log.info(
|
|
569
|
+
"%s Handling SIGNAL_ARTIFACT_RETURN for legacy gateway: %s",
|
|
570
|
+
log_id_prefix,
|
|
571
|
+
signal_data,
|
|
572
|
+
)
|
|
573
|
+
try:
|
|
574
|
+
filename = signal_data.get("filename")
|
|
575
|
+
version = signal_data.get("version")
|
|
576
|
+
|
|
577
|
+
if not filename:
|
|
578
|
+
log.error(
|
|
579
|
+
"%s SIGNAL_ARTIFACT_RETURN missing filename. Skipping.",
|
|
580
|
+
log_id_prefix,
|
|
581
|
+
)
|
|
582
|
+
continue
|
|
583
|
+
|
|
584
|
+
# Load artifact content (not just metadata) for legacy gateways
|
|
585
|
+
# Legacy gateways like Slack need the actual bytes to upload files
|
|
586
|
+
artifact_data = await load_artifact_content_or_metadata(
|
|
587
|
+
self.shared_artifact_service,
|
|
588
|
+
app_name=external_request_context.get(
|
|
589
|
+
"app_name_for_artifacts", self.gateway_id
|
|
590
|
+
),
|
|
591
|
+
user_id=external_request_context.get("user_id_for_artifacts"),
|
|
592
|
+
session_id=external_request_context.get("a2a_session_id"),
|
|
593
|
+
filename=filename,
|
|
594
|
+
version=version,
|
|
595
|
+
load_metadata_only=False, # Load full content for legacy gateways
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
if artifact_data.get("status") != "success":
|
|
599
|
+
log.error(
|
|
600
|
+
"%s Failed to load artifact content for %s v%s",
|
|
601
|
+
log_id_prefix,
|
|
602
|
+
filename,
|
|
603
|
+
version,
|
|
604
|
+
)
|
|
605
|
+
continue
|
|
606
|
+
|
|
607
|
+
# Get content and ensure it's bytes
|
|
608
|
+
content = artifact_data.get("content")
|
|
609
|
+
if not content:
|
|
610
|
+
log.error(
|
|
611
|
+
"%s No content found in artifact %s v%s",
|
|
612
|
+
log_id_prefix,
|
|
613
|
+
filename,
|
|
614
|
+
version,
|
|
615
|
+
)
|
|
616
|
+
continue
|
|
617
|
+
|
|
618
|
+
# Convert to bytes if it's a string (text-based artifacts)
|
|
619
|
+
if isinstance(content, str):
|
|
620
|
+
content_bytes = content.encode("utf-8")
|
|
621
|
+
elif isinstance(content, bytes):
|
|
622
|
+
content_bytes = content
|
|
623
|
+
else:
|
|
624
|
+
log.error(
|
|
625
|
+
"%s Artifact content is neither string nor bytes: %s",
|
|
626
|
+
log_id_prefix,
|
|
627
|
+
type(content),
|
|
628
|
+
)
|
|
629
|
+
continue
|
|
630
|
+
|
|
631
|
+
# Create FilePart with bytes for legacy gateway to upload
|
|
632
|
+
file_part = a2a.create_file_part_from_bytes(
|
|
633
|
+
content_bytes=content_bytes,
|
|
634
|
+
name=filename,
|
|
635
|
+
mime_type=artifact_data.get("metadata", {}).get("mime_type"),
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
# Create artifact with the file part
|
|
639
|
+
# Import Part type for wrapping
|
|
640
|
+
from a2a.types import Artifact, Part
|
|
641
|
+
artifact = Artifact(
|
|
642
|
+
artifact_id=str(uuid.uuid4().hex),
|
|
643
|
+
parts=[Part(root=file_part)],
|
|
644
|
+
name=filename,
|
|
645
|
+
description=f"Artifact: {filename}",
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Send as TaskArtifactUpdateEvent
|
|
649
|
+
a2a_task_id_for_signal = external_request_context.get(
|
|
650
|
+
"a2a_task_id_for_event", original_rpc_id
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
if not a2a_task_id_for_signal:
|
|
654
|
+
log.error(
|
|
655
|
+
"%s Cannot determine A2A task ID for artifact signal. Skipping.",
|
|
656
|
+
log_id_prefix,
|
|
657
|
+
)
|
|
658
|
+
continue
|
|
659
|
+
|
|
660
|
+
artifact_event = a2a.create_artifact_update(
|
|
661
|
+
task_id=a2a_task_id_for_signal,
|
|
662
|
+
context_id=external_request_context.get("a2a_session_id"),
|
|
663
|
+
artifact=artifact,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
await self._send_update_to_external(
|
|
667
|
+
external_request_context=external_request_context,
|
|
668
|
+
event_data=artifact_event,
|
|
669
|
+
is_final_chunk_of_update=False,
|
|
670
|
+
)
|
|
671
|
+
log.info(
|
|
672
|
+
"%s Sent artifact signal as TaskArtifactUpdateEvent for %s",
|
|
673
|
+
log_id_prefix,
|
|
674
|
+
filename,
|
|
675
|
+
)
|
|
676
|
+
except Exception as e:
|
|
677
|
+
log.exception(
|
|
678
|
+
"%s Error sending artifact signal: %s", log_id_prefix, e
|
|
679
|
+
)
|
|
680
|
+
elif signal_type == "SIGNAL_ARTIFACT_CREATION_COMPLETE":
|
|
681
|
+
# Handle artifact creation completion for legacy gateways
|
|
682
|
+
# This is similar to SIGNAL_ARTIFACT_RETURN but for newly created artifacts
|
|
683
|
+
log.info(
|
|
684
|
+
"%s Handling SIGNAL_ARTIFACT_CREATION_COMPLETE for legacy gateway: %s",
|
|
685
|
+
log_id_prefix,
|
|
686
|
+
signal_data,
|
|
687
|
+
)
|
|
688
|
+
try:
|
|
689
|
+
filename = signal_data.get("filename")
|
|
690
|
+
version = signal_data.get("version")
|
|
691
|
+
|
|
692
|
+
if not filename:
|
|
693
|
+
log.error(
|
|
694
|
+
"%s SIGNAL_ARTIFACT_CREATION_COMPLETE missing filename. Skipping.",
|
|
695
|
+
log_id_prefix,
|
|
696
|
+
)
|
|
697
|
+
continue
|
|
698
|
+
|
|
699
|
+
# Load artifact content (not just metadata) for legacy gateways
|
|
700
|
+
# Legacy gateways like Slack need the actual bytes to upload files
|
|
701
|
+
artifact_data = await load_artifact_content_or_metadata(
|
|
702
|
+
self.shared_artifact_service,
|
|
703
|
+
app_name=external_request_context.get(
|
|
704
|
+
"app_name_for_artifacts", self.gateway_id
|
|
705
|
+
),
|
|
706
|
+
user_id=external_request_context.get("user_id_for_artifacts"),
|
|
707
|
+
session_id=external_request_context.get("a2a_session_id"),
|
|
708
|
+
filename=filename,
|
|
709
|
+
version=version,
|
|
710
|
+
load_metadata_only=False, # Load full content for legacy gateways
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
if artifact_data.get("status") != "success":
|
|
714
|
+
log.error(
|
|
715
|
+
"%s Failed to load artifact content for %s v%s",
|
|
716
|
+
log_id_prefix,
|
|
717
|
+
filename,
|
|
718
|
+
version,
|
|
719
|
+
)
|
|
720
|
+
continue
|
|
721
|
+
|
|
722
|
+
# Get content and ensure it's bytes
|
|
723
|
+
content = artifact_data.get("content")
|
|
724
|
+
if not content:
|
|
725
|
+
log.error(
|
|
726
|
+
"%s No content found in artifact %s v%s",
|
|
727
|
+
log_id_prefix,
|
|
728
|
+
filename,
|
|
729
|
+
version,
|
|
730
|
+
)
|
|
731
|
+
continue
|
|
732
|
+
|
|
733
|
+
# Convert to bytes if it's a string (text-based artifacts)
|
|
734
|
+
if isinstance(content, str):
|
|
735
|
+
content_bytes = content.encode("utf-8")
|
|
736
|
+
elif isinstance(content, bytes):
|
|
737
|
+
content_bytes = content
|
|
738
|
+
else:
|
|
739
|
+
log.error(
|
|
740
|
+
"%s Artifact content is neither string nor bytes: %s",
|
|
741
|
+
log_id_prefix,
|
|
742
|
+
type(content),
|
|
743
|
+
)
|
|
744
|
+
continue
|
|
745
|
+
|
|
746
|
+
# Create FilePart with bytes for legacy gateway to upload
|
|
747
|
+
file_part = a2a.create_file_part_from_bytes(
|
|
748
|
+
content_bytes=content_bytes,
|
|
749
|
+
name=filename,
|
|
750
|
+
mime_type=signal_data.get("mime_type") or artifact_data.get("metadata", {}).get("mime_type"),
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
# Create artifact with the file part
|
|
754
|
+
# Import Part type for wrapping
|
|
755
|
+
from a2a.types import Artifact, Part
|
|
756
|
+
artifact = Artifact(
|
|
757
|
+
artifact_id=str(uuid.uuid4().hex),
|
|
758
|
+
parts=[Part(root=file_part)],
|
|
759
|
+
name=filename,
|
|
760
|
+
description=f"Artifact: {filename}",
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
# Send as TaskArtifactUpdateEvent
|
|
764
|
+
a2a_task_id_for_signal = external_request_context.get(
|
|
765
|
+
"a2a_task_id_for_event", original_rpc_id
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
if not a2a_task_id_for_signal:
|
|
769
|
+
log.error(
|
|
770
|
+
"%s Cannot determine A2A task ID for artifact creation signal. Skipping.",
|
|
771
|
+
log_id_prefix,
|
|
772
|
+
)
|
|
773
|
+
continue
|
|
774
|
+
|
|
775
|
+
artifact_event = a2a.create_artifact_update(
|
|
776
|
+
task_id=a2a_task_id_for_signal,
|
|
777
|
+
context_id=external_request_context.get("a2a_session_id"),
|
|
778
|
+
artifact=artifact,
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
await self._send_update_to_external(
|
|
782
|
+
external_request_context=external_request_context,
|
|
783
|
+
event_data=artifact_event,
|
|
784
|
+
is_final_chunk_of_update=False,
|
|
785
|
+
)
|
|
786
|
+
log.info(
|
|
787
|
+
"%s Sent artifact creation completion as TaskArtifactUpdateEvent for %s",
|
|
788
|
+
log_id_prefix,
|
|
789
|
+
filename,
|
|
790
|
+
)
|
|
791
|
+
except Exception as e:
|
|
792
|
+
log.exception(
|
|
793
|
+
"%s Error sending artifact creation completion signal: %s", log_id_prefix, e
|
|
794
|
+
)
|
|
529
795
|
else:
|
|
530
796
|
log.warning(
|
|
531
797
|
"%s Received unhandled signal type during embed resolution: %s",
|
|
@@ -628,6 +894,64 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
628
894
|
processed_parts.append(part)
|
|
629
895
|
return processed_parts
|
|
630
896
|
|
|
897
|
+
def _should_include_data_part_in_final_output(self, part: Any) -> bool:
|
|
898
|
+
"""
|
|
899
|
+
Determines if a DataPart should be included in the final output sent to the gateway.
|
|
900
|
+
|
|
901
|
+
This filters out internal/tool-related DataParts that shouldn't be shown to end users.
|
|
902
|
+
Gateways can override this method for custom filtering logic.
|
|
903
|
+
|
|
904
|
+
Args:
|
|
905
|
+
part: The part to check (expected to be a DataPart)
|
|
906
|
+
|
|
907
|
+
Returns:
|
|
908
|
+
True if the part should be included, False if it should be filtered out
|
|
909
|
+
"""
|
|
910
|
+
from a2a.types import DataPart
|
|
911
|
+
|
|
912
|
+
if not isinstance(part, DataPart):
|
|
913
|
+
return True
|
|
914
|
+
|
|
915
|
+
# Check if this is a tool result by looking at metadata
|
|
916
|
+
# Tool results have metadata.tool_name set
|
|
917
|
+
if part.metadata and part.metadata.get("tool_name"):
|
|
918
|
+
# This is a tool result - filter it out
|
|
919
|
+
return False
|
|
920
|
+
|
|
921
|
+
# Get the type of the data part
|
|
922
|
+
data_type = part.data.get("type") if part.data else None
|
|
923
|
+
|
|
924
|
+
# Filter out tool-related data parts that are internal
|
|
925
|
+
tool_related_types = {
|
|
926
|
+
"tool_call",
|
|
927
|
+
"tool_result",
|
|
928
|
+
"tool_error",
|
|
929
|
+
"tool_execution",
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if data_type in tool_related_types:
|
|
933
|
+
return False
|
|
934
|
+
|
|
935
|
+
# Handle artifact_creation_progress based on gateway capabilities
|
|
936
|
+
if data_type == "artifact_creation_progress":
|
|
937
|
+
# For modern gateways (HTTP SSE), keep these to display progress bubbles
|
|
938
|
+
# For legacy gateways (Slack), filter them out as they'll be converted to FileParts
|
|
939
|
+
if self.supports_inline_artifact_resolution:
|
|
940
|
+
return True # Keep for HTTP SSE
|
|
941
|
+
else:
|
|
942
|
+
return False # Filter for Slack (will be converted to FileParts instead)
|
|
943
|
+
|
|
944
|
+
# Keep user-facing data parts like general progress updates
|
|
945
|
+
user_facing_types = {
|
|
946
|
+
"agent_progress_update",
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if data_type in user_facing_types:
|
|
950
|
+
return True
|
|
951
|
+
|
|
952
|
+
# Default: include unknown types (to avoid hiding potentially useful info)
|
|
953
|
+
return True
|
|
954
|
+
|
|
631
955
|
async def _resolve_embeds_and_handle_signals(
|
|
632
956
|
self,
|
|
633
957
|
event_with_parts: Union[TaskStatusUpdateEvent, Task, TaskArtifactUpdateEvent],
|
|
@@ -636,17 +960,11 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
636
960
|
original_rpc_id: Optional[str],
|
|
637
961
|
is_finalizing_context: bool = False,
|
|
638
962
|
) -> bool:
|
|
639
|
-
"""
|
|
640
|
-
Resolves embeds and handles signals for an event containing parts.
|
|
641
|
-
Modifies event_with_parts in place if text content changes.
|
|
642
|
-
Manages stream buffer for TaskStatusUpdateEvent.
|
|
643
|
-
Returns True if the event content was modified or signals were handled, False otherwise.
|
|
644
|
-
"""
|
|
645
963
|
if not self.enable_embed_resolution:
|
|
646
964
|
return False
|
|
647
965
|
|
|
648
966
|
log_id_prefix = f"{self.log_identifier}[EmbedResolve:{a2a_task_id}]"
|
|
649
|
-
|
|
967
|
+
content_modified = False
|
|
650
968
|
|
|
651
969
|
embed_eval_context = {
|
|
652
970
|
"artifact_service": self.shared_artifact_service,
|
|
@@ -664,8 +982,6 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
664
982
|
}
|
|
665
983
|
|
|
666
984
|
parts_owner: Optional[Union[A2AMessage, A2AArtifact]] = None
|
|
667
|
-
is_streaming_status_update = isinstance(event_with_parts, TaskStatusUpdateEvent)
|
|
668
|
-
|
|
669
985
|
if isinstance(event_with_parts, (TaskStatusUpdateEvent, Task)):
|
|
670
986
|
if event_with_parts.status and event_with_parts.status.message:
|
|
671
987
|
parts_owner = event_with_parts.status.message
|
|
@@ -673,146 +989,233 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
673
989
|
if event_with_parts.artifact:
|
|
674
990
|
parts_owner = event_with_parts.artifact
|
|
675
991
|
|
|
676
|
-
if parts_owner and parts_owner.parts:
|
|
677
|
-
|
|
678
|
-
stream_buffer_key = f"{a2a_task_id}_stream_buffer"
|
|
679
|
-
current_buffer = ""
|
|
680
|
-
|
|
681
|
-
if is_streaming_status_update:
|
|
682
|
-
current_buffer = (
|
|
683
|
-
self.task_context_manager.get_context(stream_buffer_key) or ""
|
|
684
|
-
)
|
|
992
|
+
if not (parts_owner and parts_owner.parts):
|
|
993
|
+
return False
|
|
685
994
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
995
|
+
is_streaming_status_update = isinstance(event_with_parts, TaskStatusUpdateEvent)
|
|
996
|
+
stream_buffer_key = f"{a2a_task_id}_stream_buffer"
|
|
997
|
+
current_buffer = ""
|
|
998
|
+
if is_streaming_status_update:
|
|
999
|
+
current_buffer = (
|
|
1000
|
+
self.task_context_manager.get_context(stream_buffer_key) or ""
|
|
1001
|
+
)
|
|
691
1002
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
1003
|
+
original_parts: List[ContentPart] = (
|
|
1004
|
+
a2a.get_parts_from_message(parts_owner)
|
|
1005
|
+
if isinstance(parts_owner, A2AMessage)
|
|
1006
|
+
else a2a.get_parts_from_artifact(parts_owner)
|
|
1007
|
+
)
|
|
696
1008
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
1009
|
+
new_parts: List[ContentPart] = []
|
|
1010
|
+
other_signals = []
|
|
1011
|
+
|
|
1012
|
+
for part in original_parts:
|
|
1013
|
+
if isinstance(part, TextPart) and part.text:
|
|
1014
|
+
text_to_resolve = current_buffer + part.text
|
|
1015
|
+
current_buffer = "" # Buffer is now being processed
|
|
1016
|
+
|
|
1017
|
+
(
|
|
1018
|
+
resolved_text,
|
|
1019
|
+
processed_idx,
|
|
1020
|
+
signals_with_placeholders,
|
|
1021
|
+
) = await resolve_embeds_in_string(
|
|
1022
|
+
text=text_to_resolve,
|
|
1023
|
+
context=embed_eval_context,
|
|
1024
|
+
resolver_func=evaluate_embed,
|
|
1025
|
+
types_to_resolve=LATE_EMBED_TYPES.union({"status_update"}),
|
|
1026
|
+
resolution_mode=ResolutionMode.A2A_MESSAGE_TO_USER,
|
|
1027
|
+
log_identifier=log_id_prefix,
|
|
1028
|
+
config=embed_eval_config,
|
|
1029
|
+
)
|
|
700
1030
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
log_identifier=log_id_prefix,
|
|
708
|
-
config=embed_eval_config,
|
|
709
|
-
)
|
|
1031
|
+
if not signals_with_placeholders:
|
|
1032
|
+
new_parts.append(a2a.create_text_part(text=resolved_text))
|
|
1033
|
+
else:
|
|
1034
|
+
placeholder_map = {p: s for _, s, p in signals_with_placeholders}
|
|
1035
|
+
split_pattern = (
|
|
1036
|
+
f"({'|'.join(re.escape(p) for p in placeholder_map.keys())})"
|
|
710
1037
|
)
|
|
1038
|
+
text_fragments = re.split(split_pattern, resolved_text)
|
|
711
1039
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
if resolved_content != original_content:
|
|
767
|
-
new_file_content = part.file.model_copy()
|
|
768
|
-
new_file_content.bytes = base64.b64encode(
|
|
769
|
-
resolved_content.encode("utf-8")
|
|
770
|
-
).decode("utf-8")
|
|
771
|
-
new_parts.append(
|
|
772
|
-
FilePart(
|
|
773
|
-
file=new_file_content,
|
|
774
|
-
metadata=part.metadata,
|
|
1040
|
+
for i, fragment in enumerate(text_fragments):
|
|
1041
|
+
if not fragment:
|
|
1042
|
+
continue
|
|
1043
|
+
if fragment in placeholder_map:
|
|
1044
|
+
signal_tuple = placeholder_map[fragment]
|
|
1045
|
+
signal_type, signal_data = signal_tuple[1], signal_tuple[2]
|
|
1046
|
+
if signal_type == "SIGNAL_ARTIFACT_RETURN":
|
|
1047
|
+
# Only convert to FilePart if gateway supports inline artifact resolution
|
|
1048
|
+
if self.supports_inline_artifact_resolution:
|
|
1049
|
+
try:
|
|
1050
|
+
filename, version = (
|
|
1051
|
+
signal_data["filename"],
|
|
1052
|
+
signal_data["version"],
|
|
1053
|
+
)
|
|
1054
|
+
artifact_data = (
|
|
1055
|
+
await load_artifact_content_or_metadata(
|
|
1056
|
+
self.shared_artifact_service,
|
|
1057
|
+
**embed_eval_context["session_context"],
|
|
1058
|
+
filename=filename,
|
|
1059
|
+
version=version,
|
|
1060
|
+
load_metadata_only=True,
|
|
1061
|
+
)
|
|
1062
|
+
)
|
|
1063
|
+
if artifact_data.get("status") == "success":
|
|
1064
|
+
uri = format_artifact_uri(
|
|
1065
|
+
**embed_eval_context["session_context"],
|
|
1066
|
+
filename=filename,
|
|
1067
|
+
version=artifact_data.get("version"),
|
|
1068
|
+
)
|
|
1069
|
+
new_parts.append(
|
|
1070
|
+
a2a.create_file_part_from_uri(
|
|
1071
|
+
uri,
|
|
1072
|
+
filename,
|
|
1073
|
+
artifact_data.get("metadata", {}).get(
|
|
1074
|
+
"mime_type"
|
|
1075
|
+
),
|
|
1076
|
+
)
|
|
1077
|
+
)
|
|
1078
|
+
else:
|
|
1079
|
+
new_parts.append(
|
|
1080
|
+
a2a.create_text_part(
|
|
1081
|
+
f"[Error: Artifact '{filename}' v{version} not found.]"
|
|
1082
|
+
)
|
|
1083
|
+
)
|
|
1084
|
+
except Exception as e:
|
|
1085
|
+
log.exception(
|
|
1086
|
+
"%s Error handling SIGNAL_ARTIFACT_RETURN: %s",
|
|
1087
|
+
log_id_prefix,
|
|
1088
|
+
e,
|
|
1089
|
+
)
|
|
1090
|
+
new_parts.append(
|
|
1091
|
+
a2a.create_text_part(
|
|
1092
|
+
f"[Error: Could not retrieve artifact '{signal_data.get('filename')}'.]"
|
|
1093
|
+
)
|
|
775
1094
|
)
|
|
776
|
-
)
|
|
777
|
-
content_modified_or_signal_handled = True
|
|
778
1095
|
else:
|
|
779
|
-
|
|
1096
|
+
# Legacy gateway mode: pass signal through for gateway to handle
|
|
1097
|
+
other_signals.append(signal_tuple)
|
|
1098
|
+
elif signal_type == "SIGNAL_INLINE_BINARY_CONTENT":
|
|
1099
|
+
signal_data["content_bytes"] = signal_data.get("bytes")
|
|
1100
|
+
del signal_data["bytes"]
|
|
1101
|
+
new_parts.append(
|
|
1102
|
+
a2a.create_file_part_from_bytes(**signal_data)
|
|
1103
|
+
)
|
|
780
1104
|
else:
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
1105
|
+
other_signals.append(signal_tuple)
|
|
1106
|
+
else:
|
|
1107
|
+
# Check if the non-placeholder fragment is just whitespace
|
|
1108
|
+
# and is between two placeholders. If so, drop it.
|
|
1109
|
+
is_just_whitespace = not fragment.strip()
|
|
1110
|
+
prev_fragment_was_placeholder = (
|
|
1111
|
+
i > 0 and text_fragments[i - 1] in placeholder_map
|
|
1112
|
+
)
|
|
1113
|
+
next_fragment_is_placeholder = (
|
|
1114
|
+
i < len(text_fragments) - 1
|
|
1115
|
+
and text_fragments[i + 1] in placeholder_map
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
if (
|
|
1119
|
+
is_just_whitespace
|
|
1120
|
+
and prev_fragment_was_placeholder
|
|
1121
|
+
and next_fragment_is_placeholder
|
|
1122
|
+
):
|
|
1123
|
+
log.debug(
|
|
1124
|
+
"%s Dropping whitespace fragment between two file signals.",
|
|
1125
|
+
log_id_prefix,
|
|
1126
|
+
)
|
|
1127
|
+
continue
|
|
1128
|
+
|
|
1129
|
+
new_parts.append(a2a.create_text_part(text=fragment))
|
|
1130
|
+
|
|
1131
|
+
if is_streaming_status_update:
|
|
1132
|
+
current_buffer = text_to_resolve[processed_idx:]
|
|
1133
|
+
|
|
1134
|
+
elif isinstance(part, FilePart) and part.file:
|
|
1135
|
+
# Handle recursive embeds in text-based FileParts
|
|
1136
|
+
new_parts.append(part) # Placeholder for now
|
|
1137
|
+
elif isinstance(part, DataPart):
|
|
1138
|
+
# Handle artifact creation progress DataParts for legacy gateways
|
|
1139
|
+
data_type = part.data.get("type") if part.data else None
|
|
1140
|
+
if (
|
|
1141
|
+
data_type == "artifact_creation_progress"
|
|
1142
|
+
and not self.supports_inline_artifact_resolution
|
|
1143
|
+
):
|
|
1144
|
+
# Legacy gateway mode: convert completed artifact creation to FilePart
|
|
1145
|
+
status = part.data.get("status")
|
|
1146
|
+
if status == "completed" and not is_finalizing_context:
|
|
1147
|
+
# Extract artifact info from the DataPart
|
|
1148
|
+
filename = part.data.get("filename")
|
|
1149
|
+
version = part.data.get("version")
|
|
1150
|
+
mime_type = part.data.get("mime_type")
|
|
1151
|
+
|
|
1152
|
+
if filename and version is not None:
|
|
1153
|
+
log.info(
|
|
1154
|
+
"%s Converting artifact creation completion to FilePart for legacy gateway: %s v%s",
|
|
785
1155
|
log_id_prefix,
|
|
786
|
-
|
|
787
|
-
|
|
1156
|
+
filename,
|
|
1157
|
+
version,
|
|
1158
|
+
)
|
|
1159
|
+
# This will be sent as an artifact signal, so don't add to new_parts
|
|
1160
|
+
# Instead, add to other_signals for processing
|
|
1161
|
+
signal_tuple = (
|
|
1162
|
+
None,
|
|
1163
|
+
"SIGNAL_ARTIFACT_CREATION_COMPLETE",
|
|
1164
|
+
{
|
|
1165
|
+
"filename": filename,
|
|
1166
|
+
"version": version,
|
|
1167
|
+
"mime_type": mime_type,
|
|
1168
|
+
},
|
|
788
1169
|
)
|
|
1170
|
+
other_signals.append(signal_tuple)
|
|
1171
|
+
else:
|
|
1172
|
+
# Missing required info, keep the DataPart as-is
|
|
789
1173
|
new_parts.append(part)
|
|
1174
|
+
elif status == "completed" and is_finalizing_context:
|
|
1175
|
+
# Suppress during finalizing to avoid duplicates
|
|
1176
|
+
log.debug(
|
|
1177
|
+
"%s Suppressing artifact creation completion during finalizing context for %s",
|
|
1178
|
+
log_id_prefix,
|
|
1179
|
+
part.data.get("filename"),
|
|
1180
|
+
)
|
|
1181
|
+
continue
|
|
790
1182
|
else:
|
|
791
|
-
#
|
|
1183
|
+
# Keep in-progress or failed status DataParts
|
|
792
1184
|
new_parts.append(part)
|
|
793
1185
|
else:
|
|
1186
|
+
# Not an artifact creation DataPart, or modern gateway - keep as-is
|
|
794
1187
|
new_parts.append(part)
|
|
1188
|
+
else:
|
|
1189
|
+
new_parts.append(part)
|
|
795
1190
|
|
|
1191
|
+
if other_signals:
|
|
1192
|
+
await self._handle_resolved_signals(
|
|
1193
|
+
external_request_context,
|
|
1194
|
+
other_signals,
|
|
1195
|
+
original_rpc_id,
|
|
1196
|
+
is_finalizing_context,
|
|
1197
|
+
)
|
|
1198
|
+
|
|
1199
|
+
if new_parts != original_parts:
|
|
1200
|
+
content_modified = True
|
|
796
1201
|
if isinstance(parts_owner, A2AMessage):
|
|
797
1202
|
if isinstance(event_with_parts, TaskStatusUpdateEvent):
|
|
798
1203
|
event_with_parts.status.message = a2a.update_message_parts(
|
|
799
|
-
|
|
1204
|
+
parts_owner, new_parts
|
|
800
1205
|
)
|
|
801
1206
|
elif isinstance(event_with_parts, Task):
|
|
802
1207
|
event_with_parts.status.message = a2a.update_message_parts(
|
|
803
|
-
|
|
1208
|
+
parts_owner, new_parts
|
|
804
1209
|
)
|
|
805
1210
|
elif isinstance(parts_owner, A2AArtifact):
|
|
806
1211
|
event_with_parts.artifact = a2a.update_artifact_parts(
|
|
807
|
-
|
|
1212
|
+
parts_owner, new_parts
|
|
808
1213
|
)
|
|
809
1214
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
stream_buffer_key, current_buffer
|
|
813
|
-
)
|
|
1215
|
+
if is_streaming_status_update:
|
|
1216
|
+
self.task_context_manager.store_context(stream_buffer_key, current_buffer)
|
|
814
1217
|
|
|
815
|
-
return
|
|
1218
|
+
return content_modified or bool(other_signals)
|
|
816
1219
|
|
|
817
1220
|
async def _process_parsed_a2a_event(
|
|
818
1221
|
self,
|
|
@@ -845,13 +1248,6 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
845
1248
|
elif isinstance(parsed_event, Task):
|
|
846
1249
|
is_finalizing_context_for_embeds = True
|
|
847
1250
|
|
|
848
|
-
if self.resolve_artifact_uris_in_gateway:
|
|
849
|
-
log.debug(
|
|
850
|
-
"%s Resolving artifact URIs before sending to external...",
|
|
851
|
-
log_id_prefix,
|
|
852
|
-
)
|
|
853
|
-
await self._resolve_uris_in_payload(parsed_event)
|
|
854
|
-
|
|
855
1251
|
if not isinstance(parsed_event, JSONRPCError):
|
|
856
1252
|
content_was_modified_or_signals_handled = (
|
|
857
1253
|
await self._resolve_embeds_and_handle_signals(
|
|
@@ -863,6 +1259,13 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
863
1259
|
)
|
|
864
1260
|
)
|
|
865
1261
|
|
|
1262
|
+
if self.resolve_artifact_uris_in_gateway:
|
|
1263
|
+
log.debug(
|
|
1264
|
+
"%s Resolving artifact URIs before sending to external...",
|
|
1265
|
+
log_id_prefix,
|
|
1266
|
+
)
|
|
1267
|
+
await self._resolve_uris_in_payload(parsed_event)
|
|
1268
|
+
|
|
866
1269
|
send_this_event_to_external = True
|
|
867
1270
|
is_final_chunk_of_status_update = False
|
|
868
1271
|
|
|
@@ -933,6 +1336,7 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
933
1336
|
context=embed_eval_context,
|
|
934
1337
|
resolver_func=evaluate_embed,
|
|
935
1338
|
types_to_resolve=all_embed_types,
|
|
1339
|
+
resolution_mode=ResolutionMode.A2A_MESSAGE_TO_USER,
|
|
936
1340
|
log_identifier=log_id_prefix,
|
|
937
1341
|
config=embed_eval_config,
|
|
938
1342
|
)
|
|
@@ -994,12 +1398,13 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
994
1398
|
}
|
|
995
1399
|
resolved_remaining_text, _, signals = (
|
|
996
1400
|
await resolve_embeds_in_string(
|
|
997
|
-
remaining_buffer,
|
|
998
|
-
embed_eval_context,
|
|
999
|
-
evaluate_embed,
|
|
1000
|
-
LATE_EMBED_TYPES.copy(),
|
|
1001
|
-
|
|
1002
|
-
|
|
1401
|
+
text=remaining_buffer,
|
|
1402
|
+
context=embed_eval_context,
|
|
1403
|
+
resolver_func=evaluate_embed,
|
|
1404
|
+
types_to_resolve=LATE_EMBED_TYPES.copy(),
|
|
1405
|
+
resolution_mode=ResolutionMode.A2A_MESSAGE_TO_USER,
|
|
1406
|
+
log_identifier=log_id_prefix,
|
|
1407
|
+
config=embed_eval_config,
|
|
1003
1408
|
)
|
|
1004
1409
|
)
|
|
1005
1410
|
await self._handle_resolved_signals(
|
|
@@ -1025,6 +1430,32 @@ class BaseGatewayComponent(SamComponentBase):
|
|
|
1025
1430
|
|
|
1026
1431
|
if send_this_event_to_external:
|
|
1027
1432
|
if isinstance(parsed_event, Task):
|
|
1433
|
+
# Filter DataParts from final Task if gateway has filtering enabled
|
|
1434
|
+
# This prevents tool results and other internal data from appearing in user-facing output
|
|
1435
|
+
if (
|
|
1436
|
+
self.filter_tool_data_parts
|
|
1437
|
+
and parsed_event.status
|
|
1438
|
+
and parsed_event.status.message
|
|
1439
|
+
and parsed_event.status.message.parts
|
|
1440
|
+
):
|
|
1441
|
+
original_parts = a2a.get_parts_from_message(
|
|
1442
|
+
parsed_event.status.message
|
|
1443
|
+
)
|
|
1444
|
+
filtered_parts = [
|
|
1445
|
+
part
|
|
1446
|
+
for part in original_parts
|
|
1447
|
+
if self._should_include_data_part_in_final_output(part)
|
|
1448
|
+
]
|
|
1449
|
+
if len(filtered_parts) != len(original_parts):
|
|
1450
|
+
log.debug(
|
|
1451
|
+
"%s Filtered %d DataParts from final Task message",
|
|
1452
|
+
log_id_prefix,
|
|
1453
|
+
len(original_parts) - len(filtered_parts),
|
|
1454
|
+
)
|
|
1455
|
+
parsed_event.status.message = a2a.update_message_parts(
|
|
1456
|
+
parsed_event.status.message, filtered_parts
|
|
1457
|
+
)
|
|
1458
|
+
|
|
1028
1459
|
await self._send_final_response_to_external(
|
|
1029
1460
|
external_request_context, parsed_event
|
|
1030
1461
|
)
|