solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- 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 +40 -1
- 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 +619 -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 +9 -3
- solace_agent_mesh/agent/sac/component.py +160 -8
- 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/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.20feee82.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.0d198646.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-1761165361160.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1761165361160.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-BGTaW0uv.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 +73 -1
- solace_agent_mesh/common/agent_registry.py +83 -3
- solace_agent_mesh/common/constants.py +3 -1
- 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 +1 -1
- solace_agent_mesh/gateway/base/component.py +8 -1
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- solace_agent_mesh/gateway/http_sse/component.py +98 -2
- solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
- solace_agent_mesh/gateway/http_sse/main.py +2 -1
- 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/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.0.dist-info}/METADATA +47 -21
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/RECORD +162 -141
- 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.20feee82.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,6 +14,8 @@ from .artifact import (
|
|
|
14
14
|
get_artifact_id,
|
|
15
15
|
get_artifact_name,
|
|
16
16
|
get_parts_from_artifact,
|
|
17
|
+
get_text_content_from_artifact,
|
|
18
|
+
is_text_only_artifact,
|
|
17
19
|
update_artifact_parts,
|
|
18
20
|
prepare_file_part_for_publishing,
|
|
19
21
|
resolve_file_part_uri,
|
|
@@ -25,6 +27,8 @@ from .events import (
|
|
|
25
27
|
get_artifact_from_artifact_update,
|
|
26
28
|
get_data_parts_from_status_update,
|
|
27
29
|
get_message_from_status_update,
|
|
30
|
+
is_task_artifact_update,
|
|
31
|
+
is_task_status_update,
|
|
28
32
|
)
|
|
29
33
|
from .message import (
|
|
30
34
|
create_agent_data_message,
|
|
@@ -51,6 +55,11 @@ from .message import (
|
|
|
51
55
|
get_text_from_message,
|
|
52
56
|
get_text_from_text_part,
|
|
53
57
|
get_uri_from_file_part,
|
|
58
|
+
is_data_part,
|
|
59
|
+
is_file_part,
|
|
60
|
+
is_file_part_bytes,
|
|
61
|
+
is_file_part_uri,
|
|
62
|
+
is_text_part,
|
|
54
63
|
)
|
|
55
64
|
from .protocol import (
|
|
56
65
|
create_cancel_task_request,
|
|
@@ -92,8 +101,11 @@ from .protocol import (
|
|
|
92
101
|
get_response_result,
|
|
93
102
|
get_task_id_from_cancel_request,
|
|
94
103
|
extract_task_id_from_topic,
|
|
104
|
+
is_client_event,
|
|
105
|
+
is_message_object,
|
|
95
106
|
subscription_to_regex,
|
|
96
107
|
topic_matches_subscription,
|
|
108
|
+
unpack_client_event,
|
|
97
109
|
)
|
|
98
110
|
from .task import (
|
|
99
111
|
create_final_task,
|
|
@@ -122,6 +134,8 @@ __all__ = [
|
|
|
122
134
|
"get_artifact_id",
|
|
123
135
|
"get_artifact_name",
|
|
124
136
|
"get_parts_from_artifact",
|
|
137
|
+
"get_text_content_from_artifact",
|
|
138
|
+
"is_text_only_artifact",
|
|
125
139
|
"update_artifact_parts",
|
|
126
140
|
"prepare_file_part_for_publishing",
|
|
127
141
|
"resolve_file_part_uri",
|
|
@@ -132,6 +146,8 @@ __all__ = [
|
|
|
132
146
|
"get_artifact_from_artifact_update",
|
|
133
147
|
"get_data_parts_from_status_update",
|
|
134
148
|
"get_message_from_status_update",
|
|
149
|
+
"is_task_artifact_update",
|
|
150
|
+
"is_task_status_update",
|
|
135
151
|
# message.py
|
|
136
152
|
"create_agent_data_message",
|
|
137
153
|
"create_agent_parts_message",
|
|
@@ -157,6 +173,11 @@ __all__ = [
|
|
|
157
173
|
"get_text_from_message",
|
|
158
174
|
"get_uri_from_file_part",
|
|
159
175
|
"get_text_from_text_part",
|
|
176
|
+
"is_data_part",
|
|
177
|
+
"is_file_part",
|
|
178
|
+
"is_file_part_bytes",
|
|
179
|
+
"is_file_part_uri",
|
|
180
|
+
"is_text_part",
|
|
160
181
|
# protocol.py
|
|
161
182
|
"create_cancel_task_request",
|
|
162
183
|
"create_error_response",
|
|
@@ -197,8 +218,11 @@ __all__ = [
|
|
|
197
218
|
"get_response_result",
|
|
198
219
|
"get_task_id_from_cancel_request",
|
|
199
220
|
"extract_task_id_from_topic",
|
|
221
|
+
"is_client_event",
|
|
222
|
+
"is_message_object",
|
|
200
223
|
"subscription_to_regex",
|
|
201
224
|
"topic_matches_subscription",
|
|
225
|
+
"unpack_client_event",
|
|
202
226
|
# task.py
|
|
203
227
|
"create_final_task",
|
|
204
228
|
"create_initial_task",
|
|
@@ -327,3 +327,42 @@ def get_parts_from_artifact(artifact: Artifact) -> List[ContentPart]:
|
|
|
327
327
|
A list of the unwrapped content parts.
|
|
328
328
|
"""
|
|
329
329
|
return [part.root for part in artifact.parts]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def is_text_only_artifact(artifact: Artifact) -> bool:
|
|
333
|
+
"""
|
|
334
|
+
Checks if an artifact contains only TextParts.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
artifact: The Artifact object to check.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
True if all parts are TextParts, False otherwise.
|
|
341
|
+
"""
|
|
342
|
+
if not artifact.parts:
|
|
343
|
+
return False
|
|
344
|
+
|
|
345
|
+
for part in artifact.parts:
|
|
346
|
+
if not isinstance(part.root, TextPart):
|
|
347
|
+
return False
|
|
348
|
+
|
|
349
|
+
return True
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def get_text_content_from_artifact(artifact: Artifact) -> List[str]:
|
|
353
|
+
"""
|
|
354
|
+
Extracts all text content from TextParts in an artifact.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
artifact: The Artifact object to extract text from.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
A list of text strings from all TextParts. Returns empty list if no TextParts found.
|
|
361
|
+
"""
|
|
362
|
+
text_content = []
|
|
363
|
+
|
|
364
|
+
for part in artifact.parts:
|
|
365
|
+
if isinstance(part.root, TextPart):
|
|
366
|
+
text_content.append(part.root.text)
|
|
367
|
+
|
|
368
|
+
return text_content
|
|
@@ -181,3 +181,32 @@ def get_artifact_from_artifact_update(
|
|
|
181
181
|
if event:
|
|
182
182
|
return event.artifact
|
|
183
183
|
return None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# --- Type Checking Helpers ---
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def is_task_status_update(obj: Any) -> bool:
|
|
190
|
+
"""
|
|
191
|
+
Checks if an object is a TaskStatusUpdateEvent.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
obj: The object to check.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
True if the object is a TaskStatusUpdateEvent, False otherwise.
|
|
198
|
+
"""
|
|
199
|
+
return isinstance(obj, TaskStatusUpdateEvent)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def is_task_artifact_update(obj: Any) -> bool:
|
|
203
|
+
"""
|
|
204
|
+
Checks if an object is a TaskArtifactUpdateEvent.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
obj: The object to check.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
True if the object is a TaskArtifactUpdateEvent, False otherwise.
|
|
211
|
+
"""
|
|
212
|
+
return isinstance(obj, TaskArtifactUpdateEvent)
|
|
@@ -305,3 +305,71 @@ def get_filename_from_file_part(part: FilePart) -> Optional[str]:
|
|
|
305
305
|
def get_mimetype_from_file_part(part: FilePart) -> Optional[str]:
|
|
306
306
|
"""Safely retrieves the MIME type from a FilePart."""
|
|
307
307
|
return part.file.mime_type
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# --- Type Checking Helpers ---
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def is_text_part(part: Part) -> bool:
|
|
314
|
+
"""
|
|
315
|
+
Checks if a Part contains a TextPart.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
part: The Part object to check.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
True if the part contains a TextPart, False otherwise.
|
|
322
|
+
"""
|
|
323
|
+
return isinstance(part.root, TextPart)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def is_file_part(part: Part) -> bool:
|
|
327
|
+
"""
|
|
328
|
+
Checks if a Part contains a FilePart.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
part: The Part object to check.
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
True if the part contains a FilePart, False otherwise.
|
|
335
|
+
"""
|
|
336
|
+
return isinstance(part.root, FilePart)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def is_data_part(part: Part) -> bool:
|
|
340
|
+
"""
|
|
341
|
+
Checks if a Part contains a DataPart.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
part: The Part object to check.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
True if the part contains a DataPart, False otherwise.
|
|
348
|
+
"""
|
|
349
|
+
return isinstance(part.root, DataPart)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def is_file_part_bytes(part: FilePart) -> bool:
|
|
353
|
+
"""
|
|
354
|
+
Checks if a FilePart uses FileWithBytes (embedded content).
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
part: The FilePart object to check.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
True if the file content is embedded as bytes, False otherwise.
|
|
361
|
+
"""
|
|
362
|
+
return isinstance(part.file, FileWithBytes)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def is_file_part_uri(part: FilePart) -> bool:
|
|
366
|
+
"""
|
|
367
|
+
Checks if a FilePart uses FileWithUri (reference to external content).
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
part: The FilePart object to check.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
True if the file content is a URI reference, False otherwise.
|
|
374
|
+
"""
|
|
375
|
+
return isinstance(part.file, FileWithUri)
|
|
@@ -5,7 +5,7 @@ parsing of JSON-RPC requests and responses.
|
|
|
5
5
|
import logging
|
|
6
6
|
import re
|
|
7
7
|
import uuid
|
|
8
|
-
from typing import Any, Dict, Optional, Union
|
|
8
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
9
9
|
|
|
10
10
|
from a2a.types import (
|
|
11
11
|
A2ARequest,
|
|
@@ -532,3 +532,75 @@ def extract_task_id_from_topic(
|
|
|
532
532
|
subscription_pattern,
|
|
533
533
|
)
|
|
534
534
|
return None
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# --- Client Event Helpers ---
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def is_client_event(obj: Any) -> bool:
|
|
541
|
+
"""
|
|
542
|
+
Checks if an object is a ClientEvent tuple (Task, UpdateEvent).
|
|
543
|
+
|
|
544
|
+
A ClientEvent is a tuple with 2 elements where the first element is a Task
|
|
545
|
+
and the second is either a TaskStatusUpdateEvent, TaskArtifactUpdateEvent, or None.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
obj: The object to check.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
True if the object is a ClientEvent tuple, False otherwise.
|
|
552
|
+
"""
|
|
553
|
+
if not isinstance(obj, tuple) or len(obj) != 2:
|
|
554
|
+
return False
|
|
555
|
+
|
|
556
|
+
task, update_event = obj
|
|
557
|
+
|
|
558
|
+
# First element must be a Task
|
|
559
|
+
if not isinstance(task, Task):
|
|
560
|
+
return False
|
|
561
|
+
|
|
562
|
+
# Second element must be an update event or None
|
|
563
|
+
if update_event is not None and not isinstance(
|
|
564
|
+
update_event, (TaskStatusUpdateEvent, TaskArtifactUpdateEvent)
|
|
565
|
+
):
|
|
566
|
+
return False
|
|
567
|
+
|
|
568
|
+
return True
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def is_message_object(obj: Any) -> bool:
|
|
572
|
+
"""
|
|
573
|
+
Checks if an object is a Message.
|
|
574
|
+
|
|
575
|
+
Args:
|
|
576
|
+
obj: The object to check.
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
True if the object is a Message, False otherwise.
|
|
580
|
+
"""
|
|
581
|
+
return isinstance(obj, Message)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def unpack_client_event(
|
|
585
|
+
event: tuple,
|
|
586
|
+
) -> Tuple[Task, Optional[Union[TaskStatusUpdateEvent, TaskArtifactUpdateEvent]]]:
|
|
587
|
+
"""
|
|
588
|
+
Safely unpacks a ClientEvent tuple into its components.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
event: A ClientEvent tuple (Task, UpdateEvent).
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
A tuple of (Task, Optional[UpdateEvent]) where UpdateEvent can be
|
|
595
|
+
TaskStatusUpdateEvent, TaskArtifactUpdateEvent, or None.
|
|
596
|
+
|
|
597
|
+
Raises:
|
|
598
|
+
ValueError: If the event is not a valid ClientEvent tuple.
|
|
599
|
+
"""
|
|
600
|
+
if not is_client_event(event):
|
|
601
|
+
raise ValueError(
|
|
602
|
+
f"Expected a ClientEvent tuple, got {type(event).__name__}"
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
task, update_event = event
|
|
606
|
+
return task, update_event
|
|
@@ -4,26 +4,37 @@ Consolidated from src/tools/common/agent_registry.py and src/tools/a2a_cli_clien
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import threading
|
|
7
|
-
|
|
7
|
+
import time
|
|
8
|
+
from typing import Dict, List, Optional, Tuple
|
|
9
|
+
import logging
|
|
8
10
|
|
|
9
11
|
from a2a.types import AgentCard
|
|
10
12
|
|
|
13
|
+
log = logging.getLogger(__name__)
|
|
11
14
|
|
|
12
15
|
class AgentRegistry:
|
|
13
|
-
"""Stores and manages discovered AgentCards."""
|
|
16
|
+
"""Stores and manages discovered AgentCards with health tracking."""
|
|
14
17
|
|
|
15
18
|
def __init__(self):
|
|
16
19
|
self._agents: Dict[str, AgentCard] = {}
|
|
20
|
+
self._last_seen: Dict[str, float] = {} # Timestamp of last agent card received
|
|
17
21
|
self._lock = threading.Lock()
|
|
18
22
|
|
|
19
23
|
def add_or_update_agent(self, agent_card: AgentCard):
|
|
20
24
|
"""Adds a new agent or updates an existing one."""
|
|
25
|
+
|
|
21
26
|
if not agent_card or not agent_card.name:
|
|
22
|
-
|
|
27
|
+
log.warning("Attempted to register agent with invalid agent card or missing name")
|
|
28
|
+
return False
|
|
23
29
|
|
|
24
30
|
with self._lock:
|
|
25
31
|
is_new = agent_card.name not in self._agents
|
|
32
|
+
current_time = time.time()
|
|
33
|
+
|
|
34
|
+
# Store the agent information
|
|
26
35
|
self._agents[agent_card.name] = agent_card
|
|
36
|
+
self._last_seen[agent_card.name] = current_time
|
|
37
|
+
|
|
27
38
|
return is_new
|
|
28
39
|
|
|
29
40
|
def get_agent(self, agent_name: str) -> Optional[AgentCard]:
|
|
@@ -35,8 +46,77 @@ class AgentRegistry:
|
|
|
35
46
|
"""Returns a sorted list of discovered agent names."""
|
|
36
47
|
with self._lock:
|
|
37
48
|
return sorted(list(self._agents.keys()))
|
|
49
|
+
|
|
50
|
+
def get_last_seen(self, agent_name: str) -> Optional[float]:
|
|
51
|
+
"""Returns the timestamp when the agent was last seen."""
|
|
52
|
+
with self._lock:
|
|
53
|
+
return self._last_seen.get(agent_name)
|
|
54
|
+
|
|
55
|
+
def check_ttl_expired(self, agent_name: str, ttl_seconds: int) -> Tuple[bool, int]:
|
|
56
|
+
"""
|
|
57
|
+
Checks if an agent's TTL has expired.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
agent_name: The name of the agent to check
|
|
61
|
+
ttl_seconds: The TTL in seconds
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
A tuple of (is_expired, seconds_since_last_seen)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
with self._lock:
|
|
68
|
+
if agent_name not in self._last_seen:
|
|
69
|
+
log.debug("Attempted to check TTL for non-existent agent '%s'", agent_name)
|
|
70
|
+
return False, 0
|
|
71
|
+
|
|
72
|
+
last_seen_time = self._last_seen.get(agent_name)
|
|
73
|
+
current_time = time.time()
|
|
74
|
+
time_since_last_seen = int(current_time - last_seen_time) if last_seen_time else 0
|
|
75
|
+
|
|
76
|
+
is_expired = time_since_last_seen > ttl_seconds
|
|
77
|
+
|
|
78
|
+
if is_expired:
|
|
79
|
+
log.warning(
|
|
80
|
+
"AGENT HEALTH CRITICAL: Agent '%s' TTL expired. "
|
|
81
|
+
"Last seen: %s seconds ago, TTL: %d seconds",
|
|
82
|
+
agent_name,
|
|
83
|
+
time_since_last_seen,
|
|
84
|
+
ttl_seconds
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return is_expired, time_since_last_seen
|
|
88
|
+
|
|
89
|
+
def remove_agent(self, agent_name: str) -> bool:
|
|
90
|
+
"""Removes an agent from the registry."""
|
|
91
|
+
|
|
92
|
+
with self._lock:
|
|
93
|
+
if agent_name in self._agents:
|
|
94
|
+
# Get agent details before removal for logging
|
|
95
|
+
last_seen_time = self._last_seen.get(agent_name)
|
|
96
|
+
current_time = time.time()
|
|
97
|
+
time_since_last_seen = int(current_time - last_seen_time) if last_seen_time else "unknown"
|
|
98
|
+
|
|
99
|
+
# Log detailed information about the agent being removed
|
|
100
|
+
log.warning(
|
|
101
|
+
"AGENT DE-REGISTRATION: Removing agent '%s' from registry. "
|
|
102
|
+
"Last seen: %s seconds ago",
|
|
103
|
+
agent_name,
|
|
104
|
+
time_since_last_seen
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Remove the agent from all tracking dictionaries
|
|
108
|
+
del self._agents[agent_name]
|
|
109
|
+
if agent_name in self._last_seen:
|
|
110
|
+
del self._last_seen[agent_name]
|
|
111
|
+
|
|
112
|
+
log.info("Agent '%s' successfully removed from registry", agent_name)
|
|
113
|
+
return True
|
|
114
|
+
else:
|
|
115
|
+
log.debug("Attempted to remove non-existent agent '%s' from registry", agent_name)
|
|
116
|
+
return False
|
|
38
117
|
|
|
39
118
|
def clear(self):
|
|
40
119
|
"""Clears all registered agents."""
|
|
41
120
|
with self._lock:
|
|
42
121
|
self._agents.clear()
|
|
122
|
+
self._last_seen.clear()
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
DEFAULT_COMMUNICATION_TIMEOUT = 600 # 10 minutes
|
|
3
|
+
HEALTH_CHECK_TTL_SECONDS = 60 # 60 seconds - time after which a health check is considered stale
|
|
4
|
+
HEALTH_CHECK_INTERVAL_SECONDS = 10 # 10 seconds - interval between health checks
|
|
3
5
|
TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY = 200000 # maximum number of characters that can be loaded from a text artifact
|
|
4
|
-
TEXT_ARTIFACT_CONTEXT_DEFAULT_LENGTH = 100000 # default number of characters to load from a text artifact
|
|
6
|
+
TEXT_ARTIFACT_CONTEXT_DEFAULT_LENGTH = 100000 # default number of characters to load from a text artifact
|
|
@@ -58,3 +58,15 @@ class SamConfigBase(BaseModel):
|
|
|
58
58
|
def __iter__(self):
|
|
59
59
|
"""Provides dict-like iteration over keys."""
|
|
60
60
|
return iter(self.model_dump())
|
|
61
|
+
|
|
62
|
+
def pop(self, key: str, default: Any = None) -> Any:
|
|
63
|
+
"""
|
|
64
|
+
Provides dict-like .pop() method.
|
|
65
|
+
Removes the attribute and returns its value, or default if not present.
|
|
66
|
+
"""
|
|
67
|
+
if hasattr(self, key):
|
|
68
|
+
value = getattr(self, key)
|
|
69
|
+
# Set to None rather than deleting, as Pydantic models don't support delattr
|
|
70
|
+
setattr(self, key, None)
|
|
71
|
+
return value
|
|
72
|
+
return default
|
|
@@ -34,7 +34,7 @@ AGENT_DEFAULTS = {
|
|
|
34
34
|
"enable_embed_resolution": True,
|
|
35
35
|
"enable_artifact_content_instruction": True,
|
|
36
36
|
"tools": "[]",
|
|
37
|
-
"session_service_type":
|
|
37
|
+
"session_service_type": "sql",
|
|
38
38
|
"session_service_behavior": "PERSISTENT",
|
|
39
39
|
"artifact_service_type": USE_DEFAULT_SHARED_ARTIFACT,
|
|
40
40
|
"artifact_service_base_path": "/tmp/samv2",
|