solace-agent-mesh 1.3.3__py3-none-any.whl → 1.4.1__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 (89) hide show
  1. solace_agent_mesh/agent/adk/setup.py +183 -8
  2. solace_agent_mesh/agent/sac/app.py +337 -622
  3. solace_agent_mesh/agent/sac/component.py +47 -1
  4. solace_agent_mesh/agent/tools/dynamic_tool.py +36 -5
  5. solace_agent_mesh/agent/tools/tool_config_types.py +58 -0
  6. solace_agent_mesh/assets/docs/404.html +3 -3
  7. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.508ae8db.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/9a09e75d.92de8cf5.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/ae4415af.16cc58d3.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/{main.e82b32e6.js → main.9bc1a102.js} +2 -2
  11. solace_agent_mesh/assets/docs/assets/js/{runtime~main.aad1f874.js → runtime~main.f2b4ea70.js} +1 -1
  12. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +3 -3
  13. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +3 -3
  14. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  15. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
  16. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
  17. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
  18. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +20 -5
  19. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
  20. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  21. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +3 -3
  22. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +3 -3
  23. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
  24. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
  25. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +3 -3
  26. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +3 -3
  27. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +3 -3
  28. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
  38. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
  39. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  40. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +3 -3
  41. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
  42. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  43. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +5 -5
  45. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +68 -3
  47. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +3 -3
  50. solace_agent_mesh/assets/docs/lunr-index-1758036158289.json +1 -0
  51. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  52. solace_agent_mesh/assets/docs/search-doc-1758036158289.json +1 -0
  53. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  54. solace_agent_mesh/cli/__init__.py +1 -1
  55. solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +2 -0
  56. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +10 -245
  57. solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
  58. solace_agent_mesh/cli/commands/run_cmd.py +4 -7
  59. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-CAX9u8a7.js → authCallback-j1LW-wlq.js} +1 -1
  60. solace_agent_mesh/client/webui/frontend/static/assets/{client-DXU9SPI5.js → client-B9p_nFNA.js} +1 -1
  61. solace_agent_mesh/client/webui/frontend/static/assets/main-B6BpuH9K.js +339 -0
  62. solace_agent_mesh/client/webui/frontend/static/assets/main-B9s_V9tJ.css +1 -0
  63. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-B0BEKoAR.js → vendor-CS5YMf8a.js} +74 -69
  64. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  65. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  66. solace_agent_mesh/common/services/identity_service.py +2 -1
  67. solace_agent_mesh/common/services/providers/local_file_identity_service.py +1 -1
  68. solace_agent_mesh/common/utils/pydantic_utils.py +60 -0
  69. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +6 -4
  70. solace_agent_mesh/gateway/base/app.py +69 -120
  71. solace_agent_mesh/gateway/http_sse/app.py +99 -150
  72. solace_agent_mesh/gateway/http_sse/component.py +57 -30
  73. solace_agent_mesh/gateway/http_sse/main.py +337 -375
  74. solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +87 -0
  75. solace_agent_mesh/gateway/http_sse/sse_manager.py +44 -23
  76. solace_agent_mesh/templates/webui.yaml +1 -1
  77. {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/METADATA +7 -1
  78. {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/RECORD +82 -78
  79. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.3f34bf76.js +0 -1
  80. solace_agent_mesh/assets/docs/assets/js/9a09e75d.5a319fd4.js +0 -1
  81. solace_agent_mesh/assets/docs/assets/js/ae4415af.24cdc514.js +0 -1
  82. solace_agent_mesh/assets/docs/lunr-index-1757873594308.json +0 -1
  83. solace_agent_mesh/assets/docs/search-doc-1757873594308.json +0 -1
  84. solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +0 -1
  85. solace_agent_mesh/client/webui/frontend/static/assets/main-DjoMeldu.js +0 -339
  86. /solace_agent_mesh/assets/docs/assets/js/{main.e82b32e6.js.LICENSE.txt → main.9bc1a102.js.LICENSE.txt} +0 -0
  87. {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/WHEEL +0 -0
  88. {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/entry_points.txt +0 -0
  89. {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -3,15 +3,16 @@ Custom Solace AI Connector App class for the Web UI Backend.
3
3
  Defines configuration schema and programmatically creates the WebUIBackendComponent.
4
4
  """
5
5
 
6
- from typing import Any, Dict, List
6
+ from typing import Any, Dict, List, Optional
7
7
  import os
8
8
  from alembic import command
9
9
  from alembic.config import Config
10
+ from pydantic import Field, ValidationError
10
11
  from solace_ai_connector.common.log import log
11
12
 
12
13
  from ...gateway.http_sse.component import WebUIBackendComponent
13
14
 
14
- from ...gateway.base.app import BaseGatewayApp
15
+ from ...gateway.base.app import BaseGatewayApp, BaseGatewayAppConfig
15
16
  from ...gateway.base.component import BaseGatewayComponent
16
17
 
17
18
 
@@ -21,6 +22,88 @@ info = {
21
22
  }
22
23
 
23
24
 
25
+ class WebUIBackendAppConfig(BaseGatewayAppConfig):
26
+ """Pydantic model for the Web UI Backend application configuration."""
27
+
28
+ session_secret_key: str = Field(
29
+ ..., description="Secret key for signing web user sessions."
30
+ )
31
+ fastapi_host: str = Field(
32
+ default="127.0.0.1", description="Host address for the embedded FastAPI server."
33
+ )
34
+ fastapi_port: int = Field(
35
+ default=8000, description="Port for the embedded FastAPI server."
36
+ )
37
+ fastapi_https_port: int = Field(
38
+ default=8443, description="Port for the embedded FastAPI server when SSL is enabled."
39
+ )
40
+ cors_allowed_origins: List[str] = Field(
41
+ default=["*"], description="List of allowed origins for CORS requests."
42
+ )
43
+ sse_max_queue_size: int = Field(
44
+ default=200,
45
+ description="Maximum size of the SSE connection queues. Adjust based on expected load.",
46
+ )
47
+ sse_buffer_max_age_seconds: int = Field(
48
+ default=600, # 10 minutes
49
+ description="Maximum age in seconds for a pending event buffer before it is cleaned up.",
50
+ )
51
+ sse_buffer_cleanup_interval_seconds: int = Field(
52
+ default=300, # 5 minutes
53
+ description="How often to run the cleanup task for stale pending event buffers.",
54
+ )
55
+ resolve_artifact_uris_in_gateway: bool = Field(
56
+ default=True,
57
+ description="If true, the gateway will resolve artifact:// URIs found in A2A messages and embed the content as bytes before sending to the UI. If false, URIs are passed through.",
58
+ )
59
+ system_purpose: str = Field(
60
+ default="",
61
+ description="Detailed description of the system's overall purpose, to be optionally used by agents.",
62
+ )
63
+ response_format: str = Field(
64
+ default="",
65
+ description="General guidelines on how agent responses should be structured, to be optionally used by agents.",
66
+ )
67
+ frontend_welcome_message: str = Field(
68
+ default="Hi! How can I help?",
69
+ description="Initial welcome message displayed in the chat.",
70
+ )
71
+ frontend_bot_name: str = Field(
72
+ default="A2A Agent", description="Name displayed for the bot/agent in the UI."
73
+ )
74
+ frontend_collect_feedback: bool = Field(
75
+ default=False, description="Enable/disable the feedback buttons in the UI."
76
+ )
77
+ frontend_auth_login_url: str = Field(
78
+ default="", description="URL for the external login page (if auth is enabled)."
79
+ )
80
+ frontend_use_authorization: bool = Field(
81
+ default=False, description="Tell frontend whether backend expects authorization."
82
+ )
83
+ frontend_redirect_url: str = Field(
84
+ default="", description="Redirect URL for OAuth flows (if auth is enabled)."
85
+ )
86
+ external_auth_callback_uri: str = Field(
87
+ default="", description="Redirect URI for the OIDC application."
88
+ )
89
+ external_auth_service_url: str = Field(
90
+ default="http://localhost:8080",
91
+ description="External authorization service URL for login initiation.",
92
+ )
93
+ external_auth_provider: str = Field(
94
+ default="", description="The external authentication provider."
95
+ )
96
+ ssl_keyfile: str = Field(
97
+ default="", description="The file path to the SSL private key."
98
+ )
99
+ ssl_certfile: str = Field(
100
+ default="", description="The file path to the SSL certificate."
101
+ )
102
+ ssl_keyfile_password: str = Field(
103
+ default="", description="The passphrase for the SSL private key."
104
+ )
105
+
106
+
24
107
  class WebUIBackendApp(BaseGatewayApp):
25
108
  """
26
109
  Custom App class for the A2A Web UI Backend.
@@ -28,154 +111,8 @@ class WebUIBackendApp(BaseGatewayApp):
28
111
  - Defines WebUI-specific configuration parameters.
29
112
  """
30
113
 
31
- SPECIFIC_APP_SCHEMA_PARAMS: List[Dict[str, Any]] = [
32
- {
33
- "name": "session_secret_key",
34
- "required": True,
35
- "type": "string",
36
- "description": "Secret key for signing web user sessions.",
37
- },
38
- {
39
- "name": "fastapi_host",
40
- "required": False,
41
- "type": "string",
42
- "default": "127.0.0.1",
43
- "description": "Host address for the embedded FastAPI server.",
44
- },
45
- {
46
- "name": "fastapi_port",
47
- "required": False,
48
- "type": "integer",
49
- "default": 8000,
50
- "description": "Port for the embedded FastAPI server.",
51
- },
52
- {
53
- "name": "fastapi_https_port",
54
- "required": False,
55
- "type": "integer",
56
- "default": 8443,
57
- "description": "Port for the embedded FastAPI server when SSL is enabled.",
58
- },
59
- {
60
- "name": "cors_allowed_origins",
61
- "required": False,
62
- "type": "list",
63
- "default": ["*"],
64
- "description": "List of allowed origins for CORS requests.",
65
- },
66
- {
67
- "name": "sse_max_queue_size",
68
- "required": False,
69
- "type": "integer",
70
- "default": 200,
71
- "description": "Maximum size of the SSE connection queues. Adjust based on expected load.",
72
- },
73
- {
74
- "name": "resolve_artifact_uris_in_gateway",
75
- "required": False,
76
- "type": "boolean",
77
- "default": True,
78
- "description": "If true, the gateway will resolve artifact:// URIs found in A2A messages and embed the content as bytes before sending to the UI. If false, URIs are passed through.",
79
- },
80
- {
81
- "name": "system_purpose",
82
- "required": False,
83
- "type": "string",
84
- "default": "",
85
- "description": "Detailed description of the system's overall purpose, to be optionally used by agents.",
86
- },
87
- {
88
- "name": "response_format",
89
- "required": False,
90
- "type": "string",
91
- "default": "",
92
- "description": "General guidelines on how agent responses should be structured, to be optionally used by agents.",
93
- },
94
- {
95
- "name": "frontend_welcome_message",
96
- "required": False,
97
- "type": "string",
98
- "default": "Hi! How can I help?",
99
- "description": "Initial welcome message displayed in the chat.",
100
- },
101
- {
102
- "name": "frontend_bot_name",
103
- "required": False,
104
- "type": "string",
105
- "default": "A2A Agent",
106
- "description": "Name displayed for the bot/agent in the UI.",
107
- },
108
- {
109
- "name": "frontend_collect_feedback",
110
- "required": False,
111
- "type": "boolean",
112
- "default": False,
113
- "description": "Enable/disable the feedback buttons in the UI.",
114
- },
115
- {
116
- "name": "frontend_auth_login_url",
117
- "required": False,
118
- "type": "string",
119
- "default": "",
120
- "description": "URL for the external login page (if auth is enabled).",
121
- },
122
- {
123
- "name": "frontend_use_authorization",
124
- "required": False,
125
- "type": "boolean",
126
- "default": False,
127
- "description": "Tell frontend whether backend expects authorization.",
128
- },
129
- {
130
- "name": "frontend_redirect_url",
131
- "required": False,
132
- "type": "string",
133
- "default": "",
134
- "description": "Redirect URL for OAuth flows (if auth is enabled).",
135
- },
136
- {
137
- "name": "external_auth_callback_uri",
138
- "required": False,
139
- "type": "string",
140
- "default": "",
141
- "description": "Redirect URI for the OIDC application.",
142
- },
143
- {
144
- "name": "external_auth_service_url",
145
- "required": False,
146
- "type": "string",
147
- "default": "http://localhost:8080",
148
- "description": "External authorization service URL for login initiation.",
149
- },
150
- {
151
- "name": "external_auth_provider",
152
- "required": False,
153
- "type": "string",
154
- "default": "",
155
- "description": "The external authentication provider.",
156
- },
157
- {
158
- "name": "ssl_keyfile",
159
- "required": False,
160
- "type": "string",
161
- "default": "",
162
- "description": "The file path to the SSL private key.",
163
- },
164
- {
165
- "name": "ssl_certfile",
166
- "required": False,
167
- "type": "string",
168
- "default": "",
169
- "description": "The file path to the SSL certificate.",
170
- },
171
- {
172
- "name": "ssl_keyfile_password",
173
- "required": False,
174
- "type": "string",
175
- "default": "",
176
- "description": "The passphrase for the SSL private key.",
177
- },
178
- ]
114
+ # This is now a placeholder. Validation is handled by the Pydantic model.
115
+ SPECIFIC_APP_SCHEMA_PARAMS: List[Dict[str, Any]] = []
179
116
 
180
117
  def __init__(self, app_info: Dict[str, Any], **kwargs):
181
118
  """
@@ -186,6 +123,18 @@ class WebUIBackendApp(BaseGatewayApp):
186
123
  "%s Initializing WebUIBackendApp...",
187
124
  app_info.get("name", "WebUIBackendApp"),
188
125
  )
126
+
127
+ app_config_dict = app_info.get("app_config", {})
128
+ try:
129
+ # Validate the raw dict, cleaning None values to allow defaults to apply
130
+ app_config = WebUIBackendAppConfig.model_validate_and_clean(
131
+ app_config_dict
132
+ )
133
+ app_info["app_config"] = app_config
134
+ except ValidationError as e:
135
+ log.error("Web UI Gateway configuration validation failed:\n%s", e)
136
+ raise ValueError(f"Invalid Web UI Gateway configuration: {e}") from e
137
+
189
138
  super().__init__(app_info, **kwargs)
190
139
 
191
140
  try:
@@ -17,12 +17,14 @@ from fastapi import Request as FastAPIRequest
17
17
  from solace_ai_connector.common.log import log
18
18
  from solace_ai_connector.components.inputs_outputs.broker_input import BrokerInput
19
19
  from solace_ai_connector.flow.app import App as SACApp
20
+ from solace_ai_connector.common.event import Event, EventType
20
21
 
21
22
  from ...common.agent_registry import AgentRegistry
22
23
  from ...core_a2a.service import CoreA2AService
23
24
  from ...gateway.base.component import BaseGatewayComponent
24
25
  from ...gateway.http_sse.session_manager import SessionManager
25
26
  from ...gateway.http_sse.sse_manager import SSEManager
27
+ from .sse_event_buffer import SSEEventBuffer
26
28
  from .components import VisualizationForwarderComponent
27
29
 
28
30
  try:
@@ -118,8 +120,23 @@ class WebUIBackendComponent(BaseGatewayComponent):
118
120
  raise ValueError(f"Configuration retrieval error: {e}") from e
119
121
 
120
122
  sse_max_queue_size = self.get_config("sse_max_queue_size", 200)
123
+ sse_buffer_max_age_seconds = self.get_config("sse_buffer_max_age_seconds", 600)
121
124
 
122
- self.sse_manager = SSEManager(max_queue_size=sse_max_queue_size)
125
+ self.sse_event_buffer = SSEEventBuffer(
126
+ max_queue_size=sse_max_queue_size,
127
+ max_age_seconds=sse_buffer_max_age_seconds,
128
+ )
129
+ self.sse_manager = SSEManager(
130
+ max_queue_size=sse_max_queue_size, event_buffer=self.sse_event_buffer
131
+ )
132
+
133
+ self._sse_cleanup_timer_id = f"sse_cleanup_{self.gateway_id}"
134
+ cleanup_interval_sec = self.get_config("sse_buffer_cleanup_interval_seconds", 300)
135
+ self.add_timer(
136
+ delay_ms=cleanup_interval_sec * 1000,
137
+ timer_id=self._sse_cleanup_timer_id,
138
+ interval_ms=cleanup_interval_sec * 1000,
139
+ )
123
140
 
124
141
  session_config = self._resolve_session_config()
125
142
  if session_config.get("type") == "sql":
@@ -167,6 +184,15 @@ class WebUIBackendComponent(BaseGatewayComponent):
167
184
 
168
185
  log.info("%s Web UI Backend Component initialized.", self.log_identifier)
169
186
 
187
+ def process_event(self, event: Event):
188
+ if event.event_type == EventType.TIMER:
189
+ if event.data.get("timer_id") == self._sse_cleanup_timer_id:
190
+ log.debug("%s SSE buffer cleanup timer triggered.", self.log_identifier)
191
+ self.sse_event_buffer.cleanup_stale_buffers()
192
+ return
193
+
194
+ super().process_event(event)
195
+
170
196
  def _get_visualization_lock(self) -> asyncio.Lock:
171
197
  """Get or create a visualization lock for the current event loop."""
172
198
  try:
@@ -1025,6 +1051,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1025
1051
  def cleanup(self):
1026
1052
  """Gracefully shuts down the component and the FastAPI server."""
1027
1053
  log.info("%s Cleaning up Web UI Backend Component...", self.log_identifier)
1054
+ self.cancel_timer(self._sse_cleanup_timer_id)
1028
1055
  log.info("%s Cleaning up visualization resources...", self.log_identifier)
1029
1056
  if self._visualization_message_queue:
1030
1057
  self._visualization_message_queue.put(None)
@@ -1056,6 +1083,35 @@ class WebUIBackendComponent(BaseGatewayComponent):
1056
1083
  self._cleanup_visualization_locks()
1057
1084
  log.info("%s Visualization resources cleaned up.", self.log_identifier)
1058
1085
 
1086
+ super().cleanup()
1087
+
1088
+ if self.fastapi_thread and self.fastapi_thread.is_alive():
1089
+ log.info(
1090
+ "%s Waiting for FastAPI server thread to exit...", self.log_identifier
1091
+ )
1092
+ self.fastapi_thread.join(timeout=10)
1093
+ if self.fastapi_thread.is_alive():
1094
+ log.warning(
1095
+ "%s FastAPI server thread did not exit gracefully.",
1096
+ self.log_identifier,
1097
+ )
1098
+
1099
+ if self.sse_manager:
1100
+ log.info(
1101
+ "%s Closing active SSE connections (best effort)...",
1102
+ self.log_identifier,
1103
+ )
1104
+ try:
1105
+ asyncio.run(self.sse_manager.close_all())
1106
+ except Exception as sse_close_err:
1107
+ log.error(
1108
+ "%s Error closing SSE connections during cleanup: %s",
1109
+ self.log_identifier,
1110
+ sse_close_err,
1111
+ )
1112
+
1113
+ log.info("%s Web UI Backend Component cleanup finished.", self.log_identifier)
1114
+
1059
1115
  def _infer_visualization_event_details(
1060
1116
  self, topic: str, payload: dict[str, Any]
1061
1117
  ) -> dict[str, Any]:
@@ -1302,35 +1358,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
1302
1358
  )
1303
1359
  return agents
1304
1360
 
1305
- super().cleanup()
1306
-
1307
- if self.fastapi_thread and self.fastapi_thread.is_alive():
1308
- log.info(
1309
- "%s Waiting for FastAPI server thread to exit...", self.log_identifier
1310
- )
1311
- self.fastapi_thread.join(timeout=10)
1312
- if self.fastapi_thread.is_alive():
1313
- log.warning(
1314
- "%s FastAPI server thread did not exit gracefully.",
1315
- self.log_identifier,
1316
- )
1317
-
1318
- if self.sse_manager:
1319
- log.info(
1320
- "%s Closing active SSE connections (best effort)...",
1321
- self.log_identifier,
1322
- )
1323
- try:
1324
- asyncio.run(self.sse_manager.close_all())
1325
- except Exception as sse_close_err:
1326
- log.error(
1327
- "%s Error closing SSE connections during cleanup: %s",
1328
- self.log_identifier,
1329
- sse_close_err,
1330
- )
1331
-
1332
- log.info("%s Web UI Backend Component cleanup finished.", self.log_identifier)
1333
-
1334
1361
  def get_agent_registry(self) -> AgentRegistry:
1335
1362
  return self.agent_registry
1336
1363