solace-agent-mesh 1.0.9__py3-none-any.whl → 1.3.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 (220) 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 +25 -17
  7. solace_agent_mesh/agent/adk/services.py +3 -3
  8. solace_agent_mesh/agent/adk/setup.py +11 -0
  9. solace_agent_mesh/agent/adk/stream_parser.py +8 -1
  10. solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
  11. solace_agent_mesh/agent/agent_llm.txt +355 -18
  12. solace_agent_mesh/agent/protocol/event_handlers.py +460 -317
  13. solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
  14. solace_agent_mesh/agent/sac/app.py +2 -2
  15. solace_agent_mesh/agent/sac/component.py +211 -517
  16. solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
  17. solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
  18. solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
  19. solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
  20. solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
  21. solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
  22. solace_agent_mesh/assets/docs/404.html +3 -3
  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.08d30374.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 +4 -4
  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-1757433031159.json +1 -0
  65. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  66. solace_agent_mesh/assets/docs/search-doc-1757433031159.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/agent_cmd.py +125 -48
  71. solace_agent_mesh/cli/commands/eval_cmd.py +14 -0
  72. solace_agent_mesh/cli/commands/init_cmd/__init__.py +53 -31
  73. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  74. solace_agent_mesh/cli/commands/init_cmd/env_step.py +19 -8
  75. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +80 -25
  76. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +32 -10
  77. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +74 -15
  78. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +0 -2
  79. solace_agent_mesh/cli/commands/run_cmd.py +5 -3
  80. solace_agent_mesh/cli/utils.py +68 -12
  81. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-vY5eu2lI.js +1 -0
  82. solace_agent_mesh/client/webui/frontend/static/assets/client-BeBkzgWW.js +25 -0
  83. solace_agent_mesh/client/webui/frontend/static/assets/main-Bjys1KQs.js +339 -0
  84. solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +1 -0
  85. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CE0AeXyK.js +395 -0
  86. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -2
  87. solace_agent_mesh/client/webui/frontend/static/index.html +4 -3
  88. solace_agent_mesh/common/a2a/__init__.py +213 -0
  89. solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
  90. solace_agent_mesh/common/a2a/artifact.py +328 -0
  91. solace_agent_mesh/common/a2a/events.py +183 -0
  92. solace_agent_mesh/common/a2a/message.py +307 -0
  93. solace_agent_mesh/common/a2a/protocol.py +513 -0
  94. solace_agent_mesh/common/a2a/task.py +127 -0
  95. solace_agent_mesh/common/a2a/translation.py +653 -0
  96. solace_agent_mesh/common/a2a/types.py +54 -0
  97. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  98. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -0
  99. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  100. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +31 -0
  101. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
  102. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
  103. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  104. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
  105. solace_agent_mesh/common/agent_registry.py +1 -1
  106. solace_agent_mesh/common/common_llm.txt +192 -70
  107. solace_agent_mesh/common/data_parts.py +99 -0
  108. solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
  109. solace_agent_mesh/common/sac/__init__.py +0 -0
  110. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  111. solace_agent_mesh/common/sac/sam_component_base.py +252 -0
  112. solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
  113. solace_agent_mesh/common/services/services_llm.txt +206 -26
  114. solace_agent_mesh/common/utils/artifact_utils.py +29 -0
  115. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
  116. solace_agent_mesh/common/utils/embeds/resolver.py +1 -0
  117. solace_agent_mesh/common/utils/utils_llm.txt +323 -42
  118. solace_agent_mesh/config_portal/backend/common.py +2 -2
  119. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +1 -1
  120. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-bFMKlzKf.js +98 -0
  121. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-d845808d.js → manifest-89db7c30.js} +1 -1
  122. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  123. solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
  124. solace_agent_mesh/core_a2a/service.py +20 -44
  125. solace_agent_mesh/evaluation/message_organizer.py +35 -56
  126. solace_agent_mesh/evaluation/run.py +26 -5
  127. solace_agent_mesh/evaluation/subscriber.py +35 -10
  128. solace_agent_mesh/evaluation/summary_builder.py +27 -34
  129. solace_agent_mesh/gateway/base/app.py +27 -1
  130. solace_agent_mesh/gateway/base/base_llm.txt +177 -72
  131. solace_agent_mesh/gateway/base/component.py +294 -523
  132. solace_agent_mesh/gateway/gateway_llm.txt +299 -58
  133. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +676 -0
  134. solace_agent_mesh/gateway/http_sse/alembic/env.py +85 -0
  135. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  136. solace_agent_mesh/gateway/http_sse/alembic/versions/b1c2d3e4f5g6_add_database_indexes.py +83 -0
  137. solace_agent_mesh/gateway/http_sse/alembic/versions/d5b3f8f2e9a0_create_initial_database.py +58 -0
  138. solace_agent_mesh/gateway/http_sse/alembic.ini +147 -0
  139. solace_agent_mesh/gateway/http_sse/api/__init__.py +11 -0
  140. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +9 -0
  141. solace_agent_mesh/gateway/http_sse/api/controllers/session_controller.py +355 -0
  142. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +279 -0
  143. solace_agent_mesh/gateway/http_sse/api/controllers/user_controller.py +35 -0
  144. solace_agent_mesh/gateway/http_sse/api/dto/__init__.py +10 -0
  145. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +37 -0
  146. solace_agent_mesh/gateway/http_sse/api/dto/requests/session_requests.py +49 -0
  147. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +66 -0
  148. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +43 -0
  149. solace_agent_mesh/gateway/http_sse/api/dto/responses/session_responses.py +68 -0
  150. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +74 -0
  151. solace_agent_mesh/gateway/http_sse/app.py +31 -1
  152. solace_agent_mesh/gateway/http_sse/application/__init__.py +3 -0
  153. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +3 -0
  154. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +135 -0
  155. solace_agent_mesh/gateway/http_sse/component.py +371 -236
  156. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
  157. solace_agent_mesh/gateway/http_sse/dependencies.py +142 -39
  158. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +3 -0
  159. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +90 -0
  160. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +3 -0
  161. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +54 -0
  162. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
  163. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +4 -0
  164. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +3 -0
  165. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +123 -0
  166. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +4 -0
  167. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +16 -0
  168. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +119 -0
  169. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +31 -0
  170. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +12 -0
  171. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +3 -0
  172. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +174 -0
  173. solace_agent_mesh/gateway/http_sse/main.py +293 -91
  174. solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
  175. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +137 -56
  176. solace_agent_mesh/gateway/http_sse/routers/config.py +3 -1
  177. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
  178. solace_agent_mesh/gateway/http_sse/routers/tasks.py +199 -171
  179. solace_agent_mesh/gateway/http_sse/routers/visualization.py +7 -7
  180. solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
  181. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
  182. solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
  183. solace_agent_mesh/gateway/http_sse/session_manager.py +64 -30
  184. solace_agent_mesh/gateway/http_sse/shared/__init__.py +9 -0
  185. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  186. solace_agent_mesh/gateway/http_sse/shared/enums.py +45 -0
  187. solace_agent_mesh/gateway/http_sse/shared/types.py +45 -0
  188. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  189. solace_agent_mesh/templates/gateway_component_template.py +149 -98
  190. solace_agent_mesh/templates/shared_config.yaml +4 -5
  191. solace_agent_mesh/templates/webui.yaml +8 -10
  192. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/METADATA +9 -6
  193. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/RECORD +197 -141
  194. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
  195. solace_agent_mesh/assets/docs/assets/js/main.3d0e7879.js +0 -2
  196. solace_agent_mesh/assets/docs/assets/js/runtime~main.05d19492.js +0 -1
  197. solace_agent_mesh/assets/docs/lunr-index-1757091012487.json +0 -1
  198. solace_agent_mesh/assets/docs/search-doc-1757091012487.json +0 -1
  199. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-BmF2l6vg.js +0 -1
  200. solace_agent_mesh/client/webui/frontend/static/assets/client-D881Dttc.js +0 -49
  201. solace_agent_mesh/client/webui/frontend/static/assets/main-D0FnP_W4.css +0 -1
  202. solace_agent_mesh/client/webui/frontend/static/assets/main-Do32sFPX.js +0 -708
  203. solace_agent_mesh/common/a2a_protocol.py +0 -564
  204. solace_agent_mesh/common/client/__init__.py +0 -4
  205. solace_agent_mesh/common/client/card_resolver.py +0 -21
  206. solace_agent_mesh/common/client/client.py +0 -85
  207. solace_agent_mesh/common/client/client_llm.txt +0 -133
  208. solace_agent_mesh/common/server/__init__.py +0 -4
  209. solace_agent_mesh/common/server/server.py +0 -122
  210. solace_agent_mesh/common/server/server_llm.txt +0 -169
  211. solace_agent_mesh/common/server/task_manager.py +0 -291
  212. solace_agent_mesh/common/server/utils.py +0 -28
  213. solace_agent_mesh/common/types.py +0 -411
  214. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-Bym6YkMd.js +0 -98
  215. solace_agent_mesh/gateway/http_sse/routers/sessions.py +0 -80
  216. solace_agent_mesh/gateway/http_sse/routers/users.py +0 -59
  217. /solace_agent_mesh/assets/docs/assets/js/{main.3d0e7879.js.LICENSE.txt → main.08d30374.js.LICENSE.txt} +0 -0
  218. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/WHEEL +0 -0
  219. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/entry_points.txt +0 -0
  220. {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,65 +1,118 @@
1
- """
2
- Defines the FastAPI application instance, mounts routers, and configures middleware.
3
- """
4
-
5
- from fastapi import (
6
- FastAPI,
7
- Request as FastAPIRequest,
8
- HTTPException,
9
- status,
10
- )
11
- from fastapi.responses import JSONResponse
1
+ import os
2
+ from pathlib import Path
3
+ from typing import TYPE_CHECKING
4
+
5
+ import httpx
6
+ import sqlalchemy as sa
7
+ from a2a.types import InternalError, JSONRPCError
8
+ from a2a.types import JSONRPCResponse as A2AJSONRPCResponse
9
+ from alembic import command
10
+ from alembic.config import Config
11
+ from fastapi import FastAPI, HTTPException, status
12
+ from fastapi import Request as FastAPIRequest
12
13
  from fastapi.exceptions import RequestValidationError
13
14
  from fastapi.middleware.cors import CORSMiddleware
15
+ from fastapi.responses import JSONResponse
16
+ from solace_ai_connector.common.log import log
14
17
  from starlette.middleware.sessions import SessionMiddleware
15
18
  from starlette.staticfiles import StaticFiles
16
- import os
17
- from pathlib import Path
18
- import httpx
19
19
 
20
- from solace_ai_connector.common.log import log
20
+ from ...common import a2a
21
+ from ...gateway.http_sse import dependencies
21
22
  from ...gateway.http_sse.routers import (
22
23
  agents,
23
- tasks,
24
- sse,
25
- config,
26
24
  artifacts,
27
- visualization,
28
- sessions,
29
- people,
30
25
  auth,
31
- users,
32
- )
33
-
34
- from ...gateway.http_sse import dependencies
35
- from ...common.types import (
36
- JSONRPCResponse as A2AJSONRPCResponse,
37
- JSONRPCError,
38
- InternalError,
39
- InvalidRequestError,
26
+ config,
27
+ people,
28
+ sse,
29
+ tasks,
30
+ visualization,
40
31
  )
41
32
 
42
- from typing import TYPE_CHECKING
33
+ # Import persistence-aware controllers
34
+ from .api.controllers.session_controller import router as session_router
35
+ from .api.controllers.task_controller import router as task_router
36
+ from .api.controllers.user_controller import router as user_router
37
+ from .infrastructure.persistence.database_service import DatabaseService
43
38
 
44
39
  if TYPE_CHECKING:
45
40
  from gateway.http_sse.component import WebUIBackendComponent
46
41
 
47
42
  app = FastAPI(
48
43
  title="A2A Web UI Backend",
49
- version="0.1.0",
44
+ version="1.0.0", # Updated to reflect simplified architecture
50
45
  description="Backend API and SSE server for the A2A Web UI, hosted by Solace AI Connector.",
51
46
  )
52
47
 
53
48
 
54
- def setup_dependencies(component: "WebUIBackendComponent"):
49
+ def setup_dependencies(component: "WebUIBackendComponent", persistence_service=None):
55
50
  """
56
- Sets up the component instance reference and configures middleware and routers
57
- that depend on the component being available.
58
- Called from the component's startup sequence.
51
+ This function initializes the modern architecture while maintaining full
52
+ backward compatibility with existing API contracts.
53
+
54
+ If persistence_service is None, runs in compatibility mode with in-memory sessions.
59
55
  """
60
- log.info("Setting up FastAPI dependencies, middleware, and routers...")
56
+
57
+ if persistence_service:
58
+ database_url = persistence_service.engine.url.__str__()
59
+ global database_service
60
+ database_service = DatabaseService(database_url)
61
+ log.info("Database service initialized")
62
+
63
+ from .infrastructure.dependency_injection.container import initialize_container
64
+
65
+ initialize_container(database_url)
66
+ log.info("Persistence enabled - sessions will be stored in database")
67
+ else:
68
+ from .infrastructure.dependency_injection.container import initialize_container
69
+
70
+ initialize_container()
71
+ log.warning(
72
+ "No persistence service provided - using in-memory session storage (data not persisted across restarts)"
73
+ )
74
+ log.info("This maintains backward compatibility for existing SAM installations")
75
+
61
76
  dependencies.set_component_instance(component)
62
77
 
78
+ if persistence_service:
79
+ log.info("Checking database migrations...")
80
+ try:
81
+ inspector = sa.inspect(persistence_service.engine)
82
+ existing_tables = inspector.get_table_names()
83
+
84
+ if not existing_tables or "sessions" not in existing_tables:
85
+ log.info("Running database migrations...")
86
+ alembic_cfg = Config()
87
+ alembic_cfg.set_main_option(
88
+ "script_location",
89
+ os.path.join(os.path.dirname(__file__), "alembic"),
90
+ )
91
+ alembic_cfg.set_main_option("sqlalchemy.url", database_url)
92
+ command.upgrade(alembic_cfg, "head")
93
+ log.info("Database migrations complete.")
94
+ else:
95
+ log.info("Database tables already exist, skipping migrations.")
96
+ except Exception as e:
97
+ log.warning(
98
+ f"Migration check failed, attempting to run migrations anyway: {e}"
99
+ )
100
+ try:
101
+ alembic_cfg = Config()
102
+ alembic_cfg.set_main_option(
103
+ "script_location",
104
+ os.path.join(os.path.dirname(__file__), "alembic"),
105
+ )
106
+ alembic_cfg.set_main_option("sqlalchemy.url", database_url)
107
+ command.upgrade(alembic_cfg, "head")
108
+ log.info("Database migrations complete.")
109
+ except Exception as migration_error:
110
+ log.warning(f"Migration failed but continuing: {migration_error}")
111
+
112
+ dependencies.set_persistence_service(persistence_service)
113
+ else:
114
+ log.info("Skipping database migrations - no persistence service configured")
115
+
63
116
  webui_app = component.get_app()
64
117
  app_config = {}
65
118
  if webui_app:
@@ -84,6 +137,7 @@ def setup_dependencies(component: "WebUIBackendComponent"):
84
137
  "frontend_redirect_url": app_config.get(
85
138
  "frontend_redirect_url", "http://localhost:3000"
86
139
  ),
140
+ "persistence_enabled": persistence_service is not None,
87
141
  }
88
142
 
89
143
  dependencies.set_api_config(api_config_dict)
@@ -215,17 +269,80 @@ def setup_dependencies(component: "WebUIBackendComponent"):
215
269
  return
216
270
 
217
271
  user_info = userinfo_response.json()
218
- email_from_auth = user_info.get("email")
272
+ log.info(
273
+ "AuthMiddleware: Raw user info from OAuth provider: %s",
274
+ user_info,
275
+ )
276
+
277
+ # Priority order for user identifier (most specific to least specific)
278
+ user_identifier = (
279
+ user_info.get("sub") # Standard OIDC subject claim
280
+ or user_info.get("client_id") # Mini IDP and some custom IDPs
281
+ or user_info.get("username") # Mini IDP returns username field
282
+ or user_info.get("oid") # Azure AD object ID
283
+ or user_info.get(
284
+ "preferred_username"
285
+ ) # Common in enterprise IDPs
286
+ or user_info.get("upn") # Azure AD User Principal Name
287
+ or user_info.get("unique_name") # Some Azure configurations
288
+ or user_info.get("email") # Fallback to email
289
+ or user_info.get("name") # Last resort
290
+ or user_info.get("azp") # Authorized party (rare but possible)
291
+ )
292
+
293
+ # IMPORTANT: If the extracted identifier is "Unknown", it means the IDP
294
+ # didn't properly authenticate or is misconfigured. Use a fallback.
295
+ if user_identifier and user_identifier.lower() == "unknown":
296
+ log.warning(
297
+ "AuthMiddleware: IDP returned 'Unknown' as user identifier. This indicates misconfiguration. Using fallback."
298
+ )
299
+ # In development mode with mini IDP, default to sam_dev_user
300
+ # This is a workaround for the OAuth2 proxy service returning "Unknown"
301
+ user_identifier = "sam_dev_user" # Fallback for development
302
+ log.info(
303
+ "AuthMiddleware: Using development fallback user: sam_dev_user"
304
+ )
305
+
306
+ # Extract email separately (may be different from user identifier)
307
+ email_from_auth = (
308
+ user_info.get("email")
309
+ or user_info.get("preferred_username")
310
+ or user_info.get("upn")
311
+ or user_identifier
312
+ )
313
+
314
+ # Extract display name
315
+ display_name = (
316
+ user_info.get("name")
317
+ or user_info.get("given_name", "")
318
+ + " "
319
+ + user_info.get("family_name", "")
320
+ or user_info.get("preferred_username")
321
+ or user_identifier
322
+ ).strip()
323
+
324
+ log.info(
325
+ "AuthMiddleware: Extracted user identifier: %s, email: %s, name: %s",
326
+ user_identifier,
327
+ email_from_auth,
328
+ display_name,
329
+ )
219
330
 
220
- if not email_from_auth:
331
+ if not user_identifier or user_identifier.lower() in [
332
+ "null",
333
+ "none",
334
+ "",
335
+ ]:
221
336
  log.error(
222
- "AuthMiddleware: Email not found in user info from external auth provider."
337
+ "AuthMiddleware: No valid user identifier from OAuth provider. Full user info: %s. Expected valid user identifier.",
338
+ user_info,
223
339
  )
224
340
  response = JSONResponse(
225
341
  status_code=status.HTTP_401_UNAUTHORIZED,
226
342
  content={
227
- "detail": "User email not provided by auth provider",
228
- "error_type": "email_missing",
343
+ "detail": "OAuth provider returned no valid user identifier. Provider must return at least one of: sub, username, client_id, preferred_username, email, or name field.",
344
+ "error_type": "invalid_user_identifier_from_provider",
345
+ "received_user_info": user_info,
229
346
  },
230
347
  )
231
348
  await response(scope, receive, send)
@@ -233,24 +350,51 @@ def setup_dependencies(component: "WebUIBackendComponent"):
233
350
 
234
351
  identity_service = self.component.identity_service
235
352
  if not identity_service:
353
+ # Make absolutely sure we have a valid user ID - never "Unknown"
354
+ final_user_id = (
355
+ user_identifier or email_from_auth or "sam_dev_user"
356
+ )
357
+ if not final_user_id or final_user_id.lower() in [
358
+ "unknown",
359
+ "null",
360
+ "none",
361
+ "",
362
+ ]:
363
+ final_user_id = "sam_dev_user"
364
+ log.warning(
365
+ "AuthMiddleware: Had to use fallback user ID due to invalid identifier: %s",
366
+ user_identifier,
367
+ )
368
+
236
369
  log.error(
237
- "AuthMiddleware: Internal IdentityService not configured on component. Falling back to using email as ID."
370
+ "AuthMiddleware: Internal IdentityService not configured on component. Using user ID: %s",
371
+ final_user_id,
238
372
  )
239
373
  request.state.user = {
240
- "id": email_from_auth,
241
- "email": email_from_auth,
242
- "name": user_info.get("name", email_from_auth),
374
+ "id": final_user_id,
375
+ "email": email_from_auth or final_user_id,
376
+ "name": display_name or final_user_id,
243
377
  "authenticated": True,
244
378
  "auth_method": "oidc",
245
379
  }
380
+ log.info(
381
+ "AuthMiddleware: Set fallback user state with id: %s",
382
+ final_user_id,
383
+ )
246
384
  else:
385
+ # Try to look up user profile using the email or user identifier
386
+ lookup_value = (
387
+ email_from_auth
388
+ if "@" in email_from_auth
389
+ else user_identifier
390
+ )
247
391
  user_profile = await identity_service.get_user_profile(
248
- {identity_service.lookup_key: email_from_auth}
392
+ {identity_service.lookup_key: lookup_value}
249
393
  )
250
394
  if not user_profile:
251
395
  log.error(
252
396
  "AuthMiddleware: User '%s' authenticated but not found in internal IdentityService.",
253
- email_from_auth,
397
+ lookup_value,
254
398
  )
255
399
  response = JSONResponse(
256
400
  status_code=status.HTTP_403_FORBIDDEN,
@@ -263,10 +407,18 @@ def setup_dependencies(component: "WebUIBackendComponent"):
263
407
  return
264
408
 
265
409
  request.state.user = user_profile.copy()
410
+ # Ensure the ID is set from the OAuth provider if not present in the profile
411
+ if not request.state.user.get("id"):
412
+ request.state.user["id"] = user_identifier
413
+ # Also ensure email and name are set if not in profile
414
+ if not request.state.user.get("email"):
415
+ request.state.user["email"] = email_from_auth
416
+ if not request.state.user.get("name"):
417
+ request.state.user["name"] = display_name
266
418
  request.state.user["authenticated"] = True
267
419
  request.state.user["auth_method"] = "oidc"
268
- log.debug(
269
- "AuthMiddleware: Enriched and stored user profile for id: %s",
420
+ log.info(
421
+ "AuthMiddleware: Set enriched user profile with id: %s",
270
422
  request.state.user.get("id"),
271
423
  )
272
424
 
@@ -290,9 +442,22 @@ def setup_dependencies(component: "WebUIBackendComponent"):
290
442
  )
291
443
  await response(scope, receive, send)
292
444
  return
445
+ else:
446
+ # If auth is not used, set a default user
447
+ request.state.user = {
448
+ "id": "sam_dev_user",
449
+ "name": "Sam Dev User",
450
+ "email": "sam@dev.local",
451
+ "authenticated": True,
452
+ "auth_method": "development",
453
+ }
454
+ log.debug(
455
+ "AuthMiddleware: Set development user state with id: sam_dev_user"
456
+ )
293
457
 
294
458
  await self.app(scope, receive, send)
295
459
 
460
+ # Add middleware
296
461
  allowed_origins = component.get_cors_origins()
297
462
  app.add_middleware(
298
463
  CORSMiddleware,
@@ -310,10 +475,30 @@ def setup_dependencies(component: "WebUIBackendComponent"):
310
475
  app.add_middleware(AuthMiddleware, component=component)
311
476
  log.info("AuthMiddleware added.")
312
477
 
478
+ # Mount API routers
313
479
  api_prefix = "/api/v1"
480
+
481
+ # Mount persistence-aware controllers (your original controllers with full functionality)
482
+ # These provide the complete API surface with database persistence
483
+ app.include_router(
484
+ session_router, prefix=api_prefix, tags=["Sessions"]
485
+ ) # Provides /api/v1/sessions/* endpoints
486
+ app.include_router(
487
+ user_router, prefix=f"{api_prefix}/users", tags=["Users"]
488
+ ) # Provides /api/v1/users/me
489
+ app.include_router(
490
+ task_router, prefix=f"{api_prefix}/tasks", tags=["Tasks"]
491
+ ) # Provides /api/v1/tasks/send, /subscribe, /cancel
492
+
493
+ # Mount new A2A SDK routers with different paths to avoid conflicts
314
494
  app.include_router(config.router, prefix=api_prefix, tags=["Config"])
315
495
  app.include_router(agents.router, prefix=api_prefix, tags=["Agents"])
316
- app.include_router(tasks.router, prefix=f"{api_prefix}/tasks", tags=["Tasks"])
496
+ # New A2A message endpoints (non-conflicting paths)
497
+ app.include_router(
498
+ tasks.router, prefix=api_prefix, tags=["A2A Messages"]
499
+ ) # Provides /api/v1/message:send, /message:stream
500
+ # Note: We only use the full-featured session_router (from api/controllers/session_controller.py)
501
+ # which provides complete session management with database persistence
317
502
  app.include_router(sse.router, prefix=f"{api_prefix}/sse", tags=["SSE"])
318
503
  app.include_router(
319
504
  artifacts.router, prefix=f"{api_prefix}/artifacts", tags=["Artifacts"]
@@ -323,18 +508,15 @@ def setup_dependencies(component: "WebUIBackendComponent"):
323
508
  prefix=f"{api_prefix}/visualization",
324
509
  tags=["Visualization"],
325
510
  )
326
- app.include_router(
327
- sessions.router, prefix=f"{api_prefix}/sessions", tags=["Sessions"]
328
- )
329
511
  app.include_router(
330
512
  people.router,
331
513
  prefix=api_prefix,
332
514
  tags=["People"],
333
515
  )
334
516
  app.include_router(auth.router, prefix=api_prefix, tags=["Auth"])
335
- app.include_router(users.router, prefix=f"{api_prefix}/users", tags=["Users"])
336
- log.info("API routers mounted under prefix: %s", api_prefix)
517
+ log.info("Legacy routers mounted for endpoints not yet migrated")
337
518
 
519
+ # Mount static files
338
520
  current_dir = os.path.dirname(os.path.abspath(__file__))
339
521
  root_dir = Path(os.path.normpath(os.path.join(current_dir, "..", "..")))
340
522
  static_files_dir = Path.joinpath(root_dir, "client", "webui", "frontend", "static")
@@ -359,7 +541,10 @@ def setup_dependencies(component: "WebUIBackendComponent"):
359
541
 
360
542
  @app.exception_handler(HTTPException)
361
543
  async def http_exception_handler(request: FastAPIRequest, exc: HTTPException):
362
- """Handles FastAPI's built-in HTTPExceptions and formats them as JSONRPC errors."""
544
+ """
545
+ HTTP exception handler with automatic format detection.
546
+ Returns JSON-RPC format for tasks/SSE endpoints, REST format for others.
547
+ """
363
548
  log.warning(
364
549
  "HTTP Exception: Status=%s, Detail=%s, Request: %s %s",
365
550
  exc.status_code,
@@ -367,47 +552,67 @@ async def http_exception_handler(request: FastAPIRequest, exc: HTTPException):
367
552
  request.method,
368
553
  request.url,
369
554
  )
370
- error_data = None
371
- error_code = InternalError().code
372
- error_message = str(exc.detail)
373
-
374
- if isinstance(exc.detail, dict):
375
- if "code" in exc.detail and "message" in exc.detail:
376
- error_code = exc.detail["code"]
377
- error_message = exc.detail["message"]
378
- error_data = exc.detail.get("data")
379
- else:
380
- error_data = exc.detail
381
555
 
382
- elif isinstance(exc.detail, str):
383
- if exc.status_code == status.HTTP_400_BAD_REQUEST:
384
- error_code = -32600
385
- elif exc.status_code == status.HTTP_404_NOT_FOUND:
386
- error_code = -32601
387
- error_message = "Resource not found"
556
+ # Check if this is a JSON-RPC endpoint (tasks and SSE endpoints use JSON-RPC)
557
+ is_jsonrpc_endpoint = request.url.path.startswith(
558
+ "/api/v1/tasks"
559
+ ) or request.url.path.startswith("/api/v1/sse")
560
+
561
+ if is_jsonrpc_endpoint:
562
+ # Use JSON-RPC format for tasks and SSE endpoints
563
+ error_data = None
564
+ error_code = InternalError().code
565
+ error_message = str(exc.detail)
566
+
567
+ if isinstance(exc.detail, dict):
568
+ if "code" in exc.detail and "message" in exc.detail:
569
+ error_code = exc.detail["code"]
570
+ error_message = exc.detail["message"]
571
+ error_data = exc.detail.get("data")
572
+ else:
573
+ error_data = exc.detail
574
+ elif isinstance(exc.detail, str):
575
+ if exc.status_code == status.HTTP_400_BAD_REQUEST:
576
+ error_code = -32600
577
+ elif exc.status_code == status.HTTP_404_NOT_FOUND:
578
+ error_code = -32601
579
+ error_message = "Resource not found"
580
+
581
+ error_obj = JSONRPCError(
582
+ code=error_code, message=error_message, data=error_data
583
+ )
584
+ response = A2AJSONRPCResponse(error=error_obj)
585
+ return JSONResponse(
586
+ status_code=exc.status_code, content=response.model_dump(exclude_none=True)
587
+ )
588
+ else:
589
+ # Use standard REST format for sessions and other REST endpoints
590
+ if isinstance(exc.detail, dict):
591
+ error_response = exc.detail
592
+ elif isinstance(exc.detail, str):
593
+ error_response = {"detail": exc.detail}
594
+ else:
595
+ error_response = {"detail": str(exc.detail)}
388
596
 
389
- error_obj = JSONRPCError(code=error_code, message=error_message, data=error_data)
390
- response = A2AJSONRPCResponse(error=error_obj)
391
- return JSONResponse(
392
- status_code=exc.status_code, content=response.model_dump(exclude_none=True)
393
- )
597
+ return JSONResponse(status_code=exc.status_code, content=error_response)
394
598
 
395
599
 
396
600
  @app.exception_handler(RequestValidationError)
397
601
  async def validation_exception_handler(
398
602
  request: FastAPIRequest, exc: RequestValidationError
399
603
  ):
400
- """Handles Pydantic validation errors (422) and formats them."""
604
+ """
605
+ Handles Pydantic validation errors with format detection.
606
+ """
401
607
  log.warning(
402
608
  "Request Validation Error: %s, Request: %s %s",
403
609
  exc.errors(),
404
610
  request.method,
405
611
  request.url,
406
612
  )
407
- error_obj = InvalidRequestError(
408
- message="Invalid request parameters", data=exc.errors()
613
+ response = a2a.create_invalid_request_error_response(
614
+ message="Invalid request parameters", data=exc.errors(), request_id=None
409
615
  )
410
- response = A2AJSONRPCResponse(error=error_obj)
411
616
  return JSONResponse(
412
617
  status_code=status.HTTP_400_BAD_REQUEST,
413
618
  content=response.model_dump(exclude_none=True),
@@ -416,14 +621,16 @@ async def validation_exception_handler(
416
621
 
417
622
  @app.exception_handler(Exception)
418
623
  async def generic_exception_handler(request: FastAPIRequest, exc: Exception):
419
- """Handles any other unexpected exceptions."""
624
+ """
625
+ Handles any other unexpected exceptions with format detection.
626
+ """
420
627
  log.exception(
421
628
  "Unhandled Exception: %s, Request: %s %s", exc, request.method, request.url
422
629
  )
423
- error_obj = InternalError(
630
+ error_obj = a2a.create_internal_error(
424
631
  message="An unexpected server error occurred: %s" % type(exc).__name__
425
632
  )
426
- response = A2AJSONRPCResponse(error=error_obj)
633
+ response = a2a.create_error_response(error=error_obj, request_id=None)
427
634
  return JSONResponse(
428
635
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
429
636
  content=response.model_dump(exclude_none=True),
@@ -435,8 +642,3 @@ async def read_root():
435
642
  """Basic health check endpoint."""
436
643
  log.debug("Health check endpoint '/health' called")
437
644
  return {"status": "A2A Web UI Backend is running"}
438
-
439
-
440
- log.info(
441
- "FastAPI application instance created (endpoints/middleware/static files setup deferred until component startup)."
442
- )
@@ -8,7 +8,7 @@ from typing import List
8
8
  from solace_ai_connector.common.log import log
9
9
 
10
10
  from ....common.agent_registry import AgentRegistry
11
- from ....common.types import AgentCard
11
+ from a2a.types import AgentCard
12
12
  from ....gateway.http_sse.dependencies import get_agent_registry
13
13
 
14
14
  router = APIRouter()