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.

Files changed (163) 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 +7 -5
  7. solace_agent_mesh/agent/adk/services.py +9 -1
  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 +433 -296
  13. solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
  14. solace_agent_mesh/agent/sac/app.py +1 -1
  15. solace_agent_mesh/agent/sac/component.py +212 -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/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
  24. solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
  26. solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/main.a75ecc0d.js +2 -0
  29. solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
  30. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
  31. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
  32. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  34. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
  35. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
  44. 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
  45. solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
  46. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +8 -8
  48. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  52. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  53. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
  54. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
  61. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  65. solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +1 -0
  66. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  67. solace_agent_mesh/assets/docs/search-doc-1756992446316.json +1 -0
  68. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  69. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  70. solace_agent_mesh/cli/__init__.py +1 -1
  71. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +12 -3
  72. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
  73. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
  74. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
  75. solace_agent_mesh/cli/utils.py +15 -0
  76. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
  77. solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
  78. solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
  79. solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
  80. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
  81. solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
  82. solace_agent_mesh/common/a2a/__init__.py +213 -0
  83. solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
  84. solace_agent_mesh/common/a2a/artifact.py +328 -0
  85. solace_agent_mesh/common/a2a/events.py +183 -0
  86. solace_agent_mesh/common/a2a/message.py +307 -0
  87. solace_agent_mesh/common/a2a/protocol.py +513 -0
  88. solace_agent_mesh/common/a2a/task.py +127 -0
  89. solace_agent_mesh/common/a2a/translation.py +653 -0
  90. solace_agent_mesh/common/a2a/types.py +54 -0
  91. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  92. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
  93. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  94. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
  95. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
  96. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
  97. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  98. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
  99. solace_agent_mesh/common/agent_registry.py +1 -1
  100. solace_agent_mesh/common/common_llm.txt +192 -70
  101. solace_agent_mesh/common/data_parts.py +99 -0
  102. solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
  103. solace_agent_mesh/common/sac/__init__.py +0 -0
  104. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  105. solace_agent_mesh/common/sac/sam_component_base.py +252 -0
  106. solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
  107. solace_agent_mesh/common/services/services_llm.txt +206 -26
  108. solace_agent_mesh/common/utils/artifact_utils.py +29 -0
  109. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
  110. solace_agent_mesh/common/utils/utils_llm.txt +323 -42
  111. solace_agent_mesh/config_portal/backend/common.py +1 -1
  112. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
  113. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
  114. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
  115. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
  116. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
  117. solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
  118. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  119. solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
  120. solace_agent_mesh/core_a2a/service.py +20 -44
  121. solace_agent_mesh/gateway/base/app.py +27 -1
  122. solace_agent_mesh/gateway/base/base_llm.txt +177 -72
  123. solace_agent_mesh/gateway/base/component.py +294 -523
  124. solace_agent_mesh/gateway/gateway_llm.txt +299 -58
  125. solace_agent_mesh/gateway/http_sse/component.py +156 -183
  126. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
  127. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
  128. solace_agent_mesh/gateway/http_sse/main.py +8 -10
  129. solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
  130. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
  131. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
  132. solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
  133. solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
  134. solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
  135. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
  136. solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
  137. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  138. solace_agent_mesh/templates/gateway_component_template.py +149 -98
  139. {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
  140. {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +144 -127
  141. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
  142. solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +0 -2
  143. solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
  144. solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +0 -1
  145. solace_agent_mesh/assets/docs/search-doc-1756146501924.json +0 -1
  146. solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
  147. solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
  148. solace_agent_mesh/common/a2a_protocol.py +0 -564
  149. solace_agent_mesh/common/client/__init__.py +0 -4
  150. solace_agent_mesh/common/client/card_resolver.py +0 -21
  151. solace_agent_mesh/common/client/client.py +0 -85
  152. solace_agent_mesh/common/client/client_llm.txt +0 -133
  153. solace_agent_mesh/common/server/__init__.py +0 -4
  154. solace_agent_mesh/common/server/server.py +0 -122
  155. solace_agent_mesh/common/server/server_llm.txt +0 -169
  156. solace_agent_mesh/common/server/task_manager.py +0 -291
  157. solace_agent_mesh/common/server/utils.py +0 -28
  158. solace_agent_mesh/common/types.py +0 -411
  159. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
  160. /solace_agent_mesh/assets/docs/assets/js/{main.d79f063b.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
  161. {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
  162. {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
  163. {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,513 @@
1
+ """
2
+ Helpers for A2A protocol-level concerns, such as topic construction and
3
+ parsing of JSON-RPC requests and responses.
4
+ """
5
+ import re
6
+ import uuid
7
+ from typing import Any, Dict, Optional, Union
8
+
9
+ from solace_ai_connector.common.log import log
10
+
11
+ from a2a.types import (
12
+ A2ARequest,
13
+ CancelTaskRequest,
14
+ GetTaskSuccessResponse,
15
+ InternalError,
16
+ InvalidRequestError,
17
+ JSONRPCError,
18
+ JSONRPCResponse,
19
+ JSONRPCSuccessResponse,
20
+ Message,
21
+ MessageSendParams,
22
+ SendMessageRequest,
23
+ SendMessageSuccessResponse,
24
+ SendStreamingMessageRequest,
25
+ SendStreamingMessageSuccessResponse,
26
+ Task,
27
+ TaskArtifactUpdateEvent,
28
+ TaskIdParams,
29
+ TaskStatusUpdateEvent,
30
+ )
31
+
32
+ # --- Topic Construction Helpers ---
33
+
34
+ A2A_VERSION = "v1"
35
+ A2A_BASE_PATH = f"a2a/{A2A_VERSION}"
36
+
37
+
38
+ def get_a2a_base_topic(namespace: str) -> str:
39
+ """Returns the base topic prefix for all A2A communication."""
40
+ if not namespace:
41
+ raise ValueError("A2A namespace cannot be empty.")
42
+ return f"{namespace.rstrip('/')}/{A2A_BASE_PATH}"
43
+
44
+
45
+ def get_discovery_topic(namespace: str) -> str:
46
+ """Returns the topic for agent card discovery."""
47
+ return f"{get_a2a_base_topic(namespace)}/discovery/agentcards"
48
+
49
+
50
+ def get_agent_request_topic(namespace: str, agent_name: str) -> str:
51
+ """Returns the topic for sending requests to a specific agent."""
52
+ if not agent_name:
53
+ raise ValueError("Agent name cannot be empty.")
54
+ return f"{get_a2a_base_topic(namespace)}/agent/request/{agent_name}"
55
+
56
+
57
+ def get_gateway_status_topic(namespace: str, gateway_id: str, task_id: str) -> str:
58
+ """
59
+ Returns the specific topic for an agent to publish status updates TO a specific gateway instance.
60
+ """
61
+ if not gateway_id:
62
+ raise ValueError("Gateway ID cannot be empty.")
63
+ if not task_id:
64
+ raise ValueError("Task ID cannot be empty.")
65
+ return f"{get_a2a_base_topic(namespace)}/gateway/status/{gateway_id}/{task_id}"
66
+
67
+
68
+ def get_gateway_response_topic(namespace: str, gateway_id: str, task_id: str) -> str:
69
+ """
70
+ Returns the specific topic for an agent to publish the final response TO a specific gateway instance.
71
+ Includes task_id for potential correlation/filtering, though gateway might subscribe more broadly.
72
+ """
73
+ if not gateway_id:
74
+ raise ValueError("Gateway ID cannot be empty.")
75
+ if not task_id:
76
+ raise ValueError("Task ID cannot be empty.")
77
+ return f"{get_a2a_base_topic(namespace)}/gateway/response/{gateway_id}/{task_id}"
78
+
79
+
80
+ def get_gateway_status_subscription_topic(namespace: str, self_gateway_id: str) -> str:
81
+ """
82
+ Returns the wildcard topic for a gateway instance to subscribe to receive status updates
83
+ intended for it.
84
+ """
85
+ if not self_gateway_id:
86
+ raise ValueError("Gateway ID is required for gateway status subscription")
87
+ return f"{get_a2a_base_topic(namespace)}/gateway/status/{self_gateway_id}/>"
88
+
89
+
90
+ def get_gateway_response_subscription_topic(
91
+ namespace: str, self_gateway_id: str
92
+ ) -> str:
93
+ """
94
+ Returns the wildcard topic for a gateway instance to subscribe to receive final responses
95
+ intended for it.
96
+ """
97
+ if not self_gateway_id:
98
+ raise ValueError("Gateway ID is required for gateway response subscription")
99
+ return f"{get_a2a_base_topic(namespace)}/gateway/response/{self_gateway_id}/>"
100
+
101
+
102
+ def get_peer_agent_status_topic(
103
+ namespace: str, delegating_agent_name: str, sub_task_id: str
104
+ ) -> str:
105
+ """
106
+ Returns the topic for publishing status updates for a sub-task *back to the delegating agent*.
107
+ This topic includes the delegating agent's name.
108
+ """
109
+ if not delegating_agent_name:
110
+ raise ValueError("delegating_agent_name is required for peer status topic")
111
+ return (
112
+ f"{get_a2a_base_topic(namespace)}/agent/status/{delegating_agent_name}/{sub_task_id}"
113
+ )
114
+
115
+
116
+ def get_agent_response_topic(
117
+ namespace: str, delegating_agent_name: str, sub_task_id: str
118
+ ) -> str:
119
+ """
120
+ Returns the specific topic for publishing the final response for a sub-task
121
+ back to the delegating agent. Includes the delegating agent's name.
122
+ """
123
+ if not delegating_agent_name:
124
+ raise ValueError("delegating_agent_name is required for peer response topic")
125
+ if not sub_task_id:
126
+ raise ValueError("sub_task_id is required for peer response topic")
127
+ return f"{get_a2a_base_topic(namespace)}/agent/response/{delegating_agent_name}/{sub_task_id}"
128
+
129
+
130
+ def get_agent_response_subscription_topic(namespace: str, self_agent_name: str) -> str:
131
+ """
132
+ Returns the wildcard topic for an agent to subscribe to receive responses
133
+ for tasks it delegated. Includes the agent's own name.
134
+ """
135
+ if not self_agent_name:
136
+ raise ValueError("self_agent_name is required for agent response subscription")
137
+ return f"{get_a2a_base_topic(namespace)}/agent/response/{self_agent_name}/>"
138
+
139
+
140
+ def get_agent_status_subscription_topic(namespace: str, self_agent_name: str) -> str:
141
+ """
142
+ Returns the wildcard topic for an agent to subscribe to receive status updates
143
+ for tasks it delegated. Includes the agent's own name.
144
+ """
145
+ if not self_agent_name:
146
+ raise ValueError("self_agent_name is required for agent status subscription")
147
+ return f"{get_a2a_base_topic(namespace)}/agent/status/{self_agent_name}/>"
148
+
149
+
150
+ def get_client_response_topic(namespace: str, client_id: str) -> str:
151
+ """Returns the topic for publishing the final response TO a specific client."""
152
+ if not client_id:
153
+ raise ValueError("Client ID cannot be empty.")
154
+ return f"{get_a2a_base_topic(namespace)}/client/response/{client_id}"
155
+
156
+
157
+ def get_client_status_topic(namespace: str, client_id: str, task_id: str) -> str:
158
+ """
159
+ Returns the specific topic for publishing status updates for a task *to the original client*.
160
+ This topic is client and task-specific.
161
+ """
162
+ if not client_id:
163
+ raise ValueError("Client ID cannot be empty.")
164
+ if not task_id:
165
+ raise ValueError("Task ID cannot be empty.")
166
+ return f"{get_a2a_base_topic(namespace)}/client/status/{client_id}/{task_id}"
167
+
168
+
169
+ def get_client_status_subscription_topic(namespace: str, client_id: str) -> str:
170
+ """
171
+ Returns the wildcard topic for a client to subscribe to receive status updates
172
+ for tasks it initiated. Includes the client's own ID.
173
+ """
174
+ if not client_id:
175
+ raise ValueError("Client ID cannot be empty.")
176
+ return f"{get_a2a_base_topic(namespace)}/client/status/{client_id}/>"
177
+
178
+
179
+ def subscription_to_regex(subscription: str) -> str:
180
+ """Converts a Solace topic subscription string to a regex pattern."""
181
+ # Escape regex special characters except for Solace wildcards
182
+ pattern = re.escape(subscription)
183
+ # Replace Solace single-level wildcard '*' with regex equivalent '[^/]+'
184
+ pattern = pattern.replace(r"\*", r"[^/]+")
185
+ # Replace Solace multi-level wildcard '>' at the end with regex equivalent '.*'
186
+ if pattern.endswith(r"/>"):
187
+ pattern = pattern[:-1] + r".*" # Remove escaped '>' and add '.*'
188
+ return pattern
189
+
190
+
191
+ def topic_matches_subscription(topic: str, subscription: str) -> bool:
192
+ """Checks if a topic matches a Solace subscription pattern."""
193
+ regex_pattern = subscription_to_regex(subscription)
194
+ return re.fullmatch(regex_pattern, topic) is not None
195
+
196
+
197
+ # --- JSON-RPC Envelope Helpers ---
198
+
199
+
200
+ def get_request_id(request: A2ARequest) -> str | int:
201
+ """Gets the JSON-RPC request ID from any A2A request object."""
202
+ return request.root.id
203
+
204
+
205
+ def get_request_method(request: A2ARequest) -> str:
206
+ """Gets the JSON-RPC method name from any A2A request object."""
207
+ return request.root.method
208
+
209
+
210
+ def get_message_from_send_request(request: A2ARequest) -> Optional[Message]:
211
+ """
212
+ Safely gets the Message object from a SendMessageRequest or
213
+ SendStreamingMessageRequest. Returns None for other request types.
214
+ """
215
+ if isinstance(request.root, (SendMessageRequest, SendStreamingMessageRequest)):
216
+ return request.root.params.message
217
+ return None
218
+
219
+
220
+ def get_task_id_from_cancel_request(request: A2ARequest) -> Optional[str]:
221
+ """Safely gets the task ID from a CancelTaskRequest."""
222
+ if isinstance(request.root, CancelTaskRequest):
223
+ return request.root.params.id
224
+ return None
225
+
226
+
227
+ def get_response_id(response: JSONRPCResponse) -> Optional[Union[str, int]]:
228
+ """Safely gets the ID from any JSON-RPC response object."""
229
+ if hasattr(response.root, "id"):
230
+ return response.root.id
231
+ return None
232
+
233
+
234
+ def get_response_result(response: JSONRPCResponse) -> Optional[Any]:
235
+ """Safely gets the result object from any successful JSON-RPC response."""
236
+ if hasattr(response.root, "result"):
237
+ return response.root.result
238
+ return None
239
+
240
+
241
+ def get_response_error(response: JSONRPCResponse) -> Optional[JSONRPCError]:
242
+ """Safely gets the error object from any JSON-RPC error response."""
243
+ if hasattr(response.root, "error"):
244
+ return response.root.error
245
+ return None
246
+
247
+
248
+ def get_error_message(error: JSONRPCError) -> str:
249
+ """Safely gets the message string from a JSONRPCError object."""
250
+ return error.message
251
+
252
+
253
+ def get_error_code(error: JSONRPCError) -> int:
254
+ """Safely gets the code from a JSONRPCError object."""
255
+ return error.code
256
+
257
+
258
+ def get_error_data(error: JSONRPCError) -> Optional[Any]:
259
+ """Safely gets the data from a JSONRPCError object."""
260
+ return error.data
261
+
262
+
263
+ def create_success_response(
264
+ result: Any, request_id: Optional[Union[str, int]]
265
+ ) -> JSONRPCResponse:
266
+ """
267
+ Creates a successful JSON-RPC response object by wrapping the result in the
268
+ appropriate specific success response model based on the result's type.
269
+
270
+ Args:
271
+ result: The result payload (e.g., Task, TaskStatusUpdateEvent).
272
+ request_id: The ID of the original request.
273
+
274
+ Returns:
275
+ A new `JSONRPCResponse` object.
276
+
277
+ Raises:
278
+ TypeError: If the result type is not a supported A2A model.
279
+ """
280
+ specific_response: Any
281
+ if isinstance(result, (TaskStatusUpdateEvent, TaskArtifactUpdateEvent)):
282
+ specific_response = SendStreamingMessageSuccessResponse(
283
+ id=request_id, result=result
284
+ )
285
+ elif isinstance(result, Task):
286
+ # When returning a final task, GetTaskSuccessResponse is a suitable choice.
287
+ specific_response = GetTaskSuccessResponse(id=request_id, result=result)
288
+ else:
289
+ raise TypeError(
290
+ f"Unsupported result type for create_success_response: {type(result).__name__}"
291
+ )
292
+
293
+ return JSONRPCResponse(root=specific_response)
294
+
295
+
296
+ def create_internal_error_response(
297
+ message: str,
298
+ request_id: Optional[Union[str, int]],
299
+ data: Optional[Dict[str, Any]] = None,
300
+ ) -> JSONRPCResponse:
301
+ """
302
+ Creates a JSON-RPC response object for an InternalError.
303
+
304
+ Args:
305
+ message: The error message.
306
+ request_id: The ID of the original request.
307
+ data: Optional structured data to include with the error.
308
+
309
+ Returns:
310
+ A new `JSONRPCResponse` object containing an `InternalError`.
311
+ """
312
+ error = create_internal_error(message=message, data=data)
313
+ return JSONRPCResponse(id=request_id, error=error)
314
+
315
+
316
+ def create_invalid_request_error_response(
317
+ message: str,
318
+ request_id: Optional[Union[str, int]],
319
+ data: Optional[Any] = None,
320
+ ) -> JSONRPCResponse:
321
+ """
322
+ Creates a JSON-RPC response object for an InvalidRequestError.
323
+
324
+ Args:
325
+ message: The error message.
326
+ request_id: The ID of the original request.
327
+ data: Optional structured data to include with the error.
328
+
329
+ Returns:
330
+ A new `JSONRPCResponse` object containing an `InvalidRequestError`.
331
+ """
332
+ error = create_invalid_request_error(message=message, data=data)
333
+ return JSONRPCResponse(id=request_id, error=error)
334
+
335
+
336
+ def create_internal_error(
337
+ message: str,
338
+ data: Optional[Dict[str, Any]] = None,
339
+ ) -> InternalError:
340
+ """
341
+ Creates an InternalError object.
342
+
343
+ Args:
344
+ message: The error message.
345
+ data: Optional structured data to include with the error.
346
+
347
+ Returns:
348
+ A new `InternalError` object.
349
+ """
350
+ return InternalError(message=message, data=data)
351
+
352
+
353
+ def create_invalid_request_error(
354
+ message: str, data: Optional[Any] = None
355
+ ) -> InvalidRequestError:
356
+ """
357
+ Creates an InvalidRequestError object.
358
+
359
+ Args:
360
+ message: The error message.
361
+ data: Optional structured data to include with the error.
362
+
363
+ Returns:
364
+ A new `InvalidRequestError` object.
365
+ """
366
+ return InvalidRequestError(message=message, data=data)
367
+
368
+
369
+ def create_generic_success_response(
370
+ result: Any, request_id: Optional[Union[str, int]] = None
371
+ ) -> JSONRPCSuccessResponse:
372
+ """
373
+ Creates a generic successful JSON-RPC response object.
374
+ Note: This is for non-A2A-spec-compliant endpoints that use a similar structure.
375
+
376
+ Args:
377
+ result: The result payload for the response.
378
+ request_id: The ID of the original request.
379
+
380
+ Returns:
381
+ A new `JSONRPCSuccessResponse` object.
382
+ """
383
+ return JSONRPCSuccessResponse(id=request_id, result=result)
384
+
385
+
386
+ def create_error_response(
387
+ error: JSONRPCError,
388
+ request_id: Optional[Union[str, int]],
389
+ ) -> JSONRPCResponse:
390
+ """
391
+ Creates a JSON-RPC error response object from a given error model.
392
+
393
+ Args:
394
+ error: The JSONRPCError model instance.
395
+ request_id: The ID of the original request.
396
+
397
+ Returns:
398
+ A new `JSONRPCResponse` object containing the error.
399
+ """
400
+ return JSONRPCResponse(id=request_id, error=error)
401
+
402
+
403
+ def create_cancel_task_request(task_id: str) -> CancelTaskRequest:
404
+ """
405
+ Creates a CancelTaskRequest object.
406
+
407
+ Args:
408
+ task_id: The ID of the task to cancel.
409
+
410
+ Returns:
411
+ A new `CancelTaskRequest` object.
412
+ """
413
+ params = TaskIdParams(id=task_id)
414
+ return CancelTaskRequest(id=uuid.uuid4().hex, params=params)
415
+
416
+
417
+ def create_send_message_request(
418
+ message: Message,
419
+ task_id: str,
420
+ metadata: Optional[Dict[str, Any]] = None,
421
+ ) -> SendMessageRequest:
422
+ """
423
+ Creates a SendMessageRequest object.
424
+
425
+ Args:
426
+ message: The A2AMessage object to send.
427
+ task_id: The unique ID for the task.
428
+ metadata: Optional metadata for the send request.
429
+
430
+ Returns:
431
+ A new `SendMessageRequest` object.
432
+ """
433
+ send_params = MessageSendParams(message=message, metadata=metadata)
434
+ return SendMessageRequest(id=task_id, params=send_params)
435
+
436
+
437
+ def create_send_streaming_message_request(
438
+ message: Message,
439
+ task_id: str,
440
+ metadata: Optional[Dict[str, Any]] = None,
441
+ ) -> SendStreamingMessageRequest:
442
+ """
443
+ Creates a SendStreamingMessageRequest object.
444
+
445
+ Args:
446
+ message: The A2AMessage object to send.
447
+ task_id: The unique ID for the task.
448
+ metadata: Optional metadata for the send request.
449
+
450
+ Returns:
451
+ A new `SendStreamingMessageRequest` object.
452
+ """
453
+ send_params = MessageSendParams(message=message, metadata=metadata)
454
+ return SendStreamingMessageRequest(id=task_id, params=send_params)
455
+
456
+
457
+ def create_send_message_success_response(
458
+ result: Union[Task, Message], request_id: Optional[Union[str, int]]
459
+ ) -> SendMessageSuccessResponse:
460
+ """
461
+ Creates a SendMessageSuccessResponse object.
462
+
463
+ Args:
464
+ result: The result payload (Task or Message).
465
+ request_id: The ID of the original request.
466
+
467
+ Returns:
468
+ A new `SendMessageSuccessResponse` object.
469
+ """
470
+ return SendMessageSuccessResponse(id=request_id, result=result)
471
+
472
+
473
+ def create_send_streaming_message_success_response(
474
+ result: Union[Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent],
475
+ request_id: Optional[Union[str, int]],
476
+ ) -> SendStreamingMessageSuccessResponse:
477
+ """
478
+ Creates a SendStreamingMessageSuccessResponse object.
479
+
480
+ Args:
481
+ result: The result payload.
482
+ request_id: The ID of the original request.
483
+
484
+ Returns:
485
+ A new `SendStreamingMessageSuccessResponse` object.
486
+ """
487
+ return SendStreamingMessageSuccessResponse(id=request_id, result=result)
488
+
489
+
490
+ def extract_task_id_from_topic(
491
+ topic: str, subscription_pattern: str, log_identifier: str
492
+ ) -> Optional[str]:
493
+ """Extracts the task ID from the end of a topic string based on the subscription."""
494
+ base_regex_str = subscription_to_regex(subscription_pattern).replace(r".*", "")
495
+ match = re.match(base_regex_str, topic)
496
+ if match:
497
+ task_id_part = topic[match.end() :]
498
+ task_id = task_id_part.lstrip("/")
499
+ if task_id:
500
+ log.debug(
501
+ "%s Extracted Task ID '%s' from topic '%s'",
502
+ log_identifier,
503
+ task_id,
504
+ topic,
505
+ )
506
+ return task_id
507
+ log.warning(
508
+ "%s Could not extract Task ID from topic '%s' using pattern '%s'",
509
+ log_identifier,
510
+ topic,
511
+ subscription_pattern,
512
+ )
513
+ return None
@@ -0,0 +1,127 @@
1
+ """
2
+ Helpers for creating and consuming A2A Task objects.
3
+ """
4
+ from datetime import datetime, timezone
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from a2a.types import (
8
+ Artifact,
9
+ Message,
10
+ Task,
11
+ TaskState,
12
+ TaskStatus,
13
+ )
14
+
15
+
16
+ # --- Creation Helpers ---
17
+
18
+
19
+ def create_initial_task(
20
+ task_id: str,
21
+ context_id: str,
22
+ agent_name: str,
23
+ ) -> Task:
24
+ """
25
+ Creates an initial Task object, typically for returning to a client
26
+ after a task has been submitted.
27
+
28
+ Args:
29
+ task_id: The unique ID for the task.
30
+ context_id: The context/session ID for the task.
31
+ agent_name: The name of the agent handling the task.
32
+
33
+ Returns:
34
+ A new `Task` object with 'submitted' status.
35
+ """
36
+ initial_status = TaskStatus(state=TaskState.submitted)
37
+ return Task(
38
+ id=task_id,
39
+ context_id=context_id,
40
+ status=initial_status,
41
+ kind="task",
42
+ metadata={"agent_name": agent_name},
43
+ )
44
+
45
+
46
+ def create_task_status(
47
+ state: TaskState,
48
+ message: Optional[Message] = None,
49
+ ) -> TaskStatus:
50
+ """
51
+ Creates a TaskStatus object.
52
+
53
+ Args:
54
+ state: The state of the task.
55
+ message: An optional message providing more details.
56
+
57
+ Returns:
58
+ A new `TaskStatus` object with a current timestamp.
59
+ """
60
+ return TaskStatus(
61
+ state=state,
62
+ message=message,
63
+ timestamp=datetime.now(timezone.utc).isoformat(),
64
+ )
65
+
66
+
67
+ def create_final_task(
68
+ task_id: str,
69
+ context_id: str,
70
+ final_status: TaskStatus,
71
+ artifacts: Optional[List[Artifact]] = None,
72
+ metadata: Optional[Dict[str, Any]] = None,
73
+ ) -> Task:
74
+ """
75
+ Creates a final Task object, typically for sending as a final response.
76
+
77
+ Args:
78
+ task_id: The unique ID for the task.
79
+ context_id: The context/session ID for the task.
80
+ final_status: The final status of the task (e.g., completed, failed).
81
+ artifacts: A list of artifacts produced by the task.
82
+ metadata: Optional metadata to include in the task.
83
+
84
+ Returns:
85
+ A new `Task` object representing the final state.
86
+ """
87
+ return Task(
88
+ id=task_id,
89
+ context_id=context_id,
90
+ status=final_status,
91
+ artifacts=artifacts,
92
+ metadata=metadata,
93
+ kind="task",
94
+ )
95
+
96
+
97
+ # --- Consumption Helpers ---
98
+
99
+
100
+ def get_task_id(task: Task) -> str:
101
+ """Safely retrieves the ID from a Task object."""
102
+ return task.id
103
+
104
+
105
+ def get_task_context_id(task: Task) -> str:
106
+ """Safely retrieves the context ID from a Task object."""
107
+ return task.context_id
108
+
109
+
110
+ def get_task_status(task: Task) -> TaskState:
111
+ """Safely retrieves the state from a Task's status."""
112
+ return task.status.state
113
+
114
+
115
+ def get_task_history(task: Task) -> Optional[List[Message]]:
116
+ """Safely retrieves the history from a Task object."""
117
+ return task.history
118
+
119
+
120
+ def get_task_artifacts(task: Task) -> Optional[List[Artifact]]:
121
+ """Safely retrieves the artifacts from a Task object."""
122
+ return task.artifacts
123
+
124
+
125
+ def get_task_metadata(task: Task) -> Optional[Dict[str, Any]]:
126
+ """Safely retrieves the metadata from a Task object."""
127
+ return task.metadata