solace-agent-mesh 1.4.5__py3-none-any.whl → 1.4.7__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 (101) hide show
  1. solace_agent_mesh/agent/adk/runner.py +24 -8
  2. solace_agent_mesh/agent/sac/component.py +35 -21
  3. solace_agent_mesh/assets/docs/404.html +3 -3
  4. solace_agent_mesh/assets/docs/assets/js/{04989206.b9dfe831.js → 04989206.a248f00c.js} +1 -1
  5. solace_agent_mesh/assets/docs/assets/js/166ab619.bdddc63a.js +1 -0
  6. solace_agent_mesh/assets/docs/assets/js/{1c6e87d2.43771adc.js → 1c6e87d2.e056b7e0.js} +1 -1
  7. solace_agent_mesh/assets/docs/assets/js/483cef9a.4736f2d8.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/{4c2787c2.fc6804f2.js → 4c2787c2.c1290a40.js} +1 -1
  9. solace_agent_mesh/assets/docs/assets/js/{5b4258a4.dff11eca.js → 5b4258a4.fdfd2325.js} +1 -1
  10. solace_agent_mesh/assets/docs/assets/js/{75384d09.abdf9cf9.js → 75384d09.1e7d7cb7.js} +1 -1
  11. solace_agent_mesh/assets/docs/assets/js/85387663.be2bc838.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/945fb41e.16e00776.js +1 -0
  13. solace_agent_mesh/assets/docs/assets/js/a12a4955.25fbed32.js +1 -0
  14. solace_agent_mesh/assets/docs/assets/js/a3a92b25.6def8980.js +1 -0
  15. solace_agent_mesh/assets/docs/assets/js/ae0e903d.5fe5203f.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/bac0be12.bf0181cf.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/beecea0d.ce915979.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/cee5d587.47904f5e.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/f284c35a.525933db.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/f897a61a.126663fe.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/{fbfa3e75.aca209c9.js → fbfa3e75.e144b16c.js} +1 -1
  22. solace_agent_mesh/assets/docs/assets/js/main.11f9f9f3.js +2 -0
  23. solace_agent_mesh/assets/docs/assets/js/runtime~main.5922bcf0.js +1 -0
  24. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +5 -4
  25. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/rbac-setup-guilde/index.html +201 -0
  26. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +4 -4
  27. 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
  28. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +6 -6
  31. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +5 -5
  38. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +6 -6
  39. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +3 -3
  41. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +5 -5
  42. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +5 -5
  43. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +6 -6
  46. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
  47. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +3 -3
  57. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +3 -3
  58. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
  59. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +3 -3
  60. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
  61. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +5 -5
  62. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +6 -6
  63. solace_agent_mesh/assets/docs/lunr-index-1759151175744.json +1 -0
  64. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  65. solace_agent_mesh/assets/docs/search-doc-1759151175744.json +1 -0
  66. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  67. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  68. solace_agent_mesh/cli/__init__.py +1 -1
  69. solace_agent_mesh/client/webui/frontend/static/assets/main-BKIoiLSu.js +339 -0
  70. solace_agent_mesh/client/webui/frontend/static/assets/main-ChRwcV89.css +1 -0
  71. solace_agent_mesh/client/webui/frontend/static/index.html +2 -2
  72. solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +112 -42
  73. solace_agent_mesh/gateway/http_sse/app.py +0 -28
  74. solace_agent_mesh/gateway/http_sse/component.py +29 -15
  75. solace_agent_mesh/gateway/http_sse/main.py +29 -13
  76. solace_agent_mesh/gateway/http_sse/routers/sessions.py +39 -1
  77. solace_agent_mesh/gateway/http_sse/routers/tasks.py +30 -40
  78. solace_agent_mesh/gateway/http_sse/services/session_service.py +18 -2
  79. {solace_agent_mesh-1.4.5.dist-info → solace_agent_mesh-1.4.7.dist-info}/METADATA +1 -1
  80. {solace_agent_mesh-1.4.5.dist-info → solace_agent_mesh-1.4.7.dist-info}/RECORD +84 -82
  81. solace_agent_mesh/assets/docs/assets/js/166ab619.e8f3a7c7.js +0 -1
  82. solace_agent_mesh/assets/docs/assets/js/483cef9a.8d318c2f.js +0 -1
  83. solace_agent_mesh/assets/docs/assets/js/85387663.6bf41934.js +0 -1
  84. solace_agent_mesh/assets/docs/assets/js/945fb41e.abf2be91.js +0 -1
  85. solace_agent_mesh/assets/docs/assets/js/a3a92b25.1d029b81.js +0 -1
  86. solace_agent_mesh/assets/docs/assets/js/ae0e903d.7c73bc4f.js +0 -1
  87. solace_agent_mesh/assets/docs/assets/js/bac0be12.27ee2c26.js +0 -1
  88. solace_agent_mesh/assets/docs/assets/js/beecea0d.8bbd852c.js +0 -1
  89. solace_agent_mesh/assets/docs/assets/js/cee5d587.f1e1ca86.js +0 -1
  90. solace_agent_mesh/assets/docs/assets/js/f284c35a.2b2f5048.js +0 -1
  91. solace_agent_mesh/assets/docs/assets/js/f897a61a.bc634a3e.js +0 -1
  92. solace_agent_mesh/assets/docs/assets/js/main.2b4fe82a.js +0 -2
  93. solace_agent_mesh/assets/docs/assets/js/runtime~main.c0805958.js +0 -1
  94. solace_agent_mesh/assets/docs/lunr-index-1758644347760.json +0 -1
  95. solace_agent_mesh/assets/docs/search-doc-1758644347760.json +0 -1
  96. solace_agent_mesh/client/webui/frontend/static/assets/main-8xbvgfVK.css +0 -1
  97. solace_agent_mesh/client/webui/frontend/static/assets/main-B67MsY-v.js +0 -339
  98. /solace_agent_mesh/assets/docs/assets/js/{main.2b4fe82a.js.LICENSE.txt → main.11f9f9f3.js.LICENSE.txt} +0 -0
  99. {solace_agent_mesh-1.4.5.dist-info → solace_agent_mesh-1.4.7.dist-info}/WHEEL +0 -0
  100. {solace_agent_mesh-1.4.5.dist-info → solace_agent_mesh-1.4.7.dist-info}/entry_points.txt +0 -0
  101. {solace_agent_mesh-1.4.5.dist-info → solace_agent_mesh-1.4.7.dist-info}/licenses/LICENSE +0 -0
@@ -131,7 +131,9 @@ class WebUIBackendComponent(BaseGatewayComponent):
131
131
  )
132
132
 
133
133
  self._sse_cleanup_timer_id = f"sse_cleanup_{self.gateway_id}"
134
- cleanup_interval_sec = self.get_config("sse_buffer_cleanup_interval_seconds", 300)
134
+ cleanup_interval_sec = self.get_config(
135
+ "sse_buffer_cleanup_interval_seconds", 300
136
+ )
135
137
  self.add_timer(
136
138
  delay_ms=cleanup_interval_sec * 1000,
137
139
  timer_id=self._sse_cleanup_timer_id,
@@ -173,13 +175,14 @@ class WebUIBackendComponent(BaseGatewayComponent):
173
175
  self._visualization_locks_lock = threading.Lock()
174
176
  self._global_visualization_subscriptions: dict[str, int] = {}
175
177
  self._visualization_processor_task: asyncio.Task | None = None
176
-
178
+
177
179
  # Initialize SAM Events service for system events
178
180
  from ...common.sam_events import SamEventService
181
+
179
182
  self.sam_events = SamEventService(
180
183
  namespace=self.namespace,
181
- component_name=f"{self.name}_gateway",
182
- publish_func=self.publish_a2a
184
+ component_name=f"{self.name}_gateway",
185
+ publish_func=self.publish_a2a,
183
186
  )
184
187
 
185
188
  log.info("%s Web UI Backend Component initialized.", self.log_identifier)
@@ -1007,7 +1010,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1007
1010
  )
1008
1011
 
1009
1012
  except Exception as e:
1010
- log.exception(
1013
+ log.error(
1011
1014
  "%s [_start_listener] Failed to start FastAPI/Uvicorn server: %s",
1012
1015
  self.log_identifier,
1013
1016
  e,
@@ -1024,12 +1027,16 @@ class WebUIBackendComponent(BaseGatewayComponent):
1024
1027
  It's thread-safe as it uses the SAC App instance.
1025
1028
  """
1026
1029
  log.debug(f"[publish_a2a] Starting to publish message to topic: {topic}")
1027
- log.debug(f"[publish_a2a] Payload type: {type(payload)}, size: {len(str(payload))} chars")
1030
+ log.debug(
1031
+ f"[publish_a2a] Payload type: {type(payload)}, size: {len(str(payload))} chars"
1032
+ )
1028
1033
  log.debug(f"[publish_a2a] User properties: {user_properties}")
1029
-
1034
+
1030
1035
  try:
1031
1036
  super().publish_a2a_message(payload, topic, user_properties)
1032
- log.debug(f"[publish_a2a] Successfully called super().publish_a2a_message for topic: {topic}")
1037
+ log.debug(
1038
+ f"[publish_a2a] Successfully called super().publish_a2a_message for topic: {topic}"
1039
+ )
1033
1040
  except Exception as e:
1034
1041
  log.error(f"[publish_a2a] Exception in publish_a2a: {e}", exc_info=True)
1035
1042
  raise
@@ -1143,7 +1150,7 @@ class WebUIBackendComponent(BaseGatewayComponent):
1143
1150
  details["source_entity"] = payload.get("source_component", "unknown")
1144
1151
  details["target_entity"] = "system"
1145
1152
  return details
1146
-
1153
+
1147
1154
  # Try to parse as a JSON-RPC response first
1148
1155
  if "result" in payload or "error" in payload:
1149
1156
  rpc_response = JSONRPCResponse.model_validate(payload)
@@ -1264,9 +1271,9 @@ class WebUIBackendComponent(BaseGatewayComponent):
1264
1271
  (summary_str[:100] + "...") if len(summary_str) > 100 else summary_str
1265
1272
  )
1266
1273
  except Exception:
1267
- details["payload_summary"]["params_preview"] = (
1268
- "[Could not serialize payload]"
1269
- )
1274
+ details["payload_summary"][
1275
+ "params_preview"
1276
+ ] = "[Could not serialize payload]"
1270
1277
 
1271
1278
  return details
1272
1279
 
@@ -1669,7 +1676,9 @@ class WebUIBackendComponent(BaseGatewayComponent):
1669
1676
  try:
1670
1677
  session_id = external_request_context.get("a2a_session_id")
1671
1678
  user_id = external_request_context.get("user_id_for_a2a")
1672
- agent_name = external_request_context.get("target_agent_name", "agent")
1679
+ agent_name = external_request_context.get(
1680
+ "target_agent_name", "agent"
1681
+ )
1673
1682
 
1674
1683
  message_text = ""
1675
1684
  if task_data.status and task_data.status.message:
@@ -1681,10 +1690,15 @@ class WebUIBackendComponent(BaseGatewayComponent):
1681
1690
  message_text += part.text
1682
1691
 
1683
1692
  if message_text and session_id and user_id:
1684
- from .dependencies import create_session_service_with_transaction
1693
+ from .dependencies import (
1694
+ create_session_service_with_transaction,
1695
+ )
1685
1696
  from ...gateway.http_sse.shared.enums import SenderType
1686
1697
 
1687
- with create_session_service_with_transaction() as (session_service, db):
1698
+ with create_session_service_with_transaction() as (
1699
+ session_service,
1700
+ db,
1701
+ ):
1688
1702
  session_service.add_message_to_session(
1689
1703
  session_id=session_id,
1690
1704
  user_id=user_id,
@@ -310,38 +310,48 @@ def _setup_alembic_config(database_url: str) -> Config:
310
310
 
311
311
 
312
312
  def _run_community_migrations(database_url: str) -> None:
313
+ """
314
+ Run Alembic migrations for the community database schema.
315
+ This includes sessions, chat_messages tables and their indexes.
316
+ """
313
317
  try:
314
318
  from sqlalchemy import create_engine
315
319
 
320
+ log.info("Starting community migrations...")
316
321
  engine = create_engine(database_url)
317
322
  inspector = sa.inspect(engine)
318
323
  existing_tables = inspector.get_table_names()
319
324
 
320
325
  if not existing_tables or "sessions" not in existing_tables:
321
- log.info("Running community database migrations...")
326
+ log.info("Running initial community database setup")
322
327
  alembic_cfg = _setup_alembic_config(database_url)
323
328
  command.upgrade(alembic_cfg, "head")
324
- log.info("Community database migrations complete.")
329
+ log.info("Community database migrations completed")
325
330
  else:
326
- log.info(
327
- "Community database tables already exist, skipping community migrations."
328
- )
331
+ log.info("Checking for community schema updates")
332
+ alembic_cfg = _setup_alembic_config(database_url)
333
+ command.upgrade(alembic_cfg, "head")
334
+ log.info("Community database schema is current")
329
335
  except Exception as e:
330
336
  log.warning(
331
- "Community migration check failed, attempting to run migrations anyway: %s",
337
+ "Community migration check failed: %s - attempting to run migrations",
332
338
  e,
333
339
  )
334
340
  try:
335
341
  alembic_cfg = _setup_alembic_config(database_url)
336
342
  command.upgrade(alembic_cfg, "head")
337
- log.info("Community database migrations complete.")
343
+ log.info("Community database migrations completed")
338
344
  except Exception as migration_error:
339
- log.warning(
340
- "Community migration failed but continuing: %s", migration_error
341
- )
345
+ log.error("Community migration failed: %s", migration_error)
346
+ log.error("Check database connectivity and permissions")
347
+ raise RuntimeError(f"Community database migration failed: {migration_error}") from migration_error
342
348
 
343
349
 
344
350
  def _run_enterprise_migrations(component: "WebUIBackendComponent", database_url: str) -> None:
351
+ """
352
+ Run migrations for enterprise features like advanced analytics, audit logs, etc.
353
+ This is optional and only runs if the enterprise package is available.
354
+ """
345
355
  try:
346
356
  from solace_agent_mesh_enterprise.webui_backend.migration_runner import (
347
357
  run_migrations,
@@ -349,19 +359,25 @@ def _run_enterprise_migrations(component: "WebUIBackendComponent", database_url:
349
359
 
350
360
  webui_app = component.get_app()
351
361
  app_config = getattr(webui_app, "app_config", {}) if webui_app else {}
352
- log.info("Running enterprise migrations...")
362
+ log.info("Starting enterprise migrations...")
353
363
  run_migrations(database_url, app_config)
354
364
  log.info("Enterprise migrations completed")
355
365
  except (ImportError, ModuleNotFoundError):
356
366
  log.debug("Enterprise module not found - skipping enterprise migrations")
357
367
  except Exception as e:
358
- log.warning("Enterprise migration failed but continuing: %s", e)
368
+ log.error("Enterprise migration failed: %s", e)
369
+ log.error("Advanced features may be unavailable")
370
+ raise RuntimeError(f"Enterprise database migration failed: {e}") from e
359
371
 
360
372
 
361
373
  def _setup_database(component: "WebUIBackendComponent", database_url: str) -> None:
374
+ """
375
+ Initialize database connection and run all required migrations.
376
+ This sets up both community and enterprise database schemas.
377
+ """
362
378
  dependencies.init_database(database_url)
363
379
  log.info("Persistence enabled - sessions will be stored in database")
364
- log.info("Checking database migrations...")
380
+ log.info("Running database migrations...")
365
381
 
366
382
  _run_community_migrations(database_url)
367
383
  _run_enterprise_migrations(component, database_url)
@@ -1,8 +1,11 @@
1
1
  from fastapi import APIRouter, Body, Depends, HTTPException, status
2
+ from fastapi import Request as FastAPIRequest
2
3
  from solace_ai_connector.common.log import log
3
4
 
4
- from ..dependencies import get_session_business_service
5
+ from a2a.types import JSONRPCSuccessResponse
6
+ from ..dependencies import get_session_business_service, get_session_manager
5
7
  from ..services.session_service import SessionService
8
+ from ..session_manager import SessionManager
6
9
  from ..shared.auth_utils import get_current_user
7
10
  from .dto.requests.session_requests import (
8
11
  GetSessionHistoryRequest,
@@ -15,10 +18,45 @@ from .dto.responses.session_responses import (
15
18
  SessionListResponse,
16
19
  SessionResponse,
17
20
  )
21
+ from ....common.a2a import create_generic_success_response
18
22
 
19
23
  router = APIRouter()
20
24
 
21
25
 
26
+ @router.post("/sessions/new", response_model=JSONRPCSuccessResponse)
27
+ async def create_new_session(
28
+ request: FastAPIRequest,
29
+ user: dict = Depends(get_current_user),
30
+ session_manager: SessionManager = Depends(get_session_manager),
31
+ session_service: SessionService = Depends(get_session_business_service),
32
+ ):
33
+ """Creates a new session on-demand and returns its ID."""
34
+ user_id = user.get("id")
35
+ log.info("User %s requesting new session", user_id)
36
+ try:
37
+ new_session_id = session_manager.create_new_session_id(request)
38
+ log.info("Created new session ID: %s for user %s", new_session_id, user_id)
39
+
40
+ # Attempt to create the session record in the DB.
41
+ # The service will handle the check for whether persistence is enabled.
42
+ session_service.create_session(
43
+ user_id=user_id,
44
+ agent_id=None, # Agent is not known at this point
45
+ name=None,
46
+ session_id=new_session_id,
47
+ )
48
+
49
+ return create_generic_success_response(
50
+ result={"id": new_session_id}, request_id=None
51
+ )
52
+ except Exception as e:
53
+ log.error("Error creating new session for user %s: %s", user_id, e)
54
+ raise HTTPException(
55
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
56
+ detail="Failed to create a new session",
57
+ )
58
+
59
+
22
60
  @router.get("/sessions", response_model=SessionListResponse)
23
61
  async def get_all_sessions(
24
62
  user: dict = Depends(get_current_user),
@@ -76,28 +76,31 @@ async def _submit_task(
76
76
  )
77
77
 
78
78
  client_id = session_manager.get_a2a_client_id(request)
79
-
79
+
80
80
  # Use session ID from frontend request (contextId) instead of cookie-based session
81
81
  # Handle various falsy values: None, empty string, whitespace-only string
82
- log.info("%s[DEBUG] payload.params.message: %s", log_prefix, payload.params.message)
83
- log.info("%s[DEBUG] hasattr context_id: %s", log_prefix, hasattr(payload.params.message, 'context_id'))
84
- if hasattr(payload.params.message, 'context_id'):
85
- log.info("%s[DEBUG] context_id value: %s", log_prefix, payload.params.message.context_id)
86
-
87
82
  frontend_session_id = None
88
- if hasattr(payload.params.message, 'context_id') and payload.params.message.context_id:
83
+ if (
84
+ hasattr(payload.params.message, "context_id")
85
+ and payload.params.message.context_id
86
+ ):
89
87
  context_id = payload.params.message.context_id
90
88
  if isinstance(context_id, str) and context_id.strip():
91
89
  frontend_session_id = context_id.strip()
92
- log.info("%s[DEBUG] Extracted frontend_session_id: %s", log_prefix, frontend_session_id)
93
-
90
+
94
91
  if frontend_session_id:
95
92
  session_id = frontend_session_id
96
- log.info("%sUsing session ID from frontend request: %s", log_prefix, session_id)
93
+ log.info(
94
+ "%sUsing session ID from frontend request: %s", log_prefix, session_id
95
+ )
97
96
  else:
98
97
  # Create new session when frontend doesn't provide one (None, empty, or whitespace-only)
99
98
  session_id = session_manager.create_new_session_id(request)
100
- log.info("%sNo valid session ID from frontend, created new session: %s", log_prefix, session_id)
99
+ log.info(
100
+ "%sNo valid session ID from frontend, created new session: %s",
101
+ log_prefix,
102
+ session_id,
103
+ )
101
104
 
102
105
  log.info(
103
106
  "%sUsing ClientID: %s, SessionID: %s", log_prefix, client_id, session_id
@@ -106,38 +109,24 @@ async def _submit_task(
106
109
  # Store message in persistence layer if available
107
110
  user_id = user_identity.get("id")
108
111
  from ....gateway.http_sse.dependencies import SessionLocal
112
+
109
113
  if is_streaming and SessionLocal is not None:
110
114
  try:
111
- from ....gateway.http_sse.dependencies import create_session_service_with_transaction
115
+ from ....gateway.http_sse.dependencies import (
116
+ create_session_service_with_transaction,
117
+ )
112
118
  from ....gateway.http_sse.shared.enums import SenderType
113
-
119
+
114
120
  with create_session_service_with_transaction() as (session_service, db):
115
- existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
116
- if not existing_session:
117
- log.info("%sCreating new session in database: %s", log_prefix, session_id)
118
- try:
119
- session_service.create_session(
120
- user_id=user_id,
121
- agent_id=agent_name,
122
- name=None,
123
- session_id=session_id
124
- )
125
- except Exception as create_error:
126
- log.warning("%sSession creation failed, checking if session exists: %s", log_prefix, create_error)
127
- existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
128
- if not existing_session:
129
- raise create_error
130
- log.info("%sSession was created by another request: %s", log_prefix, session_id)
131
-
132
121
  message_text = ""
133
122
  if payload.params and payload.params.message:
134
123
  parts = a2a.get_parts_from_message(payload.params.message)
135
124
  for part in parts:
136
- if hasattr(part, 'text'):
125
+ if hasattr(part, "text"):
137
126
  message_text = part.text
138
127
  break
139
-
140
- message_domain = session_service.add_message_to_session(
128
+
129
+ session_service.add_message_to_session(
141
130
  session_id=session_id,
142
131
  user_id=user_id,
143
132
  message=message_text or "Task submitted",
@@ -145,15 +134,16 @@ async def _submit_task(
145
134
  sender_name=user_id or "user",
146
135
  agent_id=agent_name,
147
136
  )
148
-
149
- if message_domain:
150
- log.info("%sMessage stored in session %s", log_prefix, session_id)
151
- else:
152
- log.warning("%sFailed to store message in session %s", log_prefix, session_id)
137
+
153
138
  except Exception as e:
154
- log.error("%sFailed to store message in session service: %s", log_prefix, e)
139
+ log.error(
140
+ "%sFailed to store message in session service: %s", log_prefix, e
141
+ )
155
142
  else:
156
- log.debug("%sNo persistence available or non-streaming - skipping message storage", log_prefix)
143
+ log.debug(
144
+ "%sNo persistence available or non-streaming - skipping message storage",
145
+ log_prefix,
146
+ )
157
147
 
158
148
  # Use the helper to get the unwrapped parts from the incoming message.
159
149
  a2a_parts = a2a.get_parts_from_message(payload.params.message)
@@ -1,5 +1,5 @@
1
1
  import uuid
2
- from typing import TYPE_CHECKING
2
+ from typing import TYPE_CHECKING, Optional
3
3
 
4
4
  from solace_ai_connector.common.log import log
5
5
 
@@ -29,6 +29,12 @@ class SessionService:
29
29
  self.message_repository = message_repository
30
30
  self.component = component
31
31
 
32
+ def is_persistence_enabled(self) -> bool:
33
+ """Checks if the service is configured with a persistent backend."""
34
+ # The presence of a database_url on the component is the source of truth
35
+ # for whether SQL persistence is enabled.
36
+ return self.component and self.component.database_url is not None
37
+
32
38
  def get_user_sessions(
33
39
  self, user_id: UserId, pagination: PaginationInfo | None = None
34
40
  ) -> list[Session]:
@@ -73,7 +79,11 @@ class SessionService:
73
79
  name: str | None = None,
74
80
  agent_id: str | None = None,
75
81
  session_id: str | None = None,
76
- ) -> Session:
82
+ ) -> Optional[Session]:
83
+ if not self.is_persistence_enabled():
84
+ log.debug("Persistence is not enabled. Skipping session creation in DB.")
85
+ return None
86
+
77
87
  if not user_id or user_id.strip() == "":
78
88
  raise ValueError("User ID cannot be empty")
79
89
 
@@ -92,9 +102,15 @@ class SessionService:
92
102
  updated_time=now_ms,
93
103
  )
94
104
 
105
+ if not session:
106
+ raise ValueError(f"Failed to create session for {session_id}")
107
+
95
108
  created_session = self.session_repository.save(session)
96
109
  log.info("Created new session %s for user %s", created_session.id, user_id)
97
110
 
111
+ if not created_session:
112
+ raise ValueError(f"Failed to save session for {session_id}")
113
+
98
114
  return created_session
99
115
 
100
116
  def update_session_name(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solace-agent-mesh
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Solace Agent Mesh is an open-source framework for building event-driven, multi-agent AI systems where specialized agents collaborate on complex tasks.
5
5
  Project-URL: Homepage, https://github.com/SolaceLabs/solace-agent-mesh
6
6
  Project-URL: Repository, https://github.com/SolaceLabs/solace-agent-mesh