solace-agent-mesh 1.0.7__py3-none-any.whl → 1.1.0__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 +182 -42
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/callbacks.py +165 -104
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
- solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
- solace_agent_mesh/agent/adk/runner.py +7 -5
- solace_agent_mesh/agent/adk/services.py +9 -1
- solace_agent_mesh/agent/adk/setup.py +11 -0
- solace_agent_mesh/agent/adk/stream_parser.py +8 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
- solace_agent_mesh/agent/agent_llm.txt +355 -18
- solace_agent_mesh/agent/protocol/event_handlers.py +433 -296
- solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
- solace_agent_mesh/agent/sac/app.py +1 -1
- solace_agent_mesh/agent/sac/component.py +212 -517
- solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
- solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
- solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
- solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
- solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
- solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.a75ecc0d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +105 -0
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1756992446316.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/web_add_agent_step.py +12 -3
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
- solace_agent_mesh/cli/utils.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
- solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
- solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
- solace_agent_mesh/common/a2a/__init__.py +213 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
- solace_agent_mesh/common/a2a/artifact.py +328 -0
- solace_agent_mesh/common/a2a/events.py +183 -0
- solace_agent_mesh/common/a2a/message.py +307 -0
- solace_agent_mesh/common/a2a/protocol.py +513 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +653 -0
- solace_agent_mesh/common/a2a/types.py +54 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
- solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
- solace_agent_mesh/common/agent_registry.py +1 -1
- solace_agent_mesh/common/common_llm.txt +192 -70
- solace_agent_mesh/common/data_parts.py +99 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
- solace_agent_mesh/common/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sam_component_base.py +252 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
- solace_agent_mesh/common/services/services_llm.txt +206 -26
- solace_agent_mesh/common/utils/artifact_utils.py +29 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
- solace_agent_mesh/common/utils/utils_llm.txt +323 -42
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
- solace_agent_mesh/core_a2a/service.py +20 -44
- solace_agent_mesh/gateway/base/app.py +27 -1
- solace_agent_mesh/gateway/base/base_llm.txt +177 -72
- solace_agent_mesh/gateway/base/component.py +294 -523
- solace_agent_mesh/gateway/gateway_llm.txt +299 -58
- solace_agent_mesh/gateway/http_sse/component.py +156 -183
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
- solace_agent_mesh/gateway/http_sse/main.py +8 -10
- solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
- solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
- solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/templates/gateway_component_template.py +149 -98
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +144 -127
- solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1756146501924.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
- solace_agent_mesh/common/a2a_protocol.py +0 -564
- solace_agent_mesh/common/client/__init__.py +0 -4
- solace_agent_mesh/common/client/card_resolver.py +0 -21
- solace_agent_mesh/common/client/client.py +0 -85
- solace_agent_mesh/common/client/client_llm.txt +0 -133
- solace_agent_mesh/common/server/__init__.py +0 -4
- solace_agent_mesh/common/server/server.py +0 -122
- solace_agent_mesh/common/server/server_llm.txt +0 -169
- solace_agent_mesh/common/server/task_manager.py +0 -291
- solace_agent_mesh/common/server/utils.py +0 -28
- solace_agent_mesh/common/types.py +0 -411
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.d79f063b.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,11 +7,12 @@ import asyncio
|
|
|
7
7
|
import functools
|
|
8
8
|
import threading
|
|
9
9
|
import concurrent.futures
|
|
10
|
+
import uuid
|
|
10
11
|
import fnmatch
|
|
11
12
|
import base64
|
|
12
13
|
from datetime import datetime, timezone
|
|
13
14
|
import json
|
|
14
|
-
|
|
15
|
+
import json
|
|
15
16
|
from solace_ai_connector.common.message import (
|
|
16
17
|
Message as SolaceMessage,
|
|
17
18
|
)
|
|
@@ -37,39 +38,20 @@ from google.adk.agents.callback_context import CallbackContext
|
|
|
37
38
|
from google.adk.models.llm_request import LlmRequest
|
|
38
39
|
from google.genai import types as adk_types
|
|
39
40
|
from google.adk.tools.mcp_tool import MCPToolset
|
|
40
|
-
from
|
|
41
|
+
from a2a.types import (
|
|
41
42
|
AgentCard,
|
|
42
|
-
Task,
|
|
43
|
-
TaskStatus,
|
|
44
|
-
TaskState,
|
|
45
|
-
Message as A2AMessage,
|
|
46
|
-
TextPart,
|
|
47
|
-
FilePart,
|
|
48
|
-
DataPart,
|
|
49
|
-
FileContent,
|
|
50
43
|
Artifact as A2AArtifact,
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
Message as A2AMessage,
|
|
45
|
+
MessageSendParams,
|
|
46
|
+
SendMessageRequest,
|
|
47
|
+
TaskState,
|
|
48
|
+
TaskStatus,
|
|
53
49
|
TaskStatusUpdateEvent,
|
|
54
|
-
TaskArtifactUpdateEvent,
|
|
55
|
-
SendTaskRequest,
|
|
56
|
-
CancelTaskRequest,
|
|
57
|
-
TaskIdParams,
|
|
58
|
-
)
|
|
59
|
-
from ...common.a2a_protocol import (
|
|
60
|
-
get_a2a_base_topic,
|
|
61
|
-
get_discovery_topic,
|
|
62
|
-
get_agent_request_topic,
|
|
63
|
-
get_agent_response_topic,
|
|
64
|
-
get_client_response_topic,
|
|
65
|
-
get_peer_agent_status_topic,
|
|
66
|
-
format_and_route_adk_event,
|
|
67
|
-
get_gateway_status_topic,
|
|
68
50
|
)
|
|
51
|
+
from ...common import a2a
|
|
52
|
+
from ...common.data_parts import AgentProgressUpdateData
|
|
53
|
+
from ...common.a2a.translation import format_and_route_adk_event
|
|
69
54
|
from ...agent.utils.config_parser import resolve_instruction_provider
|
|
70
|
-
from ...agent.utils.artifact_helpers import (
|
|
71
|
-
get_latest_artifact_version,
|
|
72
|
-
)
|
|
73
55
|
from ...agent.adk.services import (
|
|
74
56
|
initialize_session_service,
|
|
75
57
|
initialize_artifact_service,
|
|
@@ -94,8 +76,7 @@ from ...agent.adk.invocation_monitor import InvocationMonitor
|
|
|
94
76
|
from ...common.middleware.registry import MiddlewareRegistry
|
|
95
77
|
from ...common.constants import DEFAULT_COMMUNICATION_TIMEOUT
|
|
96
78
|
from ...agent.tools.registry import tool_registry
|
|
97
|
-
from ...common.
|
|
98
|
-
from ...common.exceptions import MessageSizeExceededError
|
|
79
|
+
from ...common.sac.sam_component_base import SamComponentBase
|
|
99
80
|
|
|
100
81
|
if TYPE_CHECKING:
|
|
101
82
|
from .task_execution_context import TaskExecutionContext
|
|
@@ -123,7 +104,7 @@ info = {
|
|
|
123
104
|
InstructionProvider = Callable[[ReadonlyContext], str]
|
|
124
105
|
|
|
125
106
|
|
|
126
|
-
class SamAgentComponent(
|
|
107
|
+
class SamAgentComponent(SamComponentBase):
|
|
127
108
|
"""
|
|
128
109
|
A Solace AI Connector component that hosts a Google ADK agent,
|
|
129
110
|
communicating via the A2A protocol over Solace.
|
|
@@ -257,8 +238,6 @@ class SamAgentComponent(ComponentBase):
|
|
|
257
238
|
self.agent_card_tool_manifest: List[Dict[str, Any]] = []
|
|
258
239
|
self.peer_agents: Dict[str, Any] = {}
|
|
259
240
|
self._card_publish_timer_id: str = f"publish_card_{self.agent_name}"
|
|
260
|
-
self._async_loop = None
|
|
261
|
-
self._async_thread = None
|
|
262
241
|
self._async_init_future = None
|
|
263
242
|
self.peer_response_queues: Dict[str, asyncio.Queue] = {}
|
|
264
243
|
self.peer_response_queue_lock = threading.Lock()
|
|
@@ -416,40 +395,11 @@ class SamAgentComponent(ComponentBase):
|
|
|
416
395
|
raise RuntimeError(
|
|
417
396
|
f"Failed to initialize synchronous ADK services: {service_err}"
|
|
418
397
|
) from service_err
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
)
|
|
423
|
-
self._async_loop = asyncio.new_event_loop()
|
|
398
|
+
|
|
399
|
+
# Async init is now handled by the base class `run` method.
|
|
400
|
+
# We still need a future to signal completion from the async thread.
|
|
424
401
|
self._async_init_future = concurrent.futures.Future()
|
|
425
|
-
|
|
426
|
-
target=self._start_async_loop, daemon=True
|
|
427
|
-
)
|
|
428
|
-
self._async_thread.start()
|
|
429
|
-
init_coro_future = asyncio.run_coroutine_threadsafe(
|
|
430
|
-
self._perform_async_init(), self._async_loop
|
|
431
|
-
)
|
|
432
|
-
log.info(
|
|
433
|
-
"%s Waiting for async initialization to complete...",
|
|
434
|
-
self.log_identifier,
|
|
435
|
-
)
|
|
436
|
-
try:
|
|
437
|
-
init_coro_future.result(timeout=60)
|
|
438
|
-
self._async_init_future.result(timeout=1)
|
|
439
|
-
log.info(
|
|
440
|
-
"%s Async initialization completed successfully.",
|
|
441
|
-
self.log_identifier,
|
|
442
|
-
)
|
|
443
|
-
except Exception as init_err:
|
|
444
|
-
log.error(
|
|
445
|
-
"%s Async initialization failed during __init__: %s",
|
|
446
|
-
self.log_identifier,
|
|
447
|
-
init_err,
|
|
448
|
-
)
|
|
449
|
-
self.cleanup()
|
|
450
|
-
raise RuntimeError(
|
|
451
|
-
f"Failed to initialize component asynchronously: {init_err}"
|
|
452
|
-
) from init_err
|
|
402
|
+
|
|
453
403
|
publish_interval_sec = self.agent_card_publishing_config.get(
|
|
454
404
|
"interval_seconds"
|
|
455
405
|
)
|
|
@@ -798,11 +748,12 @@ class SamAgentComponent(ComponentBase):
|
|
|
798
748
|
sub_task_id,
|
|
799
749
|
)
|
|
800
750
|
task_id_for_peer = sub_task_id.replace(CORRELATION_DATA_PREFIX, "", 1)
|
|
801
|
-
|
|
802
|
-
|
|
751
|
+
cancel_request = a2a.create_cancel_task_request(
|
|
752
|
+
task_id=task_id_for_peer
|
|
753
|
+
)
|
|
803
754
|
user_props = {"clientId": self.agent_name}
|
|
804
755
|
peer_topic = self._get_agent_request_topic(peer_agent_name)
|
|
805
|
-
self.
|
|
756
|
+
self.publish_a2a_message(
|
|
806
757
|
payload=cancel_request.model_dump(exclude_none=True),
|
|
807
758
|
topic=peer_topic,
|
|
808
759
|
user_properties=user_props,
|
|
@@ -1286,17 +1237,17 @@ class SamAgentComponent(ComponentBase):
|
|
|
1286
1237
|
return
|
|
1287
1238
|
|
|
1288
1239
|
try:
|
|
1289
|
-
a2a_message =
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
timestamp=datetime.now(timezone.utc),
|
|
1240
|
+
a2a_message = a2a.create_agent_text_message(
|
|
1241
|
+
text=text_content,
|
|
1242
|
+
task_id=logical_task_id,
|
|
1243
|
+
context_id=a2a_context.get("contextId"),
|
|
1294
1244
|
)
|
|
1295
1245
|
event_metadata = {"agent_name": self.agent_name}
|
|
1296
|
-
status_update_event =
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1246
|
+
status_update_event = a2a.create_status_update(
|
|
1247
|
+
task_id=logical_task_id,
|
|
1248
|
+
context_id=a2a_context.get("contextId"),
|
|
1249
|
+
message=a2a_message,
|
|
1250
|
+
is_final=is_stream_terminating_content,
|
|
1300
1251
|
metadata=event_metadata,
|
|
1301
1252
|
)
|
|
1302
1253
|
|
|
@@ -1339,25 +1290,13 @@ class SamAgentComponent(ComponentBase):
|
|
|
1339
1290
|
return
|
|
1340
1291
|
|
|
1341
1292
|
try:
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
a2a_message = A2AMessage(role="agent", parts=[signal_data_part])
|
|
1350
|
-
task_status = TaskStatus(
|
|
1351
|
-
state=TaskState.WORKING,
|
|
1352
|
-
message=a2a_message,
|
|
1353
|
-
timestamp=datetime.now(timezone.utc),
|
|
1354
|
-
)
|
|
1355
|
-
event_metadata = {"agent_name": self.agent_name}
|
|
1356
|
-
status_update_event = TaskStatusUpdateEvent(
|
|
1357
|
-
id=logical_task_id,
|
|
1358
|
-
status=task_status,
|
|
1359
|
-
final=False,
|
|
1360
|
-
metadata=event_metadata,
|
|
1293
|
+
progress_data = AgentProgressUpdateData(status_text=status_text)
|
|
1294
|
+
status_update_event = a2a.create_data_signal_event(
|
|
1295
|
+
task_id=logical_task_id,
|
|
1296
|
+
context_id=a2a_context.get("contextId"),
|
|
1297
|
+
signal_data=progress_data,
|
|
1298
|
+
agent_name=self.agent_name,
|
|
1299
|
+
part_metadata={"source_embed_type": "status_update"},
|
|
1361
1300
|
)
|
|
1362
1301
|
|
|
1363
1302
|
await self._publish_status_update_with_buffer_flush(
|
|
@@ -1514,16 +1453,27 @@ class SamAgentComponent(ComponentBase):
|
|
|
1514
1453
|
)
|
|
1515
1454
|
|
|
1516
1455
|
try:
|
|
1517
|
-
rpc_response =
|
|
1518
|
-
|
|
1456
|
+
rpc_response = a2a.create_success_response(
|
|
1457
|
+
result=status_update_event, request_id=jsonrpc_request_id
|
|
1519
1458
|
)
|
|
1520
1459
|
payload_to_publish = rpc_response.model_dump(exclude_none=True)
|
|
1521
1460
|
|
|
1522
|
-
target_topic = a2a_context.get(
|
|
1461
|
+
target_topic = a2a_context.get(
|
|
1462
|
+
"statusTopic"
|
|
1463
|
+
) or a2a.get_gateway_status_topic(
|
|
1523
1464
|
self.namespace, self.get_gateway_id(), logical_task_id
|
|
1524
1465
|
)
|
|
1525
1466
|
|
|
1526
|
-
|
|
1467
|
+
# Construct user_properties to ensure ownership can be determined by gateways
|
|
1468
|
+
user_properties = {
|
|
1469
|
+
"a2aUserConfig": a2a_context.get("a2a_user_config"),
|
|
1470
|
+
"clientId": a2a_context.get("client_id"),
|
|
1471
|
+
"delegating_agent_name": self.get_config("agent_name"),
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
self._publish_a2a_event(
|
|
1475
|
+
payload_to_publish, target_topic, a2a_context, user_properties
|
|
1476
|
+
)
|
|
1527
1477
|
|
|
1528
1478
|
log.info(
|
|
1529
1479
|
"%s Published %s status update to %s.",
|
|
@@ -1541,121 +1491,6 @@ class SamAgentComponent(ComponentBase):
|
|
|
1541
1491
|
)
|
|
1542
1492
|
raise
|
|
1543
1493
|
|
|
1544
|
-
async def _translate_adk_part_to_a2a_filepart(
|
|
1545
|
-
self,
|
|
1546
|
-
adk_part: adk_types.Part,
|
|
1547
|
-
filename: str,
|
|
1548
|
-
a2a_context: Dict,
|
|
1549
|
-
version: Optional[int] = None,
|
|
1550
|
-
) -> Optional[FilePart]:
|
|
1551
|
-
"""
|
|
1552
|
-
Translates a loaded ADK Part (with inline_data) to an A2A FilePart
|
|
1553
|
-
based on the configured artifact_handling_mode.
|
|
1554
|
-
If version is not provided, it will be resolved to the latest.
|
|
1555
|
-
"""
|
|
1556
|
-
if self.artifact_handling_mode == "ignore":
|
|
1557
|
-
log.debug(
|
|
1558
|
-
"%s Artifact handling mode is 'ignore'. Skipping translation for '%s'.",
|
|
1559
|
-
self.log_identifier,
|
|
1560
|
-
filename,
|
|
1561
|
-
)
|
|
1562
|
-
return None
|
|
1563
|
-
|
|
1564
|
-
if not adk_part or not adk_part.inline_data:
|
|
1565
|
-
log.warning(
|
|
1566
|
-
"%s Cannot translate artifact '%s': ADK Part is missing or has no inline_data.",
|
|
1567
|
-
self.log_identifier,
|
|
1568
|
-
filename,
|
|
1569
|
-
)
|
|
1570
|
-
return None
|
|
1571
|
-
|
|
1572
|
-
resolved_version = version
|
|
1573
|
-
if resolved_version is None:
|
|
1574
|
-
try:
|
|
1575
|
-
resolved_version = await get_latest_artifact_version(
|
|
1576
|
-
artifact_service=self.artifact_service,
|
|
1577
|
-
app_name=self.get_config("agent_name"),
|
|
1578
|
-
user_id=a2a_context.get("user_id"),
|
|
1579
|
-
session_id=a2a_context.get("session_id"),
|
|
1580
|
-
filename=filename,
|
|
1581
|
-
)
|
|
1582
|
-
if resolved_version is None:
|
|
1583
|
-
log.error(
|
|
1584
|
-
"%s Could not resolve latest version for artifact '%s'.",
|
|
1585
|
-
self.log_identifier,
|
|
1586
|
-
filename,
|
|
1587
|
-
)
|
|
1588
|
-
return None
|
|
1589
|
-
except Exception as e:
|
|
1590
|
-
log.exception(
|
|
1591
|
-
"%s Failed to resolve latest version for artifact '%s': %s",
|
|
1592
|
-
self.log_identifier,
|
|
1593
|
-
filename,
|
|
1594
|
-
e,
|
|
1595
|
-
)
|
|
1596
|
-
return None
|
|
1597
|
-
|
|
1598
|
-
mime_type = adk_part.inline_data.mime_type
|
|
1599
|
-
data_bytes = adk_part.inline_data.data
|
|
1600
|
-
file_content: Optional[FileContent] = None
|
|
1601
|
-
|
|
1602
|
-
try:
|
|
1603
|
-
if self.artifact_handling_mode == "embed":
|
|
1604
|
-
encoded_bytes = base64.b64encode(data_bytes).decode("utf-8")
|
|
1605
|
-
file_content = FileContent(
|
|
1606
|
-
name=filename, mimeType=mime_type, bytes=encoded_bytes
|
|
1607
|
-
)
|
|
1608
|
-
log.debug(
|
|
1609
|
-
"%s Embedding artifact '%s' (size: %d bytes) for A2A message.",
|
|
1610
|
-
self.log_identifier,
|
|
1611
|
-
filename,
|
|
1612
|
-
len(data_bytes),
|
|
1613
|
-
)
|
|
1614
|
-
|
|
1615
|
-
elif self.artifact_handling_mode == "reference":
|
|
1616
|
-
adk_app_name = self.get_config("agent_name")
|
|
1617
|
-
user_id = a2a_context.get("user_id")
|
|
1618
|
-
original_session_id = a2a_context.get("session_id")
|
|
1619
|
-
|
|
1620
|
-
if not all([adk_app_name, user_id, original_session_id]):
|
|
1621
|
-
log.error(
|
|
1622
|
-
"%s Cannot create artifact reference URI: missing context (app_name, user_id, or session_id).",
|
|
1623
|
-
self.log_identifier,
|
|
1624
|
-
)
|
|
1625
|
-
return None
|
|
1626
|
-
|
|
1627
|
-
artifact_uri = f"artifact://{adk_app_name}/{user_id}/{original_session_id}/{filename}?version={resolved_version}"
|
|
1628
|
-
|
|
1629
|
-
log.info(
|
|
1630
|
-
"%s Creating reference URI for artifact: %s",
|
|
1631
|
-
self.log_identifier,
|
|
1632
|
-
artifact_uri,
|
|
1633
|
-
)
|
|
1634
|
-
file_content = FileContent(
|
|
1635
|
-
name=filename, mimeType=mime_type, uri=artifact_uri
|
|
1636
|
-
)
|
|
1637
|
-
|
|
1638
|
-
if file_content:
|
|
1639
|
-
return FilePart(file=file_content)
|
|
1640
|
-
else:
|
|
1641
|
-
log.warning(
|
|
1642
|
-
"%s No FileContent created for artifact '%s' despite mode '%s'.",
|
|
1643
|
-
self.log_identifier,
|
|
1644
|
-
filename,
|
|
1645
|
-
self.artifact_handling_mode,
|
|
1646
|
-
)
|
|
1647
|
-
return None
|
|
1648
|
-
|
|
1649
|
-
except Exception as e:
|
|
1650
|
-
log.exception(
|
|
1651
|
-
"%s Error translating artifact '%s' to A2A FilePart (mode: %s): %s",
|
|
1652
|
-
self.log_identifier,
|
|
1653
|
-
filename,
|
|
1654
|
-
self.artifact_handling_mode,
|
|
1655
|
-
e,
|
|
1656
|
-
)
|
|
1657
|
-
return None
|
|
1658
|
-
|
|
1659
1494
|
async def _filter_text_from_final_streaming_event(
|
|
1660
1495
|
self, adk_event: ADKEvent, a2a_context: Dict
|
|
1661
1496
|
) -> ADKEvent:
|
|
@@ -1972,7 +1807,7 @@ class SamAgentComponent(ComponentBase):
|
|
|
1972
1807
|
namespace = self.get_config("namespace")
|
|
1973
1808
|
gateway_id = self.get_gateway_id()
|
|
1974
1809
|
|
|
1975
|
-
artifact_topic = peer_status_topic or get_gateway_status_topic(
|
|
1810
|
+
artifact_topic = peer_status_topic or a2a.get_gateway_status_topic(
|
|
1976
1811
|
namespace, gateway_id, logical_task_id
|
|
1977
1812
|
)
|
|
1978
1813
|
|
|
@@ -2012,18 +1847,36 @@ class SamAgentComponent(ComponentBase):
|
|
|
2012
1847
|
)
|
|
2013
1848
|
continue
|
|
2014
1849
|
|
|
2015
|
-
a2a_file_part = await
|
|
2016
|
-
loaded_adk_part,
|
|
1850
|
+
a2a_file_part = await a2a.translate_adk_part_to_a2a_filepart(
|
|
1851
|
+
adk_part=loaded_adk_part,
|
|
1852
|
+
filename=filename,
|
|
1853
|
+
a2a_context=a2a_context,
|
|
1854
|
+
artifact_service=self.artifact_service,
|
|
1855
|
+
artifact_handling_mode=self.artifact_handling_mode,
|
|
1856
|
+
adk_app_name=self.get_config("agent_name"),
|
|
1857
|
+
log_identifier=self.log_identifier,
|
|
1858
|
+
version=version,
|
|
2017
1859
|
)
|
|
2018
1860
|
|
|
2019
1861
|
if a2a_file_part:
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
1862
|
+
a2a_message = a2a.create_agent_parts_message(
|
|
1863
|
+
parts=[a2a_file_part],
|
|
1864
|
+
task_id=logical_task_id,
|
|
1865
|
+
context_id=original_session_id,
|
|
2023
1866
|
)
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
1867
|
+
task_status = a2a.create_task_status(
|
|
1868
|
+
state=TaskState.working, message=a2a_message
|
|
1869
|
+
)
|
|
1870
|
+
status_update_event = TaskStatusUpdateEvent(
|
|
1871
|
+
task_id=logical_task_id,
|
|
1872
|
+
context_id=original_session_id,
|
|
1873
|
+
status=task_status,
|
|
1874
|
+
final=False,
|
|
1875
|
+
kind="status-update",
|
|
1876
|
+
)
|
|
1877
|
+
artifact_payload = a2a.create_success_response(
|
|
1878
|
+
result=status_update_event,
|
|
1879
|
+
request_id=a2a_context.get("jsonrpc_request_id"),
|
|
2027
1880
|
).model_dump(exclude_none=True)
|
|
2028
1881
|
|
|
2029
1882
|
self._publish_a2a_event(
|
|
@@ -2031,7 +1884,7 @@ class SamAgentComponent(ComponentBase):
|
|
|
2031
1884
|
)
|
|
2032
1885
|
|
|
2033
1886
|
log.info(
|
|
2034
|
-
"%s Published
|
|
1887
|
+
"%s Published TaskStatusUpdateEvent with FilePart for '%s' to %s",
|
|
2035
1888
|
log_id,
|
|
2036
1889
|
filename,
|
|
2037
1890
|
artifact_topic,
|
|
@@ -2052,53 +1905,51 @@ class SamAgentComponent(ComponentBase):
|
|
|
2052
1905
|
e,
|
|
2053
1906
|
)
|
|
2054
1907
|
|
|
2055
|
-
def _format_final_task_status(
|
|
1908
|
+
def _format_final_task_status(
|
|
1909
|
+
self, last_event: Optional[ADKEvent], override_text: Optional[str] = None
|
|
1910
|
+
) -> TaskStatus:
|
|
2056
1911
|
"""Helper to format the final TaskStatus based on the last ADK event."""
|
|
2057
1912
|
log.debug(
|
|
2058
1913
|
"%s Formatting final task status from last ADK event %s",
|
|
2059
1914
|
self.log_identifier,
|
|
2060
|
-
last_event.id,
|
|
1915
|
+
last_event.id if last_event else "None",
|
|
2061
1916
|
)
|
|
2062
|
-
a2a_state = TaskState.
|
|
1917
|
+
a2a_state = TaskState.completed
|
|
2063
1918
|
a2a_parts = []
|
|
2064
1919
|
|
|
2065
|
-
if
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
DataPart(
|
|
2075
|
-
data=response_data,
|
|
2076
|
-
metadata={"tool_name": part.function_response.name},
|
|
2077
|
-
)
|
|
2078
|
-
)
|
|
2079
|
-
else:
|
|
2080
|
-
a2a_parts.append(
|
|
2081
|
-
TextPart(
|
|
2082
|
-
text=f"Tool {part.function_response.name} result: {str(response_data)}"
|
|
2083
|
-
)
|
|
2084
|
-
)
|
|
2085
|
-
except Exception:
|
|
2086
|
-
a2a_parts.append(
|
|
2087
|
-
TextPart(
|
|
2088
|
-
text=f"[Tool {part.function_response.name} result omitted]"
|
|
1920
|
+
if override_text is not None:
|
|
1921
|
+
a2a_parts.append(a2a.create_text_part(text=override_text))
|
|
1922
|
+
# Add non-text parts from the last event
|
|
1923
|
+
if last_event and last_event.content and last_event.content.parts:
|
|
1924
|
+
for part in last_event.content.parts:
|
|
1925
|
+
if part.text is None:
|
|
1926
|
+
if part.function_response:
|
|
1927
|
+
a2a_parts.extend(
|
|
1928
|
+
a2a.translate_adk_function_response_to_a2a_parts(part)
|
|
2089
1929
|
)
|
|
1930
|
+
else:
|
|
1931
|
+
# Original logic
|
|
1932
|
+
if last_event and last_event.content and last_event.content.parts:
|
|
1933
|
+
for part in last_event.content.parts:
|
|
1934
|
+
if part.text:
|
|
1935
|
+
a2a_parts.append(a2a.create_text_part(text=part.text))
|
|
1936
|
+
elif part.function_response:
|
|
1937
|
+
a2a_parts.extend(
|
|
1938
|
+
a2a.translate_adk_function_response_to_a2a_parts(part)
|
|
2090
1939
|
)
|
|
2091
1940
|
|
|
2092
|
-
|
|
1941
|
+
if last_event and last_event.actions:
|
|
2093
1942
|
if last_event.actions.requested_auth_configs:
|
|
2094
|
-
a2a_state = TaskState.
|
|
2095
|
-
a2a_parts.append(
|
|
1943
|
+
a2a_state = TaskState.input_required
|
|
1944
|
+
a2a_parts.append(
|
|
1945
|
+
a2a.create_text_part(text="[Agent requires input/authentication]")
|
|
1946
|
+
)
|
|
2096
1947
|
|
|
2097
1948
|
if not a2a_parts:
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
return
|
|
1949
|
+
a2a_message = a2a.create_agent_text_message(text="")
|
|
1950
|
+
else:
|
|
1951
|
+
a2a_message = a2a.create_agent_parts_message(parts=a2a_parts)
|
|
1952
|
+
return a2a.create_task_status(state=a2a_state, message=a2a_message)
|
|
2102
1953
|
|
|
2103
1954
|
async def finalize_task_success(self, a2a_context: Dict):
|
|
2104
1955
|
"""
|
|
@@ -2156,55 +2007,16 @@ class SamAgentComponent(ComponentBase):
|
|
|
2156
2007
|
logical_task_id,
|
|
2157
2008
|
len(aggregated_text.encode("utf-8")),
|
|
2158
2009
|
)
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
if aggregated_text:
|
|
2162
|
-
final_a2a_parts.append(TextPart(text=aggregated_text))
|
|
2163
|
-
|
|
2164
|
-
if last_event and last_event.content and last_event.content.parts:
|
|
2165
|
-
for part in last_event.content.parts:
|
|
2166
|
-
if part.text is None:
|
|
2167
|
-
if part.function_response:
|
|
2168
|
-
try:
|
|
2169
|
-
response_data = part.function_response.response
|
|
2170
|
-
if isinstance(response_data, dict):
|
|
2171
|
-
final_a2a_parts.append(
|
|
2172
|
-
DataPart(
|
|
2173
|
-
data=response_data,
|
|
2174
|
-
metadata={
|
|
2175
|
-
"tool_name": part.function_response.name
|
|
2176
|
-
},
|
|
2177
|
-
)
|
|
2178
|
-
)
|
|
2179
|
-
else:
|
|
2180
|
-
final_a2a_parts.append(
|
|
2181
|
-
TextPart(
|
|
2182
|
-
text=f"Tool {part.function_response.name} result: {str(response_data)}"
|
|
2183
|
-
)
|
|
2184
|
-
)
|
|
2185
|
-
except Exception:
|
|
2186
|
-
final_a2a_parts.append(
|
|
2187
|
-
TextPart(
|
|
2188
|
-
text=f"[Tool {part.function_response.name} result omitted]"
|
|
2189
|
-
)
|
|
2190
|
-
)
|
|
2191
|
-
|
|
2192
|
-
if not final_a2a_parts:
|
|
2193
|
-
final_a2a_parts.append(TextPart(text=""))
|
|
2194
|
-
|
|
2195
|
-
final_status = TaskStatus(
|
|
2196
|
-
state=TaskState.COMPLETED,
|
|
2197
|
-
message=A2AMessage(role="agent", parts=final_a2a_parts),
|
|
2010
|
+
final_status = self._format_final_task_status(
|
|
2011
|
+
last_event, override_text=aggregated_text
|
|
2198
2012
|
)
|
|
2199
2013
|
else:
|
|
2200
2014
|
if last_event:
|
|
2201
2015
|
final_status = self._format_final_task_status(last_event)
|
|
2202
2016
|
else:
|
|
2203
|
-
final_status =
|
|
2204
|
-
state=TaskState.
|
|
2205
|
-
message=
|
|
2206
|
-
role="agent", parts=[TextPart(text="Task completed.")]
|
|
2207
|
-
),
|
|
2017
|
+
final_status = a2a.create_task_status(
|
|
2018
|
+
state=TaskState.completed,
|
|
2019
|
+
message=a2a.create_agent_text_message(text="Task completed."),
|
|
2208
2020
|
)
|
|
2209
2021
|
|
|
2210
2022
|
final_a2a_artifacts: List[A2AArtifact] = []
|
|
@@ -2224,16 +2036,18 @@ class SamAgentComponent(ComponentBase):
|
|
|
2224
2036
|
len(task_context.produced_artifacts),
|
|
2225
2037
|
)
|
|
2226
2038
|
|
|
2227
|
-
final_task =
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2039
|
+
final_task = a2a.create_final_task(
|
|
2040
|
+
task_id=logical_task_id,
|
|
2041
|
+
context_id=original_session_id,
|
|
2042
|
+
final_status=final_status,
|
|
2231
2043
|
artifacts=(final_a2a_artifacts if final_a2a_artifacts else None),
|
|
2232
2044
|
metadata=final_task_metadata,
|
|
2233
2045
|
)
|
|
2234
|
-
final_response =
|
|
2046
|
+
final_response = a2a.create_success_response(
|
|
2047
|
+
result=final_task, request_id=jsonrpc_request_id
|
|
2048
|
+
)
|
|
2235
2049
|
a2a_payload = final_response.model_dump(exclude_none=True)
|
|
2236
|
-
target_topic = peer_reply_topic or get_client_response_topic(
|
|
2050
|
+
target_topic = peer_reply_topic or a2a.get_client_response_topic(
|
|
2237
2051
|
namespace, client_id
|
|
2238
2052
|
)
|
|
2239
2053
|
|
|
@@ -2300,17 +2114,15 @@ class SamAgentComponent(ComponentBase):
|
|
|
2300
2114
|
client_id = a2a_context.get("client_id")
|
|
2301
2115
|
peer_reply_topic = a2a_context.get("replyToTopic")
|
|
2302
2116
|
namespace = self.get_config("namespace")
|
|
2303
|
-
error_response =
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
)
|
|
2310
|
-
target_topic = peer_reply_topic or get_client_response_topic(
|
|
2117
|
+
error_response = a2a.create_internal_error_response(
|
|
2118
|
+
message=f"Failed to finalize successful task: {e}",
|
|
2119
|
+
request_id=jsonrpc_request_id,
|
|
2120
|
+
data={"taskId": logical_task_id},
|
|
2121
|
+
)
|
|
2122
|
+
target_topic = peer_reply_topic or a2a.get_client_response_topic(
|
|
2311
2123
|
namespace, client_id
|
|
2312
2124
|
)
|
|
2313
|
-
self.
|
|
2125
|
+
self.publish_a2a_message(
|
|
2314
2126
|
error_response.model_dump(exclude_none=True), target_topic
|
|
2315
2127
|
)
|
|
2316
2128
|
except Exception as report_err:
|
|
@@ -2340,23 +2152,24 @@ class SamAgentComponent(ComponentBase):
|
|
|
2340
2152
|
peer_reply_topic = a2a_context.get("replyToTopic")
|
|
2341
2153
|
namespace = self.get_config("namespace")
|
|
2342
2154
|
|
|
2343
|
-
canceled_status =
|
|
2344
|
-
state=TaskState.
|
|
2345
|
-
message=
|
|
2346
|
-
|
|
2347
|
-
parts=[TextPart(text="Task cancelled by request.")],
|
|
2155
|
+
canceled_status = a2a.create_task_status(
|
|
2156
|
+
state=TaskState.canceled,
|
|
2157
|
+
message=a2a.create_agent_text_message(
|
|
2158
|
+
text="Task cancelled by request."
|
|
2348
2159
|
),
|
|
2349
2160
|
)
|
|
2350
2161
|
agent_name = self.get_config("agent_name")
|
|
2351
|
-
final_task =
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2162
|
+
final_task = a2a.create_final_task(
|
|
2163
|
+
task_id=logical_task_id,
|
|
2164
|
+
context_id=a2a_context.get("contextId"),
|
|
2165
|
+
final_status=canceled_status,
|
|
2355
2166
|
metadata={"agent_name": agent_name},
|
|
2356
2167
|
)
|
|
2357
|
-
final_response =
|
|
2168
|
+
final_response = a2a.create_success_response(
|
|
2169
|
+
result=final_task, request_id=jsonrpc_request_id
|
|
2170
|
+
)
|
|
2358
2171
|
a2a_payload = final_response.model_dump(exclude_none=True)
|
|
2359
|
-
target_topic = peer_reply_topic or get_client_response_topic(
|
|
2172
|
+
target_topic = peer_reply_topic or a2a.get_client_response_topic(
|
|
2360
2173
|
namespace, client_id
|
|
2361
2174
|
)
|
|
2362
2175
|
|
|
@@ -2416,7 +2229,7 @@ class SamAgentComponent(ComponentBase):
|
|
|
2416
2229
|
)
|
|
2417
2230
|
try:
|
|
2418
2231
|
# Create the status update event
|
|
2419
|
-
tool_error_data_part =
|
|
2232
|
+
tool_error_data_part = a2a.create_data_part(
|
|
2420
2233
|
data={
|
|
2421
2234
|
"a2a_signal_type": "tool_execution_error",
|
|
2422
2235
|
"error_message": str(exception),
|
|
@@ -2424,17 +2237,16 @@ class SamAgentComponent(ComponentBase):
|
|
|
2424
2237
|
}
|
|
2425
2238
|
)
|
|
2426
2239
|
|
|
2427
|
-
status_message =
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
timestamp=datetime.now(timezone.utc),
|
|
2240
|
+
status_message = a2a.create_agent_parts_message(
|
|
2241
|
+
parts=[tool_error_data_part],
|
|
2242
|
+
task_id=logical_task_id,
|
|
2243
|
+
context_id=a2a_context.get("contextId"),
|
|
2432
2244
|
)
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2245
|
+
status_update_event = a2a.create_status_update(
|
|
2246
|
+
task_id=logical_task_id,
|
|
2247
|
+
context_id=a2a_context.get("contextId"),
|
|
2248
|
+
message=status_message,
|
|
2249
|
+
is_final=False,
|
|
2438
2250
|
metadata={"agent_name": self.get_config("agent_name")},
|
|
2439
2251
|
)
|
|
2440
2252
|
|
|
@@ -2554,15 +2366,14 @@ class SamAgentComponent(ComponentBase):
|
|
|
2554
2366
|
"Otherwise, you can start a new topic."
|
|
2555
2367
|
)
|
|
2556
2368
|
|
|
2557
|
-
|
|
2369
|
+
final_response = a2a.create_internal_error_response(
|
|
2558
2370
|
message=limit_message_text,
|
|
2371
|
+
request_id=jsonrpc_request_id,
|
|
2559
2372
|
data={"taskId": logical_task_id, "reason": "llm_call_limit_reached"},
|
|
2560
2373
|
)
|
|
2561
|
-
|
|
2562
|
-
final_response = JSONRPCResponse(id=jsonrpc_request_id, error=error_payload)
|
|
2563
2374
|
a2a_payload = final_response.model_dump(exclude_none=True)
|
|
2564
2375
|
|
|
2565
|
-
target_topic = peer_reply_topic or get_client_response_topic(
|
|
2376
|
+
target_topic = peer_reply_topic or a2a.get_client_response_topic(
|
|
2566
2377
|
namespace, client_id
|
|
2567
2378
|
)
|
|
2568
2379
|
|
|
@@ -2631,28 +2442,25 @@ class SamAgentComponent(ComponentBase):
|
|
|
2631
2442
|
peer_reply_topic = a2a_context.get("replyToTopic")
|
|
2632
2443
|
namespace = self.get_config("namespace")
|
|
2633
2444
|
|
|
2634
|
-
failed_status =
|
|
2635
|
-
state=TaskState.
|
|
2636
|
-
message=
|
|
2637
|
-
|
|
2638
|
-
parts=[
|
|
2639
|
-
TextPart(
|
|
2640
|
-
text="An unexpected error occurred during tool execution. Please try your request again. If the problem persists, contact an administrator."
|
|
2641
|
-
)
|
|
2642
|
-
],
|
|
2445
|
+
failed_status = a2a.create_task_status(
|
|
2446
|
+
state=TaskState.failed,
|
|
2447
|
+
message=a2a.create_agent_text_message(
|
|
2448
|
+
text="An unexpected error occurred during tool execution. Please try your request again. If the problem persists, contact an administrator."
|
|
2643
2449
|
),
|
|
2644
2450
|
)
|
|
2645
2451
|
|
|
2646
|
-
final_task =
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2452
|
+
final_task = a2a.create_final_task(
|
|
2453
|
+
task_id=logical_task_id,
|
|
2454
|
+
context_id=a2a_context.get("contextId"),
|
|
2455
|
+
final_status=failed_status,
|
|
2650
2456
|
metadata={"agent_name": self.get_config("agent_name")},
|
|
2651
2457
|
)
|
|
2652
2458
|
|
|
2653
|
-
final_response =
|
|
2459
|
+
final_response = a2a.create_success_response(
|
|
2460
|
+
result=final_task, request_id=jsonrpc_request_id
|
|
2461
|
+
)
|
|
2654
2462
|
a2a_payload = final_response.model_dump(exclude_none=True)
|
|
2655
|
-
target_topic = peer_reply_topic or get_client_response_topic(
|
|
2463
|
+
target_topic = peer_reply_topic or a2a.get_client_response_topic(
|
|
2656
2464
|
namespace, client_id
|
|
2657
2465
|
)
|
|
2658
2466
|
|
|
@@ -2871,21 +2679,21 @@ class SamAgentComponent(ComponentBase):
|
|
|
2871
2679
|
|
|
2872
2680
|
def _get_a2a_base_topic(self) -> str:
|
|
2873
2681
|
"""Returns the base topic prefix using helper."""
|
|
2874
|
-
return get_a2a_base_topic(self.namespace)
|
|
2682
|
+
return a2a.get_a2a_base_topic(self.namespace)
|
|
2875
2683
|
|
|
2876
2684
|
def _get_discovery_topic(self) -> str:
|
|
2877
2685
|
"""Returns the discovery topic using helper."""
|
|
2878
|
-
return get_discovery_topic(self.namespace)
|
|
2686
|
+
return a2a.get_discovery_topic(self.namespace)
|
|
2879
2687
|
|
|
2880
2688
|
def _get_agent_request_topic(self, agent_id: str) -> str:
|
|
2881
2689
|
"""Returns the agent request topic using helper."""
|
|
2882
|
-
return get_agent_request_topic(self.namespace, agent_id)
|
|
2690
|
+
return a2a.get_agent_request_topic(self.namespace, agent_id)
|
|
2883
2691
|
|
|
2884
2692
|
def _get_agent_response_topic(
|
|
2885
2693
|
self, delegating_agent_name: str, sub_task_id: str
|
|
2886
2694
|
) -> str:
|
|
2887
2695
|
"""Returns the agent response topic using helper."""
|
|
2888
|
-
return get_agent_response_topic(
|
|
2696
|
+
return a2a.get_agent_response_topic(
|
|
2889
2697
|
self.namespace, delegating_agent_name, sub_task_id
|
|
2890
2698
|
)
|
|
2891
2699
|
|
|
@@ -2893,92 +2701,41 @@ class SamAgentComponent(ComponentBase):
|
|
|
2893
2701
|
self, delegating_agent_name: str, sub_task_id: str
|
|
2894
2702
|
) -> str:
|
|
2895
2703
|
"""Returns the peer agent status topic using helper."""
|
|
2896
|
-
return get_peer_agent_status_topic(
|
|
2704
|
+
return a2a.get_peer_agent_status_topic(
|
|
2897
2705
|
self.namespace, delegating_agent_name, sub_task_id
|
|
2898
2706
|
)
|
|
2899
2707
|
|
|
2900
2708
|
def _get_client_response_topic(self, client_id: str) -> str:
|
|
2901
2709
|
"""Returns the client response topic using helper."""
|
|
2902
|
-
return get_client_response_topic(self.namespace, client_id)
|
|
2710
|
+
return a2a.get_client_response_topic(self.namespace, client_id)
|
|
2903
2711
|
|
|
2904
|
-
def
|
|
2905
|
-
self,
|
|
2712
|
+
def _publish_a2a_event(
|
|
2713
|
+
self,
|
|
2714
|
+
payload: Dict,
|
|
2715
|
+
topic: str,
|
|
2716
|
+
a2a_context: Dict,
|
|
2717
|
+
user_properties_override: Optional[Dict] = None,
|
|
2906
2718
|
):
|
|
2907
|
-
"""Helper to publish A2A messages via the SAC App."""
|
|
2908
|
-
try:
|
|
2909
|
-
max_size_bytes = self.max_message_size_bytes
|
|
2910
|
-
|
|
2911
|
-
# Validate message size
|
|
2912
|
-
is_valid, actual_size = validate_message_size(
|
|
2913
|
-
payload, max_size_bytes, self.log_identifier
|
|
2914
|
-
)
|
|
2915
|
-
|
|
2916
|
-
if not is_valid:
|
|
2917
|
-
error_msg = (
|
|
2918
|
-
f"Message size validation failed: payload size ({actual_size} bytes) "
|
|
2919
|
-
f"exceeds maximum allowed size ({max_size_bytes} bytes)"
|
|
2920
|
-
)
|
|
2921
|
-
log.error("%s %s", self.log_identifier, error_msg)
|
|
2922
|
-
raise MessageSizeExceededError(actual_size, max_size_bytes, error_msg)
|
|
2923
|
-
|
|
2924
|
-
# Debug logging to show message size when publishing
|
|
2925
|
-
log.debug(
|
|
2926
|
-
"%s Publishing message to topic %s (size: %d bytes)",
|
|
2927
|
-
self.log_identifier,
|
|
2928
|
-
topic,
|
|
2929
|
-
actual_size,
|
|
2930
|
-
)
|
|
2931
|
-
|
|
2932
|
-
app = self.get_app()
|
|
2933
|
-
if app:
|
|
2934
|
-
if self.invocation_monitor:
|
|
2935
|
-
self.invocation_monitor.log_message_event(
|
|
2936
|
-
direction="PUBLISHED",
|
|
2937
|
-
topic=topic,
|
|
2938
|
-
payload=payload,
|
|
2939
|
-
component_identifier=self.log_identifier,
|
|
2940
|
-
)
|
|
2941
|
-
app.send_message(
|
|
2942
|
-
payload=payload, topic=topic, user_properties=user_properties
|
|
2943
|
-
)
|
|
2944
|
-
else:
|
|
2945
|
-
log.error(
|
|
2946
|
-
"%s Cannot publish message: Not running within a SAC App context.",
|
|
2947
|
-
self.log_identifier,
|
|
2948
|
-
)
|
|
2949
|
-
except MessageSizeExceededError:
|
|
2950
|
-
# Re-raise MessageSizeExceededError without wrapping
|
|
2951
|
-
raise
|
|
2952
|
-
except Exception as e:
|
|
2953
|
-
log.exception(
|
|
2954
|
-
"%s Failed to publish A2A message to topic %s: %s",
|
|
2955
|
-
self.log_identifier,
|
|
2956
|
-
topic,
|
|
2957
|
-
e,
|
|
2958
|
-
)
|
|
2959
|
-
raise
|
|
2960
|
-
|
|
2961
|
-
def _publish_a2a_event(self, payload: Dict, topic: str, a2a_context: Dict):
|
|
2962
2719
|
"""
|
|
2963
2720
|
Centralized helper to publish an A2A event, ensuring user properties
|
|
2964
|
-
are consistently attached from the a2a_context.
|
|
2721
|
+
are consistently attached from the a2a_context or an override.
|
|
2965
2722
|
"""
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2723
|
+
if user_properties_override is not None:
|
|
2724
|
+
user_properties = user_properties_override
|
|
2725
|
+
else:
|
|
2726
|
+
user_properties = {}
|
|
2727
|
+
if a2a_context.get("a2a_user_config"):
|
|
2728
|
+
user_properties["a2aUserConfig"] = a2a_context["a2a_user_config"]
|
|
2969
2729
|
|
|
2970
|
-
self.
|
|
2730
|
+
self.publish_a2a_message(payload, topic, user_properties)
|
|
2971
2731
|
|
|
2972
2732
|
def submit_a2a_task(
|
|
2973
2733
|
self,
|
|
2974
2734
|
target_agent_name: str,
|
|
2975
2735
|
a2a_message: A2AMessage,
|
|
2976
|
-
original_session_id: str,
|
|
2977
|
-
main_logical_task_id: str,
|
|
2978
2736
|
user_id: str,
|
|
2979
2737
|
user_config: Dict[str, Any],
|
|
2980
2738
|
sub_task_id: str,
|
|
2981
|
-
function_call_id: Optional[str] = None,
|
|
2982
2739
|
) -> str:
|
|
2983
2740
|
"""
|
|
2984
2741
|
Submits a task to a peer agent in a non-blocking way.
|
|
@@ -2987,25 +2744,18 @@ class SamAgentComponent(ComponentBase):
|
|
|
2987
2744
|
log_identifier_helper = (
|
|
2988
2745
|
f"{self.log_identifier}[SubmitA2ATask:{target_agent_name}]"
|
|
2989
2746
|
)
|
|
2747
|
+
main_task_id = a2a_message.metadata.get("parentTaskId", "unknown_parent")
|
|
2990
2748
|
log.debug(
|
|
2991
2749
|
"%s Submitting non-blocking task for main task %s",
|
|
2992
2750
|
log_identifier_helper,
|
|
2993
|
-
|
|
2751
|
+
main_task_id,
|
|
2994
2752
|
)
|
|
2995
2753
|
|
|
2996
2754
|
peer_request_topic = self._get_agent_request_topic(target_agent_name)
|
|
2997
2755
|
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
"message": a2a_message.model_dump(exclude_none=True),
|
|
3002
|
-
"metadata": {
|
|
3003
|
-
"sessionBehavior": "RUN_BASED",
|
|
3004
|
-
"parentTaskId": main_logical_task_id,
|
|
3005
|
-
"function_call_id": function_call_id,
|
|
3006
|
-
},
|
|
3007
|
-
}
|
|
3008
|
-
a2a_request = SendTaskRequest(params=a2a_request_params)
|
|
2756
|
+
# Create a compliant SendMessageRequest
|
|
2757
|
+
send_params = MessageSendParams(message=a2a_message)
|
|
2758
|
+
a2a_request = SendMessageRequest(id=sub_task_id, params=send_params)
|
|
3009
2759
|
|
|
3010
2760
|
delegating_agent_name = self.get_config("agent_name")
|
|
3011
2761
|
reply_to_topic = self._get_agent_response_topic(
|
|
@@ -3025,8 +2775,8 @@ class SamAgentComponent(ComponentBase):
|
|
|
3025
2775
|
if isinstance(user_config, dict):
|
|
3026
2776
|
user_properties["a2aUserConfig"] = user_config
|
|
3027
2777
|
|
|
3028
|
-
self.
|
|
3029
|
-
payload=a2a_request.model_dump(exclude_none=True),
|
|
2778
|
+
self.publish_a2a_message(
|
|
2779
|
+
payload=a2a_request.model_dump(by_alias=True, exclude_none=True),
|
|
3030
2780
|
topic=peer_request_topic,
|
|
3031
2781
|
user_properties=user_properties,
|
|
3032
2782
|
)
|
|
@@ -3072,25 +2822,6 @@ class SamAgentComponent(ComponentBase):
|
|
|
3072
2822
|
exc_info=e,
|
|
3073
2823
|
)
|
|
3074
2824
|
|
|
3075
|
-
def _start_async_loop(self):
|
|
3076
|
-
"""Target method for the dedicated async thread."""
|
|
3077
|
-
log.info("%s Dedicated async thread started.", self.log_identifier)
|
|
3078
|
-
try:
|
|
3079
|
-
asyncio.set_event_loop(self._async_loop)
|
|
3080
|
-
self._async_loop.run_forever()
|
|
3081
|
-
except Exception as e:
|
|
3082
|
-
log.exception(
|
|
3083
|
-
"%s Exception in dedicated async thread loop: %s",
|
|
3084
|
-
self.log_identifier,
|
|
3085
|
-
e,
|
|
3086
|
-
)
|
|
3087
|
-
if self._async_init_future and not self._async_init_future.done():
|
|
3088
|
-
self._async_init_future.set_exception(e)
|
|
3089
|
-
finally:
|
|
3090
|
-
log.info("%s Dedicated async thread loop finishing.", self.log_identifier)
|
|
3091
|
-
if self._async_loop.is_running():
|
|
3092
|
-
self._async_loop.call_soon_threadsafe(self._async_loop.stop)
|
|
3093
|
-
|
|
3094
2825
|
async def _perform_async_init(self):
|
|
3095
2826
|
"""Coroutine executed on the dedicated loop to perform async initialization."""
|
|
3096
2827
|
try:
|
|
@@ -3249,58 +2980,8 @@ class SamAgentComponent(ComponentBase):
|
|
|
3249
2980
|
im_clean_e,
|
|
3250
2981
|
)
|
|
3251
2982
|
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
"%s Performing async cleanup via dedicated thread...",
|
|
3255
|
-
self.log_identifier,
|
|
3256
|
-
)
|
|
3257
|
-
|
|
3258
|
-
async def _perform_async_cleanup():
|
|
3259
|
-
log.debug("%s Entering async cleanup coroutine...", self.log_identifier)
|
|
3260
|
-
pass
|
|
3261
|
-
|
|
3262
|
-
try:
|
|
3263
|
-
cleanup_future = asyncio.run_coroutine_threadsafe(
|
|
3264
|
-
_perform_async_cleanup(), self._async_loop
|
|
3265
|
-
)
|
|
3266
|
-
cleanup_future.result(timeout=30)
|
|
3267
|
-
log.info("%s Async cleanup completed.", self.log_identifier)
|
|
3268
|
-
except Exception as e:
|
|
3269
|
-
log.exception(
|
|
3270
|
-
"%s Error during async cleanup: %s", self.log_identifier, e
|
|
3271
|
-
)
|
|
3272
|
-
finally:
|
|
3273
|
-
if self._async_loop and self._async_loop.is_running():
|
|
3274
|
-
log.info(
|
|
3275
|
-
"%s Cleanup: Stopping dedicated async loop...",
|
|
3276
|
-
self.log_identifier,
|
|
3277
|
-
)
|
|
3278
|
-
self._async_loop.call_soon_threadsafe(self._async_loop.stop)
|
|
3279
|
-
else:
|
|
3280
|
-
log.info(
|
|
3281
|
-
"%s Cleanup: Dedicated async loop is None or not running, no need to stop.",
|
|
3282
|
-
self.log_identifier,
|
|
3283
|
-
)
|
|
3284
|
-
if self._async_thread and self._async_thread.is_alive():
|
|
3285
|
-
log.info(
|
|
3286
|
-
"%s Cleanup: Joining dedicated async thread...",
|
|
3287
|
-
self.log_identifier,
|
|
3288
|
-
)
|
|
3289
|
-
self._async_thread.join(timeout=5)
|
|
3290
|
-
if self._async_thread.is_alive():
|
|
3291
|
-
log.warning(
|
|
3292
|
-
"%s Dedicated async thread did not exit cleanly.",
|
|
3293
|
-
self.log_identifier,
|
|
3294
|
-
)
|
|
3295
|
-
log.info(
|
|
3296
|
-
"%s Dedicated async thread stopped and joined.", self.log_identifier
|
|
3297
|
-
)
|
|
3298
|
-
else:
|
|
3299
|
-
log.info(
|
|
3300
|
-
"%s Dedicated async loop not running, skipping async cleanup.",
|
|
3301
|
-
self.log_identifier,
|
|
3302
|
-
)
|
|
3303
|
-
|
|
2983
|
+
# The base class cleanup() will handle stopping the async loop and joining the thread.
|
|
2984
|
+
# We just need to cancel any active tasks before that happens.
|
|
3304
2985
|
with self.active_tasks_lock:
|
|
3305
2986
|
if self._async_loop and self._async_loop.is_running():
|
|
3306
2987
|
for task_context in self.active_tasks.values():
|
|
@@ -3458,3 +3139,17 @@ class SamAgentComponent(ComponentBase):
|
|
|
3458
3139
|
"%s Error during embed resolution: %s", method_context_log_identifier, e
|
|
3459
3140
|
)
|
|
3460
3141
|
return raw_text, [], ""
|
|
3142
|
+
|
|
3143
|
+
async def _async_setup_and_run(self) -> None:
|
|
3144
|
+
"""
|
|
3145
|
+
Main async logic for the agent component.
|
|
3146
|
+
This is called by the base class's `_run_async_operations`.
|
|
3147
|
+
"""
|
|
3148
|
+
await self._perform_async_init()
|
|
3149
|
+
|
|
3150
|
+
def _pre_async_cleanup(self) -> None:
|
|
3151
|
+
"""
|
|
3152
|
+
Pre-cleanup actions for the agent component.
|
|
3153
|
+
Called by the base class before stopping the async loop.
|
|
3154
|
+
"""
|
|
3155
|
+
pass
|