solace-agent-mesh 1.4.12__py3-none-any.whl → 1.5.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 (181) hide show
  1. solace_agent_mesh/agent/adk/adk_llm.txt +3 -4
  2. solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
  3. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +1 -1
  4. solace_agent_mesh/agent/adk/callbacks.py +51 -2
  5. solace_agent_mesh/agent/adk/models/lite_llm.py +1 -0
  6. solace_agent_mesh/agent/adk/models/models_llm.txt +1 -2
  7. solace_agent_mesh/agent/agent_llm.txt +1 -1
  8. solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
  9. solace_agent_mesh/agent/protocol/event_handlers.py +2 -13
  10. solace_agent_mesh/agent/protocol/protocol_llm.txt +15 -2
  11. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
  12. solace_agent_mesh/agent/sac/component.py +51 -21
  13. solace_agent_mesh/agent/sac/sac_llm.txt +15 -1
  14. solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
  15. solace_agent_mesh/agent/sac/task_execution_context.py +73 -0
  16. solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
  17. solace_agent_mesh/agent/tools/tools_llm.txt +148 -154
  18. solace_agent_mesh/agent/tools/tools_llm_detail.txt +274 -0
  19. solace_agent_mesh/agent/utils/utils_llm.txt +1 -1
  20. solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
  21. solace_agent_mesh/assets/docs/404.html +3 -3
  22. solace_agent_mesh/assets/docs/assets/js/483cef9a.bf9398af.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/{main.f67fc9f4.js → main.0c149855.js} +2 -2
  24. solace_agent_mesh/assets/docs/assets/js/{runtime~main.40527046.js → runtime~main.c66557e4.js} +1 -1
  25. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +3 -3
  26. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/rbac-setup-guilde/index.html +3 -3
  27. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +8 -4
  28. 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
  29. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
  38. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
  39. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +3 -3
  40. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +3 -3
  41. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/litellm_models/index.html +3 -3
  42. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +3 -3
  43. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +3 -3
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
  47. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
  50. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
  57. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  58. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +3 -3
  59. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +3 -3
  60. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
  61. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +3 -3
  62. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
  63. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
  64. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +3 -3
  65. solace_agent_mesh/assets/docs/lunr-index-1760032255022.json +1 -0
  66. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  67. solace_agent_mesh/assets/docs/search-doc-1760032255022.json +1 -0
  68. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  69. solace_agent_mesh/cli/__init__.py +1 -1
  70. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-j1LW-wlq.js → authCallback-DwrxZE0E.js} +1 -1
  71. solace_agent_mesh/client/webui/frontend/static/assets/{client-B9p_nFNA.js → client-DarGQzyw.js} +1 -1
  72. solace_agent_mesh/client/webui/frontend/static/assets/main-CZbpmwfA.css +1 -0
  73. solace_agent_mesh/client/webui/frontend/static/assets/main-C__uuUkB.js +339 -0
  74. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CS5YMf8a.js → vendor-BKIeiHj_.js} +80 -70
  75. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  76. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  77. solace_agent_mesh/common/a2a/a2a_llm.txt +1 -1
  78. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
  79. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +1 -1
  80. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
  81. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +23 -0
  82. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +93 -15
  83. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +23 -0
  84. solace_agent_mesh/common/common_llm.txt +24 -39
  85. solace_agent_mesh/common/common_llm_detail.txt +2562 -0
  86. solace_agent_mesh/common/data_parts.py +9 -1
  87. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
  88. solace_agent_mesh/common/sac/sac_llm.txt +1 -1
  89. solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
  90. solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
  91. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
  92. solace_agent_mesh/common/services/services_llm.txt +57 -6
  93. solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
  94. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +1 -1
  95. solace_agent_mesh/common/utils/utils_llm.txt +75 -87
  96. solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
  97. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
  98. solace_agent_mesh/gateway/base/app.py +1 -1
  99. solace_agent_mesh/gateway/base/base_llm.txt +1 -1
  100. solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
  101. solace_agent_mesh/gateway/gateway_llm.txt +242 -235
  102. solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
  103. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +295 -0
  104. solace_agent_mesh/gateway/http_sse/alembic/env.py +10 -1
  105. solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
  106. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +155 -0
  107. solace_agent_mesh/gateway/http_sse/alembic.ini +1 -1
  108. solace_agent_mesh/gateway/http_sse/app.py +148 -2
  109. solace_agent_mesh/gateway/http_sse/component.py +368 -60
  110. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +46 -6
  111. solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +108 -0
  112. solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +1 -1
  113. solace_agent_mesh/gateway/http_sse/dependencies.py +116 -26
  114. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +172 -172
  115. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
  116. solace_agent_mesh/gateway/http_sse/main.py +146 -41
  117. solace_agent_mesh/gateway/http_sse/repository/__init__.py +3 -12
  118. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +103 -0
  119. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +5 -3
  120. solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
  121. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +263 -0
  122. solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
  123. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -16
  124. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +25 -0
  125. solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
  126. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +81 -0
  127. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +73 -18
  128. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -5
  129. solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
  130. solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
  131. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +266 -0
  132. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +3 -3
  133. solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
  134. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +32 -0
  135. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +340 -0
  136. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +4 -53
  137. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +173 -0
  138. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1 -1
  139. solace_agent_mesh/gateway/http_sse/routers/config.py +26 -4
  140. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +346 -0
  141. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +3 -3
  142. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +83 -0
  143. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +2 -10
  144. solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
  145. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +5 -3
  146. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +107 -0
  147. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +1 -15
  148. solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
  149. solace_agent_mesh/gateway/http_sse/routers/feedback.py +37 -0
  150. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +255 -204
  151. solace_agent_mesh/gateway/http_sse/routers/sessions.py +220 -40
  152. solace_agent_mesh/gateway/http_sse/routers/tasks.py +168 -42
  153. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +272 -0
  154. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +241 -0
  155. solace_agent_mesh/gateway/http_sse/services/people_service.py +0 -80
  156. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +177 -13
  157. solace_agent_mesh/gateway/http_sse/services/session_service.py +151 -84
  158. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +317 -0
  159. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +25 -14
  160. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +285 -0
  161. solace_agent_mesh/gateway/http_sse/shared/types.py +7 -0
  162. solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
  163. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +32 -0
  164. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
  165. solace_agent_mesh/solace_agent_mesh_llm.txt +1 -1
  166. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
  167. {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.0.dist-info}/METADATA +1 -1
  168. {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.0.dist-info}/RECORD +172 -124
  169. solace_agent_mesh/agent/adk/invocation_monitor.py +0 -295
  170. solace_agent_mesh/assets/docs/assets/js/483cef9a.4736f2d8.js +0 -1
  171. solace_agent_mesh/assets/docs/lunr-index-1759936913198.json +0 -1
  172. solace_agent_mesh/assets/docs/search-doc-1759936913198.json +0 -1
  173. solace_agent_mesh/client/webui/frontend/static/assets/main-ChRwcV89.css +0 -1
  174. solace_agent_mesh/client/webui/frontend/static/assets/main-DnnE01OM.js +0 -339
  175. solace_agent_mesh/gateway/http_sse/repository/entities/message.py +0 -41
  176. solace_agent_mesh/gateway/http_sse/repository/message_repository.py +0 -84
  177. solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +0 -45
  178. /solace_agent_mesh/assets/docs/assets/js/{main.f67fc9f4.js.LICENSE.txt → main.0c149855.js.LICENSE.txt} +0 -0
  179. {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.0.dist-info}/WHEEL +0 -0
  180. {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.0.dist-info}/entry_points.txt +0 -0
  181. {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -26,6 +26,10 @@ from ...gateway.http_sse.session_manager import SessionManager
26
26
  from ...gateway.http_sse.sse_manager import SSEManager
27
27
  from .sse_event_buffer import SSEEventBuffer
28
28
  from .components import VisualizationForwarderComponent
29
+ from .components.task_logger_forwarder import TaskLoggerForwarderComponent
30
+ from .services.feedback_service import FeedbackService
31
+ from .services.task_logger_service import TaskLoggerService
32
+ from . import dependencies
29
33
 
30
34
  try:
31
35
  from google.adk.artifacts import BaseArtifactService
@@ -153,6 +157,23 @@ class WebUIBackendComponent(BaseGatewayComponent):
153
157
  else:
154
158
  # Memory storage or no explicit configuration - no persistence service needed
155
159
  self.database_url = None
160
+
161
+ # Validate that features requiring database persistence are not enabled
162
+ task_logging_config = self.get_config("task_logging", {})
163
+ if task_logging_config.get("enabled", False):
164
+ raise ValueError(
165
+ f"{self.log_identifier} Task logging requires SQL session storage. "
166
+ "Either set session_service.type='sql' with a valid database_url, "
167
+ "or disable task_logging.enabled."
168
+ )
169
+
170
+ feedback_config = self.get_config("feedback_publishing", {})
171
+ if feedback_config.get("enabled", False):
172
+ log.warning(
173
+ "%s Feedback publishing is enabled but database persistence is not configured. "
174
+ "Feedback will only be published to the broker, not stored locally.",
175
+ self.log_identifier
176
+ )
156
177
 
157
178
  component_config = self.get_config("component_config", {})
158
179
  app_config = component_config.get("app_config", {})
@@ -170,12 +191,18 @@ class WebUIBackendComponent(BaseGatewayComponent):
170
191
  self._visualization_internal_app: SACApp | None = None
171
192
  self._visualization_broker_input: BrokerInput | None = None
172
193
  self._visualization_message_queue: queue.Queue = queue.Queue(maxsize=200)
194
+ self._task_logger_queue: queue.Queue = queue.Queue(maxsize=200)
173
195
  self._active_visualization_streams: dict[str, dict[str, Any]] = {}
174
196
  self._visualization_locks: dict[asyncio.AbstractEventLoop, asyncio.Lock] = {}
175
197
  self._visualization_locks_lock = threading.Lock()
176
198
  self._global_visualization_subscriptions: dict[str, int] = {}
177
199
  self._visualization_processor_task: asyncio.Task | None = None
178
200
 
201
+ self._task_logger_internal_app: SACApp | None = None
202
+ self._task_logger_broker_input: BrokerInput | None = None
203
+ self._task_logger_processor_task: asyncio.Task | None = None
204
+ self.task_logger_service: TaskLoggerService | None = None
205
+
179
206
  # Initialize SAM Events service for system events
180
207
  from ...common.sam_events import SamEventService
181
208
 
@@ -185,14 +212,75 @@ class WebUIBackendComponent(BaseGatewayComponent):
185
212
  publish_func=self.publish_a2a,
186
213
  )
187
214
 
215
+ # Initialize data retention service and timer
216
+ self.data_retention_service = None
217
+ self._data_retention_timer_id = None
218
+ data_retention_config = self.get_config("data_retention", {})
219
+ if data_retention_config.get("enabled", True):
220
+ log.info("%s Data retention is enabled. Initializing service and timer...", self.log_identifier)
221
+
222
+ # Import and initialize the DataRetentionService
223
+ from .services.data_retention_service import DataRetentionService
224
+
225
+ session_factory = None
226
+ if self.database_url:
227
+ # SessionLocal will be initialized later in setup_dependencies
228
+ # We'll pass a lambda that returns SessionLocal when called
229
+ session_factory = lambda: dependencies.SessionLocal() if dependencies.SessionLocal else None
230
+
231
+ self.data_retention_service = DataRetentionService(
232
+ session_factory=session_factory,
233
+ config=data_retention_config
234
+ )
235
+
236
+ # Create and start the cleanup timer
237
+ cleanup_interval_hours = data_retention_config.get("cleanup_interval_hours", 24)
238
+ cleanup_interval_ms = cleanup_interval_hours * 60 * 60 * 1000
239
+ self._data_retention_timer_id = f"data_retention_cleanup_{self.gateway_id}"
240
+
241
+ self.add_timer(
242
+ delay_ms=cleanup_interval_ms,
243
+ timer_id=self._data_retention_timer_id,
244
+ interval_ms=cleanup_interval_ms,
245
+ )
246
+ log.info(
247
+ "%s Data retention timer created with ID '%s' and interval %d hours.",
248
+ self.log_identifier,
249
+ self._data_retention_timer_id,
250
+ cleanup_interval_hours,
251
+ )
252
+ else:
253
+ log.info("%s Data retention is disabled via configuration.", self.log_identifier)
254
+
188
255
  log.info("%s Web UI Backend Component initialized.", self.log_identifier)
189
256
 
190
257
  def process_event(self, event: Event):
191
258
  if event.event_type == EventType.TIMER:
192
- if event.data.get("timer_id") == self._sse_cleanup_timer_id:
259
+ timer_id = event.data.get("timer_id")
260
+
261
+ if timer_id == self._sse_cleanup_timer_id:
193
262
  log.debug("%s SSE buffer cleanup timer triggered.", self.log_identifier)
194
263
  self.sse_event_buffer.cleanup_stale_buffers()
195
264
  return
265
+
266
+ if timer_id == self._data_retention_timer_id:
267
+ log.debug("%s Data retention cleanup timer triggered.", self.log_identifier)
268
+ if self.data_retention_service:
269
+ try:
270
+ self.data_retention_service.cleanup_old_data()
271
+ except Exception as e:
272
+ log.error(
273
+ "%s Error during data retention cleanup: %s",
274
+ self.log_identifier,
275
+ e,
276
+ exc_info=True,
277
+ )
278
+ else:
279
+ log.warning(
280
+ "%s Data retention timer fired but service is not initialized.",
281
+ self.log_identifier,
282
+ )
283
+ return
196
284
 
197
285
  super().process_event(event)
198
286
 
@@ -270,9 +358,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
270
358
  forwarder_cfg = {
271
359
  "component_class": VisualizationForwarderComponent,
272
360
  "component_name": f"{self.gateway_id}_viz_forwarder",
273
- "component_config": {
274
- "target_queue_ref": self._visualization_message_queue
275
- },
361
+ "component_config": {"target_queue_ref": self._visualization_message_queue},
276
362
  }
277
363
 
278
364
  flow_config = {
@@ -355,6 +441,121 @@ class WebUIBackendComponent(BaseGatewayComponent):
355
441
  self._visualization_broker_input = None
356
442
  raise
357
443
 
444
+ def _ensure_task_logger_flow_is_running(self) -> None:
445
+ """
446
+ Ensures the internal SAC flow for A2A task logging is created and running.
447
+ """
448
+ log_id_prefix = f"{self.log_identifier}[EnsureTaskLogFlow]"
449
+ if self._task_logger_internal_app is not None:
450
+ log.debug("%s Task logger flow already running.", log_id_prefix)
451
+ return
452
+
453
+ log.info("%s Initializing internal A2A task logger flow...", log_id_prefix)
454
+ try:
455
+ main_app = self.get_app()
456
+ if not main_app or not main_app.connector:
457
+ raise RuntimeError(
458
+ "Main app or connector not available for internal flow creation."
459
+ )
460
+
461
+ main_broker_config = main_app.app_info.get("broker", {})
462
+ if not main_broker_config:
463
+ raise ValueError("Main app broker configuration is missing.")
464
+
465
+ # The task logger needs to see ALL messages.
466
+ subscriptions = [{"topic": f"{self.namespace.rstrip('/')}/a2a/>"}]
467
+
468
+ broker_input_cfg = {
469
+ "component_module": "broker_input",
470
+ "component_name": f"{self.gateway_id}_task_log_broker_input",
471
+ "broker_queue_name": f"{self.namespace.strip('/')}/q/gdk/task_log/{self.gateway_id}/{uuid.uuid4().hex}",
472
+ "create_queue_on_start": True,
473
+ "component_config": {
474
+ "broker_url": main_broker_config.get("broker_url"),
475
+ "broker_username": main_broker_config.get("broker_username"),
476
+ "broker_password": main_broker_config.get("broker_password"),
477
+ "broker_vpn": main_broker_config.get("broker_vpn"),
478
+ "trust_store_path": main_broker_config.get("trust_store_path"),
479
+ "dev_mode": main_broker_config.get("dev_mode"),
480
+ "broker_subscriptions": subscriptions,
481
+ "reconnection_strategy": main_broker_config.get(
482
+ "reconnection_strategy"
483
+ ),
484
+ "retry_interval": main_broker_config.get("retry_interval"),
485
+ "retry_count": main_broker_config.get("retry_count"),
486
+ "temporary_queue": True,
487
+ },
488
+ }
489
+
490
+ forwarder_cfg = {
491
+ "component_class": TaskLoggerForwarderComponent,
492
+ "component_name": f"{self.gateway_id}_task_log_forwarder",
493
+ "component_config": {"target_queue_ref": self._task_logger_queue},
494
+ }
495
+
496
+ flow_config = {
497
+ "name": f"{self.gateway_id}_task_log_flow",
498
+ "components": [broker_input_cfg, forwarder_cfg],
499
+ }
500
+
501
+ internal_app_broker_config = main_broker_config.copy()
502
+ internal_app_broker_config["input_enabled"] = True
503
+ internal_app_broker_config["output_enabled"] = False
504
+
505
+ app_config_for_internal_flow = {
506
+ "name": f"{self.gateway_id}_task_log_internal_app",
507
+ "flows": [flow_config],
508
+ "broker": internal_app_broker_config,
509
+ "app_config": {},
510
+ }
511
+
512
+ self._task_logger_internal_app = main_app.connector.create_internal_app(
513
+ app_name=app_config_for_internal_flow["name"],
514
+ flows=app_config_for_internal_flow["flows"],
515
+ )
516
+
517
+ if (
518
+ not self._task_logger_internal_app
519
+ or not self._task_logger_internal_app.flows
520
+ ):
521
+ raise RuntimeError("Internal task logger app/flow creation failed.")
522
+
523
+ self._task_logger_internal_app.run()
524
+ log.info("%s Internal task logger app started.", log_id_prefix)
525
+
526
+ flow_instance = self._task_logger_internal_app.flows[0]
527
+ if flow_instance.component_groups and flow_instance.component_groups[0]:
528
+ self._task_logger_broker_input = flow_instance.component_groups[0][0]
529
+ if not isinstance(self._task_logger_broker_input, BrokerInput):
530
+ raise RuntimeError(
531
+ "Task logger flow setup error: BrokerInput not found."
532
+ )
533
+ log.info(
534
+ "%s Obtained reference to internal task logger BrokerInput component.",
535
+ log_id_prefix,
536
+ )
537
+ else:
538
+ raise RuntimeError(
539
+ "Task logger flow setup error: BrokerInput instance not accessible."
540
+ )
541
+
542
+ except Exception as e:
543
+ log.exception(
544
+ "%s Failed to ensure task logger flow is running: %s", log_id_prefix, e
545
+ )
546
+ if self._task_logger_internal_app:
547
+ try:
548
+ self._task_logger_internal_app.cleanup()
549
+ except Exception as cleanup_err:
550
+ log.error(
551
+ "%s Error during cleanup after task logger flow init failure: %s",
552
+ log_id_prefix,
553
+ cleanup_err,
554
+ )
555
+ self._task_logger_internal_app = None
556
+ self._task_logger_broker_input = None
557
+ raise
558
+
358
559
  def _resolve_session_config(self) -> dict:
359
560
  """
360
561
  Resolve session service configuration with backward compatibility.
@@ -390,7 +591,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
390
591
  """
391
592
  Asynchronously consumes messages from the _visualization_message_queue,
392
593
  filters them, and forwards them to relevant SSE connections.
393
- Placeholder for Phase 2: Just logs messages.
394
594
  """
395
595
  log_id_prefix = f"{self.log_identifier}[VizMsgProcessor]"
396
596
  log.info("%s Starting visualization message processor loop...", log_id_prefix)
@@ -417,7 +617,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
417
617
  max_size = self._visualization_message_queue.maxsize
418
618
  if max_size > 0 and (current_size / max_size) > 0.90:
419
619
  log.warning(
420
- "%s Visualization queue is over 90%% full. Current size: %d/%d",
620
+ "%s Visualization message queue is over 90%% full. Current size: %d/%d",
421
621
  log_id_prefix,
422
622
  current_size,
423
623
  max_size,
@@ -594,6 +794,59 @@ class WebUIBackendComponent(BaseGatewayComponent):
594
794
 
595
795
  log.info("%s Visualization message processor loop finished.", log_id_prefix)
596
796
 
797
+ async def _task_logger_loop(self) -> None:
798
+ """
799
+ Asynchronously consumes messages from the _task_logger_queue and
800
+ passes them to the TaskLoggerService for persistence.
801
+ """
802
+ log_id_prefix = f"{self.log_identifier}[TaskLoggerLoop]"
803
+ log.info("%s Starting task logger loop...", log_id_prefix)
804
+ loop = asyncio.get_running_loop()
805
+
806
+ while not self.stop_signal.is_set():
807
+ msg_data = None
808
+ try:
809
+ msg_data = await loop.run_in_executor(
810
+ None,
811
+ self._task_logger_queue.get,
812
+ True,
813
+ 1.0,
814
+ )
815
+
816
+ if msg_data is None:
817
+ log.info(
818
+ "%s Received shutdown signal for task logger loop.",
819
+ log_id_prefix,
820
+ )
821
+ break
822
+
823
+ if self.task_logger_service:
824
+ self.task_logger_service.log_event(msg_data)
825
+ else:
826
+ log.warning(
827
+ "%s Task logger service not available. Cannot log event.",
828
+ log_id_prefix,
829
+ )
830
+
831
+ self._task_logger_queue.task_done()
832
+
833
+ except queue.Empty:
834
+ continue
835
+ except asyncio.CancelledError:
836
+ log.info("%s Task logger loop cancelled.", log_id_prefix)
837
+ break
838
+ except Exception as e:
839
+ log.exception(
840
+ "%s Error in task logger loop: %s",
841
+ log_id_prefix,
842
+ e,
843
+ )
844
+ if msg_data and self._task_logger_queue:
845
+ self._task_logger_queue.task_done()
846
+ await asyncio.sleep(1)
847
+
848
+ log.info("%s Task logger loop finished.", log_id_prefix)
849
+
597
850
  async def _add_visualization_subscription(
598
851
  self, topic_str: str, stream_id: str
599
852
  ) -> bool:
@@ -919,6 +1172,18 @@ class WebUIBackendComponent(BaseGatewayComponent):
919
1172
 
920
1173
  setup_dependencies(self, self.database_url)
921
1174
 
1175
+ # Instantiate services that depend on the database session factory.
1176
+ # This must be done *after* setup_dependencies has run.
1177
+ session_factory = dependencies.SessionLocal if self.database_url else None
1178
+ task_logging_config = self.get_config("task_logging", {})
1179
+ self.task_logger_service = TaskLoggerService(
1180
+ session_factory=session_factory, config=task_logging_config
1181
+ )
1182
+ log.info(
1183
+ "%s Services dependent on database session factory have been initialized.",
1184
+ self.log_identifier,
1185
+ )
1186
+
922
1187
  port = (
923
1188
  self.fastapi_https_port
924
1189
  if self.ssl_keyfile and self.ssl_certfile
@@ -976,6 +1241,35 @@ class WebUIBackendComponent(BaseGatewayComponent):
976
1241
  "%s Visualization message processor task already running.",
977
1242
  self.log_identifier,
978
1243
  )
1244
+
1245
+ task_logging_config = self.get_config("task_logging", {})
1246
+ if task_logging_config.get("enabled", False):
1247
+ log.info(
1248
+ "%s Task logging is enabled. Ensuring flow is running...",
1249
+ self.log_identifier,
1250
+ )
1251
+ self._ensure_task_logger_flow_is_running()
1252
+
1253
+ if (
1254
+ self._task_logger_processor_task is None
1255
+ or self._task_logger_processor_task.done()
1256
+ ):
1257
+ log.info(
1258
+ "%s Starting task logger processor task.",
1259
+ self.log_identifier,
1260
+ )
1261
+ self._task_logger_processor_task = (
1262
+ self.fastapi_event_loop.create_task(
1263
+ self._task_logger_loop()
1264
+ )
1265
+ )
1266
+ else:
1267
+ log.info(
1268
+ "%s Task logger processor task already running.",
1269
+ self.log_identifier,
1270
+ )
1271
+ else:
1272
+ log.info("%s Task logging is disabled.", self.log_identifier)
979
1273
  else:
980
1274
  log.error(
981
1275
  "%s FastAPI event loop not captured. Cannot start visualization processor.",
@@ -990,6 +1284,27 @@ class WebUIBackendComponent(BaseGatewayComponent):
990
1284
  )
991
1285
  self.stop_signal.set()
992
1286
 
1287
+ try:
1288
+ from solace_agent_mesh_enterprise.init_enterprise import start_enterprise_background_tasks
1289
+ log.info("%s Starting enterprise background tasks...", self.log_identifier)
1290
+ await start_enterprise_background_tasks(self)
1291
+ log.info("%s Enterprise background tasks started successfully", self.log_identifier)
1292
+ except ImportError:
1293
+ log.debug("%s Enterprise package not available - skipping background tasks", self.log_identifier)
1294
+ except RuntimeError as enterprise_err:
1295
+ log.warning(
1296
+ "%s Enterprise background tasks disabled: %s - Community features will continue normally",
1297
+ self.log_identifier,
1298
+ enterprise_err
1299
+ )
1300
+ except Exception as enterprise_err:
1301
+ log.error(
1302
+ "%s Failed to start enterprise background tasks: %s - Community features will continue normally",
1303
+ self.log_identifier,
1304
+ enterprise_err,
1305
+ exc_info=True
1306
+ )
1307
+
993
1308
  @self.fastapi_app.on_event("shutdown")
994
1309
  async def shutdown_event():
995
1310
  log.info(
@@ -997,6 +1312,21 @@ class WebUIBackendComponent(BaseGatewayComponent):
997
1312
  self.log_identifier,
998
1313
  )
999
1314
 
1315
+ try:
1316
+ from solace_agent_mesh_enterprise.init_enterprise import stop_enterprise_background_tasks
1317
+ log.info("%s Stopping enterprise background tasks...", self.log_identifier)
1318
+ await stop_enterprise_background_tasks()
1319
+ log.info("%s Enterprise background tasks stopped", self.log_identifier)
1320
+ except ImportError:
1321
+ log.debug("%s Enterprise package not available - no background tasks to stop", self.log_identifier)
1322
+ except Exception as enterprise_err:
1323
+ log.error(
1324
+ "%s Failed to stop enterprise background tasks: %s",
1325
+ self.log_identifier,
1326
+ enterprise_err,
1327
+ exc_info=True
1328
+ )
1329
+
1000
1330
  self.fastapi_thread = threading.Thread(
1001
1331
  target=self.uvicorn_server.run, daemon=True, name="FastAPI_Thread"
1002
1332
  )
@@ -1059,10 +1389,23 @@ class WebUIBackendComponent(BaseGatewayComponent):
1059
1389
  def cleanup(self):
1060
1390
  """Gracefully shuts down the component and the FastAPI server."""
1061
1391
  log.info("%s Cleaning up Web UI Backend Component...", self.log_identifier)
1392
+
1393
+ # Cancel timers
1062
1394
  self.cancel_timer(self._sse_cleanup_timer_id)
1395
+ if self._data_retention_timer_id:
1396
+ self.cancel_timer(self._data_retention_timer_id)
1397
+ log.info("%s Cancelled data retention cleanup timer.", self.log_identifier)
1398
+
1399
+ # Clean up data retention service
1400
+ if self.data_retention_service:
1401
+ self.data_retention_service = None
1402
+ log.info("%s Data retention service cleaned up.", self.log_identifier)
1403
+
1063
1404
  log.info("%s Cleaning up visualization resources...", self.log_identifier)
1064
1405
  if self._visualization_message_queue:
1065
1406
  self._visualization_message_queue.put(None)
1407
+ if self._task_logger_queue:
1408
+ self._task_logger_queue.put(None)
1066
1409
 
1067
1410
  if (
1068
1411
  self._visualization_processor_task
@@ -1073,6 +1416,10 @@ class WebUIBackendComponent(BaseGatewayComponent):
1073
1416
  )
1074
1417
  self._visualization_processor_task.cancel()
1075
1418
 
1419
+ if self._task_logger_processor_task and not self._task_logger_processor_task.done():
1420
+ log.info("%s Cancelling task logger processor task...", self.log_identifier)
1421
+ self._task_logger_processor_task.cancel()
1422
+
1076
1423
  if self._visualization_internal_app:
1077
1424
  log.info(
1078
1425
  "%s Cleaning up internal visualization app...", self.log_identifier
@@ -1086,6 +1433,17 @@ class WebUIBackendComponent(BaseGatewayComponent):
1086
1433
  e,
1087
1434
  )
1088
1435
 
1436
+ if self._task_logger_internal_app:
1437
+ log.info("%s Cleaning up internal task logger app...", self.log_identifier)
1438
+ try:
1439
+ self._task_logger_internal_app.cleanup()
1440
+ except Exception as e:
1441
+ log.error(
1442
+ "%s Error cleaning up internal task logger app: %s",
1443
+ self.log_identifier,
1444
+ e,
1445
+ )
1446
+
1089
1447
  self._active_visualization_streams.clear()
1090
1448
  self._global_visualization_subscriptions.clear()
1091
1449
  self._cleanup_visualization_locks()
@@ -1375,6 +1733,10 @@ class WebUIBackendComponent(BaseGatewayComponent):
1375
1733
  def get_session_manager(self) -> SessionManager:
1376
1734
  return self.session_manager
1377
1735
 
1736
+ def get_task_logger_service(self) -> TaskLoggerService | None:
1737
+ """Returns the shared TaskLoggerService instance."""
1738
+ return self.task_logger_service
1739
+
1378
1740
  def get_namespace(self) -> str:
1379
1741
  return self.namespace
1380
1742
 
@@ -1672,60 +2034,6 @@ class WebUIBackendComponent(BaseGatewayComponent):
1672
2034
  a2a_task_id,
1673
2035
  )
1674
2036
 
1675
- # Store final agent response in persistence layer if available
1676
- if hasattr(self, "database_url") and self.database_url:
1677
- try:
1678
- session_id = external_request_context.get("a2a_session_id")
1679
- user_id = external_request_context.get("user_id_for_a2a")
1680
- agent_name = external_request_context.get(
1681
- "target_agent_name", "agent"
1682
- )
1683
-
1684
- message_text = ""
1685
- if task_data.status and task_data.status.message:
1686
- parts = a2a.get_parts_from_message(task_data.status.message)
1687
- for part in parts:
1688
- if hasattr(part, "text") and part.text:
1689
- if message_text:
1690
- message_text += "\n"
1691
- message_text += part.text
1692
-
1693
- if message_text and session_id and user_id:
1694
- from .dependencies import SessionLocal, get_session_business_service
1695
- from ...gateway.http_sse.shared.enums import SenderType
1696
-
1697
- # For background processing, create simple session wrapper
1698
- if SessionLocal:
1699
- db = SessionLocal()
1700
- try:
1701
- session_service = get_session_business_service()
1702
- session_service.add_message_to_session(
1703
- db=db,
1704
- session_id=session_id,
1705
- user_id=user_id,
1706
- message=message_text,
1707
- sender_type=SenderType.AGENT,
1708
- sender_name=agent_name,
1709
- agent_id=agent_name,
1710
- )
1711
- db.commit()
1712
- except Exception:
1713
- db.rollback()
1714
- raise
1715
- finally:
1716
- db.close()
1717
- log.info(
1718
- "%s Final agent response stored in session %s",
1719
- log_id_prefix,
1720
- session_id,
1721
- )
1722
- except Exception as storage_error:
1723
- log.warning(
1724
- "%s Failed to store final agent response: %s",
1725
- log_id_prefix,
1726
- storage_error,
1727
- )
1728
-
1729
2037
  except Exception as e:
1730
2038
  log.exception(
1731
2039
  "%s Failed to send final_response via SSE for A2A Task ID %s: %s",
@@ -1,11 +1,13 @@
1
1
  # DEVELOPER GUIDE: components
2
2
 
3
3
  ## Quick Summary
4
- This directory contains components for the HTTP SSE (Server-Sent Events) gateway, designed to work within the Solace AI Connector (SAC) framework. The primary component forwards messages received from the Solace broker to an internal queue, enabling real-time visualization in a web-based user interface.
4
+ This directory contains SAC (Solace AI Connector) components for the HTTP SSE (Server-Sent Events) gateway. These components forward messages from Solace broker inputs to internal Python queues, enabling real-time visualization and task logging in web-based user interfaces.
5
5
 
6
6
  ## Files Overview
7
- - `__init__.py` - Makes the `VisualizationForwarderComponent` class directly importable from the `components` package
8
- - `visualization_forwarder_component.py` - Defines a SAC component that forwards messages from a broker input to a Python `queue.Queue` for visualization
7
+ - `__init__.py` - Makes the `VisualizationForwarderComponent` class directly importable from the components package
8
+ - `components_llm.txt` - Developer guide documentation for this directory
9
+ - `task_logger_forwarder.py` - SAC component that forwards messages to a task logging queue
10
+ - `visualization_forwarder_component.py` - SAC component that forwards messages to a visualization queue
9
11
 
10
12
  ## Developer API Reference
11
13
 
@@ -16,12 +18,50 @@ This directory contains components for the HTTP SSE (Server-Sent Events) gateway
16
18
  **Exports:**
17
19
  - `VisualizationForwarderComponent` - The main component class for forwarding messages to a visualization queue
18
20
 
21
+ ### task_logger_forwarder.py
22
+ **Purpose:** A SAC component that forwards messages from a BrokerInput to a target queue for task logging
23
+ **Import:** `from solace_agent_mesh.gateway.http_sse.components.task_logger_forwarder import TaskLoggerForwarderComponent`
24
+
25
+ **Classes:**
26
+ - `TaskLoggerForwarderComponent(**kwargs: Any)` - A component that forwards messages to a task logging queue, initialized with configuration parameters including `target_queue_ref`
27
+ - `invoke(message: SolaceMessage, data: Dict[str, Any]) -> None` - Core method called by SAC framework for each incoming message; formats data and places it onto the target queue
28
+ - `target_queue: queue.Queue` - The queue instance where messages are forwarded
29
+
30
+ **Constants/Variables:**
31
+ - `info: Dict` - Metadata dictionary required by SAC framework describing component configuration, input schema, and purpose
32
+
33
+ **Usage Examples:**
34
+ ```python
35
+ import queue
36
+ from solace_agent_mesh.gateway.http_sse.components.task_logger_forwarder import TaskLoggerForwarderComponent
37
+ from solace_ai_connector.common.message import Message as SolaceMessage
38
+
39
+ # 1. Create a target queue for task logging
40
+ task_logging_queue = queue.Queue()
41
+
42
+ # 2. Instantiate the component with target queue reference
43
+ task_forwarder = TaskLoggerForwarderComponent(
44
+ name="task_logger",
45
+ target_queue_ref=task_logging_queue
46
+ )
47
+
48
+ # 3. The invoke method is called automatically by SAC framework
49
+ # when messages arrive from connected BrokerInput component
50
+
51
+ # 4. Consume forwarded messages from the queue
52
+ if not task_logging_queue.empty():
53
+ forwarded_data = task_logging_queue.get()
54
+ print(f"Task Topic: {forwarded_data['topic']}")
55
+ print(f"Task Payload: {forwarded_data['payload']}")
56
+ print(f"User Properties: {forwarded_data['user_properties']}")
57
+ ```
58
+
19
59
  ### visualization_forwarder_component.py
20
- **Purpose:** A Solace AI Connector (SAC) component that listens for messages from a `BrokerInput` and forwards them to a specified Python `queue.Queue` for real-time visualization
60
+ **Purpose:** A SAC component that forwards messages from a BrokerInput to a target queue for visualization
21
61
  **Import:** `from solace_agent_mesh.gateway.http_sse.components.visualization_forwarder_component import VisualizationForwarderComponent`
22
62
 
23
63
  **Classes:**
24
- - `VisualizationForwarderComponent(**kwargs: Any)` - A component that forwards messages to a target queue, initialized with configuration parameters including `target_queue_ref`
64
+ - `VisualizationForwarderComponent(**kwargs: Any)` - A component that forwards messages to a visualization queue, initialized with configuration parameters including `target_queue_ref`
25
65
  - `invoke(message: SolaceMessage, data: Dict[str, Any]) -> None` - Core method called by SAC framework for each incoming message; formats data and places it onto the target queue
26
66
  - `target_queue: queue.Queue` - The queue instance where messages are forwarded
27
67
 
@@ -62,4 +102,4 @@ if not visualization_queue.empty():
62
102
  # }
63
103
  ```
64
104
 
65
- # content_hash: cee7f7b4ea7e87ab3f94f7e24d463f22fa045e50d54991c61b84fe95e8a7f77d
105
+ # content_hash: 6add2167cb9fdee9ed3e46635d6b4e9391cf062dde9e14dba250a3f33c6c4f73