solace-agent-mesh 1.3.2__py3-none-any.whl → 1.4.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/artifacts/filesystem_artifact_service.py +16 -8
- 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/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.508ae8db.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.92de8cf5.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.4adc477a.js → main.1de3da6a.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.3188e049.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 +5 -5
- 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 +19 -19
- 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 +73 -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-1757991496554.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1757991496554.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/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-B9s_V9tJ.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Dq4AJNvn.js +339 -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/utils/pydantic_utils.py +56 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +6 -4
- solace_agent_mesh/gateway/base/app.py +58 -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/sse_event_buffer.py +87 -0
- solace_agent_mesh/gateway/http_sse/sse_manager.py +44 -23
- {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/METADATA +1 -1
- {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/RECORD +103 -100
- 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.03d5dceb.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.cf0229ea.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757704179464.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757704179464.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.4adc477a.js.LICENSE.txt → main.1de3da6a.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,9 +5,9 @@
|
|
|
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/authCallback-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/client-
|
|
8
|
+
<script type="module" crossorigin src="/assets/authCallback-j1LW-wlq.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-CS5YMf8a.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/client-B9p_nFNA.js">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
|
@@ -5,10 +5,10 @@
|
|
|
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-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/client-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
8
|
+
<script type="module" crossorigin src="/assets/main-Dq4AJNvn.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-CS5YMf8a.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/client-B9p_nFNA.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/main-B9s_V9tJ.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Provides a Pydantic BaseModel for SAM configuration with dict-like access."""
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from typing import Any, Dict, Type, TypeVar
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T", bound="SamConfigBase")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SamConfigBase(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
A Pydantic BaseModel for SAM configuration that allows dictionary-style access
|
|
11
|
+
for backward compatibility with components expecting dicts.
|
|
12
|
+
Supports .get(), ['key'], and 'in' operator.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def model_validate_and_clean(cls: Type[T], obj: Any) -> T:
|
|
17
|
+
"""
|
|
18
|
+
Validates a dictionary, first removing any keys with None values.
|
|
19
|
+
This allows Pydantic's default values to be applied correctly when
|
|
20
|
+
a config key is present but has no value in YAML.
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(obj, dict):
|
|
23
|
+
cleaned_obj = {k: v for k, v in obj.items() if v is not None}
|
|
24
|
+
return cls.model_validate(cleaned_obj)
|
|
25
|
+
return cls.model_validate(obj)
|
|
26
|
+
|
|
27
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
28
|
+
"""Provides dict-like .get() method."""
|
|
29
|
+
return getattr(self, key, default)
|
|
30
|
+
|
|
31
|
+
def __getitem__(self, key: str) -> Any:
|
|
32
|
+
"""Provides dict-like ['key'] access."""
|
|
33
|
+
return getattr(self, key)
|
|
34
|
+
|
|
35
|
+
def __contains__(self, key: str) -> bool:
|
|
36
|
+
"""
|
|
37
|
+
Provides dict-like 'in' support that mimics the old behavior.
|
|
38
|
+
Returns True only if the key was explicitly provided during model creation.
|
|
39
|
+
"""
|
|
40
|
+
return key in self.model_fields_set
|
|
41
|
+
|
|
42
|
+
def keys(self):
|
|
43
|
+
"""Provides dict-like .keys() method."""
|
|
44
|
+
return self.model_dump().keys()
|
|
45
|
+
|
|
46
|
+
def values(self):
|
|
47
|
+
"""Provides dict-like .values() method."""
|
|
48
|
+
return self.model_dump().values()
|
|
49
|
+
|
|
50
|
+
def items(self):
|
|
51
|
+
"""Provides dict-like .items() method."""
|
|
52
|
+
return self.model_dump().items()
|
|
53
|
+
|
|
54
|
+
def __iter__(self):
|
|
55
|
+
"""Provides dict-like iteration over keys."""
|
|
56
|
+
return iter(self.model_dump())
|
|
@@ -46,7 +46,7 @@ class RegistryManager:
|
|
|
46
46
|
|
|
47
47
|
if self.user_registries_file.exists():
|
|
48
48
|
try:
|
|
49
|
-
with open(self.user_registries_file, "r") as f:
|
|
49
|
+
with open(self.user_registries_file, "r", encoding="utf-8") as f:
|
|
50
50
|
loaded_data = json.load(f)
|
|
51
51
|
if isinstance(loaded_data, list):
|
|
52
52
|
for reg_dict in loaded_data:
|
|
@@ -102,7 +102,7 @@ class RegistryManager:
|
|
|
102
102
|
return False
|
|
103
103
|
|
|
104
104
|
registry_id = self._generate_registry_id(path_or_url)
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
final_name = name
|
|
107
107
|
if not final_name:
|
|
108
108
|
if registry_type == "git":
|
|
@@ -110,6 +110,8 @@ class RegistryManager:
|
|
|
110
110
|
else:
|
|
111
111
|
final_name = Path(path_or_url).name
|
|
112
112
|
|
|
113
|
+
# Sanitize name to be filesystem-friendly
|
|
114
|
+
final_name = "".join(c if c.isalnum() else '_' for c in final_name)
|
|
113
115
|
is_official_src = path_or_url == DEFAULT_OFFICIAL_REGISTRY_URL
|
|
114
116
|
|
|
115
117
|
try:
|
|
@@ -128,7 +130,7 @@ class RegistryManager:
|
|
|
128
130
|
current_registries_data: List[Dict[str, Any]] = []
|
|
129
131
|
if self.user_registries_file.exists():
|
|
130
132
|
try:
|
|
131
|
-
with open(self.user_registries_file, "r") as f:
|
|
133
|
+
with open(self.user_registries_file, "r", encoding="utf-8") as f:
|
|
132
134
|
loaded_data = json.load(f)
|
|
133
135
|
if isinstance(loaded_data, list):
|
|
134
136
|
current_registries_data = [
|
|
@@ -154,7 +156,7 @@ class RegistryManager:
|
|
|
154
156
|
current_registries_data.append(new_registry.model_dump())
|
|
155
157
|
|
|
156
158
|
try:
|
|
157
|
-
with open(self.user_registries_file, "w") as f:
|
|
159
|
+
with open(self.user_registries_file, "w", encoding="utf-8") as f:
|
|
158
160
|
json.dump(current_registries_data, f, indent=2)
|
|
159
161
|
return True
|
|
160
162
|
except Exception as e:
|
|
@@ -4,13 +4,15 @@ Base App class for Gateway implementations in the Solace AI Connector.
|
|
|
4
4
|
|
|
5
5
|
import uuid
|
|
6
6
|
from abc import abstractmethod
|
|
7
|
-
from typing import Any, Dict, List, Type
|
|
7
|
+
from typing import Any, Dict, List, Type, Optional, Literal
|
|
8
8
|
|
|
9
|
+
from pydantic import Field
|
|
9
10
|
from solace_ai_connector.common.log import log
|
|
10
11
|
from solace_ai_connector.common.utils import deep_merge
|
|
11
12
|
from solace_ai_connector.flow.app import App
|
|
12
13
|
from solace_ai_connector.components.component_base import ComponentBase
|
|
13
14
|
|
|
15
|
+
from ...common.utils.pydantic_utils import SamConfigBase
|
|
14
16
|
from ...common.a2a import (
|
|
15
17
|
get_discovery_topic,
|
|
16
18
|
get_gateway_response_subscription_topic,
|
|
@@ -22,91 +24,58 @@ class BaseGatewayComponent(ComponentBase):
|
|
|
22
24
|
pass
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
),
|
|
78
|
-
"enum": ["reference", "embed", "passthrough"],
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"name": "gateway_max_message_size_bytes",
|
|
82
|
-
"required": False,
|
|
83
|
-
"type": "integer",
|
|
84
|
-
"default": 10_000_000, # 10MB
|
|
85
|
-
"description": "Maximum allowed message size in bytes for messages published by the gateway.",
|
|
86
|
-
},
|
|
87
|
-
# --- Default User Identity Configuration ---
|
|
88
|
-
{
|
|
89
|
-
"name": "default_user_identity",
|
|
90
|
-
"required": False,
|
|
91
|
-
"type": "string",
|
|
92
|
-
"description": "Default user identity to use when no user authentication is provided. WARNING: Only use in development environments with trusted access!",
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"name": "force_user_identity",
|
|
96
|
-
"required": False,
|
|
97
|
-
"type": "string",
|
|
98
|
-
"description": "Override any provided user identity with this value. WARNING: Development only! This completely replaces authentication.",
|
|
99
|
-
},
|
|
100
|
-
# --- Identity Service Configuration ---
|
|
101
|
-
{
|
|
102
|
-
"name": "identity_service",
|
|
103
|
-
"required": False,
|
|
104
|
-
"type": "object",
|
|
105
|
-
"default": None,
|
|
106
|
-
"description": "Configuration for the pluggable Identity Service provider.",
|
|
107
|
-
},
|
|
108
|
-
]
|
|
109
|
-
}
|
|
27
|
+
class BaseGatewayAppConfig(SamConfigBase):
|
|
28
|
+
"""Base Pydantic model for gateway application configuration."""
|
|
29
|
+
|
|
30
|
+
namespace: str = Field(
|
|
31
|
+
...,
|
|
32
|
+
description="Absolute topic prefix for A2A communication (e.g., 'myorg/dev').",
|
|
33
|
+
)
|
|
34
|
+
gateway_id: Optional[str] = Field(
|
|
35
|
+
default=None,
|
|
36
|
+
description="Unique ID for this gateway instance. Auto-generated if omitted.",
|
|
37
|
+
)
|
|
38
|
+
artifact_service: Dict[str, Any] = Field(
|
|
39
|
+
...,
|
|
40
|
+
description="Configuration for the SHARED ADK Artifact Service.",
|
|
41
|
+
)
|
|
42
|
+
enable_embed_resolution: bool = Field(
|
|
43
|
+
default=True,
|
|
44
|
+
description="Enable late-stage 'artifact_content' embed resolution in the gateway.",
|
|
45
|
+
)
|
|
46
|
+
gateway_max_artifact_resolve_size_bytes: int = Field(
|
|
47
|
+
default=104857600, # 100MB
|
|
48
|
+
description="Maximum size of an individual artifact's raw content for 'artifact_content' embeds and max total accumulated size for a parent artifact after internal recursive resolution.",
|
|
49
|
+
)
|
|
50
|
+
gateway_recursive_embed_depth: int = Field(
|
|
51
|
+
default=12,
|
|
52
|
+
description="Maximum depth for recursively resolving 'artifact_content' embeds within files.",
|
|
53
|
+
)
|
|
54
|
+
artifact_handling_mode: Literal["reference", "embed", "passthrough"] = Field(
|
|
55
|
+
default="reference",
|
|
56
|
+
description=(
|
|
57
|
+
"How the gateway handles file parts from clients. "
|
|
58
|
+
"'reference': Save inline file bytes to the artifact store and replace with a URI. "
|
|
59
|
+
"'embed': Resolve file URIs and embed content as bytes. "
|
|
60
|
+
"'passthrough': Send file parts to the agent as-is."
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
gateway_max_message_size_bytes: int = Field(
|
|
64
|
+
default=10_000_000, # 10MB
|
|
65
|
+
description="Maximum allowed message size in bytes for messages published by the gateway.",
|
|
66
|
+
)
|
|
67
|
+
default_user_identity: Optional[str] = Field(
|
|
68
|
+
default=None,
|
|
69
|
+
description="Default user identity to use when no user authentication is provided. WARNING: Only use in development environments with trusted access!",
|
|
70
|
+
)
|
|
71
|
+
force_user_identity: Optional[str] = Field(
|
|
72
|
+
default=None,
|
|
73
|
+
description="Override any provided user identity with this value. WARNING: Development only! This completely replaces authentication.",
|
|
74
|
+
)
|
|
75
|
+
identity_service: Optional[Dict[str, Any]] = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="Configuration for the pluggable Identity Service provider.",
|
|
78
|
+
)
|
|
110
79
|
|
|
111
80
|
|
|
112
81
|
class BaseGatewayApp(App):
|
|
@@ -114,44 +83,13 @@ class BaseGatewayApp(App):
|
|
|
114
83
|
Base class for Gateway applications.
|
|
115
84
|
|
|
116
85
|
Handles common configuration, Solace broker setup, and instantiation
|
|
117
|
-
of the gateway-specific component.
|
|
118
|
-
base schema with specific schema parameters defined by subclasses.
|
|
86
|
+
of the gateway-specific component.
|
|
119
87
|
"""
|
|
120
88
|
|
|
121
|
-
|
|
89
|
+
# This is now a placeholder. Validation is handled by Pydantic models in subclasses.
|
|
90
|
+
app_schema: Dict[str, List[Dict[str, Any]]] = {"config_parameters": []}
|
|
122
91
|
SPECIFIC_APP_SCHEMA_PARAMS_ATTRIBUTE_NAME = "SPECIFIC_APP_SCHEMA_PARAMS"
|
|
123
92
|
|
|
124
|
-
def __init_subclass__(cls, **kwargs):
|
|
125
|
-
"""
|
|
126
|
-
Automatically merges the base gateway schema with specific schema
|
|
127
|
-
parameters defined in any subclass.
|
|
128
|
-
"""
|
|
129
|
-
super().__init_subclass__(**kwargs)
|
|
130
|
-
|
|
131
|
-
specific_params = getattr(
|
|
132
|
-
cls, cls.SPECIFIC_APP_SCHEMA_PARAMS_ATTRIBUTE_NAME, []
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
if not isinstance(specific_params, list):
|
|
136
|
-
log.warning(
|
|
137
|
-
"Class attribute '%s' in %s is not a list. Schema merging might be incorrect.",
|
|
138
|
-
cls.SPECIFIC_APP_SCHEMA_PARAMS_ATTRIBUTE_NAME,
|
|
139
|
-
cls.__name__,
|
|
140
|
-
)
|
|
141
|
-
specific_params = []
|
|
142
|
-
|
|
143
|
-
base_params = BaseGatewayApp.app_schema.get("config_parameters", [])
|
|
144
|
-
|
|
145
|
-
merged_config_parameters = list(base_params)
|
|
146
|
-
merged_config_parameters.extend(specific_params)
|
|
147
|
-
|
|
148
|
-
cls.app_schema = {"config_parameters": merged_config_parameters}
|
|
149
|
-
log.debug(
|
|
150
|
-
"BaseGatewayApp.__init_subclass__ created merged app_schema for %s with %d params.",
|
|
151
|
-
cls.__name__,
|
|
152
|
-
len(merged_config_parameters),
|
|
153
|
-
)
|
|
154
|
-
|
|
155
93
|
def __init__(self, app_info: Dict[str, Any], **kwargs):
|
|
156
94
|
"""
|
|
157
95
|
Initializes the BaseGatewayApp.
|
|
@@ -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:
|