solace-agent-mesh 1.0.9__py3-none-any.whl → 1.3.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.

Files changed (220) hide show
  1. solace_agent_mesh/agent/adk/adk_llm.txt +182 -42
  2. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
  3. solace_agent_mesh/agent/adk/callbacks.py +165 -104
  4. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
  5. solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
  6. solace_agent_mesh/agent/adk/runner.py +25 -17
  7. solace_agent_mesh/agent/adk/services.py +3 -3
  8. solace_agent_mesh/agent/adk/setup.py +11 -0
  9. solace_agent_mesh/agent/adk/stream_parser.py +8 -1
  10. solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
  11. solace_agent_mesh/agent/agent_llm.txt +355 -18
  12. solace_agent_mesh/agent/protocol/event_handlers.py +460 -317
  13. solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
  14. solace_agent_mesh/agent/sac/app.py +2 -2
  15. solace_agent_mesh/agent/sac/component.py +211 -517
  16. solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
  17. solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
  18. solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
  19. solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
  20. solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
  21. solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
  22. solace_agent_mesh/assets/docs/404.html +3 -3
  23. solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
  25. solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +2 -0
  28. solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
  29. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
  30. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
  31. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
  32. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
  34. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
  35. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
  43. 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
  44. solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  52. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
  53. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  61. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  64. solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +1 -0
  65. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  66. solace_agent_mesh/assets/docs/search-doc-1757433031159.json +1 -0
  67. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  68. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  69. solace_agent_mesh/cli/__init__.py +1 -1
  70. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +125 -48
  71. solace_agent_mesh/cli/commands/eval_cmd.py +14 -0
  72. solace_agent_mesh/cli/commands/init_cmd/__init__.py +53 -31
  73. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  74. solace_agent_mesh/cli/commands/init_cmd/env_step.py +19 -8
  75. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +80 -25
  76. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +32 -10
  77. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +74 -15
  78. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +0 -2
  79. solace_agent_mesh/cli/commands/run_cmd.py +5 -3
  80. solace_agent_mesh/cli/utils.py +68 -12
  81. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-vY5eu2lI.js +1 -0
  82. solace_agent_mesh/client/webui/frontend/static/assets/client-BeBkzgWW.js +25 -0
  83. solace_agent_mesh/client/webui/frontend/static/assets/main-Bjys1KQs.js +339 -0
  84. solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +1 -0
  85. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CE0AeXyK.js +395 -0
  86. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -2
  87. solace_agent_mesh/client/webui/frontend/static/index.html +4 -3
  88. solace_agent_mesh/common/a2a/__init__.py +213 -0
  89. solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
  90. solace_agent_mesh/common/a2a/artifact.py +328 -0
  91. solace_agent_mesh/common/a2a/events.py +183 -0
  92. solace_agent_mesh/common/a2a/message.py +307 -0
  93. solace_agent_mesh/common/a2a/protocol.py +513 -0
  94. solace_agent_mesh/common/a2a/task.py +127 -0
  95. solace_agent_mesh/common/a2a/translation.py +653 -0
  96. solace_agent_mesh/common/a2a/types.py +54 -0
  97. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  98. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
  99. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  100. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
  101. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
  102. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
  103. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  104. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
  105. solace_agent_mesh/common/agent_registry.py +1 -1
  106. solace_agent_mesh/common/common_llm.txt +192 -70
  107. solace_agent_mesh/common/data_parts.py +99 -0
  108. solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
  109. solace_agent_mesh/common/sac/__init__.py +0 -0
  110. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  111. solace_agent_mesh/common/sac/sam_component_base.py +252 -0
  112. solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
  113. solace_agent_mesh/common/services/services_llm.txt +206 -26
  114. solace_agent_mesh/common/utils/artifact_utils.py +29 -0
  115. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
  116. solace_agent_mesh/common/utils/embeds/resolver.py +1 -0
  117. solace_agent_mesh/common/utils/utils_llm.txt +323 -42
  118. solace_agent_mesh/config_portal/backend/common.py +2 -2
  119. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +1 -1
  120. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-bFMKlzKf.js +98 -0
  121. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-d845808d.js → manifest-89db7c30.js} +1 -1
  122. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  123. solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
  124. solace_agent_mesh/core_a2a/service.py +20 -44
  125. solace_agent_mesh/evaluation/message_organizer.py +35 -56
  126. solace_agent_mesh/evaluation/run.py +26 -5
  127. solace_agent_mesh/evaluation/subscriber.py +35 -10
  128. solace_agent_mesh/evaluation/summary_builder.py +27 -34
  129. solace_agent_mesh/gateway/base/app.py +27 -1
  130. solace_agent_mesh/gateway/base/base_llm.txt +177 -72
  131. solace_agent_mesh/gateway/base/component.py +294 -523
  132. solace_agent_mesh/gateway/gateway_llm.txt +299 -58
  133. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +676 -0
  134. solace_agent_mesh/gateway/http_sse/alembic/env.py +85 -0
  135. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  136. solace_agent_mesh/gateway/http_sse/alembic/versions/b1c2d3e4f5g6_add_database_indexes.py +83 -0
  137. solace_agent_mesh/gateway/http_sse/alembic/versions/d5b3f8f2e9a0_create_initial_database.py +58 -0
  138. solace_agent_mesh/gateway/http_sse/alembic.ini +147 -0
  139. solace_agent_mesh/gateway/http_sse/api/__init__.py +11 -0
  140. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +9 -0
  141. solace_agent_mesh/gateway/http_sse/api/controllers/session_controller.py +355 -0
  142. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +279 -0
  143. solace_agent_mesh/gateway/http_sse/api/controllers/user_controller.py +35 -0
  144. solace_agent_mesh/gateway/http_sse/api/dto/__init__.py +10 -0
  145. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +37 -0
  146. solace_agent_mesh/gateway/http_sse/api/dto/requests/session_requests.py +49 -0
  147. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +66 -0
  148. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +43 -0
  149. solace_agent_mesh/gateway/http_sse/api/dto/responses/session_responses.py +68 -0
  150. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +74 -0
  151. solace_agent_mesh/gateway/http_sse/app.py +31 -1
  152. solace_agent_mesh/gateway/http_sse/application/__init__.py +3 -0
  153. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +3 -0
  154. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +135 -0
  155. solace_agent_mesh/gateway/http_sse/component.py +371 -236
  156. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
  157. solace_agent_mesh/gateway/http_sse/dependencies.py +142 -39
  158. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +3 -0
  159. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +90 -0
  160. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +3 -0
  161. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +54 -0
  162. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
  163. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +4 -0
  164. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +3 -0
  165. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +123 -0
  166. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +4 -0
  167. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +16 -0
  168. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +119 -0
  169. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +31 -0
  170. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +12 -0
  171. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +3 -0
  172. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +174 -0
  173. solace_agent_mesh/gateway/http_sse/main.py +293 -91
  174. solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
  175. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +137 -56
  176. solace_agent_mesh/gateway/http_sse/routers/config.py +3 -1
  177. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
  178. solace_agent_mesh/gateway/http_sse/routers/tasks.py +199 -171
  179. solace_agent_mesh/gateway/http_sse/routers/visualization.py +7 -7
  180. solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
  181. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
  182. solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
  183. solace_agent_mesh/gateway/http_sse/session_manager.py +64 -30
  184. solace_agent_mesh/gateway/http_sse/shared/__init__.py +9 -0
  185. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  186. solace_agent_mesh/gateway/http_sse/shared/enums.py +45 -0
  187. solace_agent_mesh/gateway/http_sse/shared/types.py +45 -0
  188. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  189. solace_agent_mesh/templates/gateway_component_template.py +149 -98
  190. solace_agent_mesh/templates/shared_config.yaml +4 -5
  191. solace_agent_mesh/templates/webui.yaml +8 -10
  192. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/METADATA +9 -6
  193. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/RECORD +197 -141
  194. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
  195. solace_agent_mesh/assets/docs/assets/js/main.3d0e7879.js +0 -2
  196. solace_agent_mesh/assets/docs/assets/js/runtime~main.05d19492.js +0 -1
  197. solace_agent_mesh/assets/docs/lunr-index-1757091012487.json +0 -1
  198. solace_agent_mesh/assets/docs/search-doc-1757091012487.json +0 -1
  199. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-BmF2l6vg.js +0 -1
  200. solace_agent_mesh/client/webui/frontend/static/assets/client-D881Dttc.js +0 -49
  201. solace_agent_mesh/client/webui/frontend/static/assets/main-D0FnP_W4.css +0 -1
  202. solace_agent_mesh/client/webui/frontend/static/assets/main-Do32sFPX.js +0 -708
  203. solace_agent_mesh/common/a2a_protocol.py +0 -564
  204. solace_agent_mesh/common/client/__init__.py +0 -4
  205. solace_agent_mesh/common/client/card_resolver.py +0 -21
  206. solace_agent_mesh/common/client/client.py +0 -85
  207. solace_agent_mesh/common/client/client_llm.txt +0 -133
  208. solace_agent_mesh/common/server/__init__.py +0 -4
  209. solace_agent_mesh/common/server/server.py +0 -122
  210. solace_agent_mesh/common/server/server_llm.txt +0 -169
  211. solace_agent_mesh/common/server/task_manager.py +0 -291
  212. solace_agent_mesh/common/server/utils.py +0 -28
  213. solace_agent_mesh/common/types.py +0 -411
  214. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-Bym6YkMd.js +0 -98
  215. solace_agent_mesh/gateway/http_sse/routers/sessions.py +0 -80
  216. solace_agent_mesh/gateway/http_sse/routers/users.py +0 -59
  217. /solace_agent_mesh/assets/docs/assets/js/{main.3d0e7879.js.LICENSE.txt → main.08d30374.js.LICENSE.txt} +0 -0
  218. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/WHEEL +0 -0
  219. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/entry_points.txt +0 -0
  220. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,11 @@ 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
- from solace_ai_connector.components.component_base import ComponentBase
15
15
  from solace_ai_connector.common.message import (
16
16
  Message as SolaceMessage,
17
17
  )
@@ -37,39 +37,20 @@ from google.adk.agents.callback_context import CallbackContext
37
37
  from google.adk.models.llm_request import LlmRequest
38
38
  from google.genai import types as adk_types
39
39
  from google.adk.tools.mcp_tool import MCPToolset
40
- from ...common.types import (
40
+ from a2a.types import (
41
41
  AgentCard,
42
- Task,
43
- TaskStatus,
44
- TaskState,
45
- Message as A2AMessage,
46
- TextPart,
47
- FilePart,
48
- DataPart,
49
- FileContent,
50
42
  Artifact as A2AArtifact,
51
- JSONRPCResponse,
52
- InternalError,
43
+ Message as A2AMessage,
44
+ MessageSendParams,
45
+ SendMessageRequest,
46
+ TaskState,
47
+ TaskStatus,
53
48
  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
49
  )
50
+ from ...common import a2a
51
+ from ...common.data_parts import AgentProgressUpdateData
52
+ from ...common.a2a.translation import format_and_route_adk_event
69
53
  from ...agent.utils.config_parser import resolve_instruction_provider
70
- from ...agent.utils.artifact_helpers import (
71
- get_latest_artifact_version,
72
- )
73
54
  from ...agent.adk.services import (
74
55
  initialize_session_service,
75
56
  initialize_artifact_service,
@@ -94,8 +75,7 @@ from ...agent.adk.invocation_monitor import InvocationMonitor
94
75
  from ...common.middleware.registry import MiddlewareRegistry
95
76
  from ...common.constants import DEFAULT_COMMUNICATION_TIMEOUT
96
77
  from ...agent.tools.registry import tool_registry
97
- from ...common.utils.message_utils import validate_message_size
98
- from ...common.exceptions import MessageSizeExceededError
78
+ from ...common.sac.sam_component_base import SamComponentBase
99
79
 
100
80
  if TYPE_CHECKING:
101
81
  from .task_execution_context import TaskExecutionContext
@@ -123,7 +103,7 @@ info = {
123
103
  InstructionProvider = Callable[[ReadonlyContext], str]
124
104
 
125
105
 
126
- class SamAgentComponent(ComponentBase):
106
+ class SamAgentComponent(SamComponentBase):
127
107
  """
128
108
  A Solace AI Connector component that hosts a Google ADK agent,
129
109
  communicating via the A2A protocol over Solace.
@@ -257,8 +237,6 @@ class SamAgentComponent(ComponentBase):
257
237
  self.agent_card_tool_manifest: List[Dict[str, Any]] = []
258
238
  self.peer_agents: Dict[str, Any] = {}
259
239
  self._card_publish_timer_id: str = f"publish_card_{self.agent_name}"
260
- self._async_loop = None
261
- self._async_thread = None
262
240
  self._async_init_future = None
263
241
  self.peer_response_queues: Dict[str, asyncio.Queue] = {}
264
242
  self.peer_response_queue_lock = threading.Lock()
@@ -416,40 +394,11 @@ class SamAgentComponent(ComponentBase):
416
394
  raise RuntimeError(
417
395
  f"Failed to initialize synchronous ADK services: {service_err}"
418
396
  ) from service_err
419
- log.info(
420
- "%s Starting dedicated async thread for MCP/ADK initialization...",
421
- self.log_identifier,
422
- )
423
- self._async_loop = asyncio.new_event_loop()
397
+
398
+ # Async init is now handled by the base class `run` method.
399
+ # We still need a future to signal completion from the async thread.
424
400
  self._async_init_future = concurrent.futures.Future()
425
- self._async_thread = threading.Thread(
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
401
+
453
402
  publish_interval_sec = self.agent_card_publishing_config.get(
454
403
  "interval_seconds"
455
404
  )
@@ -798,11 +747,12 @@ class SamAgentComponent(ComponentBase):
798
747
  sub_task_id,
799
748
  )
800
749
  task_id_for_peer = sub_task_id.replace(CORRELATION_DATA_PREFIX, "", 1)
801
- cancel_params = TaskIdParams(id=task_id_for_peer)
802
- cancel_request = CancelTaskRequest(params=cancel_params)
750
+ cancel_request = a2a.create_cancel_task_request(
751
+ task_id=task_id_for_peer
752
+ )
803
753
  user_props = {"clientId": self.agent_name}
804
754
  peer_topic = self._get_agent_request_topic(peer_agent_name)
805
- self._publish_a2a_message(
755
+ self.publish_a2a_message(
806
756
  payload=cancel_request.model_dump(exclude_none=True),
807
757
  topic=peer_topic,
808
758
  user_properties=user_props,
@@ -1286,17 +1236,17 @@ class SamAgentComponent(ComponentBase):
1286
1236
  return
1287
1237
 
1288
1238
  try:
1289
- a2a_message = A2AMessage(role="agent", parts=[TextPart(text=text_content)])
1290
- task_status = TaskStatus(
1291
- state=TaskState.WORKING,
1292
- message=a2a_message,
1293
- timestamp=datetime.now(timezone.utc),
1239
+ a2a_message = a2a.create_agent_text_message(
1240
+ text=text_content,
1241
+ task_id=logical_task_id,
1242
+ context_id=a2a_context.get("contextId"),
1294
1243
  )
1295
1244
  event_metadata = {"agent_name": self.agent_name}
1296
- status_update_event = TaskStatusUpdateEvent(
1297
- id=logical_task_id,
1298
- status=task_status,
1299
- final=is_stream_terminating_content,
1245
+ status_update_event = a2a.create_status_update(
1246
+ task_id=logical_task_id,
1247
+ context_id=a2a_context.get("contextId"),
1248
+ message=a2a_message,
1249
+ is_final=is_stream_terminating_content,
1300
1250
  metadata=event_metadata,
1301
1251
  )
1302
1252
 
@@ -1339,25 +1289,13 @@ class SamAgentComponent(ComponentBase):
1339
1289
  return
1340
1290
 
1341
1291
  try:
1342
- signal_data_part = DataPart(
1343
- data={
1344
- "a2a_signal_type": "agent_status_message",
1345
- "text": status_text,
1346
- },
1347
- metadata={"source_embed_type": "status_update"},
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,
1292
+ progress_data = AgentProgressUpdateData(status_text=status_text)
1293
+ status_update_event = a2a.create_data_signal_event(
1294
+ task_id=logical_task_id,
1295
+ context_id=a2a_context.get("contextId"),
1296
+ signal_data=progress_data,
1297
+ agent_name=self.agent_name,
1298
+ part_metadata={"source_embed_type": "status_update"},
1361
1299
  )
1362
1300
 
1363
1301
  await self._publish_status_update_with_buffer_flush(
@@ -1514,16 +1452,27 @@ class SamAgentComponent(ComponentBase):
1514
1452
  )
1515
1453
 
1516
1454
  try:
1517
- rpc_response = JSONRPCResponse(
1518
- id=jsonrpc_request_id, result=status_update_event
1455
+ rpc_response = a2a.create_success_response(
1456
+ result=status_update_event, request_id=jsonrpc_request_id
1519
1457
  )
1520
1458
  payload_to_publish = rpc_response.model_dump(exclude_none=True)
1521
1459
 
1522
- target_topic = a2a_context.get("statusTopic") or get_gateway_status_topic(
1460
+ target_topic = a2a_context.get(
1461
+ "statusTopic"
1462
+ ) or a2a.get_gateway_status_topic(
1523
1463
  self.namespace, self.get_gateway_id(), logical_task_id
1524
1464
  )
1525
1465
 
1526
- self._publish_a2a_event(payload_to_publish, target_topic, a2a_context)
1466
+ # Construct user_properties to ensure ownership can be determined by gateways
1467
+ user_properties = {
1468
+ "a2aUserConfig": a2a_context.get("a2a_user_config"),
1469
+ "clientId": a2a_context.get("client_id"),
1470
+ "delegating_agent_name": self.get_config("agent_name"),
1471
+ }
1472
+
1473
+ self._publish_a2a_event(
1474
+ payload_to_publish, target_topic, a2a_context, user_properties
1475
+ )
1527
1476
 
1528
1477
  log.info(
1529
1478
  "%s Published %s status update to %s.",
@@ -1541,121 +1490,6 @@ class SamAgentComponent(ComponentBase):
1541
1490
  )
1542
1491
  raise
1543
1492
 
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
1493
  async def _filter_text_from_final_streaming_event(
1660
1494
  self, adk_event: ADKEvent, a2a_context: Dict
1661
1495
  ) -> ADKEvent:
@@ -1972,7 +1806,7 @@ class SamAgentComponent(ComponentBase):
1972
1806
  namespace = self.get_config("namespace")
1973
1807
  gateway_id = self.get_gateway_id()
1974
1808
 
1975
- artifact_topic = peer_status_topic or get_gateway_status_topic(
1809
+ artifact_topic = peer_status_topic or a2a.get_gateway_status_topic(
1976
1810
  namespace, gateway_id, logical_task_id
1977
1811
  )
1978
1812
 
@@ -2012,18 +1846,36 @@ class SamAgentComponent(ComponentBase):
2012
1846
  )
2013
1847
  continue
2014
1848
 
2015
- a2a_file_part = await self._translate_adk_part_to_a2a_filepart(
2016
- loaded_adk_part, filename, a2a_context, version=version
1849
+ a2a_file_part = await a2a.translate_adk_part_to_a2a_filepart(
1850
+ adk_part=loaded_adk_part,
1851
+ filename=filename,
1852
+ a2a_context=a2a_context,
1853
+ artifact_service=self.artifact_service,
1854
+ artifact_handling_mode=self.artifact_handling_mode,
1855
+ adk_app_name=self.get_config("agent_name"),
1856
+ log_identifier=self.log_identifier,
1857
+ version=version,
2017
1858
  )
2018
1859
 
2019
1860
  if a2a_file_part:
2020
- a2a_artifact = A2AArtifact(name=filename, parts=[a2a_file_part])
2021
- artifact_update_event = TaskArtifactUpdateEvent(
2022
- id=logical_task_id, artifact=a2a_artifact
1861
+ a2a_message = a2a.create_agent_parts_message(
1862
+ parts=[a2a_file_part],
1863
+ task_id=logical_task_id,
1864
+ context_id=original_session_id,
2023
1865
  )
2024
- artifact_payload = JSONRPCResponse(
2025
- id=a2a_context.get("jsonrpc_request_id"),
2026
- result=artifact_update_event,
1866
+ task_status = a2a.create_task_status(
1867
+ state=TaskState.working, message=a2a_message
1868
+ )
1869
+ status_update_event = TaskStatusUpdateEvent(
1870
+ task_id=logical_task_id,
1871
+ context_id=original_session_id,
1872
+ status=task_status,
1873
+ final=False,
1874
+ kind="status-update",
1875
+ )
1876
+ artifact_payload = a2a.create_success_response(
1877
+ result=status_update_event,
1878
+ request_id=a2a_context.get("jsonrpc_request_id"),
2027
1879
  ).model_dump(exclude_none=True)
2028
1880
 
2029
1881
  self._publish_a2a_event(
@@ -2031,7 +1883,7 @@ class SamAgentComponent(ComponentBase):
2031
1883
  )
2032
1884
 
2033
1885
  log.info(
2034
- "%s Published TaskArtifactUpdateEvent for '%s' to %s",
1886
+ "%s Published TaskStatusUpdateEvent with FilePart for '%s' to %s",
2035
1887
  log_id,
2036
1888
  filename,
2037
1889
  artifact_topic,
@@ -2052,53 +1904,51 @@ class SamAgentComponent(ComponentBase):
2052
1904
  e,
2053
1905
  )
2054
1906
 
2055
- def _format_final_task_status(self, last_event: ADKEvent) -> TaskStatus:
1907
+ def _format_final_task_status(
1908
+ self, last_event: Optional[ADKEvent], override_text: Optional[str] = None
1909
+ ) -> TaskStatus:
2056
1910
  """Helper to format the final TaskStatus based on the last ADK event."""
2057
1911
  log.debug(
2058
1912
  "%s Formatting final task status from last ADK event %s",
2059
1913
  self.log_identifier,
2060
- last_event.id,
1914
+ last_event.id if last_event else "None",
2061
1915
  )
2062
- a2a_state = TaskState.COMPLETED
1916
+ a2a_state = TaskState.completed
2063
1917
  a2a_parts = []
2064
1918
 
2065
- if last_event.content and last_event.content.parts:
2066
- for part in last_event.content.parts:
2067
- if part.text:
2068
- a2a_parts.append(TextPart(text=part.text))
2069
- elif part.function_response:
2070
- try:
2071
- response_data = part.function_response.response
2072
- if isinstance(response_data, dict):
2073
- a2a_parts.append(
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]"
1919
+ if override_text is not None:
1920
+ a2a_parts.append(a2a.create_text_part(text=override_text))
1921
+ # Add non-text parts from the last event
1922
+ if last_event and last_event.content and last_event.content.parts:
1923
+ for part in last_event.content.parts:
1924
+ if part.text is None:
1925
+ if part.function_response:
1926
+ a2a_parts.extend(
1927
+ a2a.translate_adk_function_response_to_a2a_parts(part)
2089
1928
  )
1929
+ else:
1930
+ # Original logic
1931
+ if last_event and last_event.content and last_event.content.parts:
1932
+ for part in last_event.content.parts:
1933
+ if part.text:
1934
+ a2a_parts.append(a2a.create_text_part(text=part.text))
1935
+ elif part.function_response:
1936
+ a2a_parts.extend(
1937
+ a2a.translate_adk_function_response_to_a2a_parts(part)
2090
1938
  )
2091
1939
 
2092
- elif last_event.actions:
1940
+ if last_event and last_event.actions:
2093
1941
  if last_event.actions.requested_auth_configs:
2094
- a2a_state = TaskState.INPUT_REQUIRED
2095
- a2a_parts.append(TextPart(text="[Agent requires input/authentication]"))
1942
+ a2a_state = TaskState.input_required
1943
+ a2a_parts.append(
1944
+ a2a.create_text_part(text="[Agent requires input/authentication]")
1945
+ )
2096
1946
 
2097
1947
  if not a2a_parts:
2098
- a2a_parts.append(TextPart(text=""))
2099
-
2100
- a2a_message = A2AMessage(role="agent", parts=a2a_parts)
2101
- return TaskStatus(state=a2a_state, message=a2a_message)
1948
+ a2a_message = a2a.create_agent_text_message(text="")
1949
+ else:
1950
+ a2a_message = a2a.create_agent_parts_message(parts=a2a_parts)
1951
+ return a2a.create_task_status(state=a2a_state, message=a2a_message)
2102
1952
 
2103
1953
  async def finalize_task_success(self, a2a_context: Dict):
2104
1954
  """
@@ -2156,55 +2006,16 @@ class SamAgentComponent(ComponentBase):
2156
2006
  logical_task_id,
2157
2007
  len(aggregated_text.encode("utf-8")),
2158
2008
  )
2159
-
2160
- final_a2a_parts = []
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),
2009
+ final_status = self._format_final_task_status(
2010
+ last_event, override_text=aggregated_text
2198
2011
  )
2199
2012
  else:
2200
2013
  if last_event:
2201
2014
  final_status = self._format_final_task_status(last_event)
2202
2015
  else:
2203
- final_status = TaskStatus(
2204
- state=TaskState.COMPLETED,
2205
- message=A2AMessage(
2206
- role="agent", parts=[TextPart(text="Task completed.")]
2207
- ),
2016
+ final_status = a2a.create_task_status(
2017
+ state=TaskState.completed,
2018
+ message=a2a.create_agent_text_message(text="Task completed."),
2208
2019
  )
2209
2020
 
2210
2021
  final_a2a_artifacts: List[A2AArtifact] = []
@@ -2224,16 +2035,18 @@ class SamAgentComponent(ComponentBase):
2224
2035
  len(task_context.produced_artifacts),
2225
2036
  )
2226
2037
 
2227
- final_task = Task(
2228
- id=logical_task_id,
2229
- sessionId=original_session_id,
2230
- status=final_status,
2038
+ final_task = a2a.create_final_task(
2039
+ task_id=logical_task_id,
2040
+ context_id=original_session_id,
2041
+ final_status=final_status,
2231
2042
  artifacts=(final_a2a_artifacts if final_a2a_artifacts else None),
2232
2043
  metadata=final_task_metadata,
2233
2044
  )
2234
- final_response = JSONRPCResponse(id=jsonrpc_request_id, result=final_task)
2045
+ final_response = a2a.create_success_response(
2046
+ result=final_task, request_id=jsonrpc_request_id
2047
+ )
2235
2048
  a2a_payload = final_response.model_dump(exclude_none=True)
2236
- target_topic = peer_reply_topic or get_client_response_topic(
2049
+ target_topic = peer_reply_topic or a2a.get_client_response_topic(
2237
2050
  namespace, client_id
2238
2051
  )
2239
2052
 
@@ -2300,17 +2113,15 @@ class SamAgentComponent(ComponentBase):
2300
2113
  client_id = a2a_context.get("client_id")
2301
2114
  peer_reply_topic = a2a_context.get("replyToTopic")
2302
2115
  namespace = self.get_config("namespace")
2303
- error_response = JSONRPCResponse(
2304
- id=jsonrpc_request_id,
2305
- error=InternalError(
2306
- message=f"Failed to finalize successful task: {e}",
2307
- data={"taskId": logical_task_id},
2308
- ),
2309
- )
2310
- target_topic = peer_reply_topic or get_client_response_topic(
2116
+ error_response = a2a.create_internal_error_response(
2117
+ message=f"Failed to finalize successful task: {e}",
2118
+ request_id=jsonrpc_request_id,
2119
+ data={"taskId": logical_task_id},
2120
+ )
2121
+ target_topic = peer_reply_topic or a2a.get_client_response_topic(
2311
2122
  namespace, client_id
2312
2123
  )
2313
- self._publish_a2a_message(
2124
+ self.publish_a2a_message(
2314
2125
  error_response.model_dump(exclude_none=True), target_topic
2315
2126
  )
2316
2127
  except Exception as report_err:
@@ -2340,23 +2151,24 @@ class SamAgentComponent(ComponentBase):
2340
2151
  peer_reply_topic = a2a_context.get("replyToTopic")
2341
2152
  namespace = self.get_config("namespace")
2342
2153
 
2343
- canceled_status = TaskStatus(
2344
- state=TaskState.CANCELED,
2345
- message=A2AMessage(
2346
- role="agent",
2347
- parts=[TextPart(text="Task cancelled by request.")],
2154
+ canceled_status = a2a.create_task_status(
2155
+ state=TaskState.canceled,
2156
+ message=a2a.create_agent_text_message(
2157
+ text="Task cancelled by request."
2348
2158
  ),
2349
2159
  )
2350
2160
  agent_name = self.get_config("agent_name")
2351
- final_task = Task(
2352
- id=logical_task_id,
2353
- sessionId=a2a_context.get("session_id"),
2354
- status=canceled_status,
2161
+ final_task = a2a.create_final_task(
2162
+ task_id=logical_task_id,
2163
+ context_id=a2a_context.get("contextId"),
2164
+ final_status=canceled_status,
2355
2165
  metadata={"agent_name": agent_name},
2356
2166
  )
2357
- final_response = JSONRPCResponse(id=jsonrpc_request_id, result=final_task)
2167
+ final_response = a2a.create_success_response(
2168
+ result=final_task, request_id=jsonrpc_request_id
2169
+ )
2358
2170
  a2a_payload = final_response.model_dump(exclude_none=True)
2359
- target_topic = peer_reply_topic or get_client_response_topic(
2171
+ target_topic = peer_reply_topic or a2a.get_client_response_topic(
2360
2172
  namespace, client_id
2361
2173
  )
2362
2174
 
@@ -2416,7 +2228,7 @@ class SamAgentComponent(ComponentBase):
2416
2228
  )
2417
2229
  try:
2418
2230
  # Create the status update event
2419
- tool_error_data_part = DataPart(
2231
+ tool_error_data_part = a2a.create_data_part(
2420
2232
  data={
2421
2233
  "a2a_signal_type": "tool_execution_error",
2422
2234
  "error_message": str(exception),
@@ -2424,17 +2236,16 @@ class SamAgentComponent(ComponentBase):
2424
2236
  }
2425
2237
  )
2426
2238
 
2427
- status_message = A2AMessage(role="agent", parts=[tool_error_data_part])
2428
- intermediate_status = TaskStatus(
2429
- state=TaskState.WORKING,
2430
- message=status_message,
2431
- timestamp=datetime.now(timezone.utc),
2239
+ status_message = a2a.create_agent_parts_message(
2240
+ parts=[tool_error_data_part],
2241
+ task_id=logical_task_id,
2242
+ context_id=a2a_context.get("contextId"),
2432
2243
  )
2433
-
2434
- status_update_event = TaskStatusUpdateEvent(
2435
- id=logical_task_id,
2436
- status=intermediate_status,
2437
- final=False,
2244
+ status_update_event = a2a.create_status_update(
2245
+ task_id=logical_task_id,
2246
+ context_id=a2a_context.get("contextId"),
2247
+ message=status_message,
2248
+ is_final=False,
2438
2249
  metadata={"agent_name": self.get_config("agent_name")},
2439
2250
  )
2440
2251
 
@@ -2554,15 +2365,14 @@ class SamAgentComponent(ComponentBase):
2554
2365
  "Otherwise, you can start a new topic."
2555
2366
  )
2556
2367
 
2557
- error_payload = InternalError(
2368
+ final_response = a2a.create_internal_error_response(
2558
2369
  message=limit_message_text,
2370
+ request_id=jsonrpc_request_id,
2559
2371
  data={"taskId": logical_task_id, "reason": "llm_call_limit_reached"},
2560
2372
  )
2561
-
2562
- final_response = JSONRPCResponse(id=jsonrpc_request_id, error=error_payload)
2563
2373
  a2a_payload = final_response.model_dump(exclude_none=True)
2564
2374
 
2565
- target_topic = peer_reply_topic or get_client_response_topic(
2375
+ target_topic = peer_reply_topic or a2a.get_client_response_topic(
2566
2376
  namespace, client_id
2567
2377
  )
2568
2378
 
@@ -2631,28 +2441,25 @@ class SamAgentComponent(ComponentBase):
2631
2441
  peer_reply_topic = a2a_context.get("replyToTopic")
2632
2442
  namespace = self.get_config("namespace")
2633
2443
 
2634
- failed_status = TaskStatus(
2635
- state=TaskState.FAILED,
2636
- message=A2AMessage(
2637
- role="agent",
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
- ],
2444
+ failed_status = a2a.create_task_status(
2445
+ state=TaskState.failed,
2446
+ message=a2a.create_agent_text_message(
2447
+ text="An unexpected error occurred during tool execution. Please try your request again. If the problem persists, contact an administrator."
2643
2448
  ),
2644
2449
  )
2645
2450
 
2646
- final_task = Task(
2647
- id=logical_task_id,
2648
- sessionId=a2a_context.get("session_id"),
2649
- status=failed_status,
2451
+ final_task = a2a.create_final_task(
2452
+ task_id=logical_task_id,
2453
+ context_id=a2a_context.get("contextId"),
2454
+ final_status=failed_status,
2650
2455
  metadata={"agent_name": self.get_config("agent_name")},
2651
2456
  )
2652
2457
 
2653
- final_response = JSONRPCResponse(id=jsonrpc_request_id, result=final_task)
2458
+ final_response = a2a.create_success_response(
2459
+ result=final_task, request_id=jsonrpc_request_id
2460
+ )
2654
2461
  a2a_payload = final_response.model_dump(exclude_none=True)
2655
- target_topic = peer_reply_topic or get_client_response_topic(
2462
+ target_topic = peer_reply_topic or a2a.get_client_response_topic(
2656
2463
  namespace, client_id
2657
2464
  )
2658
2465
 
@@ -2871,21 +2678,21 @@ class SamAgentComponent(ComponentBase):
2871
2678
 
2872
2679
  def _get_a2a_base_topic(self) -> str:
2873
2680
  """Returns the base topic prefix using helper."""
2874
- return get_a2a_base_topic(self.namespace)
2681
+ return a2a.get_a2a_base_topic(self.namespace)
2875
2682
 
2876
2683
  def _get_discovery_topic(self) -> str:
2877
2684
  """Returns the discovery topic using helper."""
2878
- return get_discovery_topic(self.namespace)
2685
+ return a2a.get_discovery_topic(self.namespace)
2879
2686
 
2880
2687
  def _get_agent_request_topic(self, agent_id: str) -> str:
2881
2688
  """Returns the agent request topic using helper."""
2882
- return get_agent_request_topic(self.namespace, agent_id)
2689
+ return a2a.get_agent_request_topic(self.namespace, agent_id)
2883
2690
 
2884
2691
  def _get_agent_response_topic(
2885
2692
  self, delegating_agent_name: str, sub_task_id: str
2886
2693
  ) -> str:
2887
2694
  """Returns the agent response topic using helper."""
2888
- return get_agent_response_topic(
2695
+ return a2a.get_agent_response_topic(
2889
2696
  self.namespace, delegating_agent_name, sub_task_id
2890
2697
  )
2891
2698
 
@@ -2893,92 +2700,41 @@ class SamAgentComponent(ComponentBase):
2893
2700
  self, delegating_agent_name: str, sub_task_id: str
2894
2701
  ) -> str:
2895
2702
  """Returns the peer agent status topic using helper."""
2896
- return get_peer_agent_status_topic(
2703
+ return a2a.get_peer_agent_status_topic(
2897
2704
  self.namespace, delegating_agent_name, sub_task_id
2898
2705
  )
2899
2706
 
2900
2707
  def _get_client_response_topic(self, client_id: str) -> str:
2901
2708
  """Returns the client response topic using helper."""
2902
- return get_client_response_topic(self.namespace, client_id)
2709
+ return a2a.get_client_response_topic(self.namespace, client_id)
2903
2710
 
2904
- def _publish_a2a_message(
2905
- self, payload: Dict, topic: str, user_properties: Optional[Dict] = None
2711
+ def _publish_a2a_event(
2712
+ self,
2713
+ payload: Dict,
2714
+ topic: str,
2715
+ a2a_context: Dict,
2716
+ user_properties_override: Optional[Dict] = None,
2906
2717
  ):
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
2718
  """
2963
2719
  Centralized helper to publish an A2A event, ensuring user properties
2964
- are consistently attached from the a2a_context.
2720
+ are consistently attached from the a2a_context or an override.
2965
2721
  """
2966
- user_properties = {}
2967
- if a2a_context.get("a2a_user_config"):
2968
- user_properties["a2aUserConfig"] = a2a_context["a2a_user_config"]
2722
+ if user_properties_override is not None:
2723
+ user_properties = user_properties_override
2724
+ else:
2725
+ user_properties = {}
2726
+ if a2a_context.get("a2a_user_config"):
2727
+ user_properties["a2aUserConfig"] = a2a_context["a2a_user_config"]
2969
2728
 
2970
- self._publish_a2a_message(payload, topic, user_properties)
2729
+ self.publish_a2a_message(payload, topic, user_properties)
2971
2730
 
2972
2731
  def submit_a2a_task(
2973
2732
  self,
2974
2733
  target_agent_name: str,
2975
2734
  a2a_message: A2AMessage,
2976
- original_session_id: str,
2977
- main_logical_task_id: str,
2978
2735
  user_id: str,
2979
2736
  user_config: Dict[str, Any],
2980
2737
  sub_task_id: str,
2981
- function_call_id: Optional[str] = None,
2982
2738
  ) -> str:
2983
2739
  """
2984
2740
  Submits a task to a peer agent in a non-blocking way.
@@ -2987,25 +2743,18 @@ class SamAgentComponent(ComponentBase):
2987
2743
  log_identifier_helper = (
2988
2744
  f"{self.log_identifier}[SubmitA2ATask:{target_agent_name}]"
2989
2745
  )
2746
+ main_task_id = a2a_message.metadata.get("parentTaskId", "unknown_parent")
2990
2747
  log.debug(
2991
2748
  "%s Submitting non-blocking task for main task %s",
2992
2749
  log_identifier_helper,
2993
- main_logical_task_id,
2750
+ main_task_id,
2994
2751
  )
2995
2752
 
2996
2753
  peer_request_topic = self._get_agent_request_topic(target_agent_name)
2997
2754
 
2998
- a2a_request_params = {
2999
- "id": sub_task_id,
3000
- "sessionId": original_session_id,
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)
2755
+ # Create a compliant SendMessageRequest
2756
+ send_params = MessageSendParams(message=a2a_message)
2757
+ a2a_request = SendMessageRequest(id=sub_task_id, params=send_params)
3009
2758
 
3010
2759
  delegating_agent_name = self.get_config("agent_name")
3011
2760
  reply_to_topic = self._get_agent_response_topic(
@@ -3025,8 +2774,8 @@ class SamAgentComponent(ComponentBase):
3025
2774
  if isinstance(user_config, dict):
3026
2775
  user_properties["a2aUserConfig"] = user_config
3027
2776
 
3028
- self._publish_a2a_message(
3029
- payload=a2a_request.model_dump(exclude_none=True),
2777
+ self.publish_a2a_message(
2778
+ payload=a2a_request.model_dump(by_alias=True, exclude_none=True),
3030
2779
  topic=peer_request_topic,
3031
2780
  user_properties=user_properties,
3032
2781
  )
@@ -3072,25 +2821,6 @@ class SamAgentComponent(ComponentBase):
3072
2821
  exc_info=e,
3073
2822
  )
3074
2823
 
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
2824
  async def _perform_async_init(self):
3095
2825
  """Coroutine executed on the dedicated loop to perform async initialization."""
3096
2826
  try:
@@ -3249,58 +2979,8 @@ class SamAgentComponent(ComponentBase):
3249
2979
  im_clean_e,
3250
2980
  )
3251
2981
 
3252
- if self._async_loop and self._async_loop.is_running():
3253
- log.info(
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
-
2982
+ # The base class cleanup() will handle stopping the async loop and joining the thread.
2983
+ # We just need to cancel any active tasks before that happens.
3304
2984
  with self.active_tasks_lock:
3305
2985
  if self._async_loop and self._async_loop.is_running():
3306
2986
  for task_context in self.active_tasks.values():
@@ -3458,3 +3138,17 @@ class SamAgentComponent(ComponentBase):
3458
3138
  "%s Error during embed resolution: %s", method_context_log_identifier, e
3459
3139
  )
3460
3140
  return raw_text, [], ""
3141
+
3142
+ async def _async_setup_and_run(self) -> None:
3143
+ """
3144
+ Main async logic for the agent component.
3145
+ This is called by the base class's `_run_async_operations`.
3146
+ """
3147
+ await self._perform_async_init()
3148
+
3149
+ def _pre_async_cleanup(self) -> None:
3150
+ """
3151
+ Pre-cleanup actions for the agent component.
3152
+ Called by the base class before stopping the async loop.
3153
+ """
3154
+ pass