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.
- solace_agent_mesh/agent/adk/setup.py +183 -8
- solace_agent_mesh/agent/sac/app.py +337 -622
- solace_agent_mesh/agent/sac/component.py +47 -1
- solace_agent_mesh/agent/tools/dynamic_tool.py +36 -5
- solace_agent_mesh/agent/tools/tool_config_types.py +58 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.508ae8db.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.92de8cf5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae4415af.16cc58d3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.e82b32e6.js → main.9bc1a102.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/{runtime~main.aad1f874.js → runtime~main.f2b4ea70.js} +1 -1
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +3 -3
- 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
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +20 -5
- 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 +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +3 -3
- 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 +3 -3
- 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 +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
- 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 +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
- 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 +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +68 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
- 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 +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1758036158289.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1758036158289.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +2 -0
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +10 -245
- solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
- solace_agent_mesh/cli/commands/run_cmd.py +4 -7
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-CAX9u8a7.js → authCallback-j1LW-wlq.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-DXU9SPI5.js → client-B9p_nFNA.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-B6BpuH9K.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-B9s_V9tJ.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-B0BEKoAR.js → vendor-CS5YMf8a.js} +74 -69
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/common/services/identity_service.py +2 -1
- solace_agent_mesh/common/services/providers/local_file_identity_service.py +1 -1
- solace_agent_mesh/common/utils/pydantic_utils.py +60 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +6 -4
- solace_agent_mesh/gateway/base/app.py +69 -120
- solace_agent_mesh/gateway/http_sse/app.py +99 -150
- solace_agent_mesh/gateway/http_sse/component.py +57 -30
- solace_agent_mesh/gateway/http_sse/main.py +337 -375
- solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +87 -0
- solace_agent_mesh/gateway/http_sse/sse_manager.py +44 -23
- solace_agent_mesh/templates/webui.yaml +1 -1
- {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/METADATA +7 -1
- {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/RECORD +82 -78
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.3f34bf76.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.5a319fd4.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae4415af.24cdc514.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757873594308.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757873594308.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-DjoMeldu.js +0 -339
- /solace_agent_mesh/assets/docs/assets/js/{main.e82b32e6.js.LICENSE.txt → main.9bc1a102.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.3.3.dist-info → solace_agent_mesh-1.4.1.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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.
|
|
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
|
|