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,182 @@
1
+ # DEVELOPER GUIDE: a2a
2
+
3
+ ## Quick Summary
4
+ The `a2a` directory provides a comprehensive abstraction layer for the A2A (Agent-to-Agent) protocol, offering helper functions for creating, consuming, and translating A2A protocol objects. It acts as a facade that insulates applications from the specifics of the underlying a2a-sdk, providing simplified interfaces for messages, artifacts, tasks, events, and protocol-level operations.
5
+
6
+ ## Files Overview
7
+ - `__init__.py` - Main entry point exposing all commonly used A2A helpers
8
+ - `artifact.py` - Helpers for creating and consuming A2A Artifact objects
9
+ - `events.py` - Helpers for creating and consuming A2A asynchronous event objects
10
+ - `message.py` - Helpers for creating and consuming A2A Message and Part objects
11
+ - `protocol.py` - Helpers for A2A protocol-level concerns like topic construction and JSON-RPC
12
+ - `task.py` - Helpers for creating and consuming A2A Task objects
13
+ - `translation.py` - Helpers for translating between A2A protocol objects and other domains
14
+ - `types.py` - Custom type aliases and models for the A2A helper layer
15
+
16
+ ## Developer API Reference
17
+
18
+ ### __init__.py
19
+ **Purpose:** Main entry point that exposes all commonly used A2A helpers for easy access
20
+ **Import:** `from solace_agent_mesh.common.a2a import *`
21
+
22
+ This file re-exports all public functions from the other modules, allowing developers to import everything from the main package.
23
+
24
+ ### artifact.py
25
+ **Purpose:** Provides helpers for creating and consuming A2A Artifact objects
26
+ **Import:** `from solace_agent_mesh.common.a2a.artifact import create_text_artifact, create_data_artifact, get_artifact_id`
27
+
28
+ **Functions:**
29
+ - `create_text_artifact(name: str, text: str, description: str = "", artifact_id: Optional[str] = None) -> Artifact` - Creates a new Artifact containing a single TextPart
30
+ - `create_data_artifact(name: str, data: dict[str, Any], description: str = "", artifact_id: Optional[str] = None) -> Artifact` - Creates a new Artifact containing a single DataPart
31
+ - `update_artifact_parts(artifact: Artifact, new_parts: List[ContentPart]) -> Artifact` - Returns a new Artifact with replaced parts
32
+ - `prepare_file_part_for_publishing(part: FilePart, mode: str, artifact_service: "BaseArtifactService", user_id: str, session_id: str, target_agent_name: str, log_identifier: str) -> Optional[FilePart]` - Prepares a FilePart for publishing based on the artifact handling mode
33
+ - `resolve_file_part_uri(part: FilePart, artifact_service: "BaseArtifactService", log_identifier: str) -> FilePart` - Resolves an artifact URI within a FilePart into embedded bytes
34
+ - `get_artifact_id(artifact: Artifact) -> str` - Safely retrieves the ID from an Artifact
35
+ - `get_artifact_name(artifact: Artifact) -> Optional[str]` - Safely retrieves the name from an Artifact
36
+ - `get_parts_from_artifact(artifact: Artifact) -> List[ContentPart]` - Extracts unwrapped content parts from an Artifact
37
+
38
+ **Usage Examples:**
39
+ ```python
40
+ from solace_agent_mesh.common.a2a.artifact import create_text_artifact, get_artifact_id
41
+
42
+ # Create a text artifact
43
+ artifact = create_text_artifact(
44
+ name="My Document",
45
+ text="This is the content of my document",
46
+ description="A sample text document"
47
+ )
48
+
49
+ # Get artifact ID
50
+ artifact_id = get_artifact_id(artifact)
51
+ ```
52
+
53
+ ### events.py
54
+ **Purpose:** Provides helpers for creating and consuming A2A asynchronous event objects
55
+ **Import:** `from solace_agent_mesh.common.a2a.events import create_status_update, create_artifact_update`
56
+
57
+ **Functions:**
58
+ - `create_data_signal_event(task_id: str, context_id: str, signal_data: SignalData, agent_name: str, part_metadata: Optional[Dict[str, Any]] = None) -> TaskStatusUpdateEvent` - Creates a TaskStatusUpdateEvent from signal data
59
+ - `create_status_update(task_id: str, context_id: str, message: Message, is_final: bool = False, metadata: Optional[Dict[str, Any]] = None) -> TaskStatusUpdateEvent` - Creates a new TaskStatusUpdateEvent
60
+ - `create_artifact_update(task_id: str, context_id: str, artifact: Artifact, append: bool = False, last_chunk: bool = False, metadata: Optional[Dict[str, Any]] = None) -> TaskArtifactUpdateEvent` - Creates a new TaskArtifactUpdateEvent
61
+ - `get_message_from_status_update(event: TaskStatusUpdateEvent) -> Optional[Message]` - Extracts Message from TaskStatusUpdateEvent
62
+ - `get_data_parts_from_status_update(event: TaskStatusUpdateEvent) -> List[DataPart]` - Extracts DataPart objects from status update
63
+ - `get_artifact_from_artifact_update(event: TaskArtifactUpdateEvent) -> Optional[Artifact]` - Extracts Artifact from TaskArtifactUpdateEvent
64
+
65
+ **Usage Examples:**
66
+ ```python
67
+ from solace_agent_mesh.common.a2a.events import create_status_update
68
+ from solace_agent_mesh.common.a2a.message import create_agent_text_message
69
+
70
+ # Create a status update event
71
+ message = create_agent_text_message("Processing your request...")
72
+ status_event = create_status_update(
73
+ task_id="task-123",
74
+ context_id="context-456",
75
+ message=message,
76
+ is_final=False
77
+ )
78
+ ```
79
+
80
+ ### message.py
81
+ **Purpose:** Provides helpers for creating and consuming A2A Message and Part objects
82
+ **Import:** `from solace_agent_mesh.common.a2a.message import create_agent_text_message, create_text_part, get_text_from_message`
83
+
84
+ **Functions:**
85
+ - `create_agent_text_message(text: str, task_id: Optional[str] = None, context_id: Optional[str] = None, message_id: Optional[str] = None) -> Message` - Creates agent message with TextPart
86
+ - `create_agent_data_message(data: dict[str, Any], task_id: Optional[str] = None, context_id: Optional[str] = None, message_id: Optional[str] = None, part_metadata: Optional[Dict[str, Any]] = None) -> Message` - Creates agent message with DataPart
87
+ - `create_agent_parts_message(parts: List[ContentPart], task_id: Optional[str] = None, context_id: Optional[str] = None, message_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> Message` - Creates agent message with multiple parts
88
+ - `create_user_message(parts: List[ContentPart], task_id: Optional[str] = None, context_id: Optional[str] = None, message_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> Message` - Creates user message with multiple parts
89
+ - `create_text_part(text: str, metadata: Optional[Dict[str, Any]] = None) -> TextPart` - Creates a TextPart object
90
+ - `create_file_part_from_uri(uri: str, name: Optional[str] = None, mime_type: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> FilePart` - Creates FilePart from URI
91
+ - `create_file_part_from_bytes(content_bytes: bytes, name: Optional[str] = None, mime_type: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> FilePart` - Creates FilePart from bytes
92
+ - `create_data_part(data: Dict[str, Any], metadata: Optional[Dict[str, Any]] = None) -> DataPart` - Creates a DataPart object
93
+ - `update_message_parts(message: Message, new_parts: List[ContentPart]) -> Message` - Returns a new Message with replaced parts
94
+ - `get_text_from_message(message: Message, delimiter: str = "\n") -> str` - Extracts and joins all text content from Message
95
+ - `get_data_parts_from_message(message: Message) -> List[DataPart]` - Extracts DataPart objects from Message
96
+ - `get_file_parts_from_message(message: Message) -> List[FilePart]` - Extracts FilePart objects from Message
97
+ - `get_message_id(message: Message) -> str` - Gets message ID
98
+ - `get_context_id(message: Message) -> Optional[str]` - Gets context ID
99
+ - `get_task_id(message: Message) -> Optional[str]` - Gets task ID
100
+ - `get_parts_from_message(message: Message) -> List[ContentPart]` - Extracts unwrapped content parts from Message
101
+ - `get_text_from_text_part(part: TextPart) -> str` - Gets text from TextPart
102
+ - `get_data_from_data_part(part: DataPart) -> Dict[str, Any]` - Gets data from DataPart
103
+ - `get_metadata_from_part(part: ContentPart) -> Optional[Dict[str, Any]]` - Gets metadata from any Part
104
+ - `get_file_from_file_part(part: FilePart) -> Optional[Union[FileWithUri, FileWithBytes]]` - Gets File object from FilePart
105
+ - `get_uri_from_file_part(part: FilePart) -> Optional[str]` - Gets URI from FilePart
106
+ - `get_bytes_from_file_part(part: FilePart) -> Optional[bytes]` - Gets decoded bytes from FilePart
107
+ - `get_filename_from_file_part(part: FilePart) -> Optional[str]` - Gets filename from FilePart
108
+ - `get_mimetype_from_file_part(part: FilePart) -> Optional[str]` - Gets MIME type from FilePart
109
+
110
+ **Usage Examples:**
111
+ ```python
112
+ from solace_agent_mesh.common.a2a.message import create_agent_text_message, create_text_part, create_user_message
113
+
114
+ # Create a simple text message
115
+ message = create_agent_text_message(
116
+ text="Hello, how can I help you?",
117
+ task_id="task-123",
118
+ context_id="context-456"
119
+ )
120
+
121
+ # Create a user message with multiple parts
122
+ text_part = create_text_part("Please analyze this data:")
123
+ data_part = create_data_part({"values": [1, 2, 3, 4, 5]})
124
+ user_message = create_user_message(
125
+ parts=[text_part, data_part],
126
+ task_id="task-123"
127
+ )
128
+ ```
129
+
130
+ ### protocol.py
131
+ **Purpose:** Provides helpers for A2A protocol-level concerns like topic construction and JSON-RPC
132
+ **Import:** `from solace_agent_mesh.common.a2a.protocol import get_agent_request_topic, create_send_message_request`
133
+
134
+ **Constants/Variables:**
135
+ - `A2A_VERSION: str` - Current A2A protocol version ("v1")
136
+ - `A2A_BASE_PATH: str` - Base path for A2A topics ("a2a/v1")
137
+
138
+ **Functions:**
139
+ - `get_a2a_base_topic(namespace: str) -> str` - Returns base topic prefix for A2A communication
140
+ - `get_discovery_topic(namespace: str) -> str` - Returns topic for agent card discovery
141
+ - `get_agent_request_topic(namespace: str, agent_name: str) -> str` - Returns topic for sending requests to specific agent
142
+ - `get_gateway_status_topic(namespace: str, gateway_id: str, task_id: str) -> str` - Returns topic for publishing status updates to gateway
143
+ - `get_gateway_response_topic(namespace: str, gateway_id: str, task_id: str) -> str` - Returns topic for publishing final response to gateway
144
+ - `get_gateway_status_subscription_topic(namespace: str, self_gateway_id: str) -> str` - Returns wildcard topic for gateway to receive status updates
145
+ - `get_gateway_response_subscription_topic(namespace: str, self_gateway_id: str) -> str` - Returns wildcard topic for gateway to receive responses
146
+ - `get_peer_agent_status_topic(namespace: str, delegating_agent_name: str, sub_task_id: str) -> str` - Returns topic for publishing status to delegating agent
147
+ - `get_agent_response_topic(namespace: str, delegating_agent_name: str, sub_task_id: str) -> str` - Returns topic for publishing response to delegating agent
148
+ - `get_agent_response_subscription_topic(namespace: str, self_agent_name: str) -> str` - Returns wildcard topic for agent to receive responses
149
+ - `get_agent_status_subscription_topic(namespace: str, self_agent_name: str) -> str` - Returns wildcard topic for agent to receive status updates
150
+ - `get_client_response_topic(namespace: str, client_id: str) -> str` - Returns topic for publishing response to client
151
+ - `get_client_status_topic(namespace: str, client_id: str, task_id: str) -> str` - Returns topic for publishing status to client
152
+ - `get_client_status_subscription_topic(namespace: str, client_id: str) -> str` - Returns wildcard topic for client to receive status
153
+ - `create_send_message_request(message: Message, task_id: str, metadata: Optional[Dict[str, Any]] = None) -> SendMessageRequest` - Creates SendMessageRequest object
154
+ - `create_send_streaming_message_request(message: Message, task_id: str, metadata: Optional[Dict[str, Any]] = None) -> SendStreamingMessageRequest` - Creates SendStreamingMessageRequest object
155
+ - `create_success_response(result: Any, request_id: Optional[Union[str, int]]) -> JSONRPCResponse` - Creates successful JSON-RPC response
156
+ - `create_internal_error_response(message: str, request_id: Optional[Union[str, int]], data: Optional[Dict[str, Any]] = None) -> JSONRPCResponse` - Creates internal error response
157
+ - `create_invalid_request_error_response(message: str, request_id: Optional[Union[str, int]], data: Optional[Any] = None) -> JSONRPCResponse` - Creates invalid request error response
158
+ - `create_cancel_task_request(task_id: str) -> CancelTaskRequest` - Creates CancelTaskRequest object
159
+ - `get_request_id(request: A2ARequest) -> str | int` - Gets JSON-RPC request ID
160
+ - `get_request_method(request: A2ARequest) -> str` - Gets JSON-RPC method name
161
+ - `get_message_from_send_request(request: A2ARequest) -> Optional[Message]` - Gets Message from send request
162
+ - `get_task_id_from_cancel_request(request: A2ARequest) -> Optional[str]` - Gets task ID from cancel request
163
+ - `get_response_id(response: JSONRPCResponse) -> Optional[Union[str, int]]` - Gets response ID
164
+ - `get_response_result(response: JSONRPCResponse) -> Optional[Any]` - Gets response result
165
+ - `get_response_error(response: JSONRPCResponse) -> Optional[JSONRPCError]` - Gets response error
166
+ - `topic_matches_subscription(topic: str, subscription: str) -> bool` - Checks if topic matches Solace subscription pattern
167
+ - `subscription_to_regex(subscription: str) -> str` - Converts Solace subscription to regex
168
+ - `extract_task_id_from_topic(topic: str, subscription_pattern: str, log_identifier: str) -> Optional[str]` - Extracts task ID from topic
169
+
170
+ **Usage Examples:**
171
+ ```python
172
+ from solace_agent_mesh.common.a2a.protocol import get_agent_request_topic, create_send_message_request
173
+ from solace_agent_mesh.common.a2a.message import create_agent_text_message
174
+
175
+ # Get topic for sending request to an agent
176
+ topic = get_agent_request_topic("my-namespace", "my-agent")
177
+
178
+ # Create a send message request
179
+ message = create_agent_text_message("Hello agent!")
180
+ request = create_sen
181
+
182
+ # content_hash: b894ad5028b63e8ee25dcae4edbefa55a29be08dbece2fd4666ab2f0f0b7d740
@@ -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