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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic configuration models for proxy applications.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Literal, Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import Field, model_validator
|
|
8
|
+
|
|
9
|
+
from ....common.utils.pydantic_utils import SamConfigBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ArtifactServiceConfig(SamConfigBase):
|
|
13
|
+
"""Configuration for the shared Artifact Service."""
|
|
14
|
+
|
|
15
|
+
type: str = Field(
|
|
16
|
+
..., description="Service type (e.g., 'memory', 'gcs', 'filesystem')."
|
|
17
|
+
)
|
|
18
|
+
base_path: Optional[str] = Field(
|
|
19
|
+
default=None,
|
|
20
|
+
description="Base directory path (required for type 'filesystem').",
|
|
21
|
+
)
|
|
22
|
+
bucket_name: Optional[str] = Field(
|
|
23
|
+
default=None, description="GCS bucket name (required for type 'gcs')."
|
|
24
|
+
)
|
|
25
|
+
artifact_scope: Literal["namespace", "app", "custom"] = Field(
|
|
26
|
+
default="namespace", description="Process-wide scope for all artifact services."
|
|
27
|
+
)
|
|
28
|
+
artifact_scope_value: Optional[str] = Field(
|
|
29
|
+
default=None,
|
|
30
|
+
description="Custom identifier for artifact scope (required if artifact_scope is 'custom').",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@model_validator(mode="after")
|
|
34
|
+
def check_artifact_scope(self) -> "ArtifactServiceConfig":
|
|
35
|
+
if self.artifact_scope == "custom" and not self.artifact_scope_value:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
"'artifact_scope_value' is required when 'artifact_scope' is 'custom'."
|
|
38
|
+
)
|
|
39
|
+
if self.artifact_scope != "custom" and self.artifact_scope_value:
|
|
40
|
+
from solace_ai_connector.common.log import log
|
|
41
|
+
log.warning(
|
|
42
|
+
"Configuration Warning: 'artifact_scope_value' is ignored when 'artifact_scope' is not 'custom'."
|
|
43
|
+
)
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ProxiedAgentConfig(SamConfigBase):
|
|
48
|
+
"""Base configuration for a proxied agent."""
|
|
49
|
+
|
|
50
|
+
name: str = Field(
|
|
51
|
+
...,
|
|
52
|
+
description="The name the agent will have on the Solace mesh.",
|
|
53
|
+
)
|
|
54
|
+
request_timeout_seconds: Optional[int] = Field(
|
|
55
|
+
default=None,
|
|
56
|
+
description="Optional timeout override for this specific agent.",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BaseProxyAppConfig(SamConfigBase):
|
|
61
|
+
"""Base configuration for all proxy applications."""
|
|
62
|
+
|
|
63
|
+
namespace: str = Field(
|
|
64
|
+
...,
|
|
65
|
+
description="Absolute topic prefix for A2A communication (e.g., 'myorg/dev').",
|
|
66
|
+
)
|
|
67
|
+
proxied_agents: List[ProxiedAgentConfig] = Field(
|
|
68
|
+
...,
|
|
69
|
+
min_length=1,
|
|
70
|
+
description="A list of downstream agents to be proxied.",
|
|
71
|
+
)
|
|
72
|
+
artifact_service: ArtifactServiceConfig = Field(
|
|
73
|
+
default_factory=lambda: ArtifactServiceConfig(type="memory"),
|
|
74
|
+
description="Configuration for the shared Artifact Service.",
|
|
75
|
+
)
|
|
76
|
+
discovery_interval_seconds: int = Field(
|
|
77
|
+
default=60,
|
|
78
|
+
ge=0,
|
|
79
|
+
description="Interval (seconds) to re-fetch agent cards. <= 0 disables periodic discovery.",
|
|
80
|
+
)
|
|
81
|
+
default_request_timeout_seconds: int = Field(
|
|
82
|
+
default=300,
|
|
83
|
+
gt=0,
|
|
84
|
+
description="Default timeout in seconds for requests to downstream agents.",
|
|
85
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Encapsulates the runtime state for a single, in-flight proxied agent task.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ProxyTaskContext:
|
|
11
|
+
"""
|
|
12
|
+
A class to hold all runtime state and control mechanisms for a single proxied agent task.
|
|
13
|
+
This object is created when a task is initiated and destroyed when it completes.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
task_id: str
|
|
17
|
+
a2a_context: Dict[str, Any]
|
|
@@ -23,7 +23,13 @@ from ...common.a2a import (
|
|
|
23
23
|
get_agent_status_subscription_topic,
|
|
24
24
|
get_sam_events_subscription_topic,
|
|
25
25
|
)
|
|
26
|
-
from ...common.constants import
|
|
26
|
+
from ...common.constants import (
|
|
27
|
+
DEFAULT_COMMUNICATION_TIMEOUT,
|
|
28
|
+
TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY,
|
|
29
|
+
TEXT_ARTIFACT_CONTEXT_DEFAULT_LENGTH,
|
|
30
|
+
HEALTH_CHECK_TTL_SECONDS,
|
|
31
|
+
HEALTH_CHECK_INTERVAL_SECONDS,
|
|
32
|
+
)
|
|
27
33
|
from ...agent.sac.component import SamAgentComponent
|
|
28
34
|
from ...agent.utils.artifact_helpers import DEFAULT_SCHEMA_MAX_KEYS
|
|
29
35
|
from ...common.utils.pydantic_utils import SamConfigBase
|
|
@@ -31,6 +37,14 @@ from ..tools.tool_config_types import AnyToolConfig
|
|
|
31
37
|
|
|
32
38
|
log = logging.getLogger(__name__)
|
|
33
39
|
|
|
40
|
+
# Try to import TrustManagerConfig from enterprise repo
|
|
41
|
+
try:
|
|
42
|
+
from solace_agent_mesh_enterprise.common.trust.config import TrustManagerConfig
|
|
43
|
+
|
|
44
|
+
except ImportError:
|
|
45
|
+
# Enterprise features not available - create a placeholder type
|
|
46
|
+
TrustManagerConfig = Dict[str, Any] # type: ignore
|
|
47
|
+
|
|
34
48
|
info = {
|
|
35
49
|
"class_name": "SamAgentApp",
|
|
36
50
|
"description": "Custom App class for SAM Agent Host with namespace prefixing and automatic subscription generation.",
|
|
@@ -79,6 +93,14 @@ class AgentDiscoveryConfig(SamConfigBase):
|
|
|
79
93
|
enabled: bool = Field(
|
|
80
94
|
default=True, description="Enable discovery and instruction injection."
|
|
81
95
|
)
|
|
96
|
+
health_check_ttl_seconds: int = Field(
|
|
97
|
+
default=HEALTH_CHECK_TTL_SECONDS,
|
|
98
|
+
description="Time-to-live in seconds after which an unresponsive agent is de-registered.",
|
|
99
|
+
)
|
|
100
|
+
health_check_interval_seconds: int = Field(
|
|
101
|
+
default=HEALTH_CHECK_INTERVAL_SECONDS,
|
|
102
|
+
description="Interval in seconds between health checks.",
|
|
103
|
+
)
|
|
82
104
|
|
|
83
105
|
|
|
84
106
|
class InterAgentCommunicationConfig(SamConfigBase):
|
|
@@ -212,7 +234,9 @@ class ArtifactServiceConfig(SamConfigBase):
|
|
|
212
234
|
class SessionServiceConfig(SamConfigBase):
|
|
213
235
|
"""Configuration for the ADK Session Service."""
|
|
214
236
|
|
|
215
|
-
type: str = Field(
|
|
237
|
+
type: str = Field(
|
|
238
|
+
..., description="Service type (e.g., 'memory', 'sql', 'vertex_rag')."
|
|
239
|
+
)
|
|
216
240
|
default_behavior: Literal["PERSISTENT", "RUN_BASED"] = Field(
|
|
217
241
|
default="PERSISTENT", description="Default behavior for session service."
|
|
218
242
|
)
|
|
@@ -229,10 +253,21 @@ class SamAgentAppConfig(SamConfigBase):
|
|
|
229
253
|
description="Absolute topic prefix for A2A communication (e.g., 'myorg/dev').",
|
|
230
254
|
)
|
|
231
255
|
agent_name: str = Field(..., description="Unique name for this ADK agent instance.")
|
|
232
|
-
display_name: str = Field(
|
|
256
|
+
display_name: str = Field(
|
|
257
|
+
default=None,
|
|
258
|
+
description="Human-friendly display name for this ADK agent instance.",
|
|
259
|
+
)
|
|
260
|
+
deployment: Optional[Dict[str, Any]] = Field(
|
|
261
|
+
default=None,
|
|
262
|
+
description="Deployment tracking information for rolling updates and version control.",
|
|
263
|
+
)
|
|
233
264
|
model: Union[str, Dict[str, Any]] = Field(
|
|
234
265
|
..., description="ADK model name (string) or BaseLlm config dict."
|
|
235
266
|
)
|
|
267
|
+
trust_manager: Optional[Union[TrustManagerConfig, Dict[str, Any]]] = Field(
|
|
268
|
+
default=None,
|
|
269
|
+
description="Configuration for the Trust Manager (enterprise feature)",
|
|
270
|
+
)
|
|
236
271
|
instruction: Any = Field(
|
|
237
272
|
default="",
|
|
238
273
|
description="User-provided instructions for the ADK agent (string or invoke block).",
|
|
@@ -423,6 +458,20 @@ class SamAgentApp(App):
|
|
|
423
458
|
get_agent_status_subscription_topic(namespace, agent_name),
|
|
424
459
|
get_sam_events_subscription_topic(namespace, "session"),
|
|
425
460
|
]
|
|
461
|
+
|
|
462
|
+
# Add trust card subscription if trust manager is enabled
|
|
463
|
+
trust_config = app_config.get("trust_manager")
|
|
464
|
+
if trust_config and trust_config.get("enabled", False):
|
|
465
|
+
from ...common.a2a.protocol import get_trust_card_subscription_topic
|
|
466
|
+
|
|
467
|
+
trust_card_topic = get_trust_card_subscription_topic(namespace)
|
|
468
|
+
required_topics.append(trust_card_topic)
|
|
469
|
+
log.info(
|
|
470
|
+
"Trust Manager enabled for agent '%s', added trust card subscription: %s",
|
|
471
|
+
agent_name,
|
|
472
|
+
trust_card_topic,
|
|
473
|
+
)
|
|
474
|
+
|
|
426
475
|
generated_subs = [{"topic": topic} for topic in required_topics]
|
|
427
476
|
log.info(
|
|
428
477
|
"Automatically generated subscriptions for Agent '%s': %s",
|
|
@@ -452,8 +501,12 @@ class SamAgentApp(App):
|
|
|
452
501
|
broker_config["queue_name"] = generated_queue_name
|
|
453
502
|
log.debug("Injected generated broker.queue_name: %s", generated_queue_name)
|
|
454
503
|
|
|
455
|
-
broker_config["temporary_queue"] =
|
|
456
|
-
|
|
504
|
+
broker_config["temporary_queue"] = app_info.get("broker", {}).get(
|
|
505
|
+
"temporary_queue", True
|
|
506
|
+
)
|
|
507
|
+
log.debug(
|
|
508
|
+
"Set broker_config.temporary_queue = %s", broker_config["temporary_queue"]
|
|
509
|
+
)
|
|
457
510
|
|
|
458
511
|
super().__init__(app_info, **kwargs)
|
|
459
512
|
log.debug("%s Agent initialization complete.", agent_name)
|
|
@@ -8,10 +8,8 @@ import asyncio
|
|
|
8
8
|
import functools
|
|
9
9
|
import threading
|
|
10
10
|
import concurrent.futures
|
|
11
|
-
import uuid
|
|
12
11
|
import fnmatch
|
|
13
|
-
import
|
|
14
|
-
from datetime import datetime, timezone
|
|
12
|
+
import time
|
|
15
13
|
import json
|
|
16
14
|
from solace_ai_connector.common.message import (
|
|
17
15
|
Message as SolaceMessage,
|
|
@@ -73,9 +71,10 @@ from ...agent.tools.peer_agent_tool import (
|
|
|
73
71
|
PEER_TOOL_PREFIX,
|
|
74
72
|
)
|
|
75
73
|
from ...common.middleware.registry import MiddlewareRegistry
|
|
76
|
-
from ...common.constants import DEFAULT_COMMUNICATION_TIMEOUT
|
|
74
|
+
from ...common.constants import DEFAULT_COMMUNICATION_TIMEOUT, HEALTH_CHECK_TTL_SECONDS, HEALTH_CHECK_INTERVAL_SECONDS
|
|
77
75
|
from ...agent.tools.registry import tool_registry
|
|
78
76
|
from ...common.sac.sam_component_base import SamComponentBase
|
|
77
|
+
from ...common.agent_registry import AgentRegistry
|
|
79
78
|
|
|
80
79
|
log = logging.getLogger(__name__)
|
|
81
80
|
|
|
@@ -113,6 +112,7 @@ class SamAgentComponent(SamComponentBase):
|
|
|
113
112
|
|
|
114
113
|
CORRELATION_DATA_PREFIX = CORRELATION_DATA_PREFIX
|
|
115
114
|
HOST_COMPONENT_VERSION = "1.0.0-alpha"
|
|
115
|
+
HEALTH_CHECK_TIMER_ID = "agent_health_check"
|
|
116
116
|
|
|
117
117
|
def __init__(self, **kwargs):
|
|
118
118
|
"""
|
|
@@ -129,6 +129,9 @@ class SamAgentComponent(SamComponentBase):
|
|
|
129
129
|
super().__init__(info, **kwargs)
|
|
130
130
|
self.agent_name = self.get_config("agent_name")
|
|
131
131
|
log.info("%s Initializing A2A ADK Host Component...", self.log_identifier)
|
|
132
|
+
|
|
133
|
+
# Initialize the agent registry for health tracking
|
|
134
|
+
self.agent_registry = AgentRegistry()
|
|
132
135
|
try:
|
|
133
136
|
self.namespace = self.get_config("namespace")
|
|
134
137
|
if not self.namespace:
|
|
@@ -237,7 +240,7 @@ class SamAgentComponent(SamComponentBase):
|
|
|
237
240
|
self.adk_agent: LlmAgent = None
|
|
238
241
|
self.runner: Runner = None
|
|
239
242
|
self.agent_card_tool_manifest: List[Dict[str, Any]] = []
|
|
240
|
-
self.peer_agents: Dict[str, Any] = {}
|
|
243
|
+
self.peer_agents: Dict[str, Any] = {} # Keep for backward compatibility
|
|
241
244
|
self._card_publish_timer_id: str = f"publish_card_{self.agent_name}"
|
|
242
245
|
self._async_init_future = None
|
|
243
246
|
self.peer_response_queues: Dict[str, asyncio.Queue] = {}
|
|
@@ -276,8 +279,13 @@ class SamAgentComponent(SamComponentBase):
|
|
|
276
279
|
f"Failed to initialize synchronous ADK services: {service_err}"
|
|
277
280
|
) from service_err
|
|
278
281
|
|
|
279
|
-
from .app import
|
|
280
|
-
|
|
282
|
+
from .app import (
|
|
283
|
+
AgentInitCleanupConfig,
|
|
284
|
+
) # delayed import to avoid circular dependency
|
|
285
|
+
|
|
286
|
+
if init_func_details and isinstance(
|
|
287
|
+
init_func_details, AgentInitCleanupConfig
|
|
288
|
+
):
|
|
281
289
|
module_name = init_func_details.get("module")
|
|
282
290
|
func_name = init_func_details.get("name")
|
|
283
291
|
base_path = init_func_details.get("base_path")
|
|
@@ -404,16 +412,39 @@ class SamAgentComponent(SamComponentBase):
|
|
|
404
412
|
self.log_identifier,
|
|
405
413
|
publish_interval_sec,
|
|
406
414
|
)
|
|
415
|
+
# Register timer with callback
|
|
407
416
|
self.add_timer(
|
|
408
417
|
delay_ms=1000,
|
|
409
418
|
timer_id=self._card_publish_timer_id,
|
|
410
419
|
interval_ms=publish_interval_sec * 1000,
|
|
420
|
+
callback=lambda timer_data: publish_agent_card(self),
|
|
411
421
|
)
|
|
412
422
|
else:
|
|
413
423
|
log.warning(
|
|
414
424
|
"%s Agent card publishing interval not configured or invalid, card will not be published periodically.",
|
|
415
425
|
self.log_identifier,
|
|
416
426
|
)
|
|
427
|
+
|
|
428
|
+
# Set up health check timer if enabled
|
|
429
|
+
health_check_interval_seconds = self.agent_discovery_config.get("health_check_interval_seconds", HEALTH_CHECK_INTERVAL_SECONDS)
|
|
430
|
+
if health_check_interval_seconds > 0:
|
|
431
|
+
log.info(
|
|
432
|
+
"%s Scheduling agent health check every %d seconds.",
|
|
433
|
+
self.log_identifier,
|
|
434
|
+
health_check_interval_seconds,
|
|
435
|
+
)
|
|
436
|
+
self.add_timer(
|
|
437
|
+
delay_ms=health_check_interval_seconds * 1000,
|
|
438
|
+
timer_id=self.HEALTH_CHECK_TIMER_ID,
|
|
439
|
+
interval_ms=health_check_interval_seconds * 1000,
|
|
440
|
+
callback=lambda timer_data: self._check_agent_health(),
|
|
441
|
+
)
|
|
442
|
+
else:
|
|
443
|
+
log.warning(
|
|
444
|
+
"%s Agent health check interval not configured or invalid, health checks will not run periodically.",
|
|
445
|
+
self.log_identifier,
|
|
446
|
+
)
|
|
447
|
+
|
|
417
448
|
log.info(
|
|
418
449
|
"%s Initialization complete for agent: %s",
|
|
419
450
|
self.log_identifier,
|
|
@@ -423,75 +454,35 @@ class SamAgentComponent(SamComponentBase):
|
|
|
423
454
|
log.exception("%s Initialization failed: %s", self.log_identifier, e)
|
|
424
455
|
raise
|
|
425
456
|
|
|
457
|
+
def _get_component_id(self) -> str:
|
|
458
|
+
"""Returns the agent name as the component identifier."""
|
|
459
|
+
return self.agent_name
|
|
460
|
+
|
|
461
|
+
def _get_component_type(self) -> str:
|
|
462
|
+
"""Returns 'agent' as the component type."""
|
|
463
|
+
return "agent"
|
|
464
|
+
|
|
426
465
|
def invoke(self, message: SolaceMessage, data: dict) -> dict:
|
|
427
|
-
"""Placeholder invoke method. Primary logic resides in
|
|
466
|
+
"""Placeholder invoke method. Primary logic resides in _handle_message."""
|
|
428
467
|
log.warning(
|
|
429
|
-
"%s 'invoke' method called, but primary logic resides in '
|
|
468
|
+
"%s 'invoke' method called, but primary logic resides in '_handle_message'. This should not happen in normal operation.",
|
|
430
469
|
self.log_identifier,
|
|
431
470
|
)
|
|
432
471
|
return None
|
|
433
472
|
|
|
434
|
-
def
|
|
435
|
-
"""
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if loop and is_loop_running:
|
|
440
|
-
coro = process_event(self, event)
|
|
441
|
-
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
442
|
-
future.add_done_callback(
|
|
443
|
-
functools.partial(
|
|
444
|
-
self._handle_scheduled_task_completion,
|
|
445
|
-
event_type_for_log=event.event_type,
|
|
446
|
-
)
|
|
447
|
-
)
|
|
448
|
-
else:
|
|
449
|
-
log.error(
|
|
450
|
-
"%s Async loop not available or not running (loop is %s, is_running: %s). Cannot process event: %s",
|
|
451
|
-
self.log_identifier,
|
|
452
|
-
"present" if loop else "None",
|
|
453
|
-
is_loop_running,
|
|
454
|
-
event.event_type,
|
|
455
|
-
)
|
|
456
|
-
if event.event_type == EventType.MESSAGE:
|
|
457
|
-
try:
|
|
458
|
-
event.data.call_negative_acknowledgements()
|
|
459
|
-
log.warning(
|
|
460
|
-
"%s NACKed message due to unavailable async loop for event processing.",
|
|
461
|
-
self.log_identifier,
|
|
462
|
-
)
|
|
463
|
-
except Exception as nack_e:
|
|
464
|
-
log.error(
|
|
465
|
-
"%s Failed to NACK message after async loop issue: %s",
|
|
466
|
-
self.log_identifier,
|
|
467
|
-
nack_e,
|
|
468
|
-
)
|
|
469
|
-
except Exception as e:
|
|
470
|
-
log.error(
|
|
471
|
-
"%s Error processing event: %s. Exception: %s",
|
|
472
|
-
self.log_identifier,
|
|
473
|
-
event.event_type,
|
|
474
|
-
e,
|
|
475
|
-
)
|
|
476
|
-
if event.event_type == EventType.MESSAGE:
|
|
477
|
-
try:
|
|
478
|
-
event.data.call_negative_acknowledgements()
|
|
479
|
-
log.warning(
|
|
480
|
-
"%s NACKed message due to error in event processing.",
|
|
481
|
-
self.log_identifier,
|
|
482
|
-
)
|
|
483
|
-
except Exception as nack_e:
|
|
484
|
-
log.error(
|
|
485
|
-
"%s Failed to NACK message after error in event processing: %s",
|
|
486
|
-
self.log_identifier,
|
|
487
|
-
nack_e,
|
|
488
|
-
)
|
|
473
|
+
async def _handle_message_async(self, message: SolaceMessage, topic: str) -> None:
|
|
474
|
+
"""
|
|
475
|
+
Async handler for incoming messages.
|
|
476
|
+
|
|
477
|
+
Routes the message to the async event handler.
|
|
489
478
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
479
|
+
Args:
|
|
480
|
+
message: The Solace message
|
|
481
|
+
topic: The topic the message was received on
|
|
482
|
+
"""
|
|
483
|
+
# Create event and process asynchronously
|
|
484
|
+
event = Event(EventType.MESSAGE, message)
|
|
485
|
+
await process_event(self, event)
|
|
495
486
|
|
|
496
487
|
async def handle_cache_expiry_event(self, cache_data: Dict[str, Any]):
|
|
497
488
|
"""
|
|
@@ -866,7 +857,8 @@ class SamAgentComponent(SamComponentBase):
|
|
|
866
857
|
peer_tools_to_add = []
|
|
867
858
|
allowed_peer_descriptions = []
|
|
868
859
|
|
|
869
|
-
|
|
860
|
+
# Sort peer agents alphabetically to ensure consistent tool ordering for prompt caching
|
|
861
|
+
for peer_name, agent_card in sorted(self.peer_agents.items()):
|
|
870
862
|
if not isinstance(agent_card, AgentCard) or peer_name == self_name:
|
|
871
863
|
continue
|
|
872
864
|
|
|
@@ -1140,7 +1132,11 @@ class SamAgentComponent(SamComponentBase):
|
|
|
1140
1132
|
"""
|
|
1141
1133
|
if hasattr(tool, "origin") and tool.origin is not None:
|
|
1142
1134
|
return tool.origin
|
|
1143
|
-
elif
|
|
1135
|
+
elif (
|
|
1136
|
+
hasattr(tool, "func")
|
|
1137
|
+
and hasattr(tool.func, "origin")
|
|
1138
|
+
and tool.func.origin is not None
|
|
1139
|
+
):
|
|
1144
1140
|
return tool.func.origin
|
|
1145
1141
|
else:
|
|
1146
1142
|
return getattr(tool, "origin", "unknown")
|
|
@@ -2078,7 +2074,7 @@ class SamAgentComponent(SamComponentBase):
|
|
|
2078
2074
|
self.log_identifier,
|
|
2079
2075
|
len(task_context.produced_artifacts),
|
|
2080
2076
|
)
|
|
2081
|
-
|
|
2077
|
+
|
|
2082
2078
|
# Add token usage summary
|
|
2083
2079
|
if task_context:
|
|
2084
2080
|
token_summary = task_context.get_token_usage_summary()
|
|
@@ -2833,6 +2829,35 @@ class SamAgentComponent(SamComponentBase):
|
|
|
2833
2829
|
if isinstance(user_config, dict):
|
|
2834
2830
|
user_properties["a2aUserConfig"] = user_config
|
|
2835
2831
|
|
|
2832
|
+
# Retrieve and propagate authentication token from parent task context
|
|
2833
|
+
parent_task_id = a2a_message.metadata.get("parentTaskId")
|
|
2834
|
+
if parent_task_id:
|
|
2835
|
+
with self.active_tasks_lock:
|
|
2836
|
+
parent_task_context = self.active_tasks.get(parent_task_id)
|
|
2837
|
+
|
|
2838
|
+
if parent_task_context:
|
|
2839
|
+
auth_token = parent_task_context.get_security_data("auth_token")
|
|
2840
|
+
if auth_token:
|
|
2841
|
+
user_properties["authToken"] = auth_token
|
|
2842
|
+
log.debug(
|
|
2843
|
+
"%s Propagating authentication token to peer agent %s for sub-task %s",
|
|
2844
|
+
log_identifier_helper,
|
|
2845
|
+
target_agent_name,
|
|
2846
|
+
sub_task_id,
|
|
2847
|
+
)
|
|
2848
|
+
else:
|
|
2849
|
+
log.debug(
|
|
2850
|
+
"%s No authentication token found in parent task context for sub-task %s",
|
|
2851
|
+
log_identifier_helper,
|
|
2852
|
+
sub_task_id,
|
|
2853
|
+
)
|
|
2854
|
+
else:
|
|
2855
|
+
log.warning(
|
|
2856
|
+
"%s Parent task context not found for task %s, cannot propagate authentication token",
|
|
2857
|
+
log_identifier_helper,
|
|
2858
|
+
parent_task_id,
|
|
2859
|
+
)
|
|
2860
|
+
|
|
2836
2861
|
self.publish_a2a_message(
|
|
2837
2862
|
payload=a2a_request.model_dump(by_alias=True, exclude_none=True),
|
|
2838
2863
|
topic=peer_request_topic,
|
|
@@ -2991,11 +3016,15 @@ class SamAgentComponent(SamComponentBase):
|
|
|
2991
3016
|
"""Clean up resources on component shutdown."""
|
|
2992
3017
|
log.info("%s Cleaning up A2A ADK Host Component.", self.log_identifier)
|
|
2993
3018
|
self.cancel_timer(self._card_publish_timer_id)
|
|
3019
|
+
self.cancel_timer(self.HEALTH_CHECK_TIMER_ID)
|
|
2994
3020
|
|
|
2995
3021
|
cleanup_func_details = self.get_config("agent_cleanup_function")
|
|
2996
3022
|
|
|
2997
|
-
from .app import AgentInitCleanupConfig
|
|
2998
|
-
|
|
3023
|
+
from .app import AgentInitCleanupConfig # Avoid circular import
|
|
3024
|
+
|
|
3025
|
+
if cleanup_func_details and isinstance(
|
|
3026
|
+
cleanup_func_details, AgentInitCleanupConfig
|
|
3027
|
+
):
|
|
2999
3028
|
module_name = cleanup_func_details.get("module")
|
|
3000
3029
|
func_name = cleanup_func_details.get("name")
|
|
3001
3030
|
base_path = cleanup_func_details.get("base_path")
|
|
@@ -3151,6 +3180,129 @@ class SamAgentComponent(SamComponentBase):
|
|
|
3151
3180
|
For now, using the agent name, but could be made more robust (e.g., hostname + agent name).
|
|
3152
3181
|
"""
|
|
3153
3182
|
return self.agent_name
|
|
3183
|
+
|
|
3184
|
+
def _check_agent_health(self):
|
|
3185
|
+
"""
|
|
3186
|
+
Checks the health of peer agents and de-registers unresponsive ones.
|
|
3187
|
+
This is called periodically by the health check timer.
|
|
3188
|
+
Uses TTL-based expiration to determine if an agent is unresponsive.
|
|
3189
|
+
"""
|
|
3190
|
+
|
|
3191
|
+
log.debug("%s Performing agent health check...", self.log_identifier)
|
|
3192
|
+
|
|
3193
|
+
ttl_seconds = self.agent_discovery_config.get("health_check_ttl_seconds", HEALTH_CHECK_TTL_SECONDS)
|
|
3194
|
+
health_check_interval = self.agent_discovery_config.get("health_check_interval_seconds", HEALTH_CHECK_INTERVAL_SECONDS)
|
|
3195
|
+
|
|
3196
|
+
log.debug(
|
|
3197
|
+
"%s Health check configuration: interval=%d seconds, TTL=%d seconds",
|
|
3198
|
+
self.log_identifier,
|
|
3199
|
+
health_check_interval,
|
|
3200
|
+
ttl_seconds
|
|
3201
|
+
)
|
|
3202
|
+
|
|
3203
|
+
# Validate configuration values
|
|
3204
|
+
if ttl_seconds <= 0 or health_check_interval <= 0 or ttl_seconds < health_check_interval:
|
|
3205
|
+
log.error(
|
|
3206
|
+
"%s agent_health_check_ttl_seconds (%d) and agent_health_check_interval_seconds (%d) must be positive and TTL must be greater than interval.",
|
|
3207
|
+
self.log_identifier,
|
|
3208
|
+
ttl_seconds,
|
|
3209
|
+
health_check_interval
|
|
3210
|
+
)
|
|
3211
|
+
raise ValueError(f"Invalid health check configuration. agent_health_check_ttl_seconds ({ttl_seconds}) and agent_health_check_interval_seconds ({health_check_interval}) must be positive and TTL must be greater than interval.")
|
|
3212
|
+
|
|
3213
|
+
# Get all agent names from the registry
|
|
3214
|
+
agent_names = self.agent_registry.get_agent_names()
|
|
3215
|
+
total_agents = len(agent_names)
|
|
3216
|
+
agents_to_deregister = []
|
|
3217
|
+
|
|
3218
|
+
log.debug("%s Checking health of %d peer agents", self.log_identifier, total_agents)
|
|
3219
|
+
|
|
3220
|
+
for agent_name in agent_names:
|
|
3221
|
+
# Skip our own agent
|
|
3222
|
+
if agent_name == self.agent_name:
|
|
3223
|
+
continue
|
|
3224
|
+
|
|
3225
|
+
# Check if the agent's TTL has expired
|
|
3226
|
+
is_expired, time_since_last_seen = self.agent_registry.check_ttl_expired(agent_name, ttl_seconds)
|
|
3227
|
+
|
|
3228
|
+
if is_expired:
|
|
3229
|
+
log.warning(
|
|
3230
|
+
"%s Agent '%s' TTL has expired. De-registering. Time since last seen: %d seconds (TTL: %d seconds)",
|
|
3231
|
+
self.log_identifier,
|
|
3232
|
+
agent_name,
|
|
3233
|
+
time_since_last_seen,
|
|
3234
|
+
ttl_seconds
|
|
3235
|
+
)
|
|
3236
|
+
agents_to_deregister.append(agent_name)
|
|
3237
|
+
|
|
3238
|
+
# De-register unresponsive agents
|
|
3239
|
+
for agent_name in agents_to_deregister:
|
|
3240
|
+
self._deregister_agent(agent_name)
|
|
3241
|
+
|
|
3242
|
+
log.debug(
|
|
3243
|
+
"%s Agent health check completed. Total agents: %d, De-registered: %d",
|
|
3244
|
+
self.log_identifier,
|
|
3245
|
+
total_agents,
|
|
3246
|
+
len(agents_to_deregister)
|
|
3247
|
+
)
|
|
3248
|
+
|
|
3249
|
+
def _deregister_agent(self, agent_name: str):
|
|
3250
|
+
"""
|
|
3251
|
+
De-registers an agent from the registry and publishes a de-registration event.
|
|
3252
|
+
"""
|
|
3253
|
+
# Remove from registry
|
|
3254
|
+
registry_removed = self.agent_registry.remove_agent(agent_name)
|
|
3255
|
+
|
|
3256
|
+
# Always remove from peer_agents regardless of registry result
|
|
3257
|
+
peer_removed = False
|
|
3258
|
+
if agent_name in self.peer_agents:
|
|
3259
|
+
del self.peer_agents[agent_name]
|
|
3260
|
+
peer_removed = True
|
|
3261
|
+
log.info(
|
|
3262
|
+
"%s Removed agent '%s' from peer_agents dictionary",
|
|
3263
|
+
self.log_identifier,
|
|
3264
|
+
agent_name
|
|
3265
|
+
)
|
|
3266
|
+
|
|
3267
|
+
# Publish de-registration event if agent was in either data structure
|
|
3268
|
+
if registry_removed or peer_removed:
|
|
3269
|
+
try:
|
|
3270
|
+
# Create a de-registration event topic
|
|
3271
|
+
namespace = self.get_config("namespace")
|
|
3272
|
+
deregistration_topic = f"{namespace}/a2a/events/agent/deregistered"
|
|
3273
|
+
|
|
3274
|
+
current_time = time.time()
|
|
3275
|
+
|
|
3276
|
+
# Create the payload
|
|
3277
|
+
deregistration_payload = {
|
|
3278
|
+
"event_type": "agent.deregistered",
|
|
3279
|
+
"agent_name": agent_name,
|
|
3280
|
+
"reason": "health_check_failure",
|
|
3281
|
+
"metadata": {
|
|
3282
|
+
"timestamp": current_time,
|
|
3283
|
+
"deregistered_by": self.agent_name
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
# Publish the event
|
|
3288
|
+
self.publish_a2a_message(
|
|
3289
|
+
payload=deregistration_payload,
|
|
3290
|
+
topic=deregistration_topic
|
|
3291
|
+
)
|
|
3292
|
+
|
|
3293
|
+
log.info(
|
|
3294
|
+
"%s Published de-registration event for agent '%s' to topic '%s'",
|
|
3295
|
+
self.log_identifier,
|
|
3296
|
+
agent_name,
|
|
3297
|
+
deregistration_topic
|
|
3298
|
+
)
|
|
3299
|
+
except Exception as e:
|
|
3300
|
+
log.error(
|
|
3301
|
+
"%s Failed to publish de-registration event for agent '%s': %s",
|
|
3302
|
+
self.log_identifier,
|
|
3303
|
+
agent_name,
|
|
3304
|
+
e
|
|
3305
|
+
)
|
|
3154
3306
|
|
|
3155
3307
|
async def _resolve_early_embeds_and_handle_signals(
|
|
3156
3308
|
self, raw_text: str, a2a_context: Dict
|
|
@@ -3240,6 +3392,10 @@ class SamAgentComponent(SamComponentBase):
|
|
|
3240
3392
|
Main async logic for the agent component.
|
|
3241
3393
|
This is called by the base class's `_run_async_operations`.
|
|
3242
3394
|
"""
|
|
3395
|
+
# Call base class to initialize Trust Manager
|
|
3396
|
+
await super()._async_setup_and_run()
|
|
3397
|
+
|
|
3398
|
+
# Perform agent-specific async initialization
|
|
3243
3399
|
await self._perform_async_init()
|
|
3244
3400
|
|
|
3245
3401
|
def _pre_async_cleanup(self) -> None:
|
|
@@ -3247,4 +3403,11 @@ class SamAgentComponent(SamComponentBase):
|
|
|
3247
3403
|
Pre-cleanup actions for the agent component.
|
|
3248
3404
|
Called by the base class before stopping the async loop.
|
|
3249
3405
|
"""
|
|
3250
|
-
|
|
3406
|
+
# Cleanup Trust Manager if present (ENTERPRISE FEATURE)
|
|
3407
|
+
if self.trust_manager:
|
|
3408
|
+
try:
|
|
3409
|
+
self.trust_manager.cleanup(self.cancel_timer)
|
|
3410
|
+
except Exception as e:
|
|
3411
|
+
log.error(
|
|
3412
|
+
"%s Error during Trust Manager cleanup: %s", self.log_identifier, e
|
|
3413
|
+
)
|