solace-agent-mesh 1.6.1__py3-none-any.whl → 1.6.3__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 (124) hide show
  1. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  2. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +1 -1
  3. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +135 -31
  4. solace_agent_mesh/agent/adk/models/lite_llm.py +5 -0
  5. solace_agent_mesh/agent/adk/runner.py +10 -12
  6. solace_agent_mesh/agent/adk/services.py +53 -17
  7. solace_agent_mesh/agent/adk/setup.py +66 -38
  8. solace_agent_mesh/agent/protocol/event_handlers.py +243 -122
  9. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  10. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  11. solace_agent_mesh/agent/sac/app.py +43 -2
  12. solace_agent_mesh/agent/sac/component.py +200 -72
  13. solace_agent_mesh/agent/sac/task_execution_context.py +35 -4
  14. solace_agent_mesh/agent/tools/tool_config_types.py +3 -0
  15. solace_agent_mesh/agent/utils/artifact_helpers.py +1 -1
  16. solace_agent_mesh/assets/docs/404.html +3 -3
  17. solace_agent_mesh/assets/docs/assets/js/240a0364.c39f8388.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/94e8668d.3b883666.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/main.ed05b14d.js +2 -0
  25. solace_agent_mesh/assets/docs/assets/js/runtime~main.a8a75e0b.js +1 -0
  26. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +3 -3
  27. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +3 -3
  28. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +3 -3
  38. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +4 -25
  39. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +76 -0
  42. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +5 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +3 -3
  45. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +3 -3
  47. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +3 -3
  50. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +3 -3
  57. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +3 -3
  58. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +3 -3
  59. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  60. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  61. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +23 -28
  62. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  63. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  64. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  65. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  66. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  67. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +3 -6
  68. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  69. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  70. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +3 -3
  71. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  72. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  73. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  74. solace_agent_mesh/assets/docs/lunr-index-1761744323675.json +1 -0
  75. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  76. solace_agent_mesh/assets/docs/search-doc-1761744323675.json +1 -0
  77. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  78. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  79. solace_agent_mesh/cli/__init__.py +1 -1
  80. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-D4_RMYRh.js} +1 -1
  81. solace_agent_mesh/client/webui/frontend/static/assets/{client-CaY59VuC.js → client-UZ3qU6Bq.js} +1 -1
  82. solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +1 -0
  83. solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +342 -0
  84. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BEmvJSYz.js → vendor-DSqhjwq_.js} +1 -1
  85. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  86. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  87. solace_agent_mesh/common/a2a/events.py +2 -1
  88. solace_agent_mesh/common/sac/sam_component_base.py +24 -18
  89. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  90. solace_agent_mesh/gateway/base/component.py +12 -8
  91. solace_agent_mesh/gateway/http_sse/app.py +26 -0
  92. solace_agent_mesh/gateway/http_sse/component.py +158 -79
  93. solace_agent_mesh/gateway/http_sse/dependencies.py +83 -59
  94. solace_agent_mesh/gateway/http_sse/main.py +35 -11
  95. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +1 -1
  96. solace_agent_mesh/gateway/http_sse/routers/auth.py +103 -6
  97. solace_agent_mesh/gateway/http_sse/routers/config.py +1 -1
  98. solace_agent_mesh/gateway/http_sse/routers/sessions.py +1 -1
  99. solace_agent_mesh/gateway/http_sse/routers/sse.py +15 -5
  100. solace_agent_mesh/gateway/http_sse/routers/tasks.py +3 -3
  101. solace_agent_mesh/gateway/http_sse/routers/visualization.py +90 -8
  102. solace_agent_mesh/gateway/http_sse/services/session_service.py +1 -1
  103. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  104. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +16 -1
  105. solace_agent_mesh/gateway/http_sse/sse_manager.py +15 -6
  106. solace_agent_mesh/templates/logging_config_template.ini +2 -2
  107. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/METADATA +2 -2
  108. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/RECORD +112 -110
  109. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  110. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  111. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  114. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  115. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  116. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  117. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  118. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  119. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  120. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  121. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.ed05b14d.js.LICENSE.txt} +0 -0
  122. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/WHEEL +0 -0
  123. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/entry_points.txt +0 -0
  124. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/licenses/LICENSE +0 -0
@@ -3,19 +3,20 @@ Router for handling authentication-related endpoints.
3
3
  """
4
4
 
5
5
  import logging
6
+ import secrets
7
+ from urllib.parse import urlencode
8
+
9
+ import httpx
6
10
  from fastapi import (
7
11
  APIRouter,
8
- Request as FastAPIRequest,
9
12
  Depends,
10
13
  HTTPException,
14
+ Request as FastAPIRequest,
11
15
  Response,
12
16
  )
13
- from fastapi.responses import RedirectResponse
14
- import httpx
15
- import secrets
16
- from urllib.parse import urlencode
17
+ from fastapi.responses import HTMLResponse, RedirectResponse
17
18
 
18
- from ...http_sse.dependencies import get_sac_component, get_api_config
19
+ from ...http_sse.dependencies import get_api_config, get_sac_component
19
20
 
20
21
  log = logging.getLogger(__name__)
21
22
 
@@ -212,3 +213,99 @@ async def refresh_token(
212
213
  "access_token": access_token,
213
214
  "refresh_token": new_refresh_token,
214
215
  }
216
+
217
+
218
+ @router.get("/auth/tool/callback")
219
+ async def auth_tool_callback(
220
+ request: FastAPIRequest,
221
+ component: "WebUIBackendComponent" = Depends(get_sac_component),
222
+ ):
223
+ """
224
+ Handles OAuth2 authorization code grant response for tool authentication.
225
+ """
226
+ code = request.query_params.get("code")
227
+ state = request.query_params.get("state")
228
+ error = request.query_params.get("error")
229
+ error_description = request.query_params.get("error_description")
230
+
231
+ if error:
232
+ log.error(f"OAuth2 tool callback received error: {error} - {error_description}")
233
+ return HTMLResponse(
234
+ content=f"""
235
+ <html>
236
+ <head>
237
+ <title>Authorization Error</title>
238
+ </head>
239
+ <body>
240
+ <h2>Authorization Error</h2>
241
+ <p>Error: {error}</p>
242
+ <p>Description: {error_description or 'No description provided'}</p>
243
+ <p>Please close this window and try again.</p>
244
+ </body>
245
+ </html>
246
+ """,
247
+ status_code=400
248
+ )
249
+
250
+ # Get the current request URL for logging/debugging
251
+ url = str(request.url)
252
+
253
+
254
+ if not code:
255
+ log.warning("OAuth2 tool callback received without authorization code")
256
+ return HTMLResponse(
257
+ content="""
258
+ <html>
259
+ <head>
260
+ <title>Authorization Error</title>
261
+ </head>
262
+ <body>
263
+ <h2>Authorization Error</h2>
264
+ <p>No authorization code received. Please close this window and try again.</p>
265
+ </body>
266
+ </html>
267
+ """,
268
+ status_code=400
269
+ )
270
+
271
+ log.info(f"OAuth2 tool callback received authorization code: {code}")
272
+
273
+ try:
274
+ from solace_agent_mesh_enterprise.auth.input_required import process_auth_grant_response
275
+ await process_auth_grant_response(component, code, state, url)
276
+ except ImportError:
277
+ pass
278
+
279
+ # Return simple HTML page instructing user to close the window
280
+ return HTMLResponse(
281
+ content="""
282
+ <html>
283
+ <head>
284
+ <title>Authorization Complete</title>
285
+ <style>
286
+ body {
287
+ font-family: Arial, sans-serif;
288
+ text-align: center;
289
+ padding: 50px;
290
+ background-color: #f5f5f5;
291
+ }
292
+ .container {
293
+ background: white;
294
+ padding: 30px;
295
+ border-radius: 8px;
296
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
297
+ display: inline-block;
298
+ }
299
+ h2 { color: #28a745; }
300
+ </style>
301
+ </head>
302
+ <body>
303
+ <div class="container">
304
+ <h2>✓ Authorization Complete</h2>
305
+ <p>You have successfully authorized the tool access.</p>
306
+ <p><strong>Please close this window.</strong></p>
307
+ </div>
308
+ </body>
309
+ </html>
310
+ """
311
+ )
@@ -69,7 +69,7 @@ async def get_app_config(
69
69
  "frontend_feature_enablement": feature_enablement,
70
70
  "persistence_enabled": api_config.get("persistence_enabled", False),
71
71
  }
72
- log.info("%sReturning frontend configuration.", log_prefix)
72
+ log.debug("%sReturning frontend configuration.", log_prefix)
73
73
  return config_data
74
74
  except Exception as e:
75
75
  log.exception(
@@ -129,7 +129,7 @@ async def save_task(
129
129
  Creates a new task or updates an existing one.
130
130
  """
131
131
  user_id = user.get("id")
132
- log.info(
132
+ log.debug(
133
133
  "User %s attempting to save task %s for session %s",
134
134
  user_id,
135
135
  request.task_id,
@@ -13,6 +13,8 @@ from ....gateway.http_sse.sse_manager import SSEManager
13
13
  from ....gateway.http_sse.dependencies import get_sse_manager
14
14
 
15
15
  log = logging.getLogger(__name__)
16
+ trace_logger = logging.getLogger("sam_trace")
17
+
16
18
 
17
19
  router = APIRouter()
18
20
 
@@ -27,12 +29,12 @@ async def subscribe_to_task_events(
27
29
  Establishes an SSE connection to receive real-time updates for a specific task.
28
30
  """
29
31
  log_prefix = "[GET /api/v1/sse/subscribe/%s] " % task_id
30
- log.info("%sClient requesting SSE subscription.", log_prefix)
32
+ log.debug("%sClient requesting SSE subscription.", log_prefix)
31
33
 
32
34
  connection_queue: asyncio.Queue = None
33
35
  try:
34
36
  connection_queue = await sse_manager.create_sse_connection(task_id)
35
- log.info("%sSSE connection queue created.", log_prefix)
37
+ log.debug("%sSSE connection queue created.", log_prefix)
36
38
 
37
39
  async def event_generator():
38
40
  nonlocal connection_queue
@@ -73,9 +75,17 @@ async def subscribe_to_task_events(
73
75
  log_prefix,
74
76
  )
75
77
  break
76
- log.debug(
77
- "%sYielding event_payload: %s", log_prefix, event_payload
78
- )
78
+ if trace_logger.isEnabledFor(logging.DEBUG):
79
+ trace_logger.debug(
80
+ "%sYielding event_payload: %s",
81
+ log_prefix, event_payload
82
+ )
83
+ else:
84
+ log.debug(
85
+ "%sYielding event: %s",
86
+ log_prefix,
87
+ event_payload.get("event") if event_payload else "unknown"
88
+ )
79
89
  yield event_payload
80
90
  connection_queue.task_done()
81
91
  log.debug(
@@ -77,7 +77,7 @@ async def _submit_task(
77
77
  status_code=status.HTTP_401_UNAUTHORIZED,
78
78
  detail="User authentication failed or identity not found.",
79
79
  )
80
- log.info(
80
+ log.debug(
81
81
  "%sAuthenticated user identity: %s",
82
82
  log_prefix,
83
83
  user_identity.get("id", "unknown"),
@@ -107,7 +107,7 @@ async def _submit_task(
107
107
  else:
108
108
  # Create new session when frontend doesn't provide one
109
109
  session_id = session_manager.create_new_session_id(request)
110
- log.info(
110
+ log.debug(
111
111
  "%sNo valid session ID from frontend, created new session: %s",
112
112
  log_prefix,
113
113
  session_id,
@@ -125,7 +125,7 @@ async def _submit_task(
125
125
  session_id=session_id,
126
126
  )
127
127
  db.commit()
128
- log.info(
128
+ log.debug(
129
129
  "%sCreated session in database: %s", log_prefix, session_id
130
130
  )
131
131
  except Exception as e:
@@ -14,6 +14,7 @@ from fastapi import (
14
14
  status,
15
15
  )
16
16
  from pydantic import BaseModel, Field
17
+ import json
17
18
  from typing import List, Optional, Dict, Any, Set
18
19
 
19
20
 
@@ -31,6 +32,7 @@ if TYPE_CHECKING:
31
32
  from ....gateway.http_sse.component import WebUIBackendComponent
32
33
 
33
34
  log = logging.getLogger(__name__)
35
+ trace_logger = logging.getLogger("sam_trace")
34
36
 
35
37
  router = APIRouter()
36
38
 
@@ -231,11 +233,77 @@ def _resolve_user_identity_for_authorization(
231
233
  "%s No user_identity provided but authorization is enabled. This should not happen.",
232
234
  log_id_prefix,
233
235
  )
234
- raise ValueError("No user identity available when authorization is required")
236
+ raise ValueError(
237
+ "No user identity available when authorization is required"
238
+ )
235
239
 
236
240
  return user_identity
237
241
 
238
242
 
243
+ def _include_for_visualization(event_payload: Dict[str, Any]) -> bool:
244
+ """
245
+ Check if an event should be included for visualization based on metadata.
246
+
247
+ Args:
248
+ event_payload: The event payload containing event data
249
+
250
+ Returns:
251
+ False if the event should be excluded from visualization (when visualization is False),
252
+ True otherwise (include by default)
253
+ """
254
+ try:
255
+ # Get the data field from the event payload
256
+ data_str = event_payload.get("data")
257
+ if not data_str:
258
+ return True # Include by default if no data
259
+
260
+ # Parse the JSON data
261
+ try:
262
+ data = json.loads(data_str)
263
+ except (json.JSONDecodeError, TypeError):
264
+ return True
265
+
266
+ # Look for the full_payload in the data
267
+ full_payload = data.get("full_payload")
268
+ if not full_payload:
269
+ return True
270
+
271
+ # Check if full_payload has params
272
+ params = full_payload.get("params")
273
+ if not params:
274
+ return True
275
+
276
+ # Check if params has message
277
+ message = params.get("message")
278
+ if not message:
279
+ return True
280
+
281
+ # Check if message has metadata
282
+ metadata = message.get("metadata")
283
+ if not metadata:
284
+ return True
285
+
286
+ # Check the visualization setting in metadata
287
+ visualization_setting = metadata.get("visualization")
288
+ if visualization_setting is not None and (
289
+ (
290
+ isinstance(visualization_setting, str)
291
+ and visualization_setting.lower() == "false"
292
+ )
293
+ or (
294
+ isinstance(visualization_setting, bool)
295
+ and visualization_setting is False
296
+ )
297
+ ):
298
+ return False
299
+
300
+ return True
301
+
302
+ except Exception as e:
303
+ log.warning("Error checking visualization filter for event: %s", e)
304
+ return True
305
+
306
+
239
307
  @router.post(
240
308
  "/subscribe",
241
309
  response_model=VisualizationSubscribeResponse,
@@ -321,7 +389,7 @@ async def subscribe_to_visualization_stream(
321
389
  resolved_user_identity = _resolve_user_identity_for_authorization(
322
390
  component, user_id
323
391
  )
324
- log.info(
392
+ log.debug(
325
393
  "%s Resolved user identity for authorization: '%s' (from raw user_id: '%s')",
326
394
  log_id_prefix,
327
395
  resolved_user_identity,
@@ -339,7 +407,7 @@ async def subscribe_to_visualization_stream(
339
407
  user_config = await config_resolver.resolve_user_config(
340
408
  resolved_user_identity, gateway_context, {}
341
409
  )
342
- log.info(
410
+ log.debug(
343
411
  "%s Resolved user_config for resolved_user_identity '%s': %s",
344
412
  log_id_prefix,
345
413
  resolved_user_identity,
@@ -457,7 +525,7 @@ async def subscribe_to_visualization_stream(
457
525
  )
458
526
 
459
527
  firehose_topic = f"{component.namespace.strip('/')}/a2a/>"
460
- log.info(
528
+ log.debug(
461
529
  "%s Adding firehose subscription '%s' for my_a2a_messages stream.",
462
530
  log_id_prefix,
463
531
  firehose_topic,
@@ -780,13 +848,27 @@ async def get_visualization_stream_events(
780
848
  stream_id,
781
849
  )
782
850
  break
783
- yield event_payload
851
+ if _include_for_visualization(event_payload):
852
+ if trace_logger.isEnabledFor(logging.DEBUG):
853
+ trace_logger.debug(
854
+ "%s Yielding event for stream %s: %s",
855
+ log_id_prefix,
856
+ stream_id,
857
+ event_payload,
858
+ )
859
+ else:
860
+ log.debug(
861
+ "%s Yielding event for stream %s",
862
+ log_id_prefix,
863
+ stream_id,
864
+ )
865
+ yield event_payload
784
866
  sse_queue.task_done()
785
867
  except asyncio.TimeoutError:
786
868
  yield {"comment": "keep-alive"}
787
869
  continue
788
870
  except asyncio.CancelledError:
789
- log.info(
871
+ log.debug(
790
872
  "%s SSE event generator for stream %s cancelled.",
791
873
  log_id_prefix,
792
874
  stream_id,
@@ -800,7 +882,7 @@ async def get_visualization_stream_events(
800
882
  e,
801
883
  )
802
884
  finally:
803
- log.info(
885
+ log.debug(
804
886
  "%s SSE event generator for stream %s finished.",
805
887
  log_id_prefix,
806
888
  stream_id,
@@ -1135,4 +1217,4 @@ async def unsubscribe_from_visualization_stream(
1135
1217
  return Response(status_code=status.HTTP_204_NO_CONTENT)
1136
1218
 
1137
1219
 
1138
- log.info("Router for A2A Message Visualization initialized.")
1220
+ log.info("Initialized Router for A2A Message Visualization.")
@@ -101,7 +101,7 @@ class SessionService:
101
101
 
102
102
  session_repository = self._get_repositories(db)
103
103
  created_session = session_repository.save(db, session)
104
- log.info("Created new session %s for user %s", created_session.id, user_id)
104
+ log.debug("Created new session %s for user %s", created_session.id, user_id)
105
105
 
106
106
  if not created_session:
107
107
  raise ValueError(f"Failed to save session for {session_id}")
@@ -35,10 +35,10 @@ class SessionManager:
35
35
  self.force_user_identity = app_config.get("force_user_identity")
36
36
  self.use_authorization = app_config.get("frontend_use_authorization", False)
37
37
  self._temp_code_cache = {}
38
- log.info("[SessionManager] Initialized.")
38
+ log.info("Initialized SessionManager")
39
39
  if self.force_user_identity:
40
40
  log.warning(
41
- f"[SessionManager] Forcing user identity to: {self.force_user_identity}"
41
+ f"Forcing user identity to: {self.force_user_identity}"
42
42
  )
43
43
 
44
44
  def _get_or_create_client_id(self, request: Request) -> str | None:
@@ -54,19 +54,19 @@ class SessionManager:
54
54
  user_id = request.state.user.get("id")
55
55
  if user_id:
56
56
  log.debug(
57
- "[SessionManager] Using authenticated user ID from request.state: %s",
57
+ "Using authenticated user ID from request.state: %s",
58
58
  user_id,
59
59
  )
60
60
  return user_id
61
61
  else:
62
62
  log.warning(
63
- "[SessionManager] request.state.user exists but has no 'id' field. Falling back to other methods."
63
+ "request.state.user exists but has no 'id' field. Falling back to other methods."
64
64
  )
65
65
 
66
66
  user_id = self.get_user_id(request)
67
67
  if user_id:
68
68
  log.debug(
69
- "[SessionManager] Using authenticated user_id from session as A2A Client ID: %s",
69
+ "Using authenticated user_id from session as A2A Client ID: %s",
70
70
  user_id,
71
71
  )
72
72
  return user_id
@@ -74,7 +74,7 @@ class SessionManager:
74
74
  client_id = request.session.get(SESSION_KEY_CLIENT_ID)
75
75
  if client_id:
76
76
  log.debug(
77
- "[SessionManager] Using existing A2A Client ID: %s for web session.",
77
+ "Using existing A2A Client ID: %s for web session.",
78
78
  client_id,
79
79
  )
80
80
  return client_id
@@ -82,14 +82,14 @@ class SessionManager:
82
82
  if not self.use_authorization:
83
83
  client_id = "sam_dev_user"
84
84
  log.info(
85
- "[SessionManager] No authenticated user and auth is disabled, using client ID: %s for web session.",
85
+ "No authenticated user and auth is disabled, using client ID: %s for web session.",
86
86
  client_id,
87
87
  )
88
88
  request.session[SESSION_KEY_CLIENT_ID] = client_id
89
89
  return client_id
90
90
 
91
91
  log.warning(
92
- "[SessionManager] Could not determine client ID and authorization is enabled."
92
+ "Could not determine client ID and authorization is enabled."
93
93
  )
94
94
  return None
95
95
 
@@ -106,7 +106,7 @@ class SessionManager:
106
106
  Returns None if no session has been started for the current agent in this web session.
107
107
  """
108
108
  session_id = request.session.get(SESSION_KEY_SESSION_ID)
109
- log.debug("[SessionManager] Retrieving A2A Session ID: %s", session_id)
109
+ log.debug("Retrieving A2A Session ID: %s", session_id)
110
110
  return session_id
111
111
 
112
112
  def start_new_a2a_session(self, request: Request) -> str:
@@ -123,7 +123,7 @@ class SessionManager:
123
123
  new_session_id = f"web-session-{uuid.uuid4().hex}"
124
124
  request.session[SESSION_KEY_SESSION_ID] = new_session_id
125
125
  log.info(
126
- "[SessionManager] Started new A2A Session ID: %s for Client ID: %s",
126
+ "Started new A2A Session ID: %s for Client ID: %s",
127
127
  new_session_id,
128
128
  client_id,
129
129
  )
@@ -142,7 +142,7 @@ class SessionManager:
142
142
  )
143
143
  new_session_id = f"web-session-{uuid.uuid4().hex}"
144
144
  log.info(
145
- "[SessionManager] Generated new A2A Session ID: %s for Client ID: %s (not stored in cookies)",
145
+ "Generated new A2A Session ID: %s for Client ID: %s (not stored in cookies)",
146
146
  new_session_id,
147
147
  client_id,
148
148
  )
@@ -157,7 +157,7 @@ class SessionManager:
157
157
  if not session_id:
158
158
  session_id = self.start_new_a2a_session(request)
159
159
  log.info(
160
- "[SessionManager] No A2A Session ID found, created new one via ensure_a2a_session: %s",
160
+ "No A2A Session ID found, created new one via ensure_a2a_session: %s",
161
161
  session_id,
162
162
  )
163
163
  return session_id
@@ -171,7 +171,7 @@ class SessionManager:
171
171
  request.session[SESSION_KEY_ACCESS_TOKEN] = access_token
172
172
  if refresh_token:
173
173
  request.session[SESSION_KEY_REFRESH_TOKEN] = refresh_token
174
- log.info("[SessionManager] Stored auth tokens directly in session.")
174
+ log.info("Stored auth tokens directly in session.")
175
175
 
176
176
  def get_access_token(self, request: Request) -> str | None:
177
177
  """
@@ -191,14 +191,14 @@ class SessionManager:
191
191
  """
192
192
  request.session.pop(SESSION_KEY_ACCESS_TOKEN, None)
193
193
  request.session.pop(SESSION_KEY_REFRESH_TOKEN, None)
194
- log.info("[SessionManager] Cleared auth tokens from session")
194
+ log.info("Cleared auth tokens from session")
195
195
 
196
196
  def store_user_id(self, request: Request, user_id: str) -> None:
197
197
  """
198
198
  Stores the user ID in the web session.
199
199
  """
200
200
  request.session[SESSION_KEY_USER_ID] = user_id
201
- log.info("[SessionManager] Stored user ID in session: %s", user_id)
201
+ log.info("Stored user ID in session: %s", user_id)
202
202
 
203
203
  def get_user_id(self, request: Request) -> str | None:
204
204
  """
@@ -6,6 +6,8 @@ into appropriate HTTP responses with consistent formatting. These handlers
6
6
  can be used by any FastAPI application for uniform error handling.
7
7
  """
8
8
 
9
+ import logging
10
+
9
11
  from fastapi import HTTPException, Request, status
10
12
  from fastapi.responses import JSONResponse
11
13
  from fastapi.exceptions import RequestValidationError
@@ -24,6 +26,8 @@ from .exceptions import (
24
26
  )
25
27
  from .error_dto import EventErrorDTO
26
28
 
29
+ log = logging.getLogger(__name__)
30
+
27
31
 
28
32
  def create_error_response(
29
33
  status_code: int, message: str, validation_details: dict = None
@@ -105,7 +109,18 @@ async def webui_backend_exception_handler(
105
109
  request: Request, exc: WebUIBackendException
106
110
  ) -> JSONResponse:
107
111
  """Handle generic WebUI backend exceptions - 500 Internal Server Error."""
108
- error_dto = EventErrorDTO.create("An unexpected server error occurred.")
112
+ log.error(
113
+ f"WebUIBackendException: {exc.message}",
114
+ extra={
115
+ "path": request.url.path,
116
+ "method": request.method,
117
+ "details": exc.details if hasattr(exc, 'details') else None
118
+ },
119
+ exc_info=True
120
+ )
121
+
122
+ message = exc.message if exc.message else "An unexpected server error occurred."
123
+ error_dto = EventErrorDTO.create(message)
109
124
  return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=error_dto.model_dump())
110
125
 
111
126
 
@@ -13,6 +13,8 @@ import math
13
13
  from .sse_event_buffer import SSEEventBuffer
14
14
 
15
15
  log = logging.getLogger(__name__)
16
+ trace_logger = logging.getLogger("sam_trace")
17
+
16
18
 
17
19
  class SSEManager:
18
20
  """
@@ -180,12 +182,19 @@ class SSEManager:
180
182
  self._event_buffer.buffer_event(task_id, sse_payload)
181
183
  return
182
184
 
183
- log.debug(
184
- "%s Prepared SSE payload for Task ID %s: %s",
185
- self.log_identifier,
186
- task_id,
187
- sse_payload,
188
- )
185
+ if trace_logger.isEnabledFor(logging.DEBUG):
186
+ trace_logger.debug(
187
+ "%s Prepared SSE payload for Task ID %s: %s",
188
+ self.log_identifier,
189
+ task_id,
190
+ sse_payload,
191
+ )
192
+ else:
193
+ log.debug(
194
+ "%s Prepared SSE payload for Task ID %s",
195
+ self.log_identifier,
196
+ task_id,
197
+ )
189
198
 
190
199
  queues_to_remove = []
191
200
  for connection_queue in list(self._connections.get(task_id, [])):
@@ -38,8 +38,8 @@ args=(sys.stdout,)
38
38
  keys=simpleFormatter,jsonFormatter
39
39
 
40
40
  [formatter_simpleFormatter]
41
- format=%(asctime)s | %(levelname)-5s | %(name)s | %(message)s
41
+ format=%(asctime)s | %(levelname)-5s | %(threadName)s | %(name)s | %(message)s
42
42
 
43
43
  [formatter_jsonFormatter]
44
44
  class=pythonjsonlogger.jsonlogger.JsonFormatter
45
- format=%(asctime)s %(levelname)s %(name)s %(message)s
45
+ format=%(asctime)s %(levelname)s %(threadName)s %(name)s %(message)s
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solace-agent-mesh
3
- Version: 1.6.1
3
+ Version: 1.6.3
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
@@ -249,7 +249,7 @@ Requires-Dist: python-jwt==4.1.0
249
249
  Requires-Dist: python-multipart==0.0.20
250
250
  Requires-Dist: pyyaml==6.0.2
251
251
  Requires-Dist: rouge==1.0.1
252
- Requires-Dist: solace-ai-connector==1.3.0
252
+ Requires-Dist: solace-ai-connector==1.3.1
253
253
  Requires-Dist: sqlalchemy==2.0.40
254
254
  Requires-Dist: sse-starlette==3.0.2
255
255
  Requires-Dist: toml==0.10.2