solace-agent-mesh 1.0.7__py3-none-any.whl → 1.1.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/adk_llm.txt +182 -42
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/callbacks.py +165 -104
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
- solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
- solace_agent_mesh/agent/adk/runner.py +7 -5
- solace_agent_mesh/agent/adk/services.py +9 -1
- solace_agent_mesh/agent/adk/setup.py +11 -0
- solace_agent_mesh/agent/adk/stream_parser.py +8 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
- solace_agent_mesh/agent/agent_llm.txt +355 -18
- solace_agent_mesh/agent/protocol/event_handlers.py +433 -296
- solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
- solace_agent_mesh/agent/sac/app.py +1 -1
- solace_agent_mesh/agent/sac/component.py +212 -517
- solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
- solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
- solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
- solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
- solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
- solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.a75ecc0d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +105 -0
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1756992446316.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/web_add_agent_step.py +12 -3
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
- solace_agent_mesh/cli/utils.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
- solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
- solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
- solace_agent_mesh/common/a2a/__init__.py +213 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
- solace_agent_mesh/common/a2a/artifact.py +328 -0
- solace_agent_mesh/common/a2a/events.py +183 -0
- solace_agent_mesh/common/a2a/message.py +307 -0
- solace_agent_mesh/common/a2a/protocol.py +513 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +653 -0
- solace_agent_mesh/common/a2a/types.py +54 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
- solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
- solace_agent_mesh/common/agent_registry.py +1 -1
- solace_agent_mesh/common/common_llm.txt +192 -70
- solace_agent_mesh/common/data_parts.py +99 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
- solace_agent_mesh/common/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sam_component_base.py +252 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
- solace_agent_mesh/common/services/services_llm.txt +206 -26
- solace_agent_mesh/common/utils/artifact_utils.py +29 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
- solace_agent_mesh/common/utils/utils_llm.txt +323 -42
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
- solace_agent_mesh/core_a2a/service.py +20 -44
- solace_agent_mesh/gateway/base/app.py +27 -1
- solace_agent_mesh/gateway/base/base_llm.txt +177 -72
- solace_agent_mesh/gateway/base/component.py +294 -523
- solace_agent_mesh/gateway/gateway_llm.txt +299 -58
- solace_agent_mesh/gateway/http_sse/component.py +156 -183
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
- solace_agent_mesh/gateway/http_sse/main.py +8 -10
- solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
- solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
- solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/templates/gateway_component_template.py +149 -98
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +144 -127
- solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1756146501924.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
- solace_agent_mesh/common/a2a_protocol.py +0 -564
- solace_agent_mesh/common/client/__init__.py +0 -4
- solace_agent_mesh/common/client/card_resolver.py +0 -21
- solace_agent_mesh/common/client/client.py +0 -85
- solace_agent_mesh/common/client/client_llm.txt +0 -133
- solace_agent_mesh/common/server/__init__.py +0 -4
- solace_agent_mesh/common/server/server.py +0 -122
- solace_agent_mesh/common/server/server_llm.txt +0 -169
- solace_agent_mesh/common/server/task_manager.py +0 -291
- solace_agent_mesh/common/server/utils.py +0 -28
- solace_agent_mesh/common/types.py +0 -411
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.d79f063b.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.0.7.dist-info → solace_agent_mesh-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic models for the structured data payloads used in A2A DataPart objects.
|
|
3
|
+
These models correspond to the JSON schemas defined in a2a_spec/schemas/
|
|
4
|
+
and are used for validating non-visible status update messages.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, Literal, Union
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToolInvocationStartData(BaseModel):
|
|
12
|
+
"""
|
|
13
|
+
Data model for a tool invocation start signal.
|
|
14
|
+
Corresponds to tool_invocation_start.json schema.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
type: Literal["tool_invocation_start"] = Field(
|
|
18
|
+
"tool_invocation_start", description="The constant type for this data part."
|
|
19
|
+
)
|
|
20
|
+
tool_name: str = Field(..., description="The name of the tool being called.")
|
|
21
|
+
tool_args: Dict[str, Any] = Field(
|
|
22
|
+
..., description="The arguments passed to the tool."
|
|
23
|
+
)
|
|
24
|
+
function_call_id: str = Field(
|
|
25
|
+
..., description="The ID from the LLM's function call."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LlmInvocationData(BaseModel):
|
|
30
|
+
"""
|
|
31
|
+
Data model for an LLM invocation signal.
|
|
32
|
+
Corresponds to llm_invocation.json schema.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
type: Literal["llm_invocation"] = Field(
|
|
36
|
+
"llm_invocation", description="The constant type for this data part."
|
|
37
|
+
)
|
|
38
|
+
request: Dict[str, Any] = Field(
|
|
39
|
+
...,
|
|
40
|
+
description="A sanitized representation of the LlmRequest object sent to the model.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AgentProgressUpdateData(BaseModel):
|
|
45
|
+
"""
|
|
46
|
+
Data model for an agent progress update signal.
|
|
47
|
+
Corresponds to agent_progress_update.json schema.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
type: Literal["agent_progress_update"] = Field(
|
|
51
|
+
"agent_progress_update", description="The constant type for this data part."
|
|
52
|
+
)
|
|
53
|
+
status_text: str = Field(
|
|
54
|
+
...,
|
|
55
|
+
description="A human-readable progress message (e.g., 'Analyzing the report...').",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ArtifactCreationProgressData(BaseModel):
|
|
60
|
+
"""
|
|
61
|
+
Data model for an artifact creation progress signal.
|
|
62
|
+
Corresponds to artifact_creation_progress.json schema.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
type: Literal["artifact_creation_progress"] = Field(
|
|
66
|
+
"artifact_creation_progress",
|
|
67
|
+
description="The constant type for this data part.",
|
|
68
|
+
)
|
|
69
|
+
filename: str = Field(..., description="The name of the artifact being created.")
|
|
70
|
+
bytes_saved: int = Field(..., description="The number of bytes saved so far.")
|
|
71
|
+
artifact_chunk: str = Field(
|
|
72
|
+
...,
|
|
73
|
+
description="The chunk of artifact data that was saved in this progress update.",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ToolResultData(BaseModel):
|
|
78
|
+
"""
|
|
79
|
+
Data model for a tool execution result signal.
|
|
80
|
+
Corresponds to tool_result.json schema.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
type: Literal["tool_result"] = Field(
|
|
84
|
+
"tool_result", description="The constant type for this data part."
|
|
85
|
+
)
|
|
86
|
+
tool_name: str = Field(..., description="The name of the tool that was called.")
|
|
87
|
+
result_data: Any = Field(..., description="The data returned by the tool.")
|
|
88
|
+
function_call_id: str = Field(
|
|
89
|
+
..., description="The ID from the LLM's function call."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
SignalData = Union[
|
|
94
|
+
ToolInvocationStartData,
|
|
95
|
+
LlmInvocationData,
|
|
96
|
+
AgentProgressUpdateData,
|
|
97
|
+
ArtifactCreationProgressData,
|
|
98
|
+
ToolResultData,
|
|
99
|
+
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# DEVELOPER GUIDE: middleware
|
|
2
2
|
|
|
3
3
|
## Quick Summary
|
|
4
4
|
The `middleware` directory provides a pluggable framework for system components that can be extended or replaced at runtime. It offers a registry system to dynamically bind custom implementations for core functionalities like configuration resolution. The default implementations provide permissive behavior, making them suitable for development and testing environments where all features are enabled by default.
|
|
@@ -13,22 +13,22 @@ The `middleware` directory provides a pluggable framework for system components
|
|
|
13
13
|
### __init__.py
|
|
14
14
|
**Purpose:** This file serves as the entry point to the `middleware` package, exporting the primary public interfaces for developers to use.
|
|
15
15
|
|
|
16
|
+
**Import:** `from solace_agent_mesh.common.middleware import ConfigResolver, MiddlewareRegistry`
|
|
17
|
+
|
|
16
18
|
**Usage Examples:**
|
|
17
19
|
```python
|
|
18
20
|
# Import the main classes directly from the middleware package
|
|
19
|
-
from
|
|
21
|
+
from solace_agent_mesh.common.middleware import ConfigResolver, MiddlewareRegistry
|
|
20
22
|
|
|
21
23
|
# Now you can use ConfigResolver and MiddlewareRegistry
|
|
22
24
|
print(ConfigResolver)
|
|
23
25
|
print(MiddlewareRegistry)
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
---
|
|
27
|
-
|
|
28
28
|
### config_resolver.py
|
|
29
29
|
**Purpose:** This file provides a pluggable interface for resolving user-specific configuration and determining feature availability. The default `ConfigResolver` class is permissive, allowing all operations and enabling all features, which is ideal for development or simple deployments.
|
|
30
30
|
|
|
31
|
-
**Import:** `from
|
|
31
|
+
**Import:** `from solace_agent_mesh.common.middleware import ConfigResolver`
|
|
32
32
|
|
|
33
33
|
**Classes:**
|
|
34
34
|
- `ConfigResolver()` - A class containing static methods to resolve user-specific configuration and determine feature availability. This default implementation is permissive.
|
|
@@ -40,7 +40,7 @@ print(MiddlewareRegistry)
|
|
|
40
40
|
**Usage Examples:**
|
|
41
41
|
```python
|
|
42
42
|
import asyncio
|
|
43
|
-
from
|
|
43
|
+
from solace_agent_mesh.common.middleware import ConfigResolver
|
|
44
44
|
|
|
45
45
|
async def main():
|
|
46
46
|
# Example user identity and base configuration
|
|
@@ -89,12 +89,10 @@ if __name__ == "__main__":
|
|
|
89
89
|
asyncio.run(main())
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
---
|
|
93
|
-
|
|
94
92
|
### registry.py
|
|
95
93
|
**Purpose:** This file provides the `MiddlewareRegistry`, a static class that allows developers to dynamically bind, or "plug in," their own custom middleware implementations at runtime. This is the core of the pluggable system.
|
|
96
94
|
|
|
97
|
-
**Import:** `from
|
|
95
|
+
**Import:** `from solace_agent_mesh.common.middleware import MiddlewareRegistry`
|
|
98
96
|
|
|
99
97
|
**Classes:**
|
|
100
98
|
- `MiddlewareRegistry()` - A registry for managing middleware implementations. All methods are class methods.
|
|
@@ -109,19 +107,19 @@ if __name__ == "__main__":
|
|
|
109
107
|
```python
|
|
110
108
|
import asyncio
|
|
111
109
|
from typing import Any, Dict, List
|
|
112
|
-
from
|
|
110
|
+
from solace_agent_mesh.common.middleware import MiddlewareRegistry, ConfigResolver
|
|
113
111
|
|
|
114
112
|
# 1. Define a custom ConfigResolver implementation
|
|
115
113
|
class MyCustomConfigResolver:
|
|
116
114
|
"""A custom resolver that only allows 'admin' users to use 'gpt-4'."""
|
|
117
115
|
@staticmethod
|
|
118
|
-
async def resolve_user_config(user_identity: Any,
|
|
116
|
+
async def resolve_user_config(user_identity: Any, gateway_context: Dict[str, Any], base_config: Dict[str, Any]) -> Dict[str, Any]:
|
|
119
117
|
if user_identity == "admin":
|
|
120
118
|
return {"role": "admin", "allowed_models": ["gpt-4", "gpt-3.5-turbo"]}
|
|
121
119
|
return {"role": "user", "allowed_models": ["gpt-3.5-turbo"]}
|
|
122
120
|
|
|
123
121
|
@staticmethod
|
|
124
|
-
def validate_operation_config(user_config: Dict, operation_spec: Dict,
|
|
122
|
+
def validate_operation_config(user_config: Dict, operation_spec: Dict, validation_context: Dict) -> Dict:
|
|
125
123
|
model = operation_spec.get("model")
|
|
126
124
|
if model and model not in user_config.get("allowed_models", []):
|
|
127
125
|
return {"valid": False, "reason": f"Model '{model}' not allowed for this user."}
|
|
@@ -151,16 +149,16 @@ async def check_permissions():
|
|
|
151
149
|
print(f"Current resolver is: {CurrentResolver.__name__}")
|
|
152
150
|
|
|
153
151
|
# Check an admin user
|
|
154
|
-
admin_config = await CurrentResolver.resolve_user_config("admin")
|
|
152
|
+
admin_config = await CurrentResolver.resolve_user_config("admin", {}, {})
|
|
155
153
|
validation_result = CurrentResolver.validate_operation_config(
|
|
156
|
-
admin_config, {"model": "gpt-4"}
|
|
154
|
+
admin_config, {"model": "gpt-4"}, {}
|
|
157
155
|
)
|
|
158
156
|
print(f"Admin validation for gpt-4: {validation_result}")
|
|
159
157
|
|
|
160
158
|
# Check a regular user
|
|
161
|
-
user_config = await CurrentResolver.resolve_user_config("user")
|
|
159
|
+
user_config = await CurrentResolver.resolve_user_config("user", {}, {})
|
|
162
160
|
validation_result = CurrentResolver.validate_operation_config(
|
|
163
|
-
user_config, {"model": "gpt-4"}
|
|
161
|
+
user_config, {"model": "gpt-4"}, {}
|
|
164
162
|
)
|
|
165
163
|
print(f"User validation for gpt-4: {validation_result}")
|
|
166
164
|
|
|
@@ -171,4 +169,6 @@ asyncio.run(check_permissions())
|
|
|
171
169
|
print(f"\nRegistry Status: {MiddlewareRegistry.get_registry_status()}")
|
|
172
170
|
MiddlewareRegistry.reset_bindings()
|
|
173
171
|
print(f"Registry Status after reset: {MiddlewareRegistry.get_registry_status()}")
|
|
174
|
-
```
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
# content_hash: 9f505d5203463aeca42959c92c78fe35212f34e4ea0288b67c1c6a6450117d6c
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# DEVELOPER GUIDE: sac
|
|
2
|
+
|
|
3
|
+
## Quick Summary
|
|
4
|
+
The `sac` directory provides the base component framework for Solace Agent Mesh (SAM) implementations in the Solace AI Connector. It offers a standardized foundation for building high-level SAM components like Agents and Gateways with built-in async operations management and message publishing capabilities.
|
|
5
|
+
|
|
6
|
+
## Files Overview
|
|
7
|
+
- `__init__.py` - Empty package initialization file
|
|
8
|
+
- `sam_component_base.py` - Abstract base class providing async thread management and A2A message publishing for SAM components
|
|
9
|
+
|
|
10
|
+
## Developer API Reference
|
|
11
|
+
|
|
12
|
+
### sam_component_base.py
|
|
13
|
+
**Purpose:** Provides an abstract base class for SAM components with managed asyncio event loops and message publishing
|
|
14
|
+
**Import:** `from solace_agent_mesh.common.sac.sam_component_base import SamComponentBase`
|
|
15
|
+
|
|
16
|
+
**Classes:**
|
|
17
|
+
- `SamComponentBase(info: Dict[str, Any], **kwargs: Any)` - Abstract base class for high-level SAM components (Agents, Gateways)
|
|
18
|
+
- `publish_a2a_message(payload: Dict, topic: str, user_properties: Optional[Dict] = None) -> None` - Publishes A2A messages with size validation
|
|
19
|
+
- `run() -> None` - Starts the component's dedicated async thread
|
|
20
|
+
- `cleanup() -> None` - Cleans up resources including async thread and loop
|
|
21
|
+
- `get_async_loop() -> Optional[asyncio.AbstractEventLoop]` - Returns the dedicated asyncio event loop
|
|
22
|
+
- `_async_setup_and_run() -> None` - Abstract method for subclasses to implement main async logic
|
|
23
|
+
- `_pre_async_cleanup() -> None` - Abstract method for cleanup before async loop stops
|
|
24
|
+
- `namespace: str` - The configured namespace for the component
|
|
25
|
+
- `max_message_size_bytes: int` - Maximum allowed message size in bytes
|
|
26
|
+
|
|
27
|
+
**Usage Examples:**
|
|
28
|
+
```python
|
|
29
|
+
from solace_agent_mesh.common.sac.sam_component_base import SamComponentBase
|
|
30
|
+
from typing import Dict, Any
|
|
31
|
+
import asyncio
|
|
32
|
+
|
|
33
|
+
class MyAgent(SamComponentBase):
|
|
34
|
+
def __init__(self, info: Dict[str, Any], **kwargs: Any):
|
|
35
|
+
super().__init__(info, **kwargs)
|
|
36
|
+
# Additional initialization
|
|
37
|
+
|
|
38
|
+
async def _async_setup_and_run(self) -> None:
|
|
39
|
+
"""Implement your main async logic here"""
|
|
40
|
+
while not self.stop_signal.is_set():
|
|
41
|
+
# Your async operations
|
|
42
|
+
await asyncio.sleep(1)
|
|
43
|
+
|
|
44
|
+
def _pre_async_cleanup(self) -> None:
|
|
45
|
+
"""Cleanup before async loop stops"""
|
|
46
|
+
# Your cleanup logic
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# Usage
|
|
50
|
+
config = {
|
|
51
|
+
"namespace": "my_namespace",
|
|
52
|
+
"max_message_size_bytes": 1048576 # 1MB
|
|
53
|
+
}
|
|
54
|
+
agent = MyAgent(config)
|
|
55
|
+
|
|
56
|
+
# Publish a message
|
|
57
|
+
payload = {"message": "Hello World"}
|
|
58
|
+
agent.publish_a2a_message(
|
|
59
|
+
payload=payload,
|
|
60
|
+
topic="sam/agents/my_agent/response",
|
|
61
|
+
user_properties={"correlation_id": "123"}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Start the component
|
|
65
|
+
agent.run()
|
|
66
|
+
|
|
67
|
+
# Later, cleanup
|
|
68
|
+
agent.cleanup()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
# content_hash: 44ffaa77554dd658386afa3a420ade21c016b72b1e5394fdba814ea897b3480c
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Component class for SAM implementations in the Solace AI Connector.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import threading
|
|
7
|
+
import abc
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from solace_ai_connector.components.component_base import ComponentBase
|
|
11
|
+
from solace_ai_connector.common.log import log
|
|
12
|
+
|
|
13
|
+
from ..utils.message_utils import validate_message_size
|
|
14
|
+
from ..exceptions import MessageSizeExceededError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SamComponentBase(ComponentBase, abc.ABC):
|
|
18
|
+
"""
|
|
19
|
+
Abstract base class for high-level SAM components (Agents, Gateways).
|
|
20
|
+
|
|
21
|
+
Provides a standardized framework for:
|
|
22
|
+
- Managing a dedicated asyncio event loop running in a separate thread.
|
|
23
|
+
- Publishing A2A messages with built-in size validation.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, info: Dict[str, Any], **kwargs: Any):
|
|
27
|
+
super().__init__(info, **kwargs)
|
|
28
|
+
log.info("%s Initializing SamComponentBase...", self.log_identifier)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
self.namespace: str = self.get_config("namespace")
|
|
32
|
+
if not self.namespace:
|
|
33
|
+
raise ValueError("Namespace must be configured in the app_config.")
|
|
34
|
+
|
|
35
|
+
# For agents, this is 'max_message_size_bytes'.
|
|
36
|
+
# For gateways, this is 'gateway_max_message_size_bytes'.
|
|
37
|
+
self.max_message_size_bytes: int = self.get_config(
|
|
38
|
+
"max_message_size_bytes"
|
|
39
|
+
) or self.get_config("gateway_max_message_size_bytes")
|
|
40
|
+
|
|
41
|
+
if not self.max_message_size_bytes:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
"max_message_size_bytes (or gateway_max_message_size_bytes) must be configured."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
log.error(
|
|
48
|
+
"%s Failed to retrieve essential configuration: %s",
|
|
49
|
+
self.log_identifier,
|
|
50
|
+
e,
|
|
51
|
+
)
|
|
52
|
+
raise ValueError(f"Configuration retrieval error: {e}") from e
|
|
53
|
+
|
|
54
|
+
self._async_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
55
|
+
self._async_thread: Optional[threading.Thread] = None
|
|
56
|
+
log.info("%s SamComponentBase initialized successfully.", self.log_identifier)
|
|
57
|
+
|
|
58
|
+
def publish_a2a_message(
|
|
59
|
+
self, payload: Dict, topic: str, user_properties: Optional[Dict] = None
|
|
60
|
+
):
|
|
61
|
+
"""Helper to publish A2A messages via the SAC App with size validation."""
|
|
62
|
+
try:
|
|
63
|
+
# Validate message size
|
|
64
|
+
is_valid, actual_size = validate_message_size(
|
|
65
|
+
payload, self.max_message_size_bytes, self.log_identifier
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if not is_valid:
|
|
69
|
+
error_msg = (
|
|
70
|
+
f"Message size validation failed: payload size ({actual_size} bytes) "
|
|
71
|
+
f"exceeds maximum allowed size ({self.max_message_size_bytes} bytes)"
|
|
72
|
+
)
|
|
73
|
+
log.error("%s %s", self.log_identifier, error_msg)
|
|
74
|
+
raise MessageSizeExceededError(
|
|
75
|
+
actual_size, self.max_message_size_bytes, error_msg
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Debug logging to show message size when publishing
|
|
79
|
+
log.debug(
|
|
80
|
+
"%s Publishing message to topic %s (size: %d bytes)",
|
|
81
|
+
self.log_identifier,
|
|
82
|
+
topic,
|
|
83
|
+
actual_size,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
app = self.get_app()
|
|
87
|
+
if app:
|
|
88
|
+
# Conditionally log to invocation monitor if it exists (i.e., on an agent)
|
|
89
|
+
if hasattr(self, "invocation_monitor") and self.invocation_monitor:
|
|
90
|
+
self.invocation_monitor.log_message_event(
|
|
91
|
+
direction="PUBLISHED",
|
|
92
|
+
topic=topic,
|
|
93
|
+
payload=payload,
|
|
94
|
+
component_identifier=self.log_identifier,
|
|
95
|
+
)
|
|
96
|
+
app.send_message(
|
|
97
|
+
payload=payload, topic=topic, user_properties=user_properties
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
log.error(
|
|
101
|
+
"%s Cannot publish message: Not running within a SAC App context.",
|
|
102
|
+
self.log_identifier,
|
|
103
|
+
)
|
|
104
|
+
except MessageSizeExceededError:
|
|
105
|
+
# Re-raise MessageSizeExceededError without wrapping
|
|
106
|
+
raise
|
|
107
|
+
except Exception as e:
|
|
108
|
+
log.exception(
|
|
109
|
+
"%s Failed to publish A2A message to topic %s: %s",
|
|
110
|
+
self.log_identifier,
|
|
111
|
+
topic,
|
|
112
|
+
e,
|
|
113
|
+
)
|
|
114
|
+
raise
|
|
115
|
+
|
|
116
|
+
def _run_async_operations(self):
|
|
117
|
+
"""Target for the dedicated async thread. Sets up and runs the event loop."""
|
|
118
|
+
log.info(
|
|
119
|
+
"%s Initializing asyncio event loop in dedicated thread...",
|
|
120
|
+
self.log_identifier,
|
|
121
|
+
)
|
|
122
|
+
self._async_loop = asyncio.new_event_loop()
|
|
123
|
+
asyncio.set_event_loop(self._async_loop)
|
|
124
|
+
|
|
125
|
+
main_task = None
|
|
126
|
+
try:
|
|
127
|
+
log.info(
|
|
128
|
+
"%s Starting _async_setup_and_run as an asyncio task.",
|
|
129
|
+
self.log_identifier,
|
|
130
|
+
)
|
|
131
|
+
main_task = self._async_loop.create_task(self._async_setup_and_run())
|
|
132
|
+
|
|
133
|
+
log.info(
|
|
134
|
+
"%s Running asyncio event loop forever (or until stop_signal).",
|
|
135
|
+
self.log_identifier,
|
|
136
|
+
)
|
|
137
|
+
self._async_loop.run_forever()
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
log.exception(
|
|
141
|
+
"%s Unhandled exception in _run_async_operations: %s",
|
|
142
|
+
self.log_identifier,
|
|
143
|
+
e,
|
|
144
|
+
)
|
|
145
|
+
self.stop_signal.set()
|
|
146
|
+
finally:
|
|
147
|
+
if main_task and not main_task.done():
|
|
148
|
+
log.info(
|
|
149
|
+
"%s Cancelling main async task (_async_setup_and_run).",
|
|
150
|
+
self.log_identifier,
|
|
151
|
+
)
|
|
152
|
+
main_task.cancel()
|
|
153
|
+
try:
|
|
154
|
+
# Use gather to await the cancellation
|
|
155
|
+
self._async_loop.run_until_complete(
|
|
156
|
+
asyncio.gather(main_task, return_exceptions=True)
|
|
157
|
+
)
|
|
158
|
+
except RuntimeError as loop_err:
|
|
159
|
+
log.warning(
|
|
160
|
+
"%s Error awaiting main task during cleanup (loop closed?): %s",
|
|
161
|
+
self.log_identifier,
|
|
162
|
+
loop_err,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if self._async_loop.is_running():
|
|
166
|
+
log.info(
|
|
167
|
+
"%s Stopping asyncio event loop from _run_async_operations finally block.",
|
|
168
|
+
self.log_identifier,
|
|
169
|
+
)
|
|
170
|
+
self._async_loop.stop()
|
|
171
|
+
log.info(
|
|
172
|
+
"%s Async operations loop finished in dedicated thread.",
|
|
173
|
+
self.log_identifier,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def run(self):
|
|
177
|
+
"""Starts the component's dedicated async thread."""
|
|
178
|
+
log.info("%s Starting SamComponentBase run method.", self.log_identifier)
|
|
179
|
+
if not self._async_thread or not self._async_thread.is_alive():
|
|
180
|
+
self._async_thread = threading.Thread(
|
|
181
|
+
target=self._run_async_operations,
|
|
182
|
+
name=f"{self.name}_AsyncOpsThread",
|
|
183
|
+
daemon=True,
|
|
184
|
+
)
|
|
185
|
+
self._async_thread.start()
|
|
186
|
+
log.info("%s Async operations thread started.", self.log_identifier)
|
|
187
|
+
else:
|
|
188
|
+
log.warning(
|
|
189
|
+
"%s Async operations thread already running.", self.log_identifier
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
super().run()
|
|
193
|
+
log.info("%s SamComponentBase run method finished.", self.log_identifier)
|
|
194
|
+
|
|
195
|
+
def cleanup(self):
|
|
196
|
+
"""Cleans up the component's resources, including the async thread and loop."""
|
|
197
|
+
log.info("%s Starting cleanup for SamComponentBase...", self.log_identifier)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
self._pre_async_cleanup()
|
|
201
|
+
except Exception as e:
|
|
202
|
+
log.exception(
|
|
203
|
+
"%s Error during _pre_async_cleanup(): %s", self.log_identifier, e
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if self._async_loop and self._async_loop.is_running():
|
|
207
|
+
log.info("%s Requesting asyncio loop to stop...", self.log_identifier)
|
|
208
|
+
self._async_loop.call_soon_threadsafe(self._async_loop.stop)
|
|
209
|
+
|
|
210
|
+
if self._async_thread and self._async_thread.is_alive():
|
|
211
|
+
log.info(
|
|
212
|
+
"%s Joining async operations thread (timeout 10s)...",
|
|
213
|
+
self.log_identifier,
|
|
214
|
+
)
|
|
215
|
+
self._async_thread.join(timeout=10)
|
|
216
|
+
if self._async_thread.is_alive():
|
|
217
|
+
log.warning(
|
|
218
|
+
"%s Async operations thread did not join cleanly.",
|
|
219
|
+
self.log_identifier,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if self._async_loop and not self._async_loop.is_closed():
|
|
223
|
+
log.info(
|
|
224
|
+
"%s Closing asyncio event loop (if not already closed by its thread).",
|
|
225
|
+
self.log_identifier,
|
|
226
|
+
)
|
|
227
|
+
# The loop should have been stopped by its own thread's finally block.
|
|
228
|
+
# We just need to close it from this thread.
|
|
229
|
+
self._async_loop.call_soon_threadsafe(self._async_loop.close)
|
|
230
|
+
|
|
231
|
+
super().cleanup()
|
|
232
|
+
log.info("%s SamComponentBase cleanup finished.", self.log_identifier)
|
|
233
|
+
|
|
234
|
+
def get_async_loop(self) -> Optional[asyncio.AbstractEventLoop]:
|
|
235
|
+
"""Returns the dedicated asyncio event loop for this component's async tasks."""
|
|
236
|
+
return self._async_loop
|
|
237
|
+
|
|
238
|
+
@abc.abstractmethod
|
|
239
|
+
async def _async_setup_and_run(self) -> None:
|
|
240
|
+
"""
|
|
241
|
+
Abstract method for subclasses to implement their main asynchronous logic.
|
|
242
|
+
This coroutine is executed within the managed event loop.
|
|
243
|
+
"""
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
@abc.abstractmethod
|
|
247
|
+
def _pre_async_cleanup(self) -> None:
|
|
248
|
+
"""
|
|
249
|
+
Abstract method for subclasses to perform cleanup actions
|
|
250
|
+
before the async loop is stopped.
|
|
251
|
+
"""
|
|
252
|
+
pass
|