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
@@ -0,0 +1,328 @@
1
+ """
2
+ Helpers for creating and consuming A2A Artifact objects.
3
+ """
4
+
5
+ import uuid
6
+ import base64
7
+ from datetime import datetime, timezone
8
+ from typing import Any, List, Optional, TYPE_CHECKING
9
+ from urllib.parse import urlparse, parse_qs
10
+
11
+ from .types import ContentPart
12
+ from a2a.types import (
13
+ Artifact,
14
+ DataPart,
15
+ FilePart,
16
+ FileWithBytes,
17
+ FileWithUri,
18
+ Part,
19
+ TextPart,
20
+ )
21
+ from solace_ai_connector.common.log import log
22
+ from .. import a2a
23
+
24
+ if TYPE_CHECKING:
25
+ from google.adk.artifacts import BaseArtifactService
26
+
27
+
28
+ # --- Creation Helpers ---
29
+
30
+
31
+ def create_text_artifact(
32
+ name: str,
33
+ text: str,
34
+ description: str = "",
35
+ artifact_id: Optional[str] = None,
36
+ ) -> Artifact:
37
+ """
38
+ Creates a new Artifact object containing only a single TextPart.
39
+
40
+ Args:
41
+ name: The human-readable name of the artifact.
42
+ text: The text content of the artifact.
43
+ description: An optional description of the artifact.
44
+ artifact_id: The artifact ID. If None, a new UUID is generated.
45
+
46
+ Returns:
47
+ A new `Artifact` object.
48
+ """
49
+ text_part = TextPart(text=text)
50
+ return Artifact(
51
+ artifact_id=artifact_id or str(uuid.uuid4().hex),
52
+ parts=[Part(root=text_part)],
53
+ name=name,
54
+ description=description,
55
+ )
56
+
57
+
58
+ def create_data_artifact(
59
+ name: str,
60
+ data: dict[str, Any],
61
+ description: str = "",
62
+ artifact_id: Optional[str] = None,
63
+ ) -> Artifact:
64
+ """
65
+ Creates a new Artifact object containing only a single DataPart.
66
+
67
+ Args:
68
+ name: The human-readable name of the artifact.
69
+ data: The structured data content of the artifact.
70
+ description: An optional description of the artifact.
71
+ artifact_id: The artifact ID. If None, a new UUID is generated.
72
+
73
+ Returns:
74
+ A new `Artifact` object.
75
+ """
76
+ data_part = DataPart(data=data)
77
+ return Artifact(
78
+ artifact_id=artifact_id or str(uuid.uuid4().hex),
79
+ parts=[Part(root=data_part)],
80
+ name=name,
81
+ description=description,
82
+ )
83
+
84
+
85
+ def update_artifact_parts(artifact: Artifact, new_parts: List[ContentPart]) -> Artifact:
86
+ """Returns a new Artifact with its parts replaced."""
87
+ wrapped_parts = [Part(root=p) for p in new_parts]
88
+ return artifact.model_copy(update={"parts": wrapped_parts})
89
+
90
+
91
+ async def prepare_file_part_for_publishing(
92
+ part: FilePart,
93
+ mode: str,
94
+ artifact_service: "BaseArtifactService",
95
+ user_id: str,
96
+ session_id: str,
97
+ target_agent_name: str,
98
+ log_identifier: str,
99
+ ) -> Optional[FilePart]:
100
+ """
101
+ Prepares a FilePart for publishing based on the artifact handling mode.
102
+
103
+ - 'ignore': Returns None.
104
+ - 'embed': Ensures the part contains bytes, resolving a URI if necessary.
105
+ - 'reference': Ensures the part contains a URI, saving bytes if necessary.
106
+ - 'passthrough': Returns the part as-is.
107
+
108
+ Args:
109
+ part: The input FilePart, which may contain raw bytes or a URI.
110
+ mode: The artifact handling mode ('ignore', 'embed', 'reference', 'passthrough').
111
+ artifact_service: The ADK artifact service instance.
112
+ user_id: The user ID for the artifact context.
113
+ session_id: The session ID for the artifact context.
114
+ target_agent_name: The name of the agent the artifact will be associated with.
115
+ log_identifier: The logging identifier for log messages.
116
+
117
+ Returns:
118
+ The processed FilePart, or None if ignored.
119
+ """
120
+ log_id = f"{log_identifier}[PrepareFilePart]"
121
+
122
+ if mode == "ignore":
123
+ log.debug("%s Mode is 'ignore', filtering out FilePart.", log_id)
124
+ return None
125
+
126
+ if mode == "passthrough":
127
+ log.debug("%s Mode is 'passthrough', returning original FilePart.", log_id)
128
+ return part
129
+
130
+ if mode == "embed":
131
+ if isinstance(part.file, FileWithUri):
132
+ log.debug("%s Mode is 'embed', resolving URI for FilePart.", log_id)
133
+ return await resolve_file_part_uri(part, artifact_service, log_identifier)
134
+ return part # It's already bytes, so it's embedded.
135
+
136
+ if mode == "reference":
137
+ if isinstance(part.file, FileWithBytes):
138
+ if not artifact_service:
139
+ log.warning(
140
+ "%s Mode is 'reference' but no artifact_service is configured. Ignoring FilePart '%s'.",
141
+ log_id,
142
+ part.file.name,
143
+ )
144
+ return None
145
+
146
+ try:
147
+ filename = part.file.name or f"upload-{uuid.uuid4().hex}"
148
+ content_bytes = base64.b64decode(part.file.bytes)
149
+ mime_type = part.file.mime_type or "application/octet-stream"
150
+
151
+ # Create a concise and accurate metadata dictionary.
152
+ metadata_to_save = {
153
+ "source": log_identifier,
154
+ "description": "This artifact was uploaded via the gateway",
155
+ }
156
+
157
+ # Call the helper with the new, simpler metadata.
158
+ from ...agent.utils.artifact_helpers import save_artifact_with_metadata
159
+
160
+ save_result = await save_artifact_with_metadata(
161
+ artifact_service=artifact_service,
162
+ app_name=target_agent_name,
163
+ user_id=user_id,
164
+ session_id=session_id,
165
+ filename=filename,
166
+ content_bytes=content_bytes,
167
+ mime_type=mime_type,
168
+ metadata_dict=metadata_to_save,
169
+ timestamp=datetime.now(timezone.utc),
170
+ )
171
+
172
+ if save_result["status"] == "success":
173
+ saved_version = save_result.get("data_version")
174
+ from ...agent.utils.artifact_helpers import format_artifact_uri
175
+
176
+ artifact_uri = format_artifact_uri(
177
+ app_name=target_agent_name,
178
+ user_id=user_id,
179
+ session_id=session_id,
180
+ filename=filename,
181
+ version=saved_version,
182
+ )
183
+ ref_part = a2a.create_file_part_from_uri(
184
+ uri=artifact_uri,
185
+ name=filename,
186
+ mime_type=mime_type,
187
+ metadata=part.metadata,
188
+ )
189
+ log.info(
190
+ "%s Converted embedded file '%s' to reference: %s",
191
+ log_id,
192
+ filename,
193
+ artifact_uri,
194
+ )
195
+ return ref_part
196
+ else:
197
+ log.error(
198
+ "%s Failed to save artifact via helper: %s. Skipping FilePart.",
199
+ log_id,
200
+ save_result.get("message"),
201
+ )
202
+ return None
203
+
204
+ except Exception as e:
205
+ log.exception(
206
+ "%s Failed to save artifact for reference mode: %s. Skipping FilePart.",
207
+ log_id,
208
+ e,
209
+ )
210
+ return None
211
+ return part # It's already a reference (URI)
212
+
213
+ # Default case if mode is unrecognized
214
+ log.warning(
215
+ "%s Unrecognized artifact_handling_mode '%s'. Ignoring FilePart.", log_id, mode
216
+ )
217
+ return None
218
+
219
+
220
+ async def resolve_file_part_uri(
221
+ part: FilePart, artifact_service: "BaseArtifactService", log_identifier: str
222
+ ) -> FilePart:
223
+ """
224
+ Resolves an artifact URI within a FilePart into embedded bytes.
225
+
226
+ If the FilePart does not contain a resolvable `artifact://` URI, it is
227
+ returned unchanged.
228
+
229
+ Args:
230
+ part: The FilePart to resolve.
231
+ artifact_service: The ADK artifact service instance.
232
+ log_identifier: The logging identifier for log messages.
233
+
234
+ Returns:
235
+ A FilePart, either with embedded bytes if resolved, or the original part.
236
+ """
237
+ if not (
238
+ isinstance(part.file, FileWithUri)
239
+ and part.file.uri
240
+ and part.file.uri.startswith("artifact://")
241
+ ):
242
+ return part
243
+
244
+ if not artifact_service:
245
+ log.warning(
246
+ "%s Cannot resolve artifact URI, artifact_service is not configured.",
247
+ log_identifier,
248
+ )
249
+ return part
250
+
251
+ uri = part.file.uri
252
+ log_id_prefix = f"{log_identifier}[ResolveURI]"
253
+ try:
254
+ log.info("%s Found artifact URI to resolve: %s", log_id_prefix, uri)
255
+ parsed_uri = urlparse(uri)
256
+ app_name = parsed_uri.netloc
257
+ path_parts = parsed_uri.path.strip("/").split("/")
258
+
259
+ if not app_name or len(path_parts) != 3:
260
+ raise ValueError(
261
+ "Invalid URI structure. Expected artifact://app_name/user_id/session_id/filename"
262
+ )
263
+
264
+ user_id, session_id, filename = path_parts
265
+ version_str = parse_qs(parsed_uri.query).get("version", [None])[0]
266
+ version = int(version_str) if version_str else None
267
+
268
+ from ...agent.utils.artifact_helpers import load_artifact_content_or_metadata
269
+
270
+ loaded_artifact = await load_artifact_content_or_metadata(
271
+ artifact_service=artifact_service,
272
+ app_name=app_name,
273
+ user_id=user_id,
274
+ session_id=session_id,
275
+ filename=filename,
276
+ version=version,
277
+ return_raw_bytes=True,
278
+ )
279
+
280
+ if loaded_artifact.get("status") == "success":
281
+ content_bytes = loaded_artifact.get("raw_bytes")
282
+ new_file_content = FileWithBytes(
283
+ bytes=base64.b64encode(content_bytes).decode("utf-8"),
284
+ mime_type=part.file.mime_type,
285
+ name=part.file.name,
286
+ )
287
+ part.file = new_file_content
288
+ log.info(
289
+ "%s Successfully resolved and embedded artifact: %s",
290
+ log_id_prefix,
291
+ uri,
292
+ )
293
+ else:
294
+ log.error(
295
+ "%s Failed to resolve artifact URI '%s': %s",
296
+ log_id_prefix,
297
+ uri,
298
+ loaded_artifact.get("message"),
299
+ )
300
+ except Exception as e:
301
+ log.exception("%s Error resolving artifact URI '%s': %s", log_id_prefix, uri, e)
302
+ return part
303
+
304
+
305
+ # --- Consumption Helpers ---
306
+
307
+
308
+ def get_artifact_id(artifact: Artifact) -> str:
309
+ """Safely retrieves the ID from an Artifact object."""
310
+ return artifact.artifact_id
311
+
312
+
313
+ def get_artifact_name(artifact: Artifact) -> Optional[str]:
314
+ """Safely retrieves the name from an Artifact object."""
315
+ return artifact.name
316
+
317
+
318
+ def get_parts_from_artifact(artifact: Artifact) -> List[ContentPart]:
319
+ """
320
+ Extracts the raw, unwrapped Part objects (TextPart, DataPart, etc.) from an Artifact.
321
+
322
+ Args:
323
+ artifact: The `Artifact` object.
324
+
325
+ Returns:
326
+ A list of the unwrapped content parts.
327
+ """
328
+ return [part.root for part in artifact.parts]
@@ -0,0 +1,183 @@
1
+ """
2
+ Helpers for creating and consuming A2A asynchronous event objects, such as
3
+ TaskStatusUpdateEvent and TaskArtifactUpdateEvent.
4
+ """
5
+
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from a2a.types import (
9
+ Artifact,
10
+ DataPart,
11
+ Message,
12
+ TaskArtifactUpdateEvent,
13
+ TaskState,
14
+ TaskStatusUpdateEvent,
15
+ )
16
+
17
+ from . import message as message_helpers
18
+ from . import task as task_helpers
19
+ from ...common.data_parts import SignalData
20
+
21
+
22
+ # --- Creation Helpers ---
23
+
24
+
25
+ def create_data_signal_event(
26
+ task_id: str,
27
+ context_id: str,
28
+ signal_data: SignalData,
29
+ agent_name: str,
30
+ part_metadata: Optional[Dict[str, Any]] = None,
31
+ ) -> TaskStatusUpdateEvent:
32
+ """
33
+ Creates a TaskStatusUpdateEvent from a specific signal data model.
34
+
35
+ This is a generalized helper that takes any of the defined SignalData
36
+ types and wraps it in the full A2A event structure.
37
+
38
+ Args:
39
+ task_id: The ID of the task being updated.
40
+ context_id: The context ID for the task.
41
+ signal_data: The Pydantic model for the signal (e.g., ToolInvocationStartData).
42
+ agent_name: The name of the agent sending the signal.
43
+ part_metadata: Optional metadata for the DataPart.
44
+
45
+ Returns:
46
+ A new `TaskStatusUpdateEvent` object containing the signal.
47
+ """
48
+ a2a_message = message_helpers.create_agent_data_message(
49
+ data=signal_data.model_dump(),
50
+ task_id=task_id,
51
+ context_id=context_id,
52
+ part_metadata=part_metadata,
53
+ )
54
+ return create_status_update(
55
+ task_id=task_id,
56
+ context_id=context_id,
57
+ message=a2a_message,
58
+ is_final=False,
59
+ metadata={"agent_name": agent_name},
60
+ )
61
+
62
+
63
+ def create_status_update(
64
+ task_id: str,
65
+ context_id: str,
66
+ message: Message,
67
+ is_final: bool = False,
68
+ metadata: Optional[Dict[str, Any]] = None,
69
+ ) -> TaskStatusUpdateEvent:
70
+ """
71
+ Creates a new TaskStatusUpdateEvent.
72
+
73
+ Args:
74
+ task_id: The ID of the task being updated.
75
+ context_id: The context ID for the task.
76
+ message: The A2AMessage object containing the status details.
77
+ is_final: Whether this is the final update for the task.
78
+ metadata: Optional metadata for the event.
79
+
80
+ Returns:
81
+ A new `TaskStatusUpdateEvent` object.
82
+ """
83
+ task_status = task_helpers.create_task_status(
84
+ state=TaskState.working,
85
+ message=message,
86
+ )
87
+ return TaskStatusUpdateEvent(
88
+ task_id=task_id,
89
+ context_id=context_id,
90
+ status=task_status,
91
+ final=is_final,
92
+ metadata=metadata,
93
+ kind="status-update",
94
+ )
95
+
96
+
97
+ def create_artifact_update(
98
+ task_id: str,
99
+ context_id: str,
100
+ artifact: Artifact,
101
+ append: bool = False,
102
+ last_chunk: bool = False,
103
+ metadata: Optional[Dict[str, Any]] = None,
104
+ ) -> TaskArtifactUpdateEvent:
105
+ """
106
+ Creates a new TaskArtifactUpdateEvent.
107
+
108
+ Args:
109
+ task_id: The ID of the task this artifact belongs to.
110
+ context_id: The context ID for the task.
111
+ artifact: The Artifact object being sent.
112
+ append: If true, the content should be appended to a previous artifact.
113
+ last_chunk: If true, this is the final chunk of the artifact.
114
+ metadata: Optional metadata for the event.
115
+
116
+ Returns:
117
+ A new `TaskArtifactUpdateEvent` object.
118
+ """
119
+ return TaskArtifactUpdateEvent(
120
+ task_id=task_id,
121
+ context_id=context_id,
122
+ artifact=artifact,
123
+ append=append,
124
+ last_chunk=last_chunk,
125
+ metadata=metadata,
126
+ kind="artifact-update",
127
+ )
128
+
129
+
130
+ # --- Consumption Helpers ---
131
+
132
+
133
+ def get_message_from_status_update(
134
+ event: TaskStatusUpdateEvent,
135
+ ) -> Optional[Message]:
136
+ """
137
+ Safely extracts the Message object from a TaskStatusUpdateEvent.
138
+
139
+ Args:
140
+ event: The TaskStatusUpdateEvent object.
141
+
142
+ Returns:
143
+ The `Message` object if present, otherwise None.
144
+ """
145
+ if event and event.status:
146
+ return event.status.message
147
+ return None
148
+
149
+
150
+ def get_data_parts_from_status_update(
151
+ event: TaskStatusUpdateEvent,
152
+ ) -> List[DataPart]:
153
+ """
154
+ Safely extracts all DataPart objects from a TaskStatusUpdateEvent's message.
155
+
156
+ Args:
157
+ event: The TaskStatusUpdateEvent object.
158
+
159
+ Returns:
160
+ A list of `DataPart` objects found, or an empty list.
161
+ """
162
+ message = get_message_from_status_update(event)
163
+ if not message:
164
+ return []
165
+
166
+ return message_helpers.get_data_parts_from_message(message)
167
+
168
+
169
+ def get_artifact_from_artifact_update(
170
+ event: TaskArtifactUpdateEvent,
171
+ ) -> Optional[Artifact]:
172
+ """
173
+ Safely extracts the Artifact object from a TaskArtifactUpdateEvent.
174
+
175
+ Args:
176
+ event: The TaskArtifactUpdateEvent object.
177
+
178
+ Returns:
179
+ The `Artifact` object if present, otherwise None.
180
+ """
181
+ if event:
182
+ return event.artifact
183
+ return None