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
@@ -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-CAX9u8a7.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-B0BEKoAR.js">
10
- <link rel="modulepreload" crossorigin href="/assets/client-DXU9SPI5.js">
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-DjoMeldu.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-B0BEKoAR.js">
10
- <link rel="modulepreload" crossorigin href="/assets/client-DXU9SPI5.js">
11
- <link rel="stylesheet" crossorigin href="/assets/main-C03yrETa.css">
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
- BASE_GATEWAY_APP_SCHEMA: Dict[str, List[Dict[str, Any]]] = {
26
- "config_parameters": [
27
- {
28
- "name": "namespace",
29
- "required": True,
30
- "type": "string",
31
- "description": "Absolute topic prefix for A2A communication (e.g., 'myorg/dev').",
32
- },
33
- {
34
- "name": "gateway_id",
35
- "required": False,
36
- "type": "string",
37
- "default": None,
38
- "description": "Unique ID for this gateway instance. Auto-generated if omitted.",
39
- },
40
- {
41
- "name": "artifact_service",
42
- "required": True,
43
- "type": "object",
44
- "description": "Configuration for the SHARED ADK Artifact Service.",
45
- },
46
- {
47
- "name": "enable_embed_resolution",
48
- "required": False,
49
- "type": "boolean",
50
- "default": True,
51
- "description": "Enable late-stage 'artifact_content' embed resolution in the gateway.",
52
- },
53
- {
54
- "name": "gateway_max_artifact_resolve_size_bytes",
55
- "required": False,
56
- "type": "integer",
57
- "default": 104857600, # 100MB
58
- "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.",
59
- },
60
- {
61
- "name": "gateway_recursive_embed_depth",
62
- "required": False,
63
- "type": "integer",
64
- "default": 12,
65
- "description": "Maximum depth for recursively resolving 'artifact_content' embeds within files.",
66
- },
67
- {
68
- "name": "artifact_handling_mode",
69
- "required": False,
70
- "type": "string",
71
- "default": "reference",
72
- "description": (
73
- "How the gateway handles file parts from clients. "
74
- "'reference': Save inline file bytes to the artifact store and replace with a URI. "
75
- "'embed': Resolve file URIs and embed content as bytes. "
76
- "'passthrough': Send file parts to the agent as-is."
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. It also automatically merges its
118
- base schema with specific schema parameters defined by subclasses.
86
+ of the gateway-specific component.
119
87
  """
120
88
 
121
- app_schema: Dict[str, List[Dict[str, Any]]] = BASE_GATEWAY_APP_SCHEMA
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(