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
|
@@ -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-B6BpuH9K.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>
|
|
@@ -33,7 +33,7 @@ class BaseIdentityService(ABC):
|
|
|
33
33
|
|
|
34
34
|
@abstractmethod
|
|
35
35
|
async def get_user_profile(
|
|
36
|
-
self, auth_claims: Dict[str, Any]
|
|
36
|
+
self, auth_claims: Dict[str, Any], **kwargs: Any
|
|
37
37
|
) -> Optional[Dict[str, Any]]:
|
|
38
38
|
"""
|
|
39
39
|
Fetches additional profile details for an already authenticated user.
|
|
@@ -42,6 +42,7 @@ class BaseIdentityService(ABC):
|
|
|
42
42
|
auth_claims: A dictionary of claims from the primary authentication
|
|
43
43
|
system (e.g., decoded JWT, session data). It's guaranteed
|
|
44
44
|
to contain at least a primary user identifier.
|
|
45
|
+
kwargs: Optional additional parameters for provider-specific logic.
|
|
45
46
|
|
|
46
47
|
Returns:
|
|
47
48
|
A dictionary containing additional user details (e.g., title, manager)
|
|
@@ -68,7 +68,7 @@ class LocalFileIdentityService(BaseIdentityService):
|
|
|
68
68
|
raise
|
|
69
69
|
|
|
70
70
|
async def get_user_profile(
|
|
71
|
-
self, auth_claims: Dict[str, Any]
|
|
71
|
+
self, auth_claims: Dict[str, Any], **kwargs: Any
|
|
72
72
|
) -> Optional[Dict[str, Any]]:
|
|
73
73
|
"""Looks up a user profile from the in-memory index."""
|
|
74
74
|
lookup_value = auth_claims.get(self.lookup_key)
|
|
@@ -0,0 +1,60 @@
|
|
|
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 __setitem__(self, key: str, value: Any):
|
|
36
|
+
"""Provides dict-like ['key'] = value assignment."""
|
|
37
|
+
setattr(self, key, value)
|
|
38
|
+
|
|
39
|
+
def __contains__(self, key: str) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Provides dict-like 'in' support that mimics the old behavior.
|
|
42
|
+
Returns True only if the key was explicitly provided during model creation.
|
|
43
|
+
"""
|
|
44
|
+
return key in self.model_fields_set
|
|
45
|
+
|
|
46
|
+
def keys(self):
|
|
47
|
+
"""Provides dict-like .keys() method."""
|
|
48
|
+
return self.model_dump().keys()
|
|
49
|
+
|
|
50
|
+
def values(self):
|
|
51
|
+
"""Provides dict-like .values() method."""
|
|
52
|
+
return self.model_dump().values()
|
|
53
|
+
|
|
54
|
+
def items(self):
|
|
55
|
+
"""Provides dict-like .items() method."""
|
|
56
|
+
return self.model_dump().items()
|
|
57
|
+
|
|
58
|
+
def __iter__(self):
|
|
59
|
+
"""Provides dict-like iteration over keys."""
|
|
60
|
+
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, ValidationError
|
|
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.
|
|
@@ -173,6 +111,17 @@ class BaseGatewayApp(App):
|
|
|
173
111
|
code_config_app_block, yaml_app_config_block
|
|
174
112
|
)
|
|
175
113
|
|
|
114
|
+
try:
|
|
115
|
+
# Validate the configuration against the base model
|
|
116
|
+
validated_config = BaseGatewayAppConfig.model_validate_and_clean(
|
|
117
|
+
resolved_app_config_block
|
|
118
|
+
)
|
|
119
|
+
# Use the validated model's dict representation
|
|
120
|
+
resolved_app_config_block = validated_config
|
|
121
|
+
except ValidationError as e:
|
|
122
|
+
log.error("Base Gateway configuration validation failed:\n%s", e)
|
|
123
|
+
raise ValueError(f"Invalid Base Gateway configuration: {e}") from e
|
|
124
|
+
|
|
176
125
|
self.namespace: str = resolved_app_config_block.get("namespace")
|
|
177
126
|
if not self.namespace:
|
|
178
127
|
raise ValueError(
|