solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.1__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/callbacks.py +0 -5
- solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +213 -31
- solace_agent_mesh/agent/proxies/__init__.py +0 -0
- solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
- solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
- solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
- solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
- solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/base/app.py +99 -0
- solace_agent_mesh/agent/proxies/base/component.py +650 -0
- solace_agent_mesh/agent/proxies/base/config.py +85 -0
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
- solace_agent_mesh/agent/sac/app.py +58 -5
- solace_agent_mesh/agent/sac/component.py +238 -75
- solace_agent_mesh/agent/sac/task_execution_context.py +46 -0
- solace_agent_mesh/agent/tools/audio_tools.py +125 -8
- solace_agent_mesh/agent/tools/web_tools.py +10 -5
- solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{e3d9abda.2b916f9e.js → e3d9abda.6b9493d0.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.b12eac43.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +262 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
- 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 +3 -3
- 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 +6 -5
- 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 +100 -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-1761248203150.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1761248203150.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/cli/commands/add_cmd/agent_cmd.py +2 -69
- solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +342 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.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/__init__.py +24 -0
- solace_agent_mesh/common/a2a/artifact.py +39 -0
- solace_agent_mesh/common/a2a/events.py +29 -0
- solace_agent_mesh/common/a2a/message.py +68 -0
- solace_agent_mesh/common/a2a/protocol.py +151 -1
- solace_agent_mesh/common/agent_registry.py +83 -3
- solace_agent_mesh/common/constants.py +3 -1
- solace_agent_mesh/common/sac/sam_component_base.py +383 -4
- solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/evaluation/evaluator.py +128 -104
- solace_agent_mesh/evaluation/message_organizer.py +116 -110
- solace_agent_mesh/evaluation/report_data_processor.py +84 -86
- solace_agent_mesh/evaluation/report_generator.py +73 -79
- solace_agent_mesh/evaluation/run.py +421 -235
- solace_agent_mesh/evaluation/shared/__init__.py +92 -0
- solace_agent_mesh/evaluation/shared/constants.py +47 -0
- solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
- solace_agent_mesh/evaluation/shared/helpers.py +35 -0
- solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
- solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
- solace_agent_mesh/evaluation/subscriber.py +111 -232
- solace_agent_mesh/evaluation/summary_builder.py +227 -117
- solace_agent_mesh/gateway/base/app.py +16 -1
- solace_agent_mesh/gateway/base/component.py +112 -39
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- solace_agent_mesh/gateway/http_sse/component.py +99 -3
- solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
- solace_agent_mesh/gateway/http_sse/main.py +1 -0
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
- solace_agent_mesh/gateway/http_sse/routers/users.py +47 -1
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
- solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
- solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
- solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
- solace_agent_mesh/templates/logging_config_template.ini +10 -6
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
- solace_agent_mesh/templates/shared_config.yaml +40 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/METADATA +47 -21
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/RECORD +166 -145
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
- solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
- solace_agent_mesh/evaluation/config_loader.py +0 -657
- solace_agent_mesh/evaluation/test_case_loader.py +0 -714
- /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.b12eac43.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -49,6 +49,7 @@ from google.adk.agents.run_config import StreamingMode
|
|
|
49
49
|
|
|
50
50
|
log = logging.getLogger(__name__)
|
|
51
51
|
|
|
52
|
+
|
|
52
53
|
def _register_peer_artifacts_in_parent_context(
|
|
53
54
|
parent_task_context: "TaskExecutionContext",
|
|
54
55
|
peer_task_object: Task,
|
|
@@ -123,6 +124,30 @@ async def process_event(component, event: Event):
|
|
|
123
124
|
agent_status_sub_prefix
|
|
124
125
|
):
|
|
125
126
|
await handle_a2a_response(component, message)
|
|
127
|
+
elif hasattr(component, "trust_manager") and component.trust_manager:
|
|
128
|
+
# Check if this is a trust card message (enterprise feature)
|
|
129
|
+
try:
|
|
130
|
+
if component.trust_manager.is_trust_card_topic(topic):
|
|
131
|
+
await component.trust_manager.handle_trust_card_message(
|
|
132
|
+
message, topic
|
|
133
|
+
)
|
|
134
|
+
message.call_acknowledgements()
|
|
135
|
+
return
|
|
136
|
+
except Exception as e:
|
|
137
|
+
log.error(
|
|
138
|
+
"%s Error handling trust card message: %s",
|
|
139
|
+
component.log_identifier,
|
|
140
|
+
e,
|
|
141
|
+
)
|
|
142
|
+
message.call_acknowledgements()
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
log.warning(
|
|
146
|
+
"%s Received message on unhandled topic: %s",
|
|
147
|
+
component.log_identifier,
|
|
148
|
+
topic,
|
|
149
|
+
)
|
|
150
|
+
message.call_acknowledgements()
|
|
126
151
|
else:
|
|
127
152
|
log.warning(
|
|
128
153
|
"%s Received message on unhandled topic: %s",
|
|
@@ -137,6 +162,9 @@ async def process_event(component, event: Event):
|
|
|
137
162
|
)
|
|
138
163
|
if timer_data.get("timer_id") == component._card_publish_timer_id:
|
|
139
164
|
publish_agent_card(component)
|
|
165
|
+
else:
|
|
166
|
+
# Handle other timer events including health check timer
|
|
167
|
+
component.handle_timer_event(timer_data)
|
|
140
168
|
elif event.event_type == EventType.CACHE_EXPIRY:
|
|
141
169
|
# Delegate cache expiry handling to the component itself.
|
|
142
170
|
await component.handle_cache_expiry_event(event.data)
|
|
@@ -226,12 +254,11 @@ async def handle_a2a_request(component, message: SolaceMessage):
|
|
|
226
254
|
payload_dict = message.get_payload()
|
|
227
255
|
if not isinstance(payload_dict, dict):
|
|
228
256
|
raise ValueError("Payload is not a dictionary.")
|
|
229
|
-
|
|
230
|
-
|
|
257
|
+
|
|
231
258
|
a2a_request: A2ARequest = A2ARequest.model_validate(payload_dict)
|
|
232
259
|
jsonrpc_request_id = a2a.get_request_id(a2a_request)
|
|
233
260
|
|
|
234
|
-
# Extract properties from message user properties
|
|
261
|
+
# Extract properties from message user properties
|
|
235
262
|
client_id = message.get_user_properties().get("clientId", "default_client")
|
|
236
263
|
status_topic_from_peer = message.get_user_properties().get("a2aStatusTopic")
|
|
237
264
|
reply_topic_from_peer = message.get_user_properties().get("replyTo")
|
|
@@ -245,6 +272,90 @@ async def handle_a2a_request(component, message: SolaceMessage):
|
|
|
245
272
|
# For Send, we will generate it.
|
|
246
273
|
logical_task_id = None
|
|
247
274
|
method = a2a.get_request_method(a2a_request)
|
|
275
|
+
|
|
276
|
+
# Enterprise feature: Verify user authentication if trust manager enabled
|
|
277
|
+
verified_user_identity = None
|
|
278
|
+
if hasattr(component, "trust_manager") and component.trust_manager:
|
|
279
|
+
# Determine task_id for verification
|
|
280
|
+
if method == "tasks/cancel":
|
|
281
|
+
verification_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
|
|
282
|
+
elif method in ["message/send", "message/stream"]:
|
|
283
|
+
verification_task_id = str(a2a.get_request_id(a2a_request))
|
|
284
|
+
else:
|
|
285
|
+
verification_task_id = None
|
|
286
|
+
|
|
287
|
+
if verification_task_id:
|
|
288
|
+
try:
|
|
289
|
+
# Enterprise handles all verification logic
|
|
290
|
+
verified_user_identity = (
|
|
291
|
+
component.trust_manager.verify_request_authentication(
|
|
292
|
+
message=message,
|
|
293
|
+
task_id=verification_task_id,
|
|
294
|
+
namespace=namespace,
|
|
295
|
+
jsonrpc_request_id=jsonrpc_request_id,
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if verified_user_identity:
|
|
300
|
+
log.info(
|
|
301
|
+
"%s Successfully authenticated user '%s' for task %s",
|
|
302
|
+
component.log_identifier,
|
|
303
|
+
verified_user_identity.get("user_id"),
|
|
304
|
+
verification_task_id,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
# Authentication failed - enterprise provides error details
|
|
309
|
+
log.error(
|
|
310
|
+
"%s Authentication failed for task %s: %s",
|
|
311
|
+
component.log_identifier,
|
|
312
|
+
verification_task_id,
|
|
313
|
+
e,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Build error response using enterprise exception data if available
|
|
317
|
+
error_data = {
|
|
318
|
+
"reason": "authentication_failed",
|
|
319
|
+
"task_id": verification_task_id,
|
|
320
|
+
}
|
|
321
|
+
if hasattr(e, "create_error_response_data"):
|
|
322
|
+
error_data = e.create_error_response_data()
|
|
323
|
+
|
|
324
|
+
error_response = a2a.create_invalid_request_error_response(
|
|
325
|
+
message="Authentication failed",
|
|
326
|
+
request_id=jsonrpc_request_id,
|
|
327
|
+
data=error_data,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Determine reply topic
|
|
331
|
+
reply_topic = message.get_user_properties().get("replyTo")
|
|
332
|
+
if not reply_topic:
|
|
333
|
+
client_id = message.get_user_properties().get(
|
|
334
|
+
"clientId", "default_client"
|
|
335
|
+
)
|
|
336
|
+
reply_topic = a2a.get_client_response_topic(
|
|
337
|
+
namespace, client_id
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
component.publish_a2a_message(
|
|
341
|
+
payload=error_response.model_dump(exclude_none=True),
|
|
342
|
+
topic=reply_topic,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
message.call_acknowledgements()
|
|
347
|
+
log.debug(
|
|
348
|
+
"%s ACKed message with failed authentication",
|
|
349
|
+
component.log_identifier,
|
|
350
|
+
)
|
|
351
|
+
except Exception as ack_e:
|
|
352
|
+
log.error(
|
|
353
|
+
"%s Failed to ACK message after authentication failure: %s",
|
|
354
|
+
component.log_identifier,
|
|
355
|
+
ack_e,
|
|
356
|
+
)
|
|
357
|
+
return None
|
|
358
|
+
|
|
248
359
|
if method == "tasks/cancel":
|
|
249
360
|
logical_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
|
|
250
361
|
log.info(
|
|
@@ -534,6 +645,15 @@ async def handle_a2a_request(component, message: SolaceMessage):
|
|
|
534
645
|
"response_format": response_format,
|
|
535
646
|
"host_agent_name": agent_name,
|
|
536
647
|
}
|
|
648
|
+
|
|
649
|
+
# Store verified user identity claims in a2a_context (not the raw token)
|
|
650
|
+
if verified_user_identity:
|
|
651
|
+
a2a_context["verified_user_identity"] = verified_user_identity
|
|
652
|
+
log.debug(
|
|
653
|
+
"%s Stored verified user identity in a2a_context for task %s",
|
|
654
|
+
component.log_identifier,
|
|
655
|
+
logical_task_id,
|
|
656
|
+
)
|
|
537
657
|
log.debug(
|
|
538
658
|
"%s A2A Context (shared service model): %s",
|
|
539
659
|
component.log_identifier,
|
|
@@ -544,6 +664,18 @@ async def handle_a2a_request(component, message: SolaceMessage):
|
|
|
544
664
|
task_context = TaskExecutionContext(
|
|
545
665
|
task_id=logical_task_id, a2a_context=a2a_context
|
|
546
666
|
)
|
|
667
|
+
|
|
668
|
+
# Store auth token for peer delegation using generic security storage
|
|
669
|
+
if hasattr(component, "trust_manager") and component.trust_manager:
|
|
670
|
+
auth_token = message.get_user_properties().get("authToken")
|
|
671
|
+
if auth_token:
|
|
672
|
+
task_context.set_security_data("auth_token", auth_token)
|
|
673
|
+
log.debug(
|
|
674
|
+
"%s Stored authentication token in TaskExecutionContext security storage for task %s",
|
|
675
|
+
component.log_identifier,
|
|
676
|
+
logical_task_id,
|
|
677
|
+
)
|
|
678
|
+
|
|
547
679
|
with component.active_tasks_lock:
|
|
548
680
|
component.active_tasks[logical_task_id] = task_context
|
|
549
681
|
log.info(
|
|
@@ -794,9 +926,26 @@ def handle_agent_card_message(component, message: SolaceMessage):
|
|
|
794
926
|
break
|
|
795
927
|
|
|
796
928
|
if is_allowed:
|
|
797
|
-
|
|
929
|
+
|
|
930
|
+
# Also store in peer_agents for backward compatibility
|
|
798
931
|
component.peer_agents[agent_name] = agent_card
|
|
799
932
|
|
|
933
|
+
# Store the agent card in the registry for health tracking
|
|
934
|
+
is_new = component.agent_registry.add_or_update_agent(agent_card)
|
|
935
|
+
|
|
936
|
+
if is_new:
|
|
937
|
+
log.info(
|
|
938
|
+
"%s Registered new agent '%s' in registry.",
|
|
939
|
+
component.log_identifier,
|
|
940
|
+
agent_name,
|
|
941
|
+
)
|
|
942
|
+
else:
|
|
943
|
+
log.debug(
|
|
944
|
+
"%s Updated existing agent '%s' in registry.",
|
|
945
|
+
component.log_identifier,
|
|
946
|
+
agent_name,
|
|
947
|
+
)
|
|
948
|
+
|
|
800
949
|
message.call_acknowledgements()
|
|
801
950
|
|
|
802
951
|
except Exception as e:
|
|
@@ -1438,6 +1587,7 @@ def publish_agent_card(component):
|
|
|
1438
1587
|
dynamic_url = f"solace:{agent_request_topic}"
|
|
1439
1588
|
|
|
1440
1589
|
# Define unique URIs for our custom extensions.
|
|
1590
|
+
DEPLOYMENT_EXTENSION_URI = "https://solace.com/a2a/extensions/sam/deployment"
|
|
1441
1591
|
PEER_TOPOLOGY_EXTENSION_URI = (
|
|
1442
1592
|
"https://solace.com/a2a/extensions/peer-agent-topology"
|
|
1443
1593
|
)
|
|
@@ -1446,6 +1596,24 @@ def publish_agent_card(component):
|
|
|
1446
1596
|
|
|
1447
1597
|
extensions_list = []
|
|
1448
1598
|
|
|
1599
|
+
# Create the extension object for deployment tracking.
|
|
1600
|
+
deployment_config = component.get_config("deployment", {})
|
|
1601
|
+
deployment_id = deployment_config.get("id")
|
|
1602
|
+
|
|
1603
|
+
if deployment_id:
|
|
1604
|
+
deployment_extension = AgentExtension(
|
|
1605
|
+
uri=DEPLOYMENT_EXTENSION_URI,
|
|
1606
|
+
description="SAM deployment tracking for rolling updates",
|
|
1607
|
+
required=False,
|
|
1608
|
+
params={"id": deployment_id}
|
|
1609
|
+
)
|
|
1610
|
+
extensions_list.append(deployment_extension)
|
|
1611
|
+
log.debug(
|
|
1612
|
+
"%s Added deployment extension with ID: %s",
|
|
1613
|
+
component.log_identifier,
|
|
1614
|
+
deployment_id
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1449
1617
|
# Create the extension object for peer agents.
|
|
1450
1618
|
if peer_agents:
|
|
1451
1619
|
peer_topology_extension = AgentExtension(
|
|
@@ -1542,81 +1710,95 @@ def handle_sam_event(component, message, topic):
|
|
|
1542
1710
|
"""Handle incoming SAM system events."""
|
|
1543
1711
|
try:
|
|
1544
1712
|
payload = message.get_payload()
|
|
1545
|
-
|
|
1713
|
+
|
|
1546
1714
|
if not isinstance(payload, dict):
|
|
1547
1715
|
log.warning("Invalid SAM event payload - not a dict")
|
|
1548
1716
|
message.call_acknowledgements()
|
|
1549
1717
|
return
|
|
1550
|
-
|
|
1718
|
+
|
|
1551
1719
|
event_type = payload.get("event_type")
|
|
1552
1720
|
if not event_type:
|
|
1553
1721
|
log.warning("SAM event missing event_type field")
|
|
1554
1722
|
message.call_acknowledgements()
|
|
1555
1723
|
return
|
|
1556
|
-
|
|
1724
|
+
|
|
1557
1725
|
log.info("%s Received SAM event: %s", component.log_identifier, event_type)
|
|
1558
|
-
|
|
1726
|
+
|
|
1559
1727
|
if event_type == "session.deleted":
|
|
1560
1728
|
data = payload.get("data", {})
|
|
1561
1729
|
session_id = data.get("session_id")
|
|
1562
1730
|
user_id = data.get("user_id")
|
|
1563
1731
|
agent_id = data.get("agent_id")
|
|
1564
|
-
|
|
1732
|
+
|
|
1565
1733
|
if not all([session_id, user_id, agent_id]):
|
|
1566
1734
|
log.warning("Missing required fields in session.deleted event")
|
|
1567
1735
|
message.call_acknowledgements()
|
|
1568
1736
|
return
|
|
1569
|
-
|
|
1737
|
+
|
|
1570
1738
|
current_agent = component.get_config("agent_name")
|
|
1571
|
-
|
|
1739
|
+
|
|
1572
1740
|
if agent_id == current_agent:
|
|
1573
|
-
log.info(
|
|
1574
|
-
|
|
1575
|
-
|
|
1741
|
+
log.info(
|
|
1742
|
+
"%s Processing session.deleted event for session %s",
|
|
1743
|
+
component.log_identifier,
|
|
1744
|
+
session_id,
|
|
1745
|
+
)
|
|
1746
|
+
asyncio.create_task(
|
|
1747
|
+
cleanup_agent_session(component, session_id, user_id)
|
|
1748
|
+
)
|
|
1576
1749
|
else:
|
|
1577
|
-
log.debug(
|
|
1750
|
+
log.debug(
|
|
1751
|
+
"Session deletion event for different agent: %s != %s",
|
|
1752
|
+
agent_id,
|
|
1753
|
+
current_agent,
|
|
1754
|
+
)
|
|
1578
1755
|
else:
|
|
1579
1756
|
log.debug("Unhandled SAM event type: %s", event_type)
|
|
1580
|
-
|
|
1757
|
+
|
|
1581
1758
|
message.call_acknowledgements()
|
|
1582
|
-
|
|
1759
|
+
|
|
1583
1760
|
except Exception as e:
|
|
1584
1761
|
log.error("Error handling SAM event %s: %s", topic, e)
|
|
1585
1762
|
message.call_acknowledgements()
|
|
1586
1763
|
|
|
1587
1764
|
|
|
1588
|
-
|
|
1589
1765
|
async def cleanup_agent_session(component, session_id: str, user_id: str):
|
|
1590
1766
|
"""Clean up agent-side session data."""
|
|
1591
1767
|
try:
|
|
1592
1768
|
log.info("Starting cleanup for session %s, user %s", session_id, user_id)
|
|
1593
|
-
|
|
1594
|
-
if hasattr(component,
|
|
1769
|
+
|
|
1770
|
+
if hasattr(component, "session_service") and component.session_service:
|
|
1595
1771
|
agent_name = component.get_config("agent_name")
|
|
1596
|
-
log.info(
|
|
1772
|
+
log.info(
|
|
1773
|
+
"Deleting session %s from agent %s session service",
|
|
1774
|
+
session_id,
|
|
1775
|
+
agent_name,
|
|
1776
|
+
)
|
|
1597
1777
|
await component.session_service.delete_session(
|
|
1598
|
-
app_name=agent_name,
|
|
1599
|
-
user_id=user_id,
|
|
1600
|
-
session_id=session_id
|
|
1778
|
+
app_name=agent_name, user_id=user_id, session_id=session_id
|
|
1601
1779
|
)
|
|
1602
1780
|
log.info("Successfully deleted session %s from session service", session_id)
|
|
1603
1781
|
else:
|
|
1604
1782
|
log.info("No session service available for cleanup")
|
|
1605
|
-
|
|
1783
|
+
|
|
1606
1784
|
with component.active_tasks_lock:
|
|
1607
1785
|
tasks_to_cancel = []
|
|
1608
1786
|
for task_id, context in component.active_tasks.items():
|
|
1609
|
-
if (
|
|
1610
|
-
context
|
|
1787
|
+
if (
|
|
1788
|
+
hasattr(context, "a2a_context")
|
|
1789
|
+
and context.a2a_context.get("session_id") == session_id
|
|
1790
|
+
):
|
|
1611
1791
|
tasks_to_cancel.append(task_id)
|
|
1612
|
-
|
|
1792
|
+
|
|
1613
1793
|
for task_id in tasks_to_cancel:
|
|
1614
1794
|
context = component.active_tasks.get(task_id)
|
|
1615
1795
|
if context:
|
|
1616
1796
|
context.cancel()
|
|
1617
|
-
log.info(
|
|
1618
|
-
|
|
1797
|
+
log.info(
|
|
1798
|
+
"Cancelled task %s for deleted session %s", task_id, session_id
|
|
1799
|
+
)
|
|
1800
|
+
|
|
1619
1801
|
log.info("Session cleanup completed for session %s", session_id)
|
|
1620
|
-
|
|
1802
|
+
|
|
1621
1803
|
except Exception as e:
|
|
1622
1804
|
log.error("Error cleaning up session %s: %s", session_id, e)
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Concrete App class for the A2A-over-HTTPS proxy.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, Type
|
|
8
|
+
|
|
9
|
+
from pydantic import ValidationError
|
|
10
|
+
from solace_ai_connector.common.log import log
|
|
11
|
+
|
|
12
|
+
from ..base.app import BaseProxyApp
|
|
13
|
+
from ..base.component import BaseProxyComponent
|
|
14
|
+
from .component import A2AProxyComponent
|
|
15
|
+
from .config import A2AProxyAppConfig
|
|
16
|
+
|
|
17
|
+
info = {
|
|
18
|
+
"class_name": "A2AProxyApp",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class A2AProxyApp(BaseProxyApp):
|
|
23
|
+
"""
|
|
24
|
+
Concrete App class for the A2A-over-HTTPS proxy.
|
|
25
|
+
|
|
26
|
+
Extends the BaseProxyApp to add specific configuration validation for
|
|
27
|
+
A2A agents (e.g., URL, authentication).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# Keep app_schema for documentation purposes, but it's now redundant
|
|
31
|
+
# The Pydantic models handle all validation
|
|
32
|
+
app_schema = {}
|
|
33
|
+
|
|
34
|
+
def __init__(self, app_info: Dict[str, Any], **kwargs):
|
|
35
|
+
app_info["class_name"] = "A2AProxyApp"
|
|
36
|
+
|
|
37
|
+
# Validate A2A-specific configuration before calling super().__init__
|
|
38
|
+
app_config_dict = app_info.get("app_config", {})
|
|
39
|
+
try:
|
|
40
|
+
# Validate with A2A-specific config model
|
|
41
|
+
app_config = A2AProxyAppConfig.model_validate_and_clean(app_config_dict)
|
|
42
|
+
# Overwrite the raw dict with the validated object
|
|
43
|
+
app_info["app_config"] = app_config
|
|
44
|
+
log.debug("A2A proxy configuration validated successfully.")
|
|
45
|
+
except ValidationError as e:
|
|
46
|
+
log.error("A2A proxy configuration validation failed:\n%s", e)
|
|
47
|
+
raise ValueError(f"Invalid A2A proxy configuration: {e}") from e
|
|
48
|
+
|
|
49
|
+
super().__init__(app_info, **kwargs)
|
|
50
|
+
|
|
51
|
+
def _get_component_class(self) -> Type[BaseProxyComponent]:
|
|
52
|
+
"""
|
|
53
|
+
Returns the concrete A2AProxyComponent class.
|
|
54
|
+
"""
|
|
55
|
+
return A2AProxyComponent
|