solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (184) hide show
  1. solace_agent_mesh/agent/adk/callbacks.py +0 -5
  2. solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
  3. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
  4. solace_agent_mesh/agent/protocol/event_handlers.py +213 -31
  5. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  6. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  7. solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
  8. solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
  9. solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
  10. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  11. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  12. solace_agent_mesh/agent/proxies/base/app.py +99 -0
  13. solace_agent_mesh/agent/proxies/base/component.py +650 -0
  14. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  15. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
  16. solace_agent_mesh/agent/sac/app.py +58 -5
  17. solace_agent_mesh/agent/sac/component.py +238 -75
  18. solace_agent_mesh/agent/sac/task_execution_context.py +46 -0
  19. solace_agent_mesh/agent/tools/audio_tools.py +125 -8
  20. solace_agent_mesh/agent/tools/web_tools.py +10 -5
  21. solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
  22. solace_agent_mesh/assets/docs/404.html +3 -3
  23. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
  30. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
  32. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/{e3d9abda.2b916f9e.js → e3d9abda.6b9493d0.js} +1 -1
  35. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
  37. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.b12eac43.js} +2 -2
  39. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +1 -0
  40. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +262 -0
  52. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +5 -5
  58. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  61. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
  65. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
  66. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
  67. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
  69. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
  70. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
  71. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
  72. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  73. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  74. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
  75. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  76. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  77. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  78. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  79. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  80. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +6 -5
  81. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  82. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  83. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +100 -3
  84. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  85. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  86. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  87. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +1 -0
  88. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  89. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +1 -0
  90. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  91. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  92. solace_agent_mesh/cli/__init__.py +1 -1
  93. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +2 -69
  94. solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
  95. solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
  96. solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
  97. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
  98. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
  99. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
  100. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
  101. solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
  102. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +342 -0
  103. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
  104. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.js} +1 -1
  105. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  106. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  107. solace_agent_mesh/common/a2a/__init__.py +24 -0
  108. solace_agent_mesh/common/a2a/artifact.py +39 -0
  109. solace_agent_mesh/common/a2a/events.py +29 -0
  110. solace_agent_mesh/common/a2a/message.py +68 -0
  111. solace_agent_mesh/common/a2a/protocol.py +151 -1
  112. solace_agent_mesh/common/agent_registry.py +83 -3
  113. solace_agent_mesh/common/constants.py +3 -1
  114. solace_agent_mesh/common/sac/sam_component_base.py +383 -4
  115. solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
  116. solace_agent_mesh/config_portal/backend/common.py +1 -1
  117. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
  118. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
  119. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  120. solace_agent_mesh/evaluation/evaluator.py +128 -104
  121. solace_agent_mesh/evaluation/message_organizer.py +116 -110
  122. solace_agent_mesh/evaluation/report_data_processor.py +84 -86
  123. solace_agent_mesh/evaluation/report_generator.py +73 -79
  124. solace_agent_mesh/evaluation/run.py +421 -235
  125. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  126. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  127. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  128. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  129. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  130. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  131. solace_agent_mesh/evaluation/subscriber.py +111 -232
  132. solace_agent_mesh/evaluation/summary_builder.py +227 -117
  133. solace_agent_mesh/gateway/base/app.py +16 -1
  134. solace_agent_mesh/gateway/base/component.py +112 -39
  135. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  136. solace_agent_mesh/gateway/http_sse/component.py +99 -3
  137. solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
  138. solace_agent_mesh/gateway/http_sse/main.py +1 -0
  139. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
  140. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
  141. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
  142. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
  143. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
  144. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
  145. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
  146. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
  147. solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
  148. solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
  149. solace_agent_mesh/gateway/http_sse/routers/users.py +47 -1
  150. solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
  151. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
  152. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
  153. solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
  154. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
  155. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
  156. solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
  157. solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
  158. solace_agent_mesh/templates/logging_config_template.ini +10 -6
  159. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
  160. solace_agent_mesh/templates/shared_config.yaml +40 -0
  161. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/METADATA +47 -21
  162. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/RECORD +166 -145
  163. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
  164. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
  165. solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
  166. solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
  167. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
  168. solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
  169. solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
  170. solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
  171. solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
  172. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
  173. solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
  174. solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
  175. solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
  176. solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
  177. solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
  178. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
  179. solace_agent_mesh/evaluation/config_loader.py +0 -657
  180. solace_agent_mesh/evaluation/test_case_loader.py +0 -714
  181. /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.b12eac43.js.LICENSE.txt} +0 -0
  182. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/WHEEL +0 -0
  183. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/entry_points.txt +0 -0
  184. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -134,26 +134,32 @@ from sse_starlette.sse import EventSourceResponse
134
134
 
135
135
  def _generate_sse_url(fastapi_request: FastAPIRequest, stream_id: str) -> str:
136
136
  """
137
- Generate SSE endpoint URL with proper scheme detection for reverse proxy scenarios.
137
+ Generate SSE endpoint URL with proper scheme and host detection for reverse proxy scenarios.
138
138
 
139
139
  Args:
140
140
  fastapi_request: The FastAPI request object
141
141
  stream_id: The stream ID for the SSE endpoint
142
142
 
143
143
  Returns:
144
- Complete SSE URL with correct scheme (http/https)
144
+ Complete SSE URL with correct scheme (http/https) and host.
145
145
  """
146
+ base_url = fastapi_request.url_for(
147
+ "get_visualization_stream_events", stream_id=stream_id
148
+ )
149
+
146
150
  forwarded_proto = fastapi_request.headers.get("x-forwarded-proto")
147
- if forwarded_proto and forwarded_proto.lower() == "https":
148
- scheme = "https"
151
+ forwarded_host = fastapi_request.headers.get("x-forwarded-host")
152
+
153
+ if forwarded_proto and forwarded_host:
154
+ # In a reverse proxy environment like GitHub Codespaces, reconstruct the URL
155
+ # using the forwarded headers to ensure it's publicly accessible.
156
+ return str(base_url.replace(scheme=forwarded_proto, netloc=forwarded_host))
157
+ elif forwarded_proto:
158
+ # Handle cases with only a forwarded protocol (standard reverse proxy)
159
+ return str(base_url.replace(scheme=forwarded_proto))
149
160
  else:
150
- scheme = fastapi_request.url.scheme
151
-
152
- return str(
153
- fastapi_request.url_for(
154
- "get_visualization_stream_events", stream_id=stream_id
155
- ).replace(scheme=scheme)
156
- )
161
+ # Default behavior when not behind a reverse proxy
162
+ return str(base_url)
157
163
 
158
164
 
159
165
  def _translate_target_to_solace_topics(
@@ -188,8 +188,8 @@ class DataRetentionService:
188
188
 
189
189
  db = self.session_factory()
190
190
  try:
191
- repo = TaskRepository(db)
192
- total_deleted = repo.delete_tasks_older_than(cutoff_time_ms, batch_size)
191
+ repo = TaskRepository()
192
+ total_deleted = repo.delete_tasks_older_than(db, cutoff_time_ms, batch_size)
193
193
 
194
194
  if total_deleted == 0:
195
195
  log.info(
@@ -241,8 +241,8 @@ class DataRetentionService:
241
241
 
242
242
  db = self.session_factory()
243
243
  try:
244
- repo = FeedbackRepository(db)
245
- total_deleted = repo.delete_feedback_older_than(cutoff_time_ms, batch_size)
244
+ repo = FeedbackRepository()
245
+ total_deleted = repo.delete_feedback_older_than(db, cutoff_time_ms, batch_size)
246
246
 
247
247
  if total_deleted == 0:
248
248
  log.info(
@@ -71,8 +71,8 @@ class FeedbackService:
71
71
 
72
72
  db = self.session_factory()
73
73
  try:
74
- repo = FeedbackRepository(db)
75
- repo.save(feedback_entity)
74
+ repo = FeedbackRepository()
75
+ repo.save(db, feedback_entity)
76
76
  db.commit()
77
77
  log.info(
78
78
  "Feedback from user '%s' for task '%s' saved to database.",
@@ -131,14 +131,14 @@ class FeedbackService:
131
131
  db = self.session_factory()
132
132
  try:
133
133
  from ..repository.chat_task_repository import ChatTaskRepository
134
-
135
- task_repo = ChatTaskRepository(db)
136
- task = task_repo.find_by_id(task_id, user_id)
137
-
134
+
135
+ task_repo = ChatTaskRepository()
136
+ task = task_repo.find_by_id(db, task_id, user_id)
137
+
138
138
  if task:
139
139
  # Update feedback in task metadata
140
140
  task.add_feedback(feedback_type, feedback_text)
141
- task_repo.save(task)
141
+ task_repo.save(db, task)
142
142
  db.commit()
143
143
  log.info(
144
144
  "Updated task metadata with feedback for task '%s' by user '%s'",
@@ -187,45 +187,53 @@ class FeedbackService:
187
187
 
188
188
  if include_task_info == "summary":
189
189
  log.debug("%s Including task summary.", log_id)
190
- task_summary_data = self.task_repo.find_by_id(payload.task_id)
191
- if task_summary_data:
192
- event_payload["task_summary"] = task_summary_data.model_dump()
190
+ db = self.session_factory()
191
+ try:
192
+ task_summary_data = self.task_repo.find_by_id(db, payload.task_id)
193
+ if task_summary_data:
194
+ event_payload["task_summary"] = task_summary_data.model_dump()
195
+ finally:
196
+ db.close()
193
197
 
194
198
  elif include_task_info == "stim":
195
199
  log.debug("%s Including task stim data.", log_id)
196
- task_with_events = self.task_repo.find_by_id_with_events(payload.task_id)
197
- if task_with_events:
198
- task, events = task_with_events
199
- stim_data = create_stim_from_task_data(task, events)
200
- event_payload["task_stim_data"] = stim_data
201
-
202
- # Check payload size
203
- max_size = config.get("max_payload_size_bytes", 9000000)
204
- try:
205
- payload_bytes = json.dumps(event_payload).encode("utf-8")
206
- if len(payload_bytes) > max_size:
207
- log.warning(
208
- "%s Stim payload size (%d bytes) exceeds limit (%d bytes). Falling back to summary.",
209
- log_id,
210
- len(payload_bytes),
211
- max_size,
212
- )
213
- # Fallback to summary
214
- del event_payload["task_stim_data"]
215
- task_summary_data = self.task_repo.find_by_id(payload.task_id)
216
- if task_summary_data:
217
- event_payload[
218
- "task_summary"
219
- ] = task_summary_data.model_dump()
220
- event_payload["truncation_details"] = {
221
- "strategy": "fallback_to_summary",
222
- "reason": "payload_too_large",
223
- }
224
- except Exception as e:
225
- log.error("%s Error checking payload size: %s", log_id, e)
226
- # If we can't check size, better to not send a potentially huge message
227
- if "task_stim_data" in event_payload:
228
- del event_payload["task_stim_data"]
200
+ db = self.session_factory()
201
+ try:
202
+ task_with_events = self.task_repo.find_by_id_with_events(db, payload.task_id)
203
+ if task_with_events:
204
+ task, events = task_with_events
205
+ stim_data = create_stim_from_task_data(task, events)
206
+ event_payload["task_stim_data"] = stim_data
207
+
208
+ # Check payload size
209
+ max_size = config.get("max_payload_size_bytes", 9000000)
210
+ try:
211
+ payload_bytes = json.dumps(event_payload).encode("utf-8")
212
+ if len(payload_bytes) > max_size:
213
+ log.warning(
214
+ "%s Stim payload size (%d bytes) exceeds limit (%d bytes). Falling back to summary.",
215
+ log_id,
216
+ len(payload_bytes),
217
+ max_size,
218
+ )
219
+ # Fallback to summary
220
+ del event_payload["task_stim_data"]
221
+ task_summary_data = self.task_repo.find_by_id(db, payload.task_id)
222
+ if task_summary_data:
223
+ event_payload[
224
+ "task_summary"
225
+ ] = task_summary_data.model_dump()
226
+ event_payload["truncation_details"] = {
227
+ "strategy": "fallback_to_summary",
228
+ "reason": "payload_too_large",
229
+ }
230
+ except Exception as e:
231
+ log.error("%s Error checking payload size: %s", log_id, e)
232
+ # If we can't check size, better to not send a potentially huge message
233
+ if "task_stim_data" in event_payload:
234
+ del event_payload["task_stim_data"]
235
+ finally:
236
+ db.close()
229
237
 
230
238
  # Publish the event
231
239
  topic = config.get("topic", "sam/feedback/v1")
@@ -31,7 +31,7 @@ class SessionService:
31
31
  def _get_repositories(self, db: DbSession):
32
32
  """Create session repository for the given database session."""
33
33
  from ..repository import SessionRepository
34
- session_repository = SessionRepository(db)
34
+ session_repository = SessionRepository()
35
35
  return session_repository
36
36
 
37
37
  def is_persistence_enabled(self) -> bool:
@@ -57,8 +57,8 @@ class SessionService:
57
57
  session_repository = self._get_repositories(db)
58
58
 
59
59
  # Pass pagination params directly - repository will handle offset calculation
60
- sessions = session_repository.find_by_user(user_id, pagination)
61
- total_count = session_repository.count_by_user(user_id)
60
+ sessions = session_repository.find_by_user(db, user_id, pagination)
61
+ total_count = session_repository.count_by_user(db, user_id)
62
62
 
63
63
  return PaginatedResponse.create(sessions, total_count, pagination)
64
64
 
@@ -69,7 +69,7 @@ class SessionService:
69
69
  return None
70
70
 
71
71
  session_repository = self._get_repositories(db)
72
- return session_repository.find_user_session(session_id, user_id)
72
+ return session_repository.find_user_session(db, session_id, user_id)
73
73
 
74
74
  def create_session(
75
75
  self,
@@ -100,7 +100,7 @@ class SessionService:
100
100
  )
101
101
 
102
102
  session_repository = self._get_repositories(db)
103
- created_session = session_repository.save(session)
103
+ created_session = session_repository.save(db, session)
104
104
  log.info("Created new session %s for user %s", created_session.id, user_id)
105
105
 
106
106
  if not created_session:
@@ -121,12 +121,12 @@ class SessionService:
121
121
  raise ValueError("Session name cannot exceed 255 characters")
122
122
 
123
123
  session_repository = self._get_repositories(db)
124
- session = session_repository.find_user_session(session_id, user_id)
124
+ session = session_repository.find_user_session(db, session_id, user_id)
125
125
  if not session:
126
126
  return None
127
127
 
128
128
  session.update_name(name)
129
- updated_session = session_repository.save(session)
129
+ updated_session = session_repository.save(db, session)
130
130
 
131
131
  log.info("Updated session %s name to '%s'", session_id, name)
132
132
  return updated_session
@@ -138,7 +138,7 @@ class SessionService:
138
138
  raise ValueError("Invalid session ID")
139
139
 
140
140
  session_repository = self._get_repositories(db)
141
- session = session_repository.find_user_session(session_id, user_id)
141
+ session = session_repository.find_user_session(db, session_id, user_id)
142
142
  if not session:
143
143
  log.warning(
144
144
  "Attempted to delete non-existent session %s by user %s",
@@ -155,7 +155,7 @@ class SessionService:
155
155
  )
156
156
  return False
157
157
 
158
- deleted = session_repository.delete(session_id, user_id)
158
+ deleted = session_repository.delete(db, session_id, user_id)
159
159
  if not deleted:
160
160
  return False
161
161
 
@@ -196,10 +196,10 @@ class SessionService:
196
196
  """
197
197
  # Validate session exists and belongs to user
198
198
  session_repository = self._get_repositories(db)
199
- session = session_repository.find_user_session(session_id, user_id)
199
+ session = session_repository.find_user_session(db, session_id, user_id)
200
200
  if not session:
201
201
  raise ValueError(f"Session {session_id} not found for user {user_id}")
202
-
202
+
203
203
  # Create task entity - pass strings directly
204
204
  task = ChatTask(
205
205
  id=task_id,
@@ -211,14 +211,14 @@ class SessionService:
211
211
  created_time=now_epoch_ms(),
212
212
  updated_time=None
213
213
  )
214
-
214
+
215
215
  # Save via repository
216
- task_repo = ChatTaskRepository(db)
217
- saved_task = task_repo.save(task)
218
-
216
+ task_repo = ChatTaskRepository()
217
+ saved_task = task_repo.save(db, task)
218
+
219
219
  # Update session activity
220
220
  session.mark_activity()
221
- session_repository.save(session)
221
+ session_repository.save(db, session)
222
222
 
223
223
  log.info(f"Saved task {task_id} for session {session_id}")
224
224
  return saved_task
@@ -245,13 +245,13 @@ class SessionService:
245
245
  """
246
246
  # Validate session exists and belongs to user
247
247
  session_repository = self._get_repositories(db)
248
- session = session_repository.find_user_session(session_id, user_id)
248
+ session = session_repository.find_user_session(db, session_id, user_id)
249
249
  if not session:
250
250
  raise ValueError(f"Session {session_id} not found for user {user_id}")
251
-
251
+
252
252
  # Load tasks
253
- task_repo = ChatTaskRepository(db)
254
- return task_repo.find_by_session(session_id, user_id)
253
+ task_repo = ChatTaskRepository()
254
+ return task_repo.find_by_session(db, session_id, user_id)
255
255
 
256
256
  def get_session_messages_from_tasks(
257
257
  self,
@@ -71,7 +71,7 @@ class TaskLoggerService:
71
71
 
72
72
  db = self.session_factory()
73
73
  try:
74
- repo = TaskRepository(db)
74
+ repo = TaskRepository()
75
75
 
76
76
  # Infer details from the parsed event
77
77
  direction, task_id, user_id = self._infer_event_details(
@@ -95,7 +95,7 @@ class TaskLoggerService:
95
95
  sanitized_payload = self._sanitize_payload(payload)
96
96
 
97
97
  # Check for existing task or create a new one
98
- task = repo.find_by_id(task_id)
98
+ task = repo.find_by_id(db, task_id)
99
99
  if not task:
100
100
  if direction == "request":
101
101
  initial_text = self._extract_initial_text(parsed_event)
@@ -107,7 +107,7 @@ class TaskLoggerService:
107
107
  initial_text[:1024] if initial_text else None
108
108
  ), # Truncate
109
109
  )
110
- repo.save_task(new_task)
110
+ repo.save_task(db, new_task)
111
111
  log.info(
112
112
  f"{self.log_identifier} Created new task record for ID: {task_id}"
113
113
  )
@@ -120,7 +120,7 @@ class TaskLoggerService:
120
120
  start_time=now_epoch_ms(),
121
121
  initial_request_text="[Task started before logger was active]",
122
122
  )
123
- repo.save_task(placeholder_task)
123
+ repo.save_task(db, placeholder_task)
124
124
  log.info(
125
125
  f"{self.log_identifier} Created placeholder task record for ID: {task_id}"
126
126
  )
@@ -135,12 +135,12 @@ class TaskLoggerService:
135
135
  direction=direction,
136
136
  payload=sanitized_payload,
137
137
  )
138
- repo.save_event(task_event)
138
+ repo.save_event(db, task_event)
139
139
 
140
140
  # If it's a final event, update the master task record
141
141
  final_status = self._get_final_status(parsed_event)
142
142
  if final_status:
143
- task_to_update = repo.find_by_id(task_id)
143
+ task_to_update = repo.find_by_id(db, task_id)
144
144
  if task_to_update:
145
145
  task_to_update.end_time = now_epoch_ms()
146
146
  task_to_update.status = final_status
@@ -159,8 +159,8 @@ class TaskLoggerService:
159
159
  f"output={token_usage.get('total_output_tokens')}, "
160
160
  f"cached={token_usage.get('total_cached_input_tokens')}"
161
161
  )
162
-
163
- repo.save_task(task_to_update)
162
+
163
+ repo.save_task(db, task_to_update)
164
164
  log.info(
165
165
  f"{self.log_identifier} Finalized task record for ID: {task_id} with status: {final_status}"
166
166
  )
@@ -6,16 +6,15 @@ for database session management and transaction handling.
6
6
  """
7
7
 
8
8
  from abc import ABC, abstractmethod
9
- from typing import Dict, Any, List, Optional, TypeVar, Generic, Type
9
+ from typing import Any, Generic, TypeVar
10
+
10
11
  from sqlalchemy.orm import Session
11
- from sqlalchemy.exc import NoResultFound
12
- from .exceptions import EntityNotFoundError, ValidationError
13
- from .types import PaginationInfo
14
12
 
13
+ from .exceptions import EntityNotFoundError
15
14
 
16
- T = TypeVar('T')
17
- ModelType = TypeVar('ModelType')
18
- EntityType = TypeVar('EntityType')
15
+ T = TypeVar("T")
16
+ ModelType = TypeVar("ModelType")
17
+ EntityType = TypeVar("EntityType")
19
18
 
20
19
 
21
20
  class BaseRepository(ABC, Generic[ModelType, EntityType]):
@@ -27,7 +26,7 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
27
26
  transactions should be handled at the service/API layer.
28
27
  """
29
28
 
30
- def __init__(self, model_class: Type[ModelType], entity_class: Type[EntityType]):
29
+ def __init__(self, model_class: type[ModelType], entity_class: type[EntityType]):
31
30
  """
32
31
  Initialize repository with model and entity classes.
33
32
 
@@ -44,7 +43,7 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
44
43
  """Return the entity name for error messages."""
45
44
  pass
46
45
 
47
- def create(self, session: Session, create_data: Dict[str, Any]) -> EntityType:
46
+ def create(self, session: Session, create_data: dict[str, Any]) -> EntityType:
48
47
  """
49
48
  Create a new entity.
50
49
 
@@ -83,16 +82,20 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
83
82
  Raises:
84
83
  EntityNotFoundError: If entity not found
85
84
  """
86
- model_instance = session.query(self.model_class).filter(
87
- self.model_class.id == str(entity_id)
88
- ).first()
85
+ model_instance = (
86
+ session.query(self.model_class)
87
+ .filter(self.model_class.id == str(entity_id))
88
+ .first()
89
+ )
89
90
 
90
91
  if not model_instance:
91
92
  raise EntityNotFoundError(self.entity_name, entity_id)
92
93
 
93
94
  return self.entity_class.model_validate(model_instance)
94
95
 
95
- def get_all(self, session: Session, limit: Optional[int] = None, offset: Optional[int] = None) -> List[EntityType]:
96
+ def get_all(
97
+ self, session: Session, limit: int | None = None, offset: int | None = None
98
+ ) -> list[EntityType]:
96
99
  """
97
100
  Get all entities with optional pagination.
98
101
 
@@ -112,9 +115,13 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
112
115
  query = query.limit(limit)
113
116
 
114
117
  model_instances = query.all()
115
- return [self.entity_class.model_validate(instance) for instance in model_instances]
118
+ return [
119
+ self.entity_class.model_validate(instance) for instance in model_instances
120
+ ]
116
121
 
117
- def update(self, session: Session, entity_id: Any, update_data: Dict[str, Any]) -> EntityType:
122
+ def update(
123
+ self, session: Session, entity_id: Any, update_data: dict[str, Any]
124
+ ) -> EntityType:
118
125
  """
119
126
  Update an entity.
120
127
 
@@ -129,9 +136,11 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
129
136
  Raises:
130
137
  EntityNotFoundError: If entity not found
131
138
  """
132
- model_instance = session.query(self.model_class).filter(
133
- self.model_class.id == str(entity_id)
134
- ).first()
139
+ model_instance = (
140
+ session.query(self.model_class)
141
+ .filter(self.model_class.id == str(entity_id))
142
+ .first()
143
+ )
135
144
 
136
145
  if not model_instance:
137
146
  raise EntityNotFoundError(self.entity_name, entity_id)
@@ -158,9 +167,11 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
158
167
  Raises:
159
168
  EntityNotFoundError: If entity not found
160
169
  """
161
- model_instance = session.query(self.model_class).filter(
162
- self.model_class.id == str(entity_id)
163
- ).first()
170
+ model_instance = (
171
+ session.query(self.model_class)
172
+ .filter(self.model_class.id == str(entity_id))
173
+ .first()
174
+ )
164
175
 
165
176
  if not model_instance:
166
177
  raise EntityNotFoundError(self.entity_name, entity_id)
@@ -179,9 +190,11 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
179
190
  Returns:
180
191
  True if entity exists, False otherwise
181
192
  """
182
- count = session.query(self.model_class).filter(
183
- self.model_class.id == str(entity_id)
184
- ).count()
193
+ count = (
194
+ session.query(self.model_class)
195
+ .filter(self.model_class.id == str(entity_id))
196
+ .count()
197
+ )
185
198
 
186
199
  return count > 0
187
200
 
@@ -200,52 +213,13 @@ class BaseRepository(ABC, Generic[ModelType, EntityType]):
200
213
 
201
214
  class PaginatedRepository(BaseRepository[ModelType, EntityType]):
202
215
  """
203
- Base repository with enhanced pagination support.
204
- """
205
-
206
- def get_paginated(self, session: Session, page_number: int, page_size: int) -> tuple[List[EntityType], int]:
207
- """
208
- Get paginated results.
209
-
210
- Args:
211
- session: Database session
212
- page_number: Page number (1-based)
213
- page_size: Number of items per page
214
-
215
- Returns:
216
- Tuple of (entities, total_count)
217
- """
218
- offset = (page_number - 1) * page_size
216
+ Base repository with pagination support.
219
217
 
220
- total_count = self.count(session)
221
-
222
- entities = self.get_all(session, limit=page_size, offset=offset)
223
-
224
- return entities, total_count
225
-
226
- def get_paginated_with_info(self, session: Session, pagination: PaginationInfo) -> tuple[List[EntityType], PaginationInfo]:
227
- """
228
- Get paginated results with complete pagination info.
229
-
230
- Args:
231
- session: Database session
232
- pagination: Pagination parameters
233
-
234
- Returns:
235
- Tuple of (entities, updated_pagination_info)
236
- """
237
- entities, total_count = self.get_paginated(session, pagination.page, pagination.page_size)
238
-
239
- updated_pagination = PaginationInfo(
240
- page=pagination.page,
241
- page_size=pagination.page_size,
242
- total_items=total_count,
243
- total_pages=(total_count + pagination.page_size - 1) // pagination.page_size,
244
- has_next=pagination.page * pagination.page_size < total_count,
245
- has_previous=pagination.page > 1
246
- )
218
+ Concrete repositories should implement their own pagination methods
219
+ that apply specific filters and ordering before pagination.
220
+ """
247
221
 
248
- return entities, updated_pagination
222
+ pass
249
223
 
250
224
 
251
225
  class ValidationMixin:
@@ -253,7 +227,7 @@ class ValidationMixin:
253
227
  Mixin for repositories that need validation logic.
254
228
  """
255
229
 
256
- def validate_create_data(self, create_data: Dict[str, Any]) -> None:
230
+ def validate_create_data(self, create_data: dict[str, Any]) -> None:
257
231
  """
258
232
  Validate data before creation.
259
233
 
@@ -265,7 +239,7 @@ class ValidationMixin:
265
239
  """
266
240
  pass
267
241
 
268
- def validate_update_data(self, update_data: Dict[str, Any]) -> None:
242
+ def validate_update_data(self, update_data: dict[str, Any]) -> None:
269
243
  """
270
244
  Validate data before update.
271
245
 
@@ -275,4 +249,4 @@ class ValidationMixin:
275
249
  Raises:
276
250
  ValidationError: If validation fails
277
251
  """
278
- pass
252
+ pass
@@ -35,24 +35,6 @@ class LegacyTimestamp(BaseModel):
35
35
  updated_at: datetime | None = None
36
36
 
37
37
 
38
- class PaginationParams(BaseModel):
39
- """Pagination parameters for list requests."""
40
-
41
- page: int
42
- page_size: int
43
-
44
-
45
- class PaginationInfo(BaseModel):
46
- """Pagination information for list responses."""
47
-
48
- page: int
49
- page_size: int
50
- total_items: int
51
- total_pages: int
52
- has_next: bool
53
- has_previous: bool
54
-
55
-
56
38
  class SortInfo(BaseModel):
57
39
  """Sorting information for list requests."""
58
40
 
@@ -21,11 +21,6 @@ apps:
21
21
 
22
22
  artifact_service: __ARTIFACT_SERVICE__
23
23
 
24
- authorization_service:
25
- type: "none" # Or "default_rbac"
26
- # role_definitions_path: "config/auth/roles.yaml"
27
- # user_assignments_path: "config/auth/users.yaml"
28
-
29
24
  system_purpose: >
30
25
  __SYSTEM_PURPOSE__
31
26
 
@@ -2,22 +2,22 @@
2
2
  keys=root,solace_ai_connector,solace_agent_mesh,sam_trace
3
3
 
4
4
  [logger_root]
5
- level=WARN
5
+ level=${LOGGING_ROOT_LEVEL, WARNING}
6
6
  handlers=streamHandler,rotatingFileHandler
7
7
  qualname=root
8
8
 
9
9
  [logger_solace_ai_connector]
10
- level=INFO
10
+ level=${LOGGING_SOLACE_AI_CONNECTOR_LEVEL, INFO}
11
11
  handlers=
12
12
  qualname=solace_ai_connector
13
13
 
14
14
  [logger_solace_agent_mesh]
15
- level=INFO
15
+ level=${LOGGING_SAM_LEVEL, INFO}
16
16
  handlers=
17
17
  qualname=solace_agent_mesh
18
18
 
19
19
  [logger_sam_trace]
20
- level=INFO
20
+ level=${LOGGING_SAM_TRACE_LEVEL, INFO}
21
21
  handlers=
22
22
  qualname=sam_trace
23
23
 
@@ -35,7 +35,11 @@ formatter=simpleFormatter
35
35
  args=(sys.stdout,)
36
36
 
37
37
  [formatters]
38
- keys=simpleFormatter
38
+ keys=simpleFormatter,jsonFormatter
39
39
 
40
40
  [formatter_simpleFormatter]
41
- format=%(asctime)s | %(levelname)-5s | %(name)s | %(message)s
41
+ format=%(asctime)s | %(levelname)-5s | %(name)s | %(message)s
42
+
43
+ [formatter_jsonFormatter]
44
+ class=pythonjsonlogger.jsonlogger.JsonFormatter
45
+ format=%(asctime)s %(levelname)s %(name)s %(message)s
@@ -47,9 +47,6 @@ apps:
47
47
  gateway_id: __COMPONENT_KEBAB_CASE_NAME__-gw-01
48
48
  artifact_service: *default_artifact_service
49
49
 
50
- authorization_service:
51
- type: "none" # Or "default_rbac"
52
-
53
50
  system_purpose: >
54
51
  The system is an AI Chatbot with agentic capabilities.
55
52
  It will use the agents available to provide information,