solace-agent-mesh 1.0.8__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.

Files changed (162) hide show
  1. solace_agent_mesh/agent/adk/adk_llm.txt +182 -42
  2. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
  3. solace_agent_mesh/agent/adk/callbacks.py +165 -104
  4. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
  5. solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
  6. solace_agent_mesh/agent/adk/runner.py +7 -5
  7. solace_agent_mesh/agent/adk/setup.py +11 -0
  8. solace_agent_mesh/agent/adk/stream_parser.py +8 -1
  9. solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
  10. solace_agent_mesh/agent/agent_llm.txt +355 -18
  11. solace_agent_mesh/agent/protocol/event_handlers.py +433 -296
  12. solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
  13. solace_agent_mesh/agent/sac/app.py +1 -1
  14. solace_agent_mesh/agent/sac/component.py +212 -517
  15. solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
  16. solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
  17. solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
  18. solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
  19. solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
  20. solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
  21. solace_agent_mesh/assets/docs/404.html +3 -3
  22. solace_agent_mesh/assets/docs/assets/js/{3d406171.7d02a73b.js → 3d406171.0b9eeed1.js} +1 -1
  23. solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
  25. solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/main.a75ecc0d.js +2 -0
  28. solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
  29. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
  30. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
  31. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
  32. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
  34. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
  35. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
  43. 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
  44. solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +8 -8
  47. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  52. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
  53. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  61. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  64. solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +1 -0
  65. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  66. solace_agent_mesh/assets/docs/search-doc-1756992446316.json +1 -0
  67. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  68. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  69. solace_agent_mesh/cli/__init__.py +1 -1
  70. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +12 -3
  71. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +10 -14
  72. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +2 -15
  73. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +6 -2
  74. solace_agent_mesh/cli/utils.py +15 -0
  75. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DvlO62me.js → authCallback-BmF2l6vg.js} +1 -1
  76. solace_agent_mesh/client/webui/frontend/static/assets/{client-bp6u3qVZ.js → client-D881Dttc.js} +4 -4
  77. solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +699 -0
  78. solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +1 -0
  79. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +2 -2
  80. solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
  81. solace_agent_mesh/common/a2a/__init__.py +213 -0
  82. solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
  83. solace_agent_mesh/common/a2a/artifact.py +328 -0
  84. solace_agent_mesh/common/a2a/events.py +183 -0
  85. solace_agent_mesh/common/a2a/message.py +307 -0
  86. solace_agent_mesh/common/a2a/protocol.py +513 -0
  87. solace_agent_mesh/common/a2a/task.py +127 -0
  88. solace_agent_mesh/common/a2a/translation.py +653 -0
  89. solace_agent_mesh/common/a2a/types.py +54 -0
  90. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  91. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
  92. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  93. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
  94. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
  95. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
  96. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  97. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
  98. solace_agent_mesh/common/agent_registry.py +1 -1
  99. solace_agent_mesh/common/common_llm.txt +192 -70
  100. solace_agent_mesh/common/data_parts.py +99 -0
  101. solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
  102. solace_agent_mesh/common/sac/__init__.py +0 -0
  103. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  104. solace_agent_mesh/common/sac/sam_component_base.py +252 -0
  105. solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
  106. solace_agent_mesh/common/services/services_llm.txt +206 -26
  107. solace_agent_mesh/common/utils/artifact_utils.py +29 -0
  108. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
  109. solace_agent_mesh/common/utils/utils_llm.txt +323 -42
  110. solace_agent_mesh/config_portal/backend/common.py +1 -1
  111. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-MqsrTd6g.js → _index-Bym6YkMd.js} +74 -24
  112. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-B7lKcHVY.js → components-Rk0n-9cK.js} +1 -1
  113. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-CEumGClk.js → entry.client-mvZjNKiz.js} +1 -1
  114. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DSo1AH_7.js → index-DzNKzXrc.js} +1 -1
  115. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-d845808d.js +1 -0
  116. solace_agent_mesh/config_portal/frontend/static/client/assets/{root-C4XmHinv.js → root-BWvk5-gF.js} +1 -1
  117. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  118. solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
  119. solace_agent_mesh/core_a2a/service.py +20 -44
  120. solace_agent_mesh/gateway/base/app.py +27 -1
  121. solace_agent_mesh/gateway/base/base_llm.txt +177 -72
  122. solace_agent_mesh/gateway/base/component.py +294 -523
  123. solace_agent_mesh/gateway/gateway_llm.txt +299 -58
  124. solace_agent_mesh/gateway/http_sse/component.py +156 -183
  125. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
  126. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
  127. solace_agent_mesh/gateway/http_sse/main.py +8 -10
  128. solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
  129. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +18 -4
  130. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
  131. solace_agent_mesh/gateway/http_sse/routers/sessions.py +12 -7
  132. solace_agent_mesh/gateway/http_sse/routers/tasks.py +116 -169
  133. solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
  134. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
  135. solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
  136. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  137. solace_agent_mesh/templates/gateway_component_template.py +149 -98
  138. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/METADATA +5 -4
  139. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/RECORD +143 -126
  140. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
  141. solace_agent_mesh/assets/docs/assets/js/main.6dba4a66.js +0 -2
  142. solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +0 -1
  143. solace_agent_mesh/assets/docs/lunr-index-1756153049706.json +0 -1
  144. solace_agent_mesh/assets/docs/search-doc-1756153049706.json +0 -1
  145. solace_agent_mesh/client/webui/frontend/static/assets/main-BCpII1-0.css +0 -1
  146. solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +0 -673
  147. solace_agent_mesh/common/a2a_protocol.py +0 -564
  148. solace_agent_mesh/common/client/__init__.py +0 -4
  149. solace_agent_mesh/common/client/card_resolver.py +0 -21
  150. solace_agent_mesh/common/client/client.py +0 -85
  151. solace_agent_mesh/common/client/client_llm.txt +0 -133
  152. solace_agent_mesh/common/server/__init__.py +0 -4
  153. solace_agent_mesh/common/server/server.py +0 -122
  154. solace_agent_mesh/common/server/server_llm.txt +0 -169
  155. solace_agent_mesh/common/server/task_manager.py +0 -291
  156. solace_agent_mesh/common/server/utils.py +0 -28
  157. solace_agent_mesh/common/types.py +0 -411
  158. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-28271392.js +0 -1
  159. /solace_agent_mesh/assets/docs/assets/js/{main.6dba4a66.js.LICENSE.txt → main.a75ecc0d.js.LICENSE.txt} +0 -0
  160. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/WHEEL +0 -0
  161. {solace_agent_mesh-1.0.8.dist-info → solace_agent_mesh-1.1.0.dist-info}/entry_points.txt +0 -0
  162. {solace_agent_mesh-1.0.8.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
- Here is the DEVELOPER GUIDE for the `middleware` directory.
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 solace_ai_connector.common.middleware import ConfigResolver, MiddlewareRegistry
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 solace_ai_connector.common.middleware import ConfigResolver`
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 solace_ai_connector.common.middleware import ConfigResolver
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 solace_ai_connector.common.middleware import MiddlewareRegistry`
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 solace_ai_connector.common.middleware import MiddlewareRegistry, ConfigResolver
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, **kwargs) -> Dict[str, 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, **kwargs) -> 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