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.
- solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +1 -1
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +135 -31
- solace_agent_mesh/agent/adk/models/lite_llm.py +5 -0
- solace_agent_mesh/agent/adk/runner.py +10 -12
- solace_agent_mesh/agent/adk/services.py +53 -17
- solace_agent_mesh/agent/adk/setup.py +66 -38
- solace_agent_mesh/agent/protocol/event_handlers.py +243 -122
- solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
- solace_agent_mesh/agent/proxies/base/app.py +3 -2
- solace_agent_mesh/agent/sac/app.py +43 -2
- solace_agent_mesh/agent/sac/component.py +200 -72
- solace_agent_mesh/agent/sac/task_execution_context.py +35 -4
- solace_agent_mesh/agent/tools/tool_config_types.py +3 -0
- solace_agent_mesh/agent/utils/artifact_helpers.py +1 -1
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/240a0364.c39f8388.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.3b883666.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.ed05b14d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.a8a75e0b.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +4 -25
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +76 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +5 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +23 -28
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +3 -6
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1761744323675.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1761744323675.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-D4_RMYRh.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-CaY59VuC.js → client-UZ3qU6Bq.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +342 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BEmvJSYz.js → vendor-DSqhjwq_.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/common/a2a/events.py +2 -1
- solace_agent_mesh/common/sac/sam_component_base.py +24 -18
- solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
- solace_agent_mesh/gateway/base/component.py +12 -8
- solace_agent_mesh/gateway/http_sse/app.py +26 -0
- solace_agent_mesh/gateway/http_sse/component.py +158 -79
- solace_agent_mesh/gateway/http_sse/dependencies.py +83 -59
- solace_agent_mesh/gateway/http_sse/main.py +35 -11
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/auth.py +103 -6
- solace_agent_mesh/gateway/http_sse/routers/config.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/sse.py +15 -5
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +3 -3
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +90 -8
- solace_agent_mesh/gateway/http_sse/services/session_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +16 -1
- solace_agent_mesh/gateway/http_sse/sse_manager.py +15 -6
- solace_agent_mesh/templates/logging_config_template.ini +2 -2
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/METADATA +2 -2
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/RECORD +112 -110
- solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.ed05b14d.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.3.dist-info}/entry_points.txt +0 -0
- {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
|
|
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.
|
|
72
|
+
log.debug("%sReturning frontend configuration.", log_prefix)
|
|
73
73
|
return config_data
|
|
74
74
|
except Exception as e:
|
|
75
75
|
log.exception(
|
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
77
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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("
|
|
38
|
+
log.info("Initialized SessionManager")
|
|
39
39
|
if self.force_user_identity:
|
|
40
40
|
log.warning(
|
|
41
|
-
f"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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("
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
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.
|
|
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
|