solace-agent-mesh 1.0.8__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 (162) 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/setup.py +11 -0
  8. solace_agent_mesh/agent/adk/stream_parser.py +8 -1
  9. solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
  10. solace_agent_mesh/agent/agent_llm.txt +355 -18
  11. solace_agent_mesh/agent/protocol/event_handlers.py +433 -296
  12. solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
  13. solace_agent_mesh/agent/sac/app.py +1 -1
  14. solace_agent_mesh/agent/sac/component.py +212 -517
  15. solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
  16. solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
  17. solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
  18. solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
  19. solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
  20. solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
  21. solace_agent_mesh/assets/docs/404.html +3 -3
  22. solace_agent_mesh/assets/docs/assets/js/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
  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.a75ecc0d.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 +8 -8
  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-1756992446316.json +1 -0
  65. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  66. solace_agent_mesh/assets/docs/search-doc-1756992446316.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/web_add_agent_step.py +12 -3
  71. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
  72. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
  73. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
  74. solace_agent_mesh/cli/utils.py +15 -0
  75. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
  76. solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
  77. solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
  78. solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
  79. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
  80. solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
  81. solace_agent_mesh/common/a2a/__init__.py +213 -0
  82. solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
  83. solace_agent_mesh/common/a2a/artifact.py +328 -0
  84. solace_agent_mesh/common/a2a/events.py +183 -0
  85. solace_agent_mesh/common/a2a/message.py +307 -0
  86. solace_agent_mesh/common/a2a/protocol.py +513 -0
  87. solace_agent_mesh/common/a2a/task.py +127 -0
  88. solace_agent_mesh/common/a2a/translation.py +653 -0
  89. solace_agent_mesh/common/a2a/types.py +54 -0
  90. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  91. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
  92. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  93. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
  94. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
  95. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
  96. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  97. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
  98. solace_agent_mesh/common/agent_registry.py +1 -1
  99. solace_agent_mesh/common/common_llm.txt +192 -70
  100. solace_agent_mesh/common/data_parts.py +99 -0
  101. solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
  102. solace_agent_mesh/common/sac/__init__.py +0 -0
  103. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  104. solace_agent_mesh/common/sac/sam_component_base.py +252 -0
  105. solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
  106. solace_agent_mesh/common/services/services_llm.txt +206 -26
  107. solace_agent_mesh/common/utils/artifact_utils.py +29 -0
  108. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
  109. solace_agent_mesh/common/utils/utils_llm.txt +323 -42
  110. solace_agent_mesh/config_portal/backend/common.py +1 -1
  111. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
  112. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
  113. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
  114. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
  115. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
  116. solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
  117. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  118. solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
  119. solace_agent_mesh/core_a2a/service.py +20 -44
  120. solace_agent_mesh/gateway/base/app.py +27 -1
  121. solace_agent_mesh/gateway/base/base_llm.txt +177 -72
  122. solace_agent_mesh/gateway/base/component.py +294 -523
  123. solace_agent_mesh/gateway/gateway_llm.txt +299 -58
  124. solace_agent_mesh/gateway/http_sse/component.py +156 -183
  125. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
  126. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
  127. solace_agent_mesh/gateway/http_sse/main.py +8 -10
  128. solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
  129. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
  130. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
  131. solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
  132. solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
  133. solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
  134. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
  135. solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
  136. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  137. solace_agent_mesh/templates/gateway_component_template.py +149 -98
  138. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
  139. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +143 -126
  140. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
  141. solace_agent_mesh/assets/docs/assets/js/main.6dba4a66.js +0 -2
  142. solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
  143. solace_agent_mesh/assets/docs/lunr-index-1756153049706.json +0 -1
  144. solace_agent_mesh/assets/docs/search-doc-1756153049706.json +0 -1
  145. solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
  146. solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
  147. solace_agent_mesh/common/a2a_protocol.py +0 -564
  148. solace_agent_mesh/common/client/__init__.py +0 -4
  149. solace_agent_mesh/common/client/card_resolver.py +0 -21
  150. solace_agent_mesh/common/client/client.py +0 -85
  151. solace_agent_mesh/common/client/client_llm.txt +0 -133
  152. solace_agent_mesh/common/server/__init__.py +0 -4
  153. solace_agent_mesh/common/server/server.py +0 -122
  154. solace_agent_mesh/common/server/server_llm.txt +0 -169
  155. solace_agent_mesh/common/server/task_manager.py +0 -291
  156. solace_agent_mesh/common/server/utils.py +0 -28
  157. solace_agent_mesh/common/types.py +0 -411
  158. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
  159. /solace_agent_mesh/assets/docs/assets/js/{main.6dba4a66.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
  160. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
  161. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
  162. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -30,22 +30,17 @@ from ...common.agent_registry import AgentRegistry
30
30
  from ...core_a2a.service import CoreA2AService
31
31
  from google.adk.artifacts import BaseArtifactService
32
32
 
33
- from ...common.types import (
33
+ from ...common.a2a.types import ContentPart
34
+ from a2a.types import (
35
+ A2ARequest,
34
36
  AgentCard,
35
- Part as A2APart,
36
- Task,
37
- TaskStatusUpdateEvent,
38
- TaskArtifactUpdateEvent,
39
37
  JSONRPCError,
40
38
  JSONRPCResponse,
41
- TextPart,
42
- FilePart,
43
- FileContent,
44
- )
45
- from ...common.a2a_protocol import (
46
- _topic_matches_subscription,
39
+ Task,
40
+ TaskArtifactUpdateEvent,
41
+ TaskStatusUpdateEvent,
47
42
  )
48
-
43
+ from ...common import a2a
49
44
  from ...agent.utils.artifact_helpers import save_artifact_with_metadata
50
45
  from ...common.middleware.config_resolver import ConfigResolver
51
46
 
@@ -82,7 +77,11 @@ class WebUIBackendComponent(BaseGatewayComponent):
82
77
  """
83
78
  Initializes the WebUIBackendComponent, inheriting from BaseGatewayComponent.
84
79
  """
85
- super().__init__(**kwargs)
80
+ component_config = kwargs.get("component_config", {})
81
+ app_config = component_config.get("app_config", {})
82
+ resolve_uris = app_config.get("resolve_artifact_uris_in_gateway", True)
83
+
84
+ super().__init__(resolve_artifact_uris_in_gateway=resolve_uris, **kwargs)
86
85
  log.info("%s Initializing Web UI Backend Component...", self.log_identifier)
87
86
 
88
87
  try:
@@ -97,9 +96,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
97
96
  self.fastapi_https_port = self.get_config("fastapi_https_port", 8443)
98
97
  self.session_secret_key = self.get_config("session_secret_key")
99
98
  self.cors_allowed_origins = self.get_config("cors_allowed_origins", ["*"])
100
- self.resolve_artifact_uris_in_gateway = self.get_config(
101
- "resolve_artifact_uris_in_gateway", True
102
- )
103
99
  self.ssl_keyfile = self.get_config("ssl_keyfile", "")
104
100
  self.ssl_certfile = self.get_config("ssl_certfile", "")
105
101
  self.ssl_keyfile_password = self.get_config("ssl_keyfile_password", "")
@@ -427,7 +423,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
427
423
  "solace_topics", set()
428
424
  )
429
425
  if any(
430
- _topic_matches_subscription(topic, pattern)
426
+ a2a.topic_matches_subscription(topic, pattern)
431
427
  for pattern in subscribed_topics_for_stream
432
428
  ):
433
429
  is_permitted = True
@@ -449,6 +445,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
449
445
  "task_id": event_details["task_id"],
450
446
  "payload_summary": event_details["payload_summary"],
451
447
  "full_payload": payload_dict,
448
+ "debug_type": event_details["debug_type"],
452
449
  }
453
450
 
454
451
  try:
@@ -838,7 +835,11 @@ class WebUIBackendComponent(BaseGatewayComponent):
838
835
 
839
836
  setup_dependencies(self)
840
837
 
841
- port = self.fastapi_https_port if self.ssl_keyfile and self.ssl_certfile else self.fastapi_port
838
+ port = (
839
+ self.fastapi_https_port
840
+ if self.ssl_keyfile and self.ssl_certfile
841
+ else self.fastapi_port
842
+ )
842
843
 
843
844
  config = uvicorn.Config(
844
845
  app=self.fastapi_app,
@@ -997,11 +998,13 @@ class WebUIBackendComponent(BaseGatewayComponent):
997
998
  ) -> Dict[str, Any]:
998
999
  """
999
1000
  Infers details for the visualization SSE payload from the Solace topic and A2A message.
1001
+ This version is updated to parse the official A2A SDK message formats.
1000
1002
  """
1001
1003
  details = {
1002
1004
  "direction": "unknown",
1003
1005
  "source_entity": "unknown",
1004
1006
  "target_entity": "unknown",
1007
+ "debug_type": "unknown",
1005
1008
  "message_id": payload.get("id"),
1006
1009
  "task_id": None,
1007
1010
  "payload_summary": {
@@ -1010,125 +1013,131 @@ class WebUIBackendComponent(BaseGatewayComponent):
1010
1013
  },
1011
1014
  }
1012
1015
 
1013
- topic_parts = topic.split("/")
1014
-
1016
+ # --- Phase 1: Parse the payload to extract core info ---
1015
1017
  try:
1016
- a2a_base_index = topic_parts.index("a2a")
1017
- domain_index = a2a_base_index + 2
1018
- action_type_index = a2a_base_index + 3
1019
- entity_name_index = a2a_base_index + 4
1020
- task_id_from_topic_index = a2a_base_index + 5
1021
-
1022
- domain = (
1023
- topic_parts[domain_index] if len(topic_parts) > domain_index else None
1024
- )
1025
- action_type = (
1026
- topic_parts[action_type_index]
1027
- if len(topic_parts) > action_type_index
1028
- else None
1029
- )
1030
- entity_name = (
1031
- topic_parts[entity_name_index]
1032
- if len(topic_parts) > entity_name_index
1033
- else None
1034
- )
1035
-
1036
- if domain == "agent":
1037
- if action_type == "request":
1038
- details["direction"] = "request"
1039
- details["target_entity"] = entity_name
1040
- user_props = (
1041
- payload.get("params", {})
1042
- .get("metadata", {})
1043
- .get("solaceUserProperties", {})
1044
- )
1045
- details["source_entity"] = (
1046
- user_props.get("clientId")
1047
- or user_props.get("delegating_agent_name")
1048
- or self.gateway_id
1049
- )
1050
- elif action_type == "response":
1051
- details["direction"] = "response"
1052
- details["source_entity"] = entity_name
1053
- details["target_entity"] = (
1054
- payload.get("result", {}).get("metadata", {}).get("clientId")
1055
- )
1056
- elif action_type == "status":
1057
- details["direction"] = "status_update"
1058
- details["source_entity"] = entity_name
1059
- details["target_entity"] = (
1060
- payload.get("result", {}).get("metadata", {}).get("clientId")
1018
+ # Try to parse as a JSON-RPC response first
1019
+ if "result" in payload or "error" in payload:
1020
+ rpc_response = JSONRPCResponse.model_validate(payload)
1021
+ result = a2a.get_response_result(rpc_response)
1022
+ error = a2a.get_response_error(rpc_response)
1023
+ details["message_id"] = a2a.get_response_id(rpc_response)
1024
+
1025
+ if result:
1026
+ kind = getattr(result, "kind", None)
1027
+ details["direction"] = kind or "response"
1028
+ details["task_id"] = getattr(result, "task_id", None) or getattr(
1029
+ result, "id", None
1061
1030
  )
1062
- elif domain == "gateway":
1063
- if action_type == "response":
1064
- details["direction"] = "response"
1065
- details["source_entity"] = (
1066
- payload.get("result", {})
1067
- .get("status", {})
1068
- .get("message", {})
1069
- .get("metadata", {})
1070
- .get("agent_name", "unknown_agent")
1031
+
1032
+ if isinstance(result, TaskStatusUpdateEvent):
1033
+ details["source_entity"] = (
1034
+ result.metadata.get("agent_name")
1035
+ if result.metadata
1036
+ else None
1037
+ )
1038
+ message = a2a.get_message_from_status_update(result)
1039
+ if message:
1040
+ if not details["source_entity"]:
1041
+ details["source_entity"] = (
1042
+ message.metadata.get("agent_name")
1043
+ if message.metadata
1044
+ else None
1045
+ )
1046
+ data_parts = a2a.get_data_parts_from_message(message)
1047
+ if data_parts:
1048
+ details["debug_type"] = data_parts[0].data.get(
1049
+ "type", "unknown"
1050
+ )
1051
+ elif a2a.get_text_from_message(message):
1052
+ details["debug_type"] = "streaming_text"
1053
+ elif isinstance(result, Task):
1054
+ details["source_entity"] = (
1055
+ result.metadata.get("agent_name")
1056
+ if result.metadata
1057
+ else None
1058
+ )
1059
+ elif isinstance(result, TaskArtifactUpdateEvent):
1060
+ artifact = a2a.get_artifact_from_artifact_update(result)
1061
+ if artifact:
1062
+ details["source_entity"] = (
1063
+ artifact.metadata.get("agent_name")
1064
+ if artifact.metadata
1065
+ else None
1066
+ )
1067
+ elif error:
1068
+ details["direction"] = "error_response"
1069
+ details["task_id"] = (
1070
+ error.data.get("taskId")
1071
+ if isinstance(error.data, dict)
1072
+ else None
1071
1073
  )
1072
- details["target_entity"] = entity_name
1073
- elif action_type == "status":
1074
- details["direction"] = "status_update"
1075
- details["source_entity"] = (
1076
- payload.get("result", {})
1077
- .get("status", {})
1078
- .get("message", {})
1079
- .get("metadata", {})
1080
- .get("agent_name", "unknown_agent")
1074
+ details["debug_type"] = "error"
1075
+
1076
+ # Try to parse as a JSON-RPC request
1077
+ elif "method" in payload:
1078
+ rpc_request = A2ARequest.model_validate(payload)
1079
+ method = a2a.get_request_method(rpc_request)
1080
+ details["direction"] = "request"
1081
+ details["payload_summary"]["method"] = method
1082
+ details["message_id"] = a2a.get_request_id(rpc_request)
1083
+
1084
+ if method in ["message/send", "message/stream"]:
1085
+ details["debug_type"] = method
1086
+ message = a2a.get_message_from_send_request(rpc_request)
1087
+ details["task_id"] = a2a.get_request_id(rpc_request)
1088
+ if message:
1089
+ details["target_entity"] = (
1090
+ message.metadata.get("agent_name")
1091
+ if message.metadata
1092
+ else None
1093
+ )
1094
+ elif method == "tasks/cancel":
1095
+ details["task_id"] = a2a.get_task_id_from_cancel_request(
1096
+ rpc_request
1081
1097
  )
1082
- details["target_entity"] = entity_name
1083
- elif domain == "discovery" and action_type == "agentcards":
1098
+
1099
+ # Handle Discovery messages (which are not JSON-RPC)
1100
+ elif "/a2a/v1/discovery/" in topic:
1101
+ agent_card = AgentCard.model_validate(payload)
1084
1102
  details["direction"] = "discovery"
1085
- details["source_entity"] = payload.get("name", "unknown_agent")
1103
+ details["source_entity"] = agent_card.name
1086
1104
  details["target_entity"] = "broadcast"
1105
+ details["message_id"] = None # Discovery has no ID
1087
1106
 
1088
- if payload.get("method") in [
1089
- "tasks/send",
1090
- "tasks/sendSubscribe",
1091
- "tasks/cancel",
1092
- ]:
1093
- details["task_id"] = payload.get("params", {}).get("id")
1094
- elif "result" in payload and isinstance(payload["result"], dict):
1095
- details["task_id"] = payload["result"].get("id")
1096
- elif len(topic_parts) > task_id_from_topic_index and (
1097
- action_type == "status" or action_type == "response"
1098
- ):
1099
- details["task_id"] = topic_parts[task_id_from_topic_index]
1100
-
1101
- except (ValueError, IndexError):
1102
- log.debug(
1103
- "%s Could not parse A2A structure from topic: %s",
1107
+ except Exception as e:
1108
+ log.warning(
1109
+ "[%s] Failed to parse A2A payload for visualization details: %s",
1104
1110
  self.log_identifier,
1105
- topic,
1111
+ e,
1106
1112
  )
1113
+
1114
+ # --- Phase 2: Refine details using topic information as a fallback ---
1115
+ if details["direction"] == "unknown":
1107
1116
  if "request" in topic:
1108
1117
  details["direction"] = "request"
1109
1118
  elif "response" in topic:
1110
1119
  details["direction"] = "response"
1111
1120
  elif "status" in topic:
1112
1121
  details["direction"] = "status_update"
1113
- elif "discovery" in topic:
1114
- details["direction"] = "discovery"
1122
+ # TEMP - add debug_type based on the type in the data
1123
+ details["debug_type"] = "unknown"
1115
1124
 
1116
- if "params" in payload:
1117
- params_str = json.dumps(payload["params"])
1118
- details["payload_summary"]["params_preview"] = (
1119
- (params_str[:100] + "...") if len(params_str) > 100 else params_str
1120
- )
1121
- elif "result" in payload:
1122
- result_str = json.dumps(payload["result"])
1123
- details["payload_summary"]["params_preview"] = (
1124
- (result_str[:100] + "...") if len(result_str) > 100 else result_str
1125
+ # --- Phase 3: Create a payload summary ---
1126
+ try:
1127
+ summary_source = (
1128
+ payload.get("result")
1129
+ or payload.get("params")
1130
+ or payload.get("error")
1131
+ or payload
1125
1132
  )
1126
- elif "error" in payload:
1127
- details["payload_summary"]["method"] = "JSONRPCError"
1128
- error_str = json.dumps(payload["error"])
1133
+ summary_str = json.dumps(summary_source)
1129
1134
  details["payload_summary"]["params_preview"] = (
1130
- (error_str[:100] + "...") if len(error_str) > 100 else error_str
1135
+ (summary_str[:100] + "...") if len(summary_str) > 100 else summary_str
1131
1136
  )
1137
+ except Exception:
1138
+ details["payload_summary"][
1139
+ "params_preview"
1140
+ ] = "[Could not serialize payload]"
1132
1141
 
1133
1142
  return details
1134
1143
 
@@ -1309,7 +1318,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1309
1318
 
1310
1319
  async def _translate_external_input(
1311
1320
  self, external_event_data: Dict[str, Any]
1312
- ) -> Tuple[str, List[A2APart], Dict[str, Any]]:
1321
+ ) -> Tuple[str, List[ContentPart], Dict[str, Any]]:
1313
1322
  """
1314
1323
  Translates raw HTTP request data (from FastAPI form) into A2A task parameters.
1315
1324
 
@@ -1321,7 +1330,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1321
1330
  Returns:
1322
1331
  A tuple containing:
1323
1332
  - target_agent_name (str): The name of the A2A agent to target.
1324
- - a2a_parts (List[A2APart]): A list of A2A Part objects for the message.
1333
+ - a2a_parts (List[ContentPart]): A list of unwrapped A2A Part objects.
1325
1334
  - external_request_context (Dict[str, Any]): Context for TaskContextManager.
1326
1335
  """
1327
1336
  log_id_prefix = f"{self.log_identifier}[TranslateInput]"
@@ -1344,10 +1353,9 @@ class WebUIBackendComponent(BaseGatewayComponent):
1344
1353
  "Client ID or A2A Session ID is missing in external_event_data."
1345
1354
  )
1346
1355
 
1347
- a2a_parts: List[A2APart] = []
1356
+ a2a_parts: List[ContentPart] = []
1348
1357
 
1349
- if files and self.shared_artifact_service:
1350
- file_metadata_summary_parts = []
1358
+ if files:
1351
1359
  for upload_file in files:
1352
1360
  try:
1353
1361
  content_bytes = await upload_file.read()
@@ -1358,52 +1366,21 @@ class WebUIBackendComponent(BaseGatewayComponent):
1358
1366
  upload_file.filename,
1359
1367
  )
1360
1368
  continue
1361
- save_result = await save_artifact_with_metadata(
1362
- artifact_service=self.shared_artifact_service,
1363
- app_name=self.gateway_id,
1364
- user_id=client_id,
1365
- session_id=a2a_session_id,
1366
- filename=upload_file.filename,
1369
+
1370
+ # The BaseGatewayComponent will handle normalization based on policy.
1371
+ # Here, we just create the FilePart with inline bytes.
1372
+ file_part = a2a.create_file_part_from_bytes(
1367
1373
  content_bytes=content_bytes,
1368
- mime_type=upload_file.content_type
1369
- or "application/octet-stream",
1370
- metadata_dict={
1371
- "source": "webui_gateway_upload",
1372
- "original_filename": upload_file.filename,
1373
- "upload_timestamp_utc": datetime.now(
1374
- timezone.utc
1375
- ).isoformat(),
1376
- "gateway_id": self.gateway_id,
1377
- "web_client_id": client_id,
1378
- "a2a_session_id": a2a_session_id,
1379
- },
1380
- timestamp=datetime.now(timezone.utc),
1374
+ name=upload_file.filename,
1375
+ mime_type=upload_file.content_type,
1376
+ )
1377
+ a2a_parts.append(file_part)
1378
+ log.info(
1379
+ "%s Created inline FilePart for uploaded file: %s (%d bytes)",
1380
+ log_id_prefix,
1381
+ upload_file.filename,
1382
+ len(content_bytes),
1381
1383
  )
1382
-
1383
- if save_result["status"] in ["success", "partial_success"]:
1384
- data_version = save_result.get("data_version", 0)
1385
- artifact_uri = f"artifact://{self.gateway_id}/{client_id}/{a2a_session_id}/{upload_file.filename}?version={data_version}"
1386
- file_content = FileContent(
1387
- name=upload_file.filename,
1388
- mimeType=upload_file.content_type,
1389
- uri=artifact_uri,
1390
- )
1391
- a2a_parts.append(FilePart(file=file_content))
1392
- file_metadata_summary_parts.append(
1393
- f"- {upload_file.filename} ({upload_file.content_type}, {len(content_bytes)} bytes, URI: {artifact_uri})"
1394
- )
1395
- log.info(
1396
- "%s Processed and created URI for uploaded file: %s",
1397
- log_id_prefix,
1398
- artifact_uri,
1399
- )
1400
- else:
1401
- log.error(
1402
- "%s Failed to save artifact %s: %s",
1403
- log_id_prefix,
1404
- upload_file.filename,
1405
- save_result.get("message"),
1406
- )
1407
1384
 
1408
1385
  except Exception as e:
1409
1386
  log.exception(
@@ -1415,15 +1392,8 @@ class WebUIBackendComponent(BaseGatewayComponent):
1415
1392
  finally:
1416
1393
  await upload_file.close()
1417
1394
 
1418
- if file_metadata_summary_parts:
1419
- user_message = (
1420
- "The user uploaded the following file(s):\n"
1421
- + "\n".join(file_metadata_summary_parts)
1422
- + f"\n\nUser message: {user_message}"
1423
- )
1424
-
1425
1395
  if user_message:
1426
- a2a_parts.append(TextPart(text=user_message))
1396
+ a2a_parts.append(a2a.create_text_part(text=user_message))
1427
1397
 
1428
1398
  external_request_context = {
1429
1399
  "app_name_for_artifacts": self.gateway_id,
@@ -1453,7 +1423,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1453
1423
  """
1454
1424
  log_id_prefix = f"{self.log_identifier}[SendUpdate]"
1455
1425
  sse_task_id = external_request_context.get("a2a_task_id_for_event")
1456
- a2a_task_id = event_data.id
1426
+ a2a_task_id = event_data.task_id
1457
1427
 
1458
1428
  if not sse_task_id:
1459
1429
  log.error(
@@ -1474,9 +1444,10 @@ class WebUIBackendComponent(BaseGatewayComponent):
1474
1444
  if isinstance(event_data, TaskArtifactUpdateEvent):
1475
1445
  sse_event_type = "artifact_update"
1476
1446
 
1477
- sse_payload = JSONRPCResponse(id=a2a_task_id, result=event_data).model_dump(
1478
- exclude_none=True
1447
+ sse_payload_model = a2a.create_success_response(
1448
+ result=event_data, request_id=a2a_task_id
1479
1449
  )
1450
+ sse_payload = sse_payload_model.model_dump(by_alias=True, exclude_none=True)
1480
1451
 
1481
1452
  try:
1482
1453
  await self.sse_manager.send_event(
@@ -1521,9 +1492,10 @@ class WebUIBackendComponent(BaseGatewayComponent):
1521
1492
  sse_task_id,
1522
1493
  )
1523
1494
 
1524
- sse_payload = JSONRPCResponse(id=a2a_task_id, result=task_data).model_dump(
1525
- exclude_none=True
1495
+ sse_payload_model = a2a.create_success_response(
1496
+ result=task_data, request_id=a2a_task_id
1526
1497
  )
1498
+ sse_payload = sse_payload_model.model_dump(by_alias=True, exclude_none=True)
1527
1499
 
1528
1500
  try:
1529
1501
  await self.sse_manager.send_event(
@@ -1572,10 +1544,11 @@ class WebUIBackendComponent(BaseGatewayComponent):
1572
1544
  error_data,
1573
1545
  )
1574
1546
 
1575
- sse_payload = JSONRPCResponse(
1576
- id=external_request_context.get("original_rpc_id", sse_task_id),
1547
+ sse_payload_model = a2a.create_error_response(
1577
1548
  error=error_data,
1578
- ).model_dump(exclude_none=True)
1549
+ request_id=external_request_context.get("original_rpc_id", sse_task_id),
1550
+ )
1551
+ sse_payload = sse_payload_model.model_dump(by_alias=True, exclude_none=True)
1579
1552
 
1580
1553
  try:
1581
1554
  await self.sse_manager.send_event(
@@ -1,65 +1,65 @@
1
+ # DEVELOPER GUIDE: components
2
+
1
3
  ## Quick Summary
2
4
  This directory contains components for the HTTP SSE (Server-Sent Events) gateway, designed to work within the Solace AI Connector (SAC) framework. The primary component forwards messages received from the Solace broker to an internal queue, enabling real-time visualization in a web-based user interface.
3
5
 
4
6
  ## Files Overview
5
- - `__init__.py`: Makes the `VisualizationForwarderComponent` class directly importable from the `components` package.
6
- - `visualization_forwarder_component.py`: Defines a component that forwards messages from a broker input to a Python `queue.Queue` for visualization.
7
+ - `__init__.py` - Makes the `VisualizationForwarderComponent` class directly importable from the `components` package
8
+ - `visualization_forwarder_component.py` - Defines a SAC component that forwards messages from a broker input to a Python `queue.Queue` for visualization
7
9
 
8
10
  ## Developer API Reference
9
11
 
10
12
  ### __init__.py
11
- **Purpose:** Exposes the public components of this directory for easy importing.
12
- **Import:** `from gateway.http_sse.components import VisualizationForwarderComponent`
13
+ **Purpose:** Exposes the public components of this directory for easy importing
14
+ **Import:** `from solace_agent_mesh.gateway.http_sse.components import VisualizationForwarderComponent`
13
15
 
14
16
  **Exports:**
15
- - `VisualizationForwarderComponent`: The main component class for forwarding messages to a visualization queue.
16
-
17
- ---
17
+ - `VisualizationForwarderComponent` - The main component class for forwarding messages to a visualization queue
18
18
 
19
19
  ### visualization_forwarder_component.py
20
- **Purpose:** A Solace AI Connector (SAC) component that listens for messages from a `BrokerInput` and forwards them to a specified Python `queue.Queue`. This is primarily used to send data to the Web UI for real-time display.
21
- **Import:** `from gateway.http_sse.components.visualization_forwarder_component import VisualizationForwarderComponent`
20
+ **Purpose:** A Solace AI Connector (SAC) component that listens for messages from a `BrokerInput` and forwards them to a specified Python `queue.Queue` for real-time visualization
21
+ **Import:** `from solace_agent_mesh.gateway.http_sse.components.visualization_forwarder_component import VisualizationForwarderComponent`
22
22
 
23
23
  **Classes:**
24
- - `VisualizationForwarderComponent(**kwargs: Any)` - A component that forwards messages to a target queue. It is initialized with configuration parameters, most importantly `target_queue_ref`.
25
- - `invoke(self, message: SolaceMessage, data: Dict[str, Any]) -> None` - The core method called by the SAC framework for each incoming message. It formats the data and places it onto the target queue. This method should not be called directly by developers; the framework handles its execution.
24
+ - `VisualizationForwarderComponent(**kwargs: Any)` - A component that forwards messages to a target queue, initialized with configuration parameters including `target_queue_ref`
25
+ - `invoke(message: SolaceMessage, data: Dict[str, Any]) -> None` - Core method called by SAC framework for each incoming message; formats data and places it onto the target queue
26
+ - `target_queue: queue.Queue` - The queue instance where messages are forwarded
26
27
 
27
28
  **Constants/Variables:**
28
- - `info: Dict` - A metadata dictionary required by the SAC framework. It describes the component's configuration parameters, input schema, and purpose. This is for framework use and not for direct interaction.
29
+ - `info: Dict` - Metadata dictionary required by SAC framework describing component configuration, input schema, and purpose
29
30
 
30
31
  **Usage Examples:**
31
32
  ```python
32
33
  import queue
33
- from gateway.http_sse.components import VisualizationForwarderComponent
34
+ from solace_agent_mesh.gateway.http_sse.components import VisualizationForwarderComponent
34
35
  from solace_ai_connector.common.message import Message as SolaceMessage
35
36
 
36
- # 1. Create a target queue that will receive the forwarded messages.
37
- # This queue is typically managed by another component, like a Web UI backend.
37
+ # 1. Create a target queue that will receive the forwarded messages
38
38
  visualization_queue = queue.Queue()
39
39
 
40
- # 2. Instantiate the component, providing a reference to the target queue.
41
- # This is usually done within a SAC flow configuration file.
40
+ # 2. Instantiate the component with target queue reference
42
41
  forwarder = VisualizationForwarderComponent(
43
42
  name="my_forwarder",
44
43
  target_queue_ref=visualization_queue
45
44
  )
46
45
 
47
- # 3. The `invoke` method is called automatically by the SAC framework when a message
48
- # arrives from a connected BrokerInput component.
46
+ # 3. The invoke method is called automatically by SAC framework
47
+ # when messages arrive from connected BrokerInput component
49
48
 
50
- # Example of what the consuming component would get from the queue:
51
- # A dictionary containing the message topic, payload, and other details.
52
- #
53
- # if not visualization_queue.empty():
54
- # forwarded_data = visualization_queue.get()
55
- # print(f"Received topic: {forwarded_data['topic']}")
56
- # print(f"Received payload: {forwarded_data['payload']}")
57
- #
58
- # Expected structure of `forwarded_data`:
49
+ # 4. Consume forwarded messages from the queue
50
+ if not visualization_queue.empty():
51
+ forwarded_data = visualization_queue.get()
52
+ print(f"Topic: {forwarded_data['topic']}")
53
+ print(f"Payload: {forwarded_data['payload']}")
54
+ print(f"User Properties: {forwarded_data['user_properties']}")
55
+
56
+ # Expected structure of forwarded_data:
59
57
  # {
60
58
  # "topic": "some/broker/topic",
61
59
  # "payload": {"key": "value"},
62
60
  # "user_properties": {"prop1": "value1"},
63
61
  # "_original_broker_message": <SolaceMessage object>
64
62
  # }
65
- ```
63
+ ```
64
+
65
+ # content_hash: cee7f7b4ea7e87ab3f94f7e24d463f22fa045e50d54991c61b84fe95e8a7f77d