solace-agent-mesh 1.3.0__py3-none-any.whl → 1.3.2__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 (145) hide show
  1. solace_agent_mesh/agent/adk/setup.py +141 -34
  2. solace_agent_mesh/agent/protocol/event_handlers.py +91 -0
  3. solace_agent_mesh/agent/sac/app.py +3 -2
  4. solace_agent_mesh/agent/tools/__init__.py +1 -0
  5. solace_agent_mesh/agent/tools/dynamic_tool.py +362 -0
  6. solace_agent_mesh/assets/docs/404.html +3 -3
  7. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/483cef9a.03d5dceb.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +1 -0
  11. solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +1 -0
  13. solace_agent_mesh/assets/docs/assets/js/aba87c2f.071e2d94.js +1 -0
  14. solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +1 -0
  15. solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/main.4adc477a.js +2 -0
  18. solace_agent_mesh/assets/docs/assets/js/runtime~main.cf0229ea.js +1 -0
  19. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +29 -0
  20. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +25 -0
  21. solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html } +6 -6
  22. solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html } +6 -6
  23. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +19 -27
  24. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
  25. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
  26. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  27. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
  28. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
  29. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  30. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  31. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  32. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  34. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
  35. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +5 -4
  52. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  53. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +63 -0
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  57. solace_agent_mesh/assets/docs/lunr-index-1757704179464.json +1 -0
  58. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  59. solace_agent_mesh/assets/docs/search-doc-1757704179464.json +1 -0
  60. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  61. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  62. solace_agent_mesh/cli/__init__.py +1 -1
  63. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-vY5eu2lI.js → authCallback-CAX9u8a7.js} +1 -1
  64. solace_agent_mesh/client/webui/frontend/static/assets/{client-BeBkzgWW.js → client-DXU9SPI5.js} +1 -1
  65. solace_agent_mesh/client/webui/frontend/static/assets/{main-Bjys1KQs.js → main-DjoMeldu.js} +26 -26
  66. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CE0AeXyK.js → vendor-B0BEKoAR.js} +69 -74
  67. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  68. solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
  69. solace_agent_mesh/common/a2a/__init__.py +4 -0
  70. solace_agent_mesh/common/a2a/protocol.py +20 -0
  71. solace_agent_mesh/common/sac/sam_component_base.py +29 -9
  72. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  73. solace_agent_mesh/common/sam_events/event_service.py +207 -0
  74. solace_agent_mesh/gateway/http_sse/alembic/env.py +1 -1
  75. solace_agent_mesh/gateway/http_sse/component.py +45 -35
  76. solace_agent_mesh/gateway/http_sse/dependencies.py +129 -66
  77. solace_agent_mesh/gateway/http_sse/main.py +22 -35
  78. solace_agent_mesh/gateway/http_sse/repository/__init__.py +37 -0
  79. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +9 -0
  80. solace_agent_mesh/gateway/http_sse/repository/entities/message.py +41 -0
  81. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +45 -0
  82. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +16 -0
  83. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +64 -0
  84. solace_agent_mesh/gateway/http_sse/repository/message_repository.py +78 -0
  85. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -0
  86. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  87. solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +27 -0
  88. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +27 -0
  89. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +139 -0
  90. solace_agent_mesh/gateway/http_sse/routers/{agents.py → agent_cards.py} +7 -7
  91. solace_agent_mesh/gateway/http_sse/routers/config.py +1 -0
  92. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +20 -0
  93. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/requests/session_requests.py +1 -8
  94. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +16 -0
  95. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/responses/session_responses.py +3 -30
  96. solace_agent_mesh/gateway/http_sse/{api/controllers/session_controller.py → routers/sessions.py} +20 -77
  97. solace_agent_mesh/gateway/http_sse/routers/tasks.py +42 -49
  98. solace_agent_mesh/gateway/http_sse/{api/controllers/user_controller.py → routers/users.py} +1 -1
  99. solace_agent_mesh/gateway/http_sse/services/{agent_service.py → agent_card_service.py} +19 -19
  100. solace_agent_mesh/gateway/http_sse/services/session_service.py +245 -0
  101. solace_agent_mesh/gateway/http_sse/session_manager.py +0 -3
  102. solace_agent_mesh/gateway/http_sse/shared/enums.py +0 -5
  103. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/METADATA +1 -1
  104. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/RECORD +109 -110
  105. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +0 -1
  106. solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.js +0 -1
  107. solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +0 -1
  108. solace_agent_mesh/assets/docs/assets/js/75384d09.bf78fbdb.js +0 -1
  109. solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +0 -1
  110. solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +0 -1
  111. solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +0 -2
  113. solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +0 -1
  114. solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +0 -1
  115. solace_agent_mesh/assets/docs/search-doc-1757433031159.json +0 -1
  116. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +0 -676
  117. solace_agent_mesh/gateway/http_sse/api/__init__.py +0 -11
  118. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +0 -9
  119. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +0 -279
  120. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +0 -37
  121. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +0 -66
  122. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +0 -43
  123. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +0 -74
  124. solace_agent_mesh/gateway/http_sse/application/__init__.py +0 -3
  125. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +0 -3
  126. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +0 -135
  127. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +0 -3
  128. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +0 -90
  129. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +0 -3
  130. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +0 -54
  131. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +0 -4
  132. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +0 -3
  133. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +0 -123
  134. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +0 -4
  135. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +0 -16
  136. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +0 -119
  137. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +0 -31
  138. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +0 -12
  139. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +0 -3
  140. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +0 -174
  141. /solace_agent_mesh/assets/docs/assets/js/{main.08d30374.js.LICENSE.txt → main.4adc477a.js.LICENSE.txt} +0 -0
  142. /solace_agent_mesh/gateway/http_sse/{api → routers}/dto/__init__.py +0 -0
  143. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/WHEEL +0 -0
  144. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/entry_points.txt +0 -0
  145. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -18,7 +18,6 @@ from google.adk import tools as adk_tools_module
18
18
  from google.adk.agents.callback_context import CallbackContext
19
19
  from google.adk.models.llm_request import LlmRequest
20
20
  from google.adk.models.llm_response import LlmResponse
21
- from google.adk.tools.mcp_tool import MCPToolset
22
21
  from google.adk.tools.mcp_tool.mcp_session_manager import (
23
22
  SseServerParams,
24
23
  StdioConnectionParams,
@@ -28,12 +27,49 @@ from mcp import StdioServerParameters
28
27
 
29
28
  from ..tools.registry import tool_registry
30
29
  from ..tools.tool_definition import BuiltinTool
30
+ from ..tools.dynamic_tool import DynamicTool, DynamicToolProvider
31
31
 
32
32
 
33
33
  from ...agent.adk import callbacks as adk_callbacks
34
34
  from ...agent.adk.models.lite_llm import LiteLlm
35
35
 
36
36
 
37
+ def _find_dynamic_tool_class(module) -> Optional[type]:
38
+ """Finds a single non-abstract DynamicTool subclass in a module."""
39
+ found_classes = []
40
+ for name, obj in inspect.getmembers(module, inspect.isclass):
41
+ if (
42
+ issubclass(obj, DynamicTool)
43
+ and obj is not DynamicTool
44
+ and not inspect.isabstract(obj)
45
+ ):
46
+ found_classes.append(obj)
47
+ if len(found_classes) > 1:
48
+ raise TypeError(
49
+ f"Module '{module.__name__}' contains multiple DynamicTool subclasses. "
50
+ "Please specify which one to use with 'class_name' in the config."
51
+ )
52
+ return found_classes[0] if found_classes else None
53
+
54
+
55
+ def _find_dynamic_tool_provider_class(module) -> Optional[type]:
56
+ """Finds a single non-abstract DynamicToolProvider subclass in a module."""
57
+ found_classes = []
58
+ for name, obj in inspect.getmembers(module, inspect.isclass):
59
+ if (
60
+ issubclass(obj, DynamicToolProvider)
61
+ and obj is not DynamicToolProvider
62
+ and not inspect.isabstract(obj)
63
+ ):
64
+ found_classes.append(obj)
65
+ if len(found_classes) > 1:
66
+ raise TypeError(
67
+ f"Module '{module.__name__}' contains multiple DynamicToolProvider subclasses. "
68
+ "Only one is permitted per module."
69
+ )
70
+ return found_classes[0] if found_classes else None
71
+
72
+
37
73
  async def load_adk_tools(
38
74
  component,
39
75
  ) -> Tuple[List[Union[BaseTool, Callable]], List[BuiltinTool]]:
@@ -90,48 +126,119 @@ async def load_adk_tools(
90
126
  try:
91
127
  if tool_type == "python":
92
128
  module_name = tool_config.get("component_module")
93
- function_name = tool_config.get("function_name")
94
- tool_name = tool_config.get("tool_name")
95
- tool_description = tool_config.get("tool_description")
96
129
  base_path = tool_config.get("component_base_path")
97
- if not module_name or not function_name:
130
+ if not module_name:
98
131
  raise ValueError(
99
- "'component_module' and 'function_name' required for python tool."
132
+ "'component_module' is required for python tools."
100
133
  )
101
-
102
134
  module = import_module(module_name, base_path=base_path)
103
- func = getattr(module, function_name)
104
- if not callable(func):
105
- raise TypeError(
106
- f"'{function_name}' in module '{module_name}' is not callable."
135
+
136
+ # Case 1: Simple function-based tool
137
+ if "function_name" in tool_config:
138
+ function_name = tool_config.get("function_name")
139
+ tool_name = tool_config.get("tool_name")
140
+ tool_description = tool_config.get("tool_description")
141
+
142
+ func = getattr(module, function_name)
143
+ if not callable(func):
144
+ raise TypeError(
145
+ f"'{function_name}' in module '{module_name}' is not callable."
146
+ )
147
+
148
+ specific_tool_config = tool_config.get("tool_config")
149
+ tool_callable = ADKToolWrapper(
150
+ func,
151
+ specific_tool_config,
152
+ function_name,
153
+ origin="python",
154
+ raw_string_args=tool_config.get("raw_string_args", []),
107
155
  )
108
156
 
109
- specific_tool_config = tool_config.get("tool_config")
110
- tool_callable = ADKToolWrapper(
111
- func,
112
- specific_tool_config,
113
- function_name,
114
- origin="python",
115
- raw_string_args=tool_config.get("raw_string_args", []),
116
- )
157
+ if tool_name:
158
+ function_name = tool_name
159
+ tool_callable.__name__ = tool_name
160
+
161
+ if tool_description:
162
+ tool_callable.__doc__ = tool_description
163
+
164
+ _check_and_register_tool_name(
165
+ function_name, f"python:{module_name}"
166
+ )
167
+ loaded_tools.append(tool_callable)
168
+ log.info(
169
+ "%s Loaded Python tool: %s from %s.",
170
+ component.log_identifier,
171
+ function_name,
172
+ module_name,
173
+ )
174
+ # Case 2: Advanced class-based dynamic tool or provider
175
+ else:
176
+ specific_tool_config = tool_config.get("tool_config")
177
+ dynamic_tools = []
117
178
 
118
- if tool_name:
119
- function_name = tool_name
120
- tool_callable.__name__ = tool_name
179
+ # Determine the class to load
180
+ tool_class = None
181
+ class_name = tool_config.get("class_name")
182
+ if class_name:
183
+ tool_class = getattr(module, class_name)
184
+ else:
185
+ # Auto-discover: provider first, then single tool
186
+ tool_class = _find_dynamic_tool_provider_class(module)
187
+ if not tool_class:
188
+ tool_class = _find_dynamic_tool_class(module)
189
+
190
+ if not tool_class:
191
+ raise TypeError(
192
+ f"Module '{module_name}' does not contain a 'function_name' or 'class_name' to load, "
193
+ "and no DynamicTool or DynamicToolProvider subclass could be auto-discovered."
194
+ )
121
195
 
122
- if tool_description:
123
- tool_callable.__doc__ = tool_description
196
+ # Instantiate tools from the class
197
+ if issubclass(tool_class, DynamicToolProvider):
198
+ provider_instance = tool_class()
199
+ dynamic_tools = (
200
+ provider_instance.get_all_tools_for_framework(
201
+ tool_config=specific_tool_config
202
+ )
203
+ )
204
+ log.info(
205
+ "%s Loaded %d tools from DynamicToolProvider '%s' in %s",
206
+ component.log_identifier,
207
+ len(dynamic_tools),
208
+ tool_class.__name__,
209
+ module_name,
210
+ )
211
+ elif issubclass(tool_class, DynamicTool):
212
+ tool_instance = tool_class(tool_config=specific_tool_config)
213
+ dynamic_tools = [tool_instance]
214
+ else:
215
+ raise TypeError(
216
+ f"Class '{tool_class.__name__}' in module '{module_name}' is not a valid "
217
+ "DynamicTool or DynamicToolProvider subclass."
218
+ )
124
219
 
125
- _check_and_register_tool_name(
126
- function_name, f"python:{module_name}"
127
- )
128
- loaded_tools.append(tool_callable)
129
- log.info(
130
- "%s Loaded Python tool: %s from %s.",
131
- component.log_identifier,
132
- function_name,
133
- module_name,
134
- )
220
+ # Process all generated tools
221
+ for tool in dynamic_tools:
222
+ tool.origin = "dynamic"
223
+ declaration = tool._get_declaration()
224
+ if not declaration:
225
+ log.warning(
226
+ "Dynamic tool '%s' from module '%s' did not generate a valid declaration. Skipping.",
227
+ tool.__class__.__name__,
228
+ module_name,
229
+ )
230
+ continue
231
+
232
+ _check_and_register_tool_name(
233
+ declaration.name, f"dynamic:{module_name}"
234
+ )
235
+ loaded_tools.append(tool)
236
+ log.info(
237
+ "%s Loaded dynamic tool: %s from %s",
238
+ component.log_identifier,
239
+ declaration.name,
240
+ module_name,
241
+ )
135
242
 
136
243
  elif tool_type == "builtin":
137
244
  tool_name = tool_config.get("tool_name")
@@ -32,7 +32,9 @@ from ...common.a2a import (
32
32
  get_client_response_topic,
33
33
  get_agent_response_subscription_topic,
34
34
  get_agent_status_subscription_topic,
35
+ get_sam_events_subscription_topic,
35
36
  get_text_from_message,
37
+ topic_matches_subscription,
36
38
  )
37
39
  from ...agent.utils.artifact_helpers import (
38
40
  generate_artifact_metadata_summary,
@@ -118,6 +120,7 @@ async def process_event(component, event: Event):
118
120
  agent_status_sub_prefix = (
119
121
  get_agent_status_subscription_topic(namespace, agent_name)[:-2] + "/"
120
122
  )
123
+ sam_events_topic = get_sam_events_subscription_topic(namespace, "session")
121
124
  if topic == agent_request_topic:
122
125
  await handle_a2a_request(component, message)
123
126
  elif topic == discovery_topic:
@@ -126,6 +129,8 @@ async def process_event(component, event: Event):
126
129
  handle_agent_card_message(component, message)
127
130
  else:
128
131
  message.call_acknowledgements()
132
+ elif topic_matches_subscription(topic, sam_events_topic):
133
+ handle_sam_event(component, message, topic)
129
134
  elif topic.startswith(agent_response_sub_prefix) or topic.startswith(
130
135
  agent_status_sub_prefix
131
136
  ):
@@ -233,6 +238,8 @@ async def handle_a2a_request(component, message: SolaceMessage):
233
238
  payload_dict = message.get_payload()
234
239
  if not isinstance(payload_dict, dict):
235
240
  raise ValueError("Payload is not a dictionary.")
241
+
242
+
236
243
  a2a_request: A2ARequest = A2ARequest.model_validate(payload_dict)
237
244
  jsonrpc_request_id = a2a.get_request_id(a2a_request)
238
245
 
@@ -1539,3 +1546,87 @@ def publish_agent_card(component):
1539
1546
  "%s Failed to publish Agent Card: %s", component.log_identifier, e
1540
1547
  )
1541
1548
  component.handle_error(e, None)
1549
+
1550
+
1551
+ def handle_sam_event(component, message, topic):
1552
+ """Handle incoming SAM system events."""
1553
+ try:
1554
+ payload = message.get_payload()
1555
+
1556
+ if not isinstance(payload, dict):
1557
+ log.warning("Invalid SAM event payload - not a dict")
1558
+ message.call_acknowledgements()
1559
+ return
1560
+
1561
+ event_type = payload.get("event_type")
1562
+ if not event_type:
1563
+ log.warning("SAM event missing event_type field")
1564
+ message.call_acknowledgements()
1565
+ return
1566
+
1567
+ log.info("%s Received SAM event: %s", component.log_identifier, event_type)
1568
+
1569
+ if event_type == "session.deleted":
1570
+ data = payload.get("data", {})
1571
+ session_id = data.get("session_id")
1572
+ user_id = data.get("user_id")
1573
+ agent_id = data.get("agent_id")
1574
+
1575
+ if not all([session_id, user_id, agent_id]):
1576
+ log.warning("Missing required fields in session.deleted event")
1577
+ message.call_acknowledgements()
1578
+ return
1579
+
1580
+ current_agent = component.get_config("agent_name")
1581
+
1582
+ if agent_id == current_agent:
1583
+ log.info("%s Processing session.deleted event for session %s",
1584
+ component.log_identifier, session_id)
1585
+ asyncio.create_task(cleanup_agent_session(component, session_id, user_id))
1586
+ else:
1587
+ log.debug("Session deletion event for different agent: %s != %s", agent_id, current_agent)
1588
+ else:
1589
+ log.debug("Unhandled SAM event type: %s", event_type)
1590
+
1591
+ message.call_acknowledgements()
1592
+
1593
+ except Exception as e:
1594
+ log.error("Error handling SAM event %s: %s", topic, e)
1595
+ message.call_acknowledgements()
1596
+
1597
+
1598
+
1599
+ async def cleanup_agent_session(component, session_id: str, user_id: str):
1600
+ """Clean up agent-side session data."""
1601
+ try:
1602
+ log.info("Starting cleanup for session %s, user %s", session_id, user_id)
1603
+
1604
+ if hasattr(component, 'session_service') and component.session_service:
1605
+ agent_name = component.get_config("agent_name")
1606
+ log.info("Deleting session %s from agent %s session service", session_id, agent_name)
1607
+ await component.session_service.delete_session(
1608
+ app_name=agent_name,
1609
+ user_id=user_id,
1610
+ session_id=session_id
1611
+ )
1612
+ log.info("Successfully deleted session %s from session service", session_id)
1613
+ else:
1614
+ log.info("No session service available for cleanup")
1615
+
1616
+ with component.active_tasks_lock:
1617
+ tasks_to_cancel = []
1618
+ for task_id, context in component.active_tasks.items():
1619
+ if (hasattr(context, 'a2a_context') and
1620
+ context.a2a_context.get('session_id') == session_id):
1621
+ tasks_to_cancel.append(task_id)
1622
+
1623
+ for task_id in tasks_to_cancel:
1624
+ context = component.active_tasks.get(task_id)
1625
+ if context:
1626
+ context.cancel()
1627
+ log.info("Cancelled task %s for deleted session %s", task_id, session_id)
1628
+
1629
+ log.info("Session cleanup completed for session %s", session_id)
1630
+
1631
+ except Exception as e:
1632
+ log.error("Error cleaning up session %s: %s", session_id, e)
@@ -14,13 +14,13 @@ patch_adk()
14
14
 
15
15
  from typing import Any, Dict
16
16
  from solace_ai_connector.flow.app import App
17
- from solace_ai_connector.common.log import log
18
17
 
19
18
  from ...common.a2a import (
20
19
  get_agent_request_topic,
21
20
  get_discovery_topic,
22
21
  get_agent_response_subscription_topic,
23
22
  get_agent_status_subscription_topic,
23
+ get_sam_events_subscription_topic,
24
24
  )
25
25
  from ...common.constants import DEFAULT_COMMUNICATION_TIMEOUT
26
26
  from ...agent.sac.component import SamAgentComponent
@@ -680,6 +680,7 @@ class SamAgentApp(App):
680
680
  get_discovery_topic(namespace),
681
681
  get_agent_response_subscription_topic(namespace, agent_name),
682
682
  get_agent_status_subscription_topic(namespace, agent_name),
683
+ get_sam_events_subscription_topic(namespace, "session"),
683
684
  ]
684
685
  generated_subs = [{"topic": topic} for topic in required_topics]
685
686
  log.info(
@@ -714,4 +715,4 @@ class SamAgentApp(App):
714
715
  log.debug("Set broker_config.temporary_queue = True")
715
716
 
716
717
  super().__init__(app_info, **kwargs)
717
- log.debug("A2A_ADK_App initialization complete.")
718
+ log.debug("A2A_ADK_App initialization complete.")
@@ -12,3 +12,4 @@ from . import audio_tools
12
12
  from . import image_tools
13
13
  from . import web_tools
14
14
  from . import test_tools
15
+ from . import dynamic_tool