solace-agent-mesh 1.3.1__py3-none-any.whl → 1.3.3__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/artifacts/filesystem_artifact_service.py +16 -8
- solace_agent_mesh/agent/protocol/event_handlers.py +91 -0
- solace_agent_mesh/agent/sac/app.py +2 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/0e682baa.da822665.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1023fc19.8a8a9309.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1523c6b4.2645ef68.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1c6e87d2.43771adc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2a9cab12.2afaee76.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/332e10b5.f7629851.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3d406171.5560fdf9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.3f34bf76.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/442a8107.b5c2532a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/483cef9a.8d318c2f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55f47984.bcd00a86.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5b4258a4.dff11eca.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/664b740a.ba305a89.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/75384d09.abdf9cf9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/768e31b0.9abcdc48.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/945fb41e.abf2be91.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.5a319fd4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9eff14a2.d62aad71.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.1d029b81.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{aba87c2f.071e2d94.js → aba87c2f.4ddf32f2.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.abca774a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae4415af.24cdc514.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/bac0be12.27ee2c26.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c2c06897.87cb1f47.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c835a94d.ce21f0bf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cc969b05.feef7dcc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cd3d4052.a19e7d78.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{cee5d587.f5b73ca1.js → cee5d587.f1e1ca86.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.cad4dbf2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f897a61a.bc634a3e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.1c79039d.js → main.e82b32e6.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.aad1f874.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +18 -18
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +10 -10
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +23 -23
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +5 -5
- 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 +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +10 -10
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +18 -18
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +10 -10
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1757873594308.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1757873594308.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{main-C1k9E0aC.js → main-DjoMeldu.js} +8 -8
- solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
- solace_agent_mesh/common/a2a/__init__.py +4 -0
- solace_agent_mesh/common/a2a/protocol.py +20 -0
- solace_agent_mesh/common/sac/sam_component_base.py +29 -9
- solace_agent_mesh/common/sam_events/__init__.py +9 -0
- solace_agent_mesh/common/sam_events/event_service.py +207 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +1 -1
- solace_agent_mesh/gateway/http_sse/component.py +45 -35
- solace_agent_mesh/gateway/http_sse/dependencies.py +123 -60
- solace_agent_mesh/gateway/http_sse/main.py +20 -33
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +37 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +41 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +45 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +16 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +64 -0
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +78 -0
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +27 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +27 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +139 -0
- solace_agent_mesh/gateway/http_sse/routers/config.py +1 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +20 -0
- solace_agent_mesh/gateway/http_sse/{api → routers}/dto/requests/session_requests.py +1 -8
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +16 -0
- solace_agent_mesh/gateway/http_sse/{api → routers}/dto/responses/session_responses.py +3 -30
- solace_agent_mesh/gateway/http_sse/{api/controllers/session_controller.py → routers/sessions.py} +20 -77
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +42 -49
- solace_agent_mesh/gateway/http_sse/{api/controllers/user_controller.py → routers/users.py} +1 -1
- solace_agent_mesh/gateway/http_sse/services/session_service.py +245 -0
- solace_agent_mesh/gateway/http_sse/session_manager.py +0 -3
- solace_agent_mesh/gateway/http_sse/shared/enums.py +0 -5
- {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/METADATA +1 -1
- {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/RECORD +120 -128
- solace_agent_mesh/assets/docs/assets/js/0e682baa.b3bbde9a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1023fc19.364235d5.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1523c6b4.1b0ec6f9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1c6e87d2.a8c5ce5a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/2a9cab12.8909df92.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/332e10b5.7a103f42.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3d406171.0b9eeed1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/442a8107.b3159bb2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/483cef9a.4e972867.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5b4258a4.0d080cd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/768e31b0.8b51cd70.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/945fb41e.c63791d1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9eff14a2.472b0310.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.4b7fa6a2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae4415af.7a2f0bbf.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/bac0be12.f50d9bac.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c2c06897.587b4af5.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cc969b05.bd3e0d6c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cd3d4052.b6535013.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f897a61a.0aa29dbb.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/runtime~main.858117b7.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757531604543.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757531604543.json +0 -1
- solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +0 -676
- solace_agent_mesh/gateway/http_sse/api/__init__.py +0 -11
- solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +0 -9
- solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +0 -279
- solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +0 -37
- solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +0 -66
- solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +0 -43
- solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +0 -74
- solace_agent_mesh/gateway/http_sse/application/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/application/services/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/application/services/session_service.py +0 -135
- solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/domain/entities/session.py +0 -90
- solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +0 -54
- solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +0 -4
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +0 -123
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +0 -4
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +0 -16
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +0 -119
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +0 -31
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +0 -12
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +0 -174
- /solace_agent_mesh/assets/docs/assets/js/{main.1c79039d.js.LICENSE.txt → main.e82b32e6.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/gateway/http_sse/{api → routers}/dto/__init__.py +0 -0
- {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon-BLgzUch9.ico" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Solace Agent Mesh</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/main-
|
|
8
|
+
<script type="module" crossorigin src="/assets/main-DjoMeldu.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vendor-B0BEKoAR.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/client-DXU9SPI5.js">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/main-C03yrETa.css">
|
|
@@ -81,6 +81,8 @@ from .protocol import (
|
|
|
81
81
|
get_message_from_send_request,
|
|
82
82
|
get_peer_agent_status_topic,
|
|
83
83
|
get_request_id,
|
|
84
|
+
get_sam_events_topic,
|
|
85
|
+
get_sam_events_subscription_topic,
|
|
84
86
|
get_request_method,
|
|
85
87
|
get_response_error,
|
|
86
88
|
get_error_code,
|
|
@@ -184,6 +186,8 @@ __all__ = [
|
|
|
184
186
|
"get_message_from_send_request",
|
|
185
187
|
"get_peer_agent_status_topic",
|
|
186
188
|
"get_request_id",
|
|
189
|
+
"get_sam_events_topic",
|
|
190
|
+
"get_sam_events_subscription_topic",
|
|
187
191
|
"get_request_method",
|
|
188
192
|
"get_response_error",
|
|
189
193
|
"get_error_code",
|
|
@@ -176,6 +176,26 @@ def get_client_status_subscription_topic(namespace: str, client_id: str) -> str:
|
|
|
176
176
|
return f"{get_a2a_base_topic(namespace)}/client/status/{client_id}/>"
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
def get_sam_events_topic(namespace: str, category: str, action: str) -> str:
|
|
180
|
+
"""Returns SAM system events topic."""
|
|
181
|
+
if not namespace:
|
|
182
|
+
raise ValueError("Namespace cannot be empty.")
|
|
183
|
+
if not category:
|
|
184
|
+
raise ValueError("Category cannot be empty.")
|
|
185
|
+
if not action:
|
|
186
|
+
raise ValueError("Action cannot be empty.")
|
|
187
|
+
return f"{namespace.rstrip('/')}/sam/events/{category}/{action}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_sam_events_subscription_topic(namespace: str, category: str) -> str:
|
|
191
|
+
"""Returns SAM system events subscription topic."""
|
|
192
|
+
if not namespace:
|
|
193
|
+
raise ValueError("Namespace cannot be empty.")
|
|
194
|
+
if not category:
|
|
195
|
+
raise ValueError("Category cannot be empty.")
|
|
196
|
+
return f"{namespace.rstrip('/')}/sam/events/{category}/>"
|
|
197
|
+
|
|
198
|
+
|
|
179
199
|
def subscription_to_regex(subscription: str) -> str:
|
|
180
200
|
"""Converts a Solace topic subscription string to a regex pattern."""
|
|
181
201
|
# Escape regex special characters except for Solace wildcards
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
Base Component class for SAM implementations in the Solace AI Connector.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import abc
|
|
5
6
|
import asyncio
|
|
6
7
|
import threading
|
|
7
|
-
import
|
|
8
|
-
from typing import Any, Dict, Optional
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
|
-
from solace_ai_connector.components.component_base import ComponentBase
|
|
11
10
|
from solace_ai_connector.common.log import log
|
|
11
|
+
from solace_ai_connector.components.component_base import ComponentBase
|
|
12
12
|
|
|
13
|
-
from ..utils.message_utils import validate_message_size
|
|
14
13
|
from ..exceptions import MessageSizeExceededError
|
|
14
|
+
from ..utils.message_utils import validate_message_size
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class SamComponentBase(ComponentBase, abc.ABC):
|
|
@@ -23,7 +23,7 @@ class SamComponentBase(ComponentBase, abc.ABC):
|
|
|
23
23
|
- Publishing A2A messages with built-in size validation.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
def __init__(self, info:
|
|
26
|
+
def __init__(self, info: dict[str, Any], **kwargs: Any):
|
|
27
27
|
super().__init__(info, **kwargs)
|
|
28
28
|
log.info("%s Initializing SamComponentBase...", self.log_identifier)
|
|
29
29
|
|
|
@@ -51,15 +51,19 @@ class SamComponentBase(ComponentBase, abc.ABC):
|
|
|
51
51
|
)
|
|
52
52
|
raise ValueError(f"Configuration retrieval error: {e}") from e
|
|
53
53
|
|
|
54
|
-
self._async_loop:
|
|
55
|
-
self._async_thread:
|
|
54
|
+
self._async_loop: asyncio.AbstractEventLoop | None = None
|
|
55
|
+
self._async_thread: threading.Thread | None = None
|
|
56
56
|
log.info("%s SamComponentBase initialized successfully.", self.log_identifier)
|
|
57
57
|
|
|
58
58
|
def publish_a2a_message(
|
|
59
|
-
self, payload:
|
|
59
|
+
self, payload: dict, topic: str, user_properties: dict | None = None
|
|
60
60
|
):
|
|
61
61
|
"""Helper to publish A2A messages via the SAC App with size validation."""
|
|
62
62
|
try:
|
|
63
|
+
log.debug(
|
|
64
|
+
f"{self.log_identifier} [publish_a2a_message] Starting - topic: {topic}, payload keys: {list(payload.keys()) if isinstance(payload, dict) else 'not_dict'}"
|
|
65
|
+
)
|
|
66
|
+
|
|
63
67
|
# Validate message size
|
|
64
68
|
is_valid, actual_size = validate_message_size(
|
|
65
69
|
payload, self.max_message_size_bytes, self.log_identifier
|
|
@@ -85,6 +89,10 @@ class SamComponentBase(ComponentBase, abc.ABC):
|
|
|
85
89
|
|
|
86
90
|
app = self.get_app()
|
|
87
91
|
if app:
|
|
92
|
+
log.debug(
|
|
93
|
+
f"{self.log_identifier} [publish_a2a_message] Got app instance, about to call app.send_message"
|
|
94
|
+
)
|
|
95
|
+
|
|
88
96
|
# Conditionally log to invocation monitor if it exists (i.e., on an agent)
|
|
89
97
|
if hasattr(self, "invocation_monitor") and self.invocation_monitor:
|
|
90
98
|
self.invocation_monitor.log_message_event(
|
|
@@ -93,9 +101,21 @@ class SamComponentBase(ComponentBase, abc.ABC):
|
|
|
93
101
|
payload=payload,
|
|
94
102
|
component_identifier=self.log_identifier,
|
|
95
103
|
)
|
|
104
|
+
|
|
105
|
+
log.debug(
|
|
106
|
+
f"{self.log_identifier} [publish_a2a_message] About to call app.send_message with payload: {payload}"
|
|
107
|
+
)
|
|
108
|
+
log.debug(
|
|
109
|
+
f"{self.log_identifier} [publish_a2a_message] App send_message params - topic: {topic}, user_properties: {user_properties}"
|
|
110
|
+
)
|
|
111
|
+
|
|
96
112
|
app.send_message(
|
|
97
113
|
payload=payload, topic=topic, user_properties=user_properties
|
|
98
114
|
)
|
|
115
|
+
|
|
116
|
+
log.debug(
|
|
117
|
+
f"{self.log_identifier} [publish_a2a_message] Successfully called app.send_message"
|
|
118
|
+
)
|
|
99
119
|
else:
|
|
100
120
|
log.error(
|
|
101
121
|
"%s Cannot publish message: Not running within a SAC App context.",
|
|
@@ -231,7 +251,7 @@ class SamComponentBase(ComponentBase, abc.ABC):
|
|
|
231
251
|
super().cleanup()
|
|
232
252
|
log.info("%s SamComponentBase cleanup finished.", self.log_identifier)
|
|
233
253
|
|
|
234
|
-
def get_async_loop(self) ->
|
|
254
|
+
def get_async_loop(self) -> asyncio.AbstractEventLoop | None:
|
|
235
255
|
"""Returns the dedicated asyncio event loop for this component's async tasks."""
|
|
236
256
|
return self._async_loop
|
|
237
257
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SAM Events - System-level event messaging for Solace Agent Mesh.
|
|
3
|
+
|
|
4
|
+
Provides clean separation between A2A task communication and system events.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .event_service import SamEventService, SamEvent, SessionDeletedEvent
|
|
8
|
+
|
|
9
|
+
__all__ = ["SamEventService", "SamEvent", "SessionDeletedEvent"]
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SAM Events Service - Clean system-level event messaging.
|
|
3
|
+
|
|
4
|
+
Provides event publishing and subscription for system events like session lifecycle,
|
|
5
|
+
agent health, configuration changes, etc.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import uuid
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from dataclasses import dataclass, asdict
|
|
11
|
+
from typing import Any, Dict, Optional, Callable, List
|
|
12
|
+
|
|
13
|
+
from solace_ai_connector.common.log import log
|
|
14
|
+
from ..a2a.protocol import get_sam_events_topic
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class SamEvent:
|
|
19
|
+
"""Base class for all SAM system events."""
|
|
20
|
+
event_type: str
|
|
21
|
+
event_id: str
|
|
22
|
+
timestamp: str
|
|
23
|
+
source_component: str
|
|
24
|
+
namespace: str
|
|
25
|
+
data: Dict[str, Any]
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def create(cls, event_type: str, source_component: str, namespace: str, data: Dict[str, Any]) -> "SamEvent":
|
|
29
|
+
"""Create a new SAM event with auto-generated ID and timestamp."""
|
|
30
|
+
return cls(
|
|
31
|
+
event_type=event_type,
|
|
32
|
+
event_id=uuid.uuid4().hex,
|
|
33
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
34
|
+
source_component=source_component,
|
|
35
|
+
namespace=namespace,
|
|
36
|
+
data=data
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
40
|
+
"""Convert event to dictionary for messaging."""
|
|
41
|
+
return asdict(self)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class SessionDeletedEvent(SamEvent):
|
|
46
|
+
"""System event for session deletion."""
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def create(cls, namespace: str, source_component: str, session_id: str,
|
|
50
|
+
user_id: str, agent_id: str, gateway_id: str) -> "SessionDeletedEvent":
|
|
51
|
+
"""Create a session deleted event."""
|
|
52
|
+
data = {
|
|
53
|
+
"session_id": session_id,
|
|
54
|
+
"user_id": user_id,
|
|
55
|
+
"agent_id": agent_id,
|
|
56
|
+
"gateway_id": gateway_id
|
|
57
|
+
}
|
|
58
|
+
return super().create("session.deleted", source_component, namespace, data)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SamEventService:
|
|
62
|
+
"""Service for publishing and subscribing to SAM system events."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, namespace: str, component_name: str, publish_func: Callable[[str, Dict, Optional[Dict]], None]):
|
|
65
|
+
"""
|
|
66
|
+
Initialize the SAM event service.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
namespace: The SAM namespace
|
|
70
|
+
component_name: Name of the component using this service
|
|
71
|
+
publish_func: Function to publish messages (from WebUIBackendComponent.publish_a2a)
|
|
72
|
+
"""
|
|
73
|
+
self.namespace = namespace
|
|
74
|
+
self.component_name = component_name
|
|
75
|
+
self.publish_func = publish_func
|
|
76
|
+
self._subscribers: Dict[str, List[Callable]] = {}
|
|
77
|
+
|
|
78
|
+
log.info(f"[SamEventService] Initialized for component {component_name} in namespace {namespace}")
|
|
79
|
+
|
|
80
|
+
def publish_event(self, event: SamEvent) -> bool:
|
|
81
|
+
"""
|
|
82
|
+
Publish a SAM system event.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
event: The system event to publish
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
bool: True if published successfully, False otherwise
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
# Extract category and action from event_type (e.g., "session.deleted" -> "session", "deleted")
|
|
92
|
+
parts = event.event_type.split(".", 1)
|
|
93
|
+
if len(parts) != 2:
|
|
94
|
+
raise ValueError(f"Invalid event_type format: {event.event_type}. Expected 'category.action'")
|
|
95
|
+
|
|
96
|
+
category, action = parts
|
|
97
|
+
topic = get_sam_events_topic(self.namespace, category, action)
|
|
98
|
+
payload = event.to_dict()
|
|
99
|
+
|
|
100
|
+
log.info(f"[SamEventService] Publishing {event.event_type} event (ID: {event.event_id}) to topic {topic}")
|
|
101
|
+
|
|
102
|
+
# Use the component's publish function (which goes through proper A2A infrastructure)
|
|
103
|
+
self.publish_func(topic, payload, {"eventType": event.event_type, "eventId": event.event_id})
|
|
104
|
+
|
|
105
|
+
log.info(f"[SamEventService] Successfully published event {event.event_id}")
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
log.error(f"[SamEventService] Failed to publish event {event.event_id}: {e}")
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
def publish_session_deleted(self, session_id: str, user_id: str, agent_id: str, gateway_id: str) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
Convenience method to publish session deleted event.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
session_id: The deleted session ID
|
|
118
|
+
user_id: The user who owned the session
|
|
119
|
+
agent_id: The agent that was handling the session
|
|
120
|
+
gateway_id: The gateway that deleted the session
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
bool: True if published successfully
|
|
124
|
+
"""
|
|
125
|
+
event = SessionDeletedEvent.create(
|
|
126
|
+
namespace=self.namespace,
|
|
127
|
+
source_component=self.component_name,
|
|
128
|
+
session_id=session_id,
|
|
129
|
+
user_id=user_id,
|
|
130
|
+
agent_id=agent_id,
|
|
131
|
+
gateway_id=gateway_id
|
|
132
|
+
)
|
|
133
|
+
return self.publish_event(event)
|
|
134
|
+
|
|
135
|
+
def subscribe_to_events(self, event_type: str, handler: Callable[[SamEvent], None]) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
Subscribe to system events of a specific type.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
event_type: The type of events to subscribe to (e.g., "session.deleted")
|
|
141
|
+
handler: Function to call when event is received
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
bool: True if subscription was successful
|
|
145
|
+
"""
|
|
146
|
+
try:
|
|
147
|
+
if event_type not in self._subscribers:
|
|
148
|
+
self._subscribers[event_type] = []
|
|
149
|
+
|
|
150
|
+
self._subscribers[event_type].append(handler)
|
|
151
|
+
|
|
152
|
+
log.info(f"[SamEventService] Subscribed to {event_type} events")
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
log.error(f"[SamEventService] Failed to subscribe to {event_type} events: {e}")
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
def handle_incoming_event(self, topic: str, payload: Dict[str, Any]) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Handle incoming SAM event from messaging system.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
topic: The topic the event was received on
|
|
165
|
+
payload: Event payload
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
event_type = payload.get("event_type")
|
|
169
|
+
|
|
170
|
+
if not event_type:
|
|
171
|
+
log.warning(f"[SamEventService] Received event without event_type on topic {topic}")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Create event object
|
|
175
|
+
event = SamEvent(**payload)
|
|
176
|
+
|
|
177
|
+
# Call registered handlers
|
|
178
|
+
handlers = self._subscribers.get(event_type, [])
|
|
179
|
+
for handler in handlers:
|
|
180
|
+
try:
|
|
181
|
+
handler(event)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
log.error(f"[SamEventService] Error in event handler for {event_type}: {e}")
|
|
184
|
+
|
|
185
|
+
log.debug(f"[SamEventService] Processed event {event.event_id} with {len(handlers)} handlers")
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
log.error(f"[SamEventService] Error handling event from topic {topic}: {e}")
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def get_event_topic(namespace: str, event_type: str) -> str:
|
|
192
|
+
"""
|
|
193
|
+
Get the topic for a specific event type.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
namespace: The SAM namespace
|
|
197
|
+
event_type: Event type in format "category.action"
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: The topic for the event
|
|
201
|
+
"""
|
|
202
|
+
parts = event_type.split(".", 1)
|
|
203
|
+
if len(parts) != 2:
|
|
204
|
+
raise ValueError(f"Invalid event_type format: {event_type}. Expected 'category.action'")
|
|
205
|
+
|
|
206
|
+
category, action = parts
|
|
207
|
+
return get_sam_events_topic(namespace, category, action)
|
|
@@ -14,7 +14,7 @@ if config.config_file_name is not None:
|
|
|
14
14
|
|
|
15
15
|
# add your model's MetaData object here
|
|
16
16
|
# for 'autogenerate' support
|
|
17
|
-
from solace_agent_mesh.gateway.http_sse.
|
|
17
|
+
from solace_agent_mesh.gateway.http_sse.repository import Base
|
|
18
18
|
|
|
19
19
|
target_metadata = Base.metadata
|
|
20
20
|
|
|
@@ -24,7 +24,6 @@ from ...gateway.base.component import BaseGatewayComponent
|
|
|
24
24
|
from ...gateway.http_sse.session_manager import SessionManager
|
|
25
25
|
from ...gateway.http_sse.sse_manager import SSEManager
|
|
26
26
|
from .components import VisualizationForwarderComponent
|
|
27
|
-
from .infrastructure.persistence_service import PersistenceService
|
|
28
27
|
|
|
29
28
|
try:
|
|
30
29
|
from google.adk.artifacts import BaseArtifactService
|
|
@@ -131,10 +130,10 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
131
130
|
f"{self.log_identifier} Session service type is 'sql' but no database_url provided. "
|
|
132
131
|
"Please provide a database_url in the session_service configuration or use type 'memory'."
|
|
133
132
|
)
|
|
134
|
-
self.
|
|
133
|
+
self.database_url = database_url
|
|
135
134
|
else:
|
|
136
135
|
# Memory storage or no explicit configuration - no persistence service needed
|
|
137
|
-
self.
|
|
136
|
+
self.database_url = None
|
|
138
137
|
|
|
139
138
|
component_config = self.get_config("component_config", {})
|
|
140
139
|
app_config = component_config.get("app_config", {})
|
|
@@ -142,7 +141,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
142
141
|
self.session_manager = SessionManager(
|
|
143
142
|
secret_key=self.session_secret_key,
|
|
144
143
|
app_config=app_config,
|
|
145
|
-
persistence_service=self.persistence_service,
|
|
146
144
|
)
|
|
147
145
|
|
|
148
146
|
self.fastapi_app: FastAPI | None = None
|
|
@@ -158,6 +156,14 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
158
156
|
self._visualization_locks_lock = threading.Lock()
|
|
159
157
|
self._global_visualization_subscriptions: dict[str, int] = {}
|
|
160
158
|
self._visualization_processor_task: asyncio.Task | None = None
|
|
159
|
+
|
|
160
|
+
# Initialize SAM Events service for system events
|
|
161
|
+
from ...common.sam_events import SamEventService
|
|
162
|
+
self.sam_events = SamEventService(
|
|
163
|
+
namespace=self.namespace,
|
|
164
|
+
component_name=f"{self.name}_gateway",
|
|
165
|
+
publish_func=self.publish_a2a
|
|
166
|
+
)
|
|
161
167
|
|
|
162
168
|
log.info("%s Web UI Backend Component initialized.", self.log_identifier)
|
|
163
169
|
|
|
@@ -881,7 +887,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
881
887
|
|
|
882
888
|
self.fastapi_app = fastapi_app_instance
|
|
883
889
|
|
|
884
|
-
setup_dependencies(self, self.
|
|
890
|
+
setup_dependencies(self, self.database_url)
|
|
885
891
|
|
|
886
892
|
port = (
|
|
887
893
|
self.fastapi_https_port
|
|
@@ -991,7 +997,16 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
991
997
|
This method can be called from FastAPI handlers (via dependency injection).
|
|
992
998
|
It's thread-safe as it uses the SAC App instance.
|
|
993
999
|
"""
|
|
994
|
-
|
|
1000
|
+
log.debug(f"[publish_a2a] Starting to publish message to topic: {topic}")
|
|
1001
|
+
log.debug(f"[publish_a2a] Payload type: {type(payload)}, size: {len(str(payload))} chars")
|
|
1002
|
+
log.debug(f"[publish_a2a] User properties: {user_properties}")
|
|
1003
|
+
|
|
1004
|
+
try:
|
|
1005
|
+
super().publish_a2a_message(payload, topic, user_properties)
|
|
1006
|
+
log.debug(f"[publish_a2a] Successfully called super().publish_a2a_message for topic: {topic}")
|
|
1007
|
+
except Exception as e:
|
|
1008
|
+
log.error(f"[publish_a2a] Exception in publish_a2a: {e}", exc_info=True)
|
|
1009
|
+
raise
|
|
995
1010
|
|
|
996
1011
|
def _cleanup_visualization_locks(self):
|
|
997
1012
|
"""Remove locks for closed event loops to prevent memory leaks."""
|
|
@@ -1063,6 +1078,16 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
1063
1078
|
|
|
1064
1079
|
# --- Phase 1: Parse the payload to extract core info ---
|
|
1065
1080
|
try:
|
|
1081
|
+
# Handle SAM Events (system events)
|
|
1082
|
+
event_type = payload.get("event_type")
|
|
1083
|
+
if event_type:
|
|
1084
|
+
details["direction"] = "system_event"
|
|
1085
|
+
details["debug_type"] = "sam_event"
|
|
1086
|
+
details["payload_summary"]["method"] = event_type
|
|
1087
|
+
details["source_entity"] = payload.get("source_component", "unknown")
|
|
1088
|
+
details["target_entity"] = "system"
|
|
1089
|
+
return details
|
|
1090
|
+
|
|
1066
1091
|
# Try to parse as a JSON-RPC response first
|
|
1067
1092
|
if "result" in payload or "error" in payload:
|
|
1068
1093
|
rpc_response = JSONRPCResponse.model_validate(payload)
|
|
@@ -1613,15 +1638,12 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
1613
1638
|
)
|
|
1614
1639
|
|
|
1615
1640
|
# Store final agent response in persistence layer if available
|
|
1616
|
-
if hasattr(self, "
|
|
1641
|
+
if hasattr(self, "database_url") and self.database_url:
|
|
1617
1642
|
try:
|
|
1618
1643
|
session_id = external_request_context.get("a2a_session_id")
|
|
1619
1644
|
user_id = external_request_context.get("user_id_for_a2a")
|
|
1620
|
-
agent_name = external_request_context.get(
|
|
1621
|
-
"target_agent_name", "agent"
|
|
1622
|
-
)
|
|
1645
|
+
agent_name = external_request_context.get("target_agent_name", "agent")
|
|
1623
1646
|
|
|
1624
|
-
# Extract message content from the task status
|
|
1625
1647
|
message_text = ""
|
|
1626
1648
|
if task_data.status and task_data.status.message:
|
|
1627
1649
|
parts = a2a.get_parts_from_message(task_data.status.message)
|
|
@@ -1631,30 +1653,19 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
1631
1653
|
message_text += "\n"
|
|
1632
1654
|
message_text += part.text
|
|
1633
1655
|
|
|
1634
|
-
log.info(
|
|
1635
|
-
"%s Final agent response storage debug - session_id: %s, user_id: %s, message_text: '%s', parts_count: %s",
|
|
1636
|
-
log_id_prefix,
|
|
1637
|
-
session_id,
|
|
1638
|
-
user_id,
|
|
1639
|
-
message_text[:100] if message_text else None,
|
|
1640
|
-
len(a2a.get_parts_from_message(task_data.status.message))
|
|
1641
|
-
if task_data.status and task_data.status.message
|
|
1642
|
-
else 0,
|
|
1643
|
-
)
|
|
1644
|
-
|
|
1645
1656
|
if message_text and session_id and user_id:
|
|
1646
|
-
from .dependencies import
|
|
1647
|
-
from .shared.enums import SenderType
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1657
|
+
from .dependencies import create_session_service_with_transaction
|
|
1658
|
+
from ...gateway.http_sse.shared.enums import SenderType
|
|
1659
|
+
|
|
1660
|
+
with create_session_service_with_transaction() as (session_service, db):
|
|
1661
|
+
session_service.add_message_to_session(
|
|
1662
|
+
session_id=session_id,
|
|
1663
|
+
user_id=user_id,
|
|
1664
|
+
message=message_text,
|
|
1665
|
+
sender_type=SenderType.AGENT,
|
|
1666
|
+
sender_name=agent_name,
|
|
1667
|
+
agent_id=agent_name,
|
|
1668
|
+
)
|
|
1658
1669
|
log.info(
|
|
1659
1670
|
"%s Final agent response stored in session %s",
|
|
1660
1671
|
log_id_prefix,
|
|
@@ -1666,7 +1677,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
|
|
|
1666
1677
|
log_id_prefix,
|
|
1667
1678
|
storage_error,
|
|
1668
1679
|
)
|
|
1669
|
-
# Don't fail the SSE send if storage fails
|
|
1670
1680
|
|
|
1671
1681
|
except Exception as e:
|
|
1672
1682
|
log.exception(
|