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.

Files changed (136) hide show
  1. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +16 -8
  2. solace_agent_mesh/agent/adk/setup.py +183 -8
  3. solace_agent_mesh/agent/sac/app.py +337 -622
  4. solace_agent_mesh/agent/sac/component.py +47 -1
  5. solace_agent_mesh/agent/tools/dynamic_tool.py +36 -5
  6. solace_agent_mesh/agent/tools/tool_config_types.py +58 -0
  7. solace_agent_mesh/assets/docs/404.html +3 -3
  8. solace_agent_mesh/assets/docs/assets/js/0e682baa.da822665.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/1023fc19.8a8a9309.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/1523c6b4.2645ef68.js +1 -0
  11. solace_agent_mesh/assets/docs/assets/js/1c6e87d2.43771adc.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/2a9cab12.2afaee76.js +1 -0
  13. solace_agent_mesh/assets/docs/assets/js/332e10b5.f7629851.js +1 -0
  14. solace_agent_mesh/assets/docs/assets/js/3d406171.5560fdf9.js +1 -0
  15. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.508ae8db.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/442a8107.b5c2532a.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/483cef9a.8d318c2f.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/55f47984.bcd00a86.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/5b4258a4.dff11eca.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/664b740a.ba305a89.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/75384d09.abdf9cf9.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/768e31b0.9abcdc48.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/945fb41e.abf2be91.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/9a09e75d.92de8cf5.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/9eff14a2.d62aad71.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/a3a92b25.1d029b81.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/{aba87c2f.071e2d94.js → aba87c2f.4ddf32f2.js} +1 -1
  28. solace_agent_mesh/assets/docs/assets/js/ae0e903d.abca774a.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/ae4415af.24cdc514.js +1 -0
  30. solace_agent_mesh/assets/docs/assets/js/bac0be12.27ee2c26.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/c2c06897.87cb1f47.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/c835a94d.ce21f0bf.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/cc969b05.feef7dcc.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/cd3d4052.a19e7d78.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/{cee5d587.f5b73ca1.js → cee5d587.f1e1ca86.js} +1 -1
  36. solace_agent_mesh/assets/docs/assets/js/f284c35a.cad4dbf2.js +1 -0
  37. solace_agent_mesh/assets/docs/assets/js/f897a61a.bc634a3e.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/{main.4adc477a.js → main.1de3da6a.js} +2 -2
  39. solace_agent_mesh/assets/docs/assets/js/runtime~main.3188e049.js +1 -0
  40. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +5 -5
  42. 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
  43. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +6 -6
  44. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +6 -6
  45. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +18 -18
  47. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +10 -10
  50. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +7 -7
  54. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +9 -9
  56. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +6 -6
  57. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +7 -7
  58. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +23 -23
  59. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +5 -5
  60. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +5 -5
  61. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +9 -9
  63. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +10 -10
  64. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +8 -8
  65. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +6 -6
  66. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +9 -9
  67. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  68. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +5 -5
  69. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +5 -5
  70. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  71. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +5 -5
  72. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +19 -19
  73. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +7 -7
  74. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +73 -8
  75. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +10 -10
  76. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
  77. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  78. solace_agent_mesh/assets/docs/lunr-index-1757991496554.json +1 -0
  79. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  80. solace_agent_mesh/assets/docs/search-doc-1757991496554.json +1 -0
  81. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  82. solace_agent_mesh/cli/__init__.py +1 -1
  83. solace_agent_mesh/cli/commands/run_cmd.py +4 -7
  84. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-CAX9u8a7.js → authCallback-j1LW-wlq.js} +1 -1
  85. solace_agent_mesh/client/webui/frontend/static/assets/{client-DXU9SPI5.js → client-B9p_nFNA.js} +1 -1
  86. solace_agent_mesh/client/webui/frontend/static/assets/main-B9s_V9tJ.css +1 -0
  87. solace_agent_mesh/client/webui/frontend/static/assets/main-Dq4AJNvn.js +339 -0
  88. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-B0BEKoAR.js → vendor-CS5YMf8a.js} +74 -69
  89. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  90. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  91. solace_agent_mesh/common/utils/pydantic_utils.py +56 -0
  92. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +6 -4
  93. solace_agent_mesh/gateway/base/app.py +58 -120
  94. solace_agent_mesh/gateway/http_sse/app.py +99 -150
  95. solace_agent_mesh/gateway/http_sse/component.py +57 -30
  96. solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +87 -0
  97. solace_agent_mesh/gateway/http_sse/sse_manager.py +44 -23
  98. {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/METADATA +1 -1
  99. {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/RECORD +103 -100
  100. solace_agent_mesh/assets/docs/assets/js/0e682baa.b3bbde9a.js +0 -1
  101. solace_agent_mesh/assets/docs/assets/js/1023fc19.364235d5.js +0 -1
  102. solace_agent_mesh/assets/docs/assets/js/1523c6b4.1b0ec6f9.js +0 -1
  103. solace_agent_mesh/assets/docs/assets/js/1c6e87d2.a8c5ce5a.js +0 -1
  104. solace_agent_mesh/assets/docs/assets/js/2a9cab12.8909df92.js +0 -1
  105. solace_agent_mesh/assets/docs/assets/js/332e10b5.7a103f42.js +0 -1
  106. solace_agent_mesh/assets/docs/assets/js/3d406171.0b9eeed1.js +0 -1
  107. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +0 -1
  108. solace_agent_mesh/assets/docs/assets/js/442a8107.b3159bb2.js +0 -1
  109. solace_agent_mesh/assets/docs/assets/js/483cef9a.03d5dceb.js +0 -1
  110. solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +0 -1
  111. solace_agent_mesh/assets/docs/assets/js/5b4258a4.0d080cd9.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +0 -1
  114. solace_agent_mesh/assets/docs/assets/js/768e31b0.8b51cd70.js +0 -1
  115. solace_agent_mesh/assets/docs/assets/js/945fb41e.c63791d1.js +0 -1
  116. solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +0 -1
  117. solace_agent_mesh/assets/docs/assets/js/9eff14a2.472b0310.js +0 -1
  118. solace_agent_mesh/assets/docs/assets/js/a3a92b25.4b7fa6a2.js +0 -1
  119. solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +0 -1
  120. solace_agent_mesh/assets/docs/assets/js/ae4415af.7a2f0bbf.js +0 -1
  121. solace_agent_mesh/assets/docs/assets/js/bac0be12.f50d9bac.js +0 -1
  122. solace_agent_mesh/assets/docs/assets/js/c2c06897.587b4af5.js +0 -1
  123. solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +0 -1
  124. solace_agent_mesh/assets/docs/assets/js/cc969b05.bd3e0d6c.js +0 -1
  125. solace_agent_mesh/assets/docs/assets/js/cd3d4052.b6535013.js +0 -1
  126. solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +0 -1
  127. solace_agent_mesh/assets/docs/assets/js/f897a61a.0aa29dbb.js +0 -1
  128. solace_agent_mesh/assets/docs/assets/js/runtime~main.cf0229ea.js +0 -1
  129. solace_agent_mesh/assets/docs/lunr-index-1757704179464.json +0 -1
  130. solace_agent_mesh/assets/docs/search-doc-1757704179464.json +0 -1
  131. solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +0 -1
  132. solace_agent_mesh/client/webui/frontend/static/assets/main-DjoMeldu.js +0 -339
  133. /solace_agent_mesh/assets/docs/assets/js/{main.4adc477a.js.LICENSE.txt → main.1de3da6a.js.LICENSE.txt} +0 -0
  134. {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/WHEEL +0 -0
  135. {solace_agent_mesh-1.3.2.dist-info → solace_agent_mesh-1.4.0.dist-info}/entry_points.txt +0 -0
  136. {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-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-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
- 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.
@@ -3,15 +3,16 @@ Custom Solace AI Connector App class for the Web UI Backend.
3
3
  Defines configuration schema and programmatically creates the WebUIBackendComponent.
4
4
  """
5
5
 
6
- from typing import Any, Dict, List
6
+ from typing import Any, Dict, List, Optional
7
7
  import os
8
8
  from alembic import command
9
9
  from alembic.config import Config
10
+ from pydantic import Field, ValidationError
10
11
  from solace_ai_connector.common.log import log
11
12
 
12
13
  from ...gateway.http_sse.component import WebUIBackendComponent
13
14
 
14
- from ...gateway.base.app import BaseGatewayApp
15
+ from ...gateway.base.app import BaseGatewayApp, BaseGatewayAppConfig
15
16
  from ...gateway.base.component import BaseGatewayComponent
16
17
 
17
18
 
@@ -21,6 +22,88 @@ info = {
21
22
  }
22
23
 
23
24
 
25
+ class WebUIBackendAppConfig(BaseGatewayAppConfig):
26
+ """Pydantic model for the Web UI Backend application configuration."""
27
+
28
+ session_secret_key: str = Field(
29
+ ..., description="Secret key for signing web user sessions."
30
+ )
31
+ fastapi_host: str = Field(
32
+ default="127.0.0.1", description="Host address for the embedded FastAPI server."
33
+ )
34
+ fastapi_port: int = Field(
35
+ default=8000, description="Port for the embedded FastAPI server."
36
+ )
37
+ fastapi_https_port: int = Field(
38
+ default=8443, description="Port for the embedded FastAPI server when SSL is enabled."
39
+ )
40
+ cors_allowed_origins: List[str] = Field(
41
+ default=["*"], description="List of allowed origins for CORS requests."
42
+ )
43
+ sse_max_queue_size: int = Field(
44
+ default=200,
45
+ description="Maximum size of the SSE connection queues. Adjust based on expected load.",
46
+ )
47
+ sse_buffer_max_age_seconds: int = Field(
48
+ default=600, # 10 minutes
49
+ description="Maximum age in seconds for a pending event buffer before it is cleaned up.",
50
+ )
51
+ sse_buffer_cleanup_interval_seconds: int = Field(
52
+ default=300, # 5 minutes
53
+ description="How often to run the cleanup task for stale pending event buffers.",
54
+ )
55
+ resolve_artifact_uris_in_gateway: bool = Field(
56
+ default=True,
57
+ description="If true, the gateway will resolve artifact:// URIs found in A2A messages and embed the content as bytes before sending to the UI. If false, URIs are passed through.",
58
+ )
59
+ system_purpose: str = Field(
60
+ default="",
61
+ description="Detailed description of the system's overall purpose, to be optionally used by agents.",
62
+ )
63
+ response_format: str = Field(
64
+ default="",
65
+ description="General guidelines on how agent responses should be structured, to be optionally used by agents.",
66
+ )
67
+ frontend_welcome_message: str = Field(
68
+ default="Hi! How can I help?",
69
+ description="Initial welcome message displayed in the chat.",
70
+ )
71
+ frontend_bot_name: str = Field(
72
+ default="A2A Agent", description="Name displayed for the bot/agent in the UI."
73
+ )
74
+ frontend_collect_feedback: bool = Field(
75
+ default=False, description="Enable/disable the feedback buttons in the UI."
76
+ )
77
+ frontend_auth_login_url: str = Field(
78
+ default="", description="URL for the external login page (if auth is enabled)."
79
+ )
80
+ frontend_use_authorization: bool = Field(
81
+ default=False, description="Tell frontend whether backend expects authorization."
82
+ )
83
+ frontend_redirect_url: str = Field(
84
+ default="", description="Redirect URL for OAuth flows (if auth is enabled)."
85
+ )
86
+ external_auth_callback_uri: str = Field(
87
+ default="", description="Redirect URI for the OIDC application."
88
+ )
89
+ external_auth_service_url: str = Field(
90
+ default="http://localhost:8080",
91
+ description="External authorization service URL for login initiation.",
92
+ )
93
+ external_auth_provider: str = Field(
94
+ default="", description="The external authentication provider."
95
+ )
96
+ ssl_keyfile: str = Field(
97
+ default="", description="The file path to the SSL private key."
98
+ )
99
+ ssl_certfile: str = Field(
100
+ default="", description="The file path to the SSL certificate."
101
+ )
102
+ ssl_keyfile_password: str = Field(
103
+ default="", description="The passphrase for the SSL private key."
104
+ )
105
+
106
+
24
107
  class WebUIBackendApp(BaseGatewayApp):
25
108
  """
26
109
  Custom App class for the A2A Web UI Backend.
@@ -28,154 +111,8 @@ class WebUIBackendApp(BaseGatewayApp):
28
111
  - Defines WebUI-specific configuration parameters.
29
112
  """
30
113
 
31
- SPECIFIC_APP_SCHEMA_PARAMS: List[Dict[str, Any]] = [
32
- {
33
- "name": "session_secret_key",
34
- "required": True,
35
- "type": "string",
36
- "description": "Secret key for signing web user sessions.",
37
- },
38
- {
39
- "name": "fastapi_host",
40
- "required": False,
41
- "type": "string",
42
- "default": "127.0.0.1",
43
- "description": "Host address for the embedded FastAPI server.",
44
- },
45
- {
46
- "name": "fastapi_port",
47
- "required": False,
48
- "type": "integer",
49
- "default": 8000,
50
- "description": "Port for the embedded FastAPI server.",
51
- },
52
- {
53
- "name": "fastapi_https_port",
54
- "required": False,
55
- "type": "integer",
56
- "default": 8443,
57
- "description": "Port for the embedded FastAPI server when SSL is enabled.",
58
- },
59
- {
60
- "name": "cors_allowed_origins",
61
- "required": False,
62
- "type": "list",
63
- "default": ["*"],
64
- "description": "List of allowed origins for CORS requests.",
65
- },
66
- {
67
- "name": "sse_max_queue_size",
68
- "required": False,
69
- "type": "integer",
70
- "default": 200,
71
- "description": "Maximum size of the SSE connection queues. Adjust based on expected load.",
72
- },
73
- {
74
- "name": "resolve_artifact_uris_in_gateway",
75
- "required": False,
76
- "type": "boolean",
77
- "default": True,
78
- "description": "If true, the gateway will resolve artifact:// URIs found in A2A messages and embed the content as bytes before sending to the UI. If false, URIs are passed through.",
79
- },
80
- {
81
- "name": "system_purpose",
82
- "required": False,
83
- "type": "string",
84
- "default": "",
85
- "description": "Detailed description of the system's overall purpose, to be optionally used by agents.",
86
- },
87
- {
88
- "name": "response_format",
89
- "required": False,
90
- "type": "string",
91
- "default": "",
92
- "description": "General guidelines on how agent responses should be structured, to be optionally used by agents.",
93
- },
94
- {
95
- "name": "frontend_welcome_message",
96
- "required": False,
97
- "type": "string",
98
- "default": "Hi! How can I help?",
99
- "description": "Initial welcome message displayed in the chat.",
100
- },
101
- {
102
- "name": "frontend_bot_name",
103
- "required": False,
104
- "type": "string",
105
- "default": "A2A Agent",
106
- "description": "Name displayed for the bot/agent in the UI.",
107
- },
108
- {
109
- "name": "frontend_collect_feedback",
110
- "required": False,
111
- "type": "boolean",
112
- "default": False,
113
- "description": "Enable/disable the feedback buttons in the UI.",
114
- },
115
- {
116
- "name": "frontend_auth_login_url",
117
- "required": False,
118
- "type": "string",
119
- "default": "",
120
- "description": "URL for the external login page (if auth is enabled).",
121
- },
122
- {
123
- "name": "frontend_use_authorization",
124
- "required": False,
125
- "type": "boolean",
126
- "default": False,
127
- "description": "Tell frontend whether backend expects authorization.",
128
- },
129
- {
130
- "name": "frontend_redirect_url",
131
- "required": False,
132
- "type": "string",
133
- "default": "",
134
- "description": "Redirect URL for OAuth flows (if auth is enabled).",
135
- },
136
- {
137
- "name": "external_auth_callback_uri",
138
- "required": False,
139
- "type": "string",
140
- "default": "",
141
- "description": "Redirect URI for the OIDC application.",
142
- },
143
- {
144
- "name": "external_auth_service_url",
145
- "required": False,
146
- "type": "string",
147
- "default": "http://localhost:8080",
148
- "description": "External authorization service URL for login initiation.",
149
- },
150
- {
151
- "name": "external_auth_provider",
152
- "required": False,
153
- "type": "string",
154
- "default": "",
155
- "description": "The external authentication provider.",
156
- },
157
- {
158
- "name": "ssl_keyfile",
159
- "required": False,
160
- "type": "string",
161
- "default": "",
162
- "description": "The file path to the SSL private key.",
163
- },
164
- {
165
- "name": "ssl_certfile",
166
- "required": False,
167
- "type": "string",
168
- "default": "",
169
- "description": "The file path to the SSL certificate.",
170
- },
171
- {
172
- "name": "ssl_keyfile_password",
173
- "required": False,
174
- "type": "string",
175
- "default": "",
176
- "description": "The passphrase for the SSL private key.",
177
- },
178
- ]
114
+ # This is now a placeholder. Validation is handled by the Pydantic model.
115
+ SPECIFIC_APP_SCHEMA_PARAMS: List[Dict[str, Any]] = []
179
116
 
180
117
  def __init__(self, app_info: Dict[str, Any], **kwargs):
181
118
  """
@@ -186,6 +123,18 @@ class WebUIBackendApp(BaseGatewayApp):
186
123
  "%s Initializing WebUIBackendApp...",
187
124
  app_info.get("name", "WebUIBackendApp"),
188
125
  )
126
+
127
+ app_config_dict = app_info.get("app_config", {})
128
+ try:
129
+ # Validate the raw dict, cleaning None values to allow defaults to apply
130
+ app_config = WebUIBackendAppConfig.model_validate_and_clean(
131
+ app_config_dict
132
+ )
133
+ app_info["app_config"] = app_config
134
+ except ValidationError as e:
135
+ log.error("Web UI Gateway configuration validation failed:\n%s", e)
136
+ raise ValueError(f"Invalid Web UI Gateway configuration: {e}") from e
137
+
189
138
  super().__init__(app_info, **kwargs)
190
139
 
191
140
  try: