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.
- solace_agent_mesh/agent/adk/adk_llm.txt +182 -42
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/callbacks.py +165 -104
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
- solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
- solace_agent_mesh/agent/adk/runner.py +7 -5
- solace_agent_mesh/agent/adk/services.py +9 -1
- solace_agent_mesh/agent/adk/setup.py +11 -0
- solace_agent_mesh/agent/adk/stream_parser.py +8 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
- solace_agent_mesh/agent/agent_llm.txt +355 -18
- solace_agent_mesh/agent/protocol/event_handlers.py +433 -296
- solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
- solace_agent_mesh/agent/sac/app.py +1 -1
- solace_agent_mesh/agent/sac/component.py +212 -517
- solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
- solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
- solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
- solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
- solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
- solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.a75ecc0d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- 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
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1756992446316.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +12 -3
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
- solace_agent_mesh/cli/utils.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
- solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
- solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
- solace_agent_mesh/common/a2a/__init__.py +213 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
- solace_agent_mesh/common/a2a/artifact.py +328 -0
- solace_agent_mesh/common/a2a/events.py +183 -0
- solace_agent_mesh/common/a2a/message.py +307 -0
- solace_agent_mesh/common/a2a/protocol.py +513 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +653 -0
- solace_agent_mesh/common/a2a/types.py +54 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
- solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
- solace_agent_mesh/common/agent_registry.py +1 -1
- solace_agent_mesh/common/common_llm.txt +192 -70
- solace_agent_mesh/common/data_parts.py +99 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
- solace_agent_mesh/common/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sam_component_base.py +252 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
- solace_agent_mesh/common/services/services_llm.txt +206 -26
- solace_agent_mesh/common/utils/artifact_utils.py +29 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
- solace_agent_mesh/common/utils/utils_llm.txt +323 -42
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
- solace_agent_mesh/core_a2a/service.py +20 -44
- solace_agent_mesh/gateway/base/app.py +27 -1
- solace_agent_mesh/gateway/base/base_llm.txt +177 -72
- solace_agent_mesh/gateway/base/component.py +294 -523
- solace_agent_mesh/gateway/gateway_llm.txt +299 -58
- solace_agent_mesh/gateway/http_sse/component.py +156 -183
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
- solace_agent_mesh/gateway/http_sse/main.py +8 -10
- solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
- solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
- solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/templates/gateway_component_template.py +149 -98
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +144 -127
- solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1756146501924.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
- solace_agent_mesh/common/a2a_protocol.py +0 -564
- solace_agent_mesh/common/client/__init__.py +0 -4
- solace_agent_mesh/common/client/card_resolver.py +0 -21
- solace_agent_mesh/common/client/client.py +0 -85
- solace_agent_mesh/common/client/client_llm.txt +0 -133
- solace_agent_mesh/common/server/__init__.py +0 -4
- solace_agent_mesh/common/server/server.py +0 -122
- solace_agent_mesh/common/server/server_llm.txt +0 -169
- solace_agent_mesh/common/server/task_manager.py +0 -291
- solace_agent_mesh/common/server/utils.py +0 -28
- solace_agent_mesh/common/types.py +0 -411
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.d79f063b.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.0.7.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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1014
|
-
|
|
1016
|
+
# --- Phase 1: Parse the payload to extract core info ---
|
|
1015
1017
|
try:
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
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
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
.
|
|
1070
|
-
|
|
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["
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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
|
-
|
|
1083
|
-
|
|
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"] =
|
|
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
|
-
|
|
1089
|
-
|
|
1090
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
1114
|
-
details["
|
|
1122
|
+
# TEMP - add debug_type based on the type in the data
|
|
1123
|
+
details["debug_type"] = "unknown"
|
|
1115
1124
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
(
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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[
|
|
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[
|
|
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[
|
|
1356
|
+
a2a_parts: List[ContentPart] = []
|
|
1348
1357
|
|
|
1349
|
-
if files
|
|
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
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
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
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
1478
|
-
|
|
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
|
-
|
|
1525
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
6
|
-
- `visualization_forwarder_component.py
|
|
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
|
|
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
|
|
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
|
|
25
|
-
- `invoke(
|
|
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` -
|
|
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
|
|
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
|
|
48
|
-
#
|
|
46
|
+
# 3. The invoke method is called automatically by SAC framework
|
|
47
|
+
# when messages arrive from connected BrokerInput component
|
|
49
48
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|