solace-agent-mesh 1.3.1__py3-none-any.whl → 1.3.3__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 (176) hide show
  1. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +16 -8
  2. solace_agent_mesh/agent/protocol/event_handlers.py +91 -0
  3. solace_agent_mesh/agent/sac/app.py +2 -0
  4. solace_agent_mesh/assets/docs/404.html +3 -3
  5. solace_agent_mesh/assets/docs/assets/js/0e682baa.da822665.js +1 -0
  6. solace_agent_mesh/assets/docs/assets/js/1023fc19.8a8a9309.js +1 -0
  7. solace_agent_mesh/assets/docs/assets/js/1523c6b4.2645ef68.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/1c6e87d2.43771adc.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/2a9cab12.2afaee76.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/332e10b5.f7629851.js +1 -0
  11. solace_agent_mesh/assets/docs/assets/js/3d406171.5560fdf9.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.3f34bf76.js +1 -0
  13. solace_agent_mesh/assets/docs/assets/js/442a8107.b5c2532a.js +1 -0
  14. solace_agent_mesh/assets/docs/assets/js/483cef9a.8d318c2f.js +1 -0
  15. solace_agent_mesh/assets/docs/assets/js/55f47984.bcd00a86.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/5b4258a4.dff11eca.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/664b740a.ba305a89.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/75384d09.abdf9cf9.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/768e31b0.9abcdc48.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/945fb41e.abf2be91.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/9a09e75d.5a319fd4.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/9eff14a2.d62aad71.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/a3a92b25.1d029b81.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/{aba87c2f.071e2d94.js → aba87c2f.4ddf32f2.js} +1 -1
  25. solace_agent_mesh/assets/docs/assets/js/ae0e903d.abca774a.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/ae4415af.24cdc514.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/bac0be12.27ee2c26.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/c2c06897.87cb1f47.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/c835a94d.ce21f0bf.js +1 -0
  30. solace_agent_mesh/assets/docs/assets/js/cc969b05.feef7dcc.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/cd3d4052.a19e7d78.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/{cee5d587.f5b73ca1.js → cee5d587.f1e1ca86.js} +1 -1
  33. solace_agent_mesh/assets/docs/assets/js/f284c35a.cad4dbf2.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/f897a61a.bc634a3e.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/{main.1c79039d.js → main.e82b32e6.js} +2 -2
  36. solace_agent_mesh/assets/docs/assets/js/runtime~main.aad1f874.js +1 -0
  37. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +7 -7
  39. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +6 -6
  40. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +6 -6
  41. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +6 -6
  42. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
  43. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +18 -18
  44. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
  45. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +10 -10
  47. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
  50. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +7 -7
  51. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +9 -9
  53. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +6 -6
  54. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +7 -7
  55. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +23 -23
  56. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +5 -5
  57. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +5 -5
  58. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +9 -9
  60. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +10 -10
  61. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +8 -8
  62. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +6 -6
  63. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +9 -9
  64. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  65. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +5 -5
  66. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +5 -5
  67. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  68. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +5 -5
  69. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +18 -18
  70. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +7 -7
  71. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +8 -8
  72. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +10 -10
  73. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
  74. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  75. solace_agent_mesh/assets/docs/lunr-index-1757873594308.json +1 -0
  76. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  77. solace_agent_mesh/assets/docs/search-doc-1757873594308.json +1 -0
  78. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  79. solace_agent_mesh/cli/__init__.py +1 -1
  80. solace_agent_mesh/client/webui/frontend/static/assets/{main-C1k9E0aC.js → main-DjoMeldu.js} +8 -8
  81. solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
  82. solace_agent_mesh/common/a2a/__init__.py +4 -0
  83. solace_agent_mesh/common/a2a/protocol.py +20 -0
  84. solace_agent_mesh/common/sac/sam_component_base.py +29 -9
  85. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  86. solace_agent_mesh/common/sam_events/event_service.py +207 -0
  87. solace_agent_mesh/gateway/http_sse/alembic/env.py +1 -1
  88. solace_agent_mesh/gateway/http_sse/component.py +45 -35
  89. solace_agent_mesh/gateway/http_sse/dependencies.py +123 -60
  90. solace_agent_mesh/gateway/http_sse/main.py +20 -33
  91. solace_agent_mesh/gateway/http_sse/repository/__init__.py +37 -0
  92. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +9 -0
  93. solace_agent_mesh/gateway/http_sse/repository/entities/message.py +41 -0
  94. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +45 -0
  95. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +16 -0
  96. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +64 -0
  97. solace_agent_mesh/gateway/http_sse/repository/message_repository.py +78 -0
  98. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -0
  99. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  100. solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +27 -0
  101. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +27 -0
  102. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +139 -0
  103. solace_agent_mesh/gateway/http_sse/routers/config.py +1 -0
  104. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +20 -0
  105. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/requests/session_requests.py +1 -8
  106. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +16 -0
  107. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/responses/session_responses.py +3 -30
  108. solace_agent_mesh/gateway/http_sse/{api/controllers/session_controller.py → routers/sessions.py} +20 -77
  109. solace_agent_mesh/gateway/http_sse/routers/tasks.py +42 -49
  110. solace_agent_mesh/gateway/http_sse/{api/controllers/user_controller.py → routers/users.py} +1 -1
  111. solace_agent_mesh/gateway/http_sse/services/session_service.py +245 -0
  112. solace_agent_mesh/gateway/http_sse/session_manager.py +0 -3
  113. solace_agent_mesh/gateway/http_sse/shared/enums.py +0 -5
  114. {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/METADATA +1 -1
  115. {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/RECORD +120 -128
  116. solace_agent_mesh/assets/docs/assets/js/0e682baa.b3bbde9a.js +0 -1
  117. solace_agent_mesh/assets/docs/assets/js/1023fc19.364235d5.js +0 -1
  118. solace_agent_mesh/assets/docs/assets/js/1523c6b4.1b0ec6f9.js +0 -1
  119. solace_agent_mesh/assets/docs/assets/js/1c6e87d2.a8c5ce5a.js +0 -1
  120. solace_agent_mesh/assets/docs/assets/js/2a9cab12.8909df92.js +0 -1
  121. solace_agent_mesh/assets/docs/assets/js/332e10b5.7a103f42.js +0 -1
  122. solace_agent_mesh/assets/docs/assets/js/3d406171.0b9eeed1.js +0 -1
  123. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +0 -1
  124. solace_agent_mesh/assets/docs/assets/js/442a8107.b3159bb2.js +0 -1
  125. solace_agent_mesh/assets/docs/assets/js/483cef9a.4e972867.js +0 -1
  126. solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +0 -1
  127. solace_agent_mesh/assets/docs/assets/js/5b4258a4.0d080cd9.js +0 -1
  128. solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +0 -1
  129. solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +0 -1
  130. solace_agent_mesh/assets/docs/assets/js/768e31b0.8b51cd70.js +0 -1
  131. solace_agent_mesh/assets/docs/assets/js/945fb41e.c63791d1.js +0 -1
  132. solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +0 -1
  133. solace_agent_mesh/assets/docs/assets/js/9eff14a2.472b0310.js +0 -1
  134. solace_agent_mesh/assets/docs/assets/js/a3a92b25.4b7fa6a2.js +0 -1
  135. solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +0 -1
  136. solace_agent_mesh/assets/docs/assets/js/ae4415af.7a2f0bbf.js +0 -1
  137. solace_agent_mesh/assets/docs/assets/js/bac0be12.f50d9bac.js +0 -1
  138. solace_agent_mesh/assets/docs/assets/js/c2c06897.587b4af5.js +0 -1
  139. solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +0 -1
  140. solace_agent_mesh/assets/docs/assets/js/cc969b05.bd3e0d6c.js +0 -1
  141. solace_agent_mesh/assets/docs/assets/js/cd3d4052.b6535013.js +0 -1
  142. solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +0 -1
  143. solace_agent_mesh/assets/docs/assets/js/f897a61a.0aa29dbb.js +0 -1
  144. solace_agent_mesh/assets/docs/assets/js/runtime~main.858117b7.js +0 -1
  145. solace_agent_mesh/assets/docs/lunr-index-1757531604543.json +0 -1
  146. solace_agent_mesh/assets/docs/search-doc-1757531604543.json +0 -1
  147. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +0 -676
  148. solace_agent_mesh/gateway/http_sse/api/__init__.py +0 -11
  149. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +0 -9
  150. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +0 -279
  151. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +0 -37
  152. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +0 -66
  153. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +0 -43
  154. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +0 -74
  155. solace_agent_mesh/gateway/http_sse/application/__init__.py +0 -3
  156. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +0 -3
  157. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +0 -135
  158. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +0 -3
  159. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +0 -90
  160. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +0 -3
  161. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +0 -54
  162. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +0 -4
  163. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +0 -3
  164. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +0 -123
  165. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +0 -4
  166. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +0 -16
  167. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +0 -119
  168. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +0 -31
  169. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +0 -12
  170. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +0 -3
  171. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +0 -174
  172. /solace_agent_mesh/assets/docs/assets/js/{main.1c79039d.js.LICENSE.txt → main.e82b32e6.js.LICENSE.txt} +0 -0
  173. /solace_agent_mesh/gateway/http_sse/{api → routers}/dto/__init__.py +0 -0
  174. {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/WHEEL +0 -0
  175. {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/entry_points.txt +0 -0
  176. {solace_agent_mesh-1.3.1.dist-info → solace_agent_mesh-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,279 +0,0 @@
1
- from typing import TYPE_CHECKING
2
-
3
- from fastapi import APIRouter, Depends, File, Form, HTTPException
4
- from fastapi import Request as FastAPIRequest
5
- from fastapi import UploadFile, status
6
- from solace_ai_connector.common.log import log
7
-
8
- from a2a.types import InternalError, InvalidRequestError, JSONRPCResponse
9
- from .....common import a2a
10
- from ...dependencies import (
11
- get_sac_component,
12
- get_session_manager,
13
- get_user_id,
14
- )
15
- from ...session_manager import SessionManager
16
- from ...shared.enums import SenderType
17
- from ..dto.requests.task_requests import (
18
- CancelTaskRequest,
19
- ProcessedTaskRequest,
20
- TaskFilesInfo,
21
- )
22
-
23
- if TYPE_CHECKING:
24
- from ...component import WebUIBackendComponent
25
-
26
- router = APIRouter()
27
-
28
-
29
- @router.post("/send", response_model=JSONRPCResponse)
30
- async def send_task_to_agent(
31
- request: FastAPIRequest,
32
- agent_name: str = Form(...),
33
- message: str = Form(...),
34
- files: list[UploadFile] = File([]),
35
- session_manager: SessionManager = Depends(get_session_manager),
36
- component: "WebUIBackendComponent" = Depends(get_sac_component),
37
- user_id: str = Depends(get_user_id),
38
- ):
39
- """
40
- Submits a non-streaming task request to the specified agent.
41
- This corresponds to the A2A `tasks/send` method.
42
- """
43
- log_prefix = "[POST /api/v1/tasks/send] "
44
- log.info("%sReceived request for agent: %s", log_prefix, agent_name)
45
-
46
- try:
47
- task_files = []
48
- for file in files:
49
- if file.filename:
50
- task_files.append(
51
- TaskFilesInfo(
52
- filename=file.filename,
53
- content_type=file.content_type or "application/octet-stream",
54
- size=0, # We'd need to read the file to get size
55
- )
56
- )
57
-
58
- request_dto = ProcessedTaskRequest(
59
- agent_name=agent_name, message=message, user_id=user_id, files=task_files
60
- )
61
-
62
- # Continue with existing logic
63
- client_id = session_manager.get_a2a_client_id(request)
64
- session_id = session_manager.ensure_a2a_session(request)
65
-
66
- log.info(
67
- "%sUsing ClientID: %s, SessionID: %s", log_prefix, client_id, session_id
68
- )
69
-
70
- external_event_data = {
71
- "agent_name": agent_name,
72
- "message": message,
73
- "files": files,
74
- "client_id": client_id,
75
- "a2a_session_id": session_id,
76
- }
77
- (
78
- target_agent,
79
- a2a_parts,
80
- external_request_context,
81
- ) = await component._translate_external_input(external_event_data)
82
-
83
- user_identity = {"id": user_id}
84
- log.info(
85
- "%sAuthenticated user identity: %s",
86
- log_prefix,
87
- user_identity.get("id", "unknown"),
88
- )
89
- task_id = await component.submit_a2a_task(
90
- target_agent_name=target_agent,
91
- a2a_parts=a2a_parts,
92
- user_identity=user_identity,
93
- external_request_context=external_request_context,
94
- is_streaming=False,
95
- )
96
-
97
- log.info(
98
- "%sNon-streaming task submitted successfully. TaskID: %s",
99
- log_prefix,
100
- task_id,
101
- )
102
-
103
- return JSONRPCResponse(result={"taskId": task_id})
104
-
105
- except InvalidRequestError as e:
106
- log.warning("%sInvalid request: %s", log_prefix, e.message, exc_info=True)
107
- raise HTTPException(
108
- status_code=status.HTTP_400_BAD_REQUEST,
109
- detail=e.model_dump(exclude_none=True),
110
- )
111
- except Exception as e:
112
- log.exception("%sUnexpected error processing task: %s", log_prefix, e)
113
- error_resp = a2a.create_internal_error(message=f"Failed to process task: {e}")
114
- raise HTTPException(
115
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
116
- detail=error_resp.model_dump(exclude_none=True),
117
- )
118
-
119
-
120
- @router.post("/subscribe", response_model=JSONRPCResponse)
121
- async def subscribe_task_from_agent(
122
- request: FastAPIRequest,
123
- agent_name: str = Form(...),
124
- message: str = Form(...),
125
- files: list[UploadFile] = File([]),
126
- session_id: str | None = Form(None),
127
- session_manager: SessionManager = Depends(get_session_manager),
128
- component: "WebUIBackendComponent" = Depends(get_sac_component),
129
- user_id: str = Depends(get_user_id),
130
- ):
131
- """
132
- Submits a streaming task request (`tasks/sendSubscribe`) to the specified agent.
133
- """
134
- log_prefix = "[POST /api/v1/tasks/subscribe] "
135
- log.info("%sReceived streaming request for agent: %s", log_prefix, agent_name)
136
-
137
- try:
138
- task_files = []
139
- for file in files:
140
- if file.filename:
141
- task_files.append(
142
- TaskFilesInfo(
143
- filename=file.filename,
144
- content_type=file.content_type or "application/octet-stream",
145
- size=0,
146
- )
147
- )
148
-
149
- request_dto = ProcessedTaskRequest(
150
- agent_name=agent_name,
151
- message=message,
152
- user_id=user_id,
153
- session_id=session_id,
154
- files=task_files,
155
- )
156
-
157
- client_id = session_manager.get_a2a_client_id(request)
158
-
159
- # If session_id is not provided by the client, create a new one.
160
- if not session_id:
161
- log.info("%sNo session_id provided, creating a new one.", log_prefix)
162
- session_id = session_manager.start_new_a2a_session(request)
163
-
164
- # Store message only if persistence is available
165
- if hasattr(component, "persistence_service") and component.persistence_service:
166
- try:
167
- from ...dependencies import get_session_service
168
- session_service = get_session_service(component)
169
- message_domain = session_service.add_message_to_session(
170
- session_id=session_id,
171
- user_id=user_id,
172
- message=message,
173
- sender_type=SenderType.USER,
174
- sender_name=user_id,
175
- agent_id=agent_name,
176
- )
177
- # Use the actual session ID from the message (may be different if session was recreated)
178
- if message_domain:
179
- session_id = message_domain.session_id
180
- except ValueError as e:
181
- # Handle business domain validation errors
182
- log.warning("Validation error in session service: %s", e)
183
- raise HTTPException(
184
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
185
- )
186
- except Exception as e:
187
- log.error("Failed to store message in session service: %s", e)
188
- raise HTTPException(
189
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
190
- detail="Failed to store message",
191
- )
192
- else:
193
- log.debug("%sNo persistence available - skipping message storage", log_prefix)
194
-
195
- log.info(
196
- "%sUsing ClientID: %s, SessionID: %s", log_prefix, client_id, session_id
197
- )
198
-
199
- external_event_data = {
200
- "agent_name": agent_name,
201
- "message": message,
202
- "files": files,
203
- "client_id": client_id,
204
- "a2a_session_id": session_id,
205
- }
206
- (
207
- target_agent,
208
- a2a_parts,
209
- external_request_context,
210
- ) = await component._translate_external_input(external_event_data)
211
-
212
- user_identity = {"id": user_id}
213
- log.info(
214
- "%sAuthenticated user identity: %s",
215
- log_prefix,
216
- user_identity.get("id", "unknown"),
217
- )
218
- task_id = await component.submit_a2a_task(
219
- target_agent_name=target_agent,
220
- a2a_parts=a2a_parts,
221
- user_identity=user_identity,
222
- external_request_context=external_request_context,
223
- is_streaming=True,
224
- )
225
-
226
- log.info(
227
- "%sStreaming task submitted successfully. TaskID: %s", log_prefix, task_id
228
- )
229
-
230
- return JSONRPCResponse(result={"taskId": task_id, "sessionId": session_id})
231
-
232
- except InvalidRequestError as e:
233
- log.warning("%sInvalid request: %s", log_prefix, e.message, exc_info=True)
234
- raise HTTPException(
235
- status_code=status.HTTP_400_BAD_REQUEST,
236
- detail=e.model_dump(exclude_none=True),
237
- )
238
- except HTTPException:
239
- # Re-raise HTTPExceptions (like 422 validation errors) without modification
240
- raise
241
- except Exception as e:
242
- log.exception("%sUnexpected error processing task: %s", log_prefix, e)
243
- error_resp = a2a.create_internal_error(message=f"Failed to process task: {e}")
244
- raise HTTPException(
245
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
246
- detail=error_resp.model_dump(exclude_none=True),
247
- )
248
-
249
-
250
- @router.post("/cancel", response_model=JSONRPCResponse)
251
- async def cancel_agent_task(
252
- request: FastAPIRequest,
253
- task_id: str = Form(...),
254
- session_manager: SessionManager = Depends(get_session_manager),
255
- component: "WebUIBackendComponent" = Depends(get_sac_component),
256
- user_id: str = Depends(get_user_id),
257
- ):
258
- """
259
- Sends a cancellation request for a specific task.
260
- """
261
- log_prefix = f"[POST /api/v1/tasks/cancel] TaskID: {task_id} "
262
- log.info("%sReceived cancellation request.", log_prefix)
263
-
264
- try:
265
- request_dto = CancelTaskRequest(task_id=task_id, user_id=user_id)
266
-
267
- client_id = session_manager.get_a2a_client_id(request)
268
- await component.cancel_a2a_task(task_id, client_id)
269
- log.info("%sCancellation request sent successfully.", log_prefix)
270
- return JSONRPCResponse(
271
- result={"message": f"Cancellation request sent for task {task_id}"}
272
- )
273
- except Exception as e:
274
- log.exception("%sUnexpected error sending cancellation: %s", log_prefix, e)
275
- error_resp = a2a.create_internal_error(message="Unexpected server error: %s" % e)
276
- raise HTTPException(
277
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
278
- detail=error_resp.model_dump(exclude_none=True),
279
- )
@@ -1,37 +0,0 @@
1
- """
2
- Request DTOs for API endpoints.
3
- """
4
-
5
- from .session_requests import (
6
- GetSessionsRequest,
7
- GetSessionRequest,
8
- GetSessionHistoryRequest,
9
- UpdateSessionRequest,
10
- DeleteSessionRequest,
11
- CreateSessionRequest,
12
- )
13
- from .task_requests import (
14
- SendTaskRequest,
15
- SubscribeTaskRequest,
16
- CancelTaskRequest,
17
- GetTaskStatusRequest,
18
- TaskFilesInfo,
19
- ProcessedTaskRequest,
20
- )
21
-
22
- __all__ = [
23
- # Session requests
24
- "GetSessionsRequest",
25
- "GetSessionRequest",
26
- "GetSessionHistoryRequest",
27
- "UpdateSessionRequest",
28
- "DeleteSessionRequest",
29
- "CreateSessionRequest",
30
- # Task requests
31
- "SendTaskRequest",
32
- "SubscribeTaskRequest",
33
- "CancelTaskRequest",
34
- "GetTaskStatusRequest",
35
- "TaskFilesInfo",
36
- "ProcessedTaskRequest",
37
- ]
@@ -1,66 +0,0 @@
1
- """
2
- Task-related request DTOs.
3
- """
4
-
5
- from typing import List, Optional, Dict, Any
6
- from pydantic import BaseModel, Field
7
- from fastapi import UploadFile
8
-
9
- from ....shared.types import TaskId, UserId, SessionId, AgentId
10
-
11
-
12
- class SendTaskRequest(BaseModel):
13
- """Request DTO for sending a non-streaming task."""
14
- agent_name: str = Field(..., description="The name of the target A2A agent")
15
- message: str = Field(..., description="The user's message or prompt")
16
- user_id: UserId
17
- client_id: Optional[str] = None
18
- session_id: Optional[SessionId] = None
19
-
20
- class Config:
21
- # UploadFile cannot be included in Pydantic models directly
22
- # Files will be handled separately in the controller
23
- arbitrary_types_allowed = True
24
-
25
-
26
- class SubscribeTaskRequest(BaseModel):
27
- """Request DTO for sending a streaming task."""
28
- agent_name: str = Field(..., description="The name of the target A2A agent")
29
- message: str = Field(..., description="The user's message or prompt")
30
- user_id: UserId
31
- session_id: Optional[SessionId] = None
32
- client_id: Optional[str] = None
33
-
34
- class Config:
35
- arbitrary_types_allowed = True
36
-
37
-
38
- class CancelTaskRequest(BaseModel):
39
- """Request DTO for cancelling a task."""
40
- task_id: TaskId = Field(..., description="The ID of the task to cancel")
41
- client_id: Optional[str] = None
42
- user_id: UserId
43
-
44
-
45
- class GetTaskStatusRequest(BaseModel):
46
- """Request DTO for getting task status."""
47
- task_id: TaskId
48
- user_id: UserId
49
-
50
-
51
- class TaskFilesInfo(BaseModel):
52
- """Information about uploaded files for a task."""
53
- filename: str
54
- content_type: str
55
- size: int
56
-
57
-
58
- class ProcessedTaskRequest(BaseModel):
59
- """Internal DTO for processed task request with file information."""
60
- agent_name: str
61
- message: str
62
- user_id: UserId
63
- session_id: Optional[SessionId] = None
64
- client_id: Optional[str] = None
65
- files: List[TaskFilesInfo] = []
66
- metadata: Optional[Dict[str, Any]] = None
@@ -1,43 +0,0 @@
1
- """
2
- Response DTOs for API endpoints.
3
- """
4
-
5
- from .session_responses import (
6
- MessageResponse,
7
- SessionResponse,
8
- SessionListResponse,
9
- SessionHistoryResponse,
10
- SessionCreatedResponse,
11
- SessionUpdatedResponse,
12
- SessionDeletedResponse,
13
- )
14
- from .task_responses import (
15
- TaskResponse,
16
- SendTaskResponse,
17
- SubscribeTaskResponse,
18
- CancelTaskResponse,
19
- TaskStatusResponse,
20
- TaskListResponse,
21
- TaskErrorResponse,
22
- JSONRPCTaskResponse,
23
- )
24
-
25
- __all__ = [
26
- # Session responses
27
- "MessageResponse",
28
- "SessionResponse",
29
- "SessionListResponse",
30
- "SessionHistoryResponse",
31
- "SessionCreatedResponse",
32
- "SessionUpdatedResponse",
33
- "SessionDeletedResponse",
34
- # Task responses
35
- "TaskResponse",
36
- "SendTaskResponse",
37
- "SubscribeTaskResponse",
38
- "CancelTaskResponse",
39
- "TaskStatusResponse",
40
- "TaskListResponse",
41
- "TaskErrorResponse",
42
- "JSONRPCTaskResponse",
43
- ]
@@ -1,74 +0,0 @@
1
- """
2
- Task-related response DTOs.
3
- """
4
-
5
- from typing import Optional, Dict, Any, List
6
- from datetime import datetime
7
- from pydantic import BaseModel, Field
8
-
9
- from ....shared.types import TaskId, UserId, SessionId, AgentId
10
- from ....shared.enums import TaskStatus
11
-
12
-
13
- class TaskResponse(BaseModel):
14
- """Response DTO for task information."""
15
- task_id: TaskId
16
- agent_name: str
17
- status: TaskStatus
18
- user_id: UserId
19
- session_id: Optional[SessionId] = None
20
- created_at: datetime
21
- updated_at: Optional[datetime] = None
22
- completed_at: Optional[datetime] = None
23
- error_message: Optional[str] = None
24
- metadata: Optional[Dict[str, Any]] = None
25
-
26
-
27
- class SendTaskResponse(BaseModel):
28
- """Response DTO for send task endpoint."""
29
- task_id: TaskId
30
- message: str = "Task submitted successfully"
31
- status: TaskStatus = TaskStatus.PENDING
32
-
33
-
34
- class SubscribeTaskResponse(BaseModel):
35
- """Response DTO for subscribe task endpoint."""
36
- task_id: TaskId
37
- session_id: SessionId
38
- message: str = "Streaming task submitted successfully"
39
- status: TaskStatus = TaskStatus.PENDING
40
-
41
-
42
- class CancelTaskResponse(BaseModel):
43
- """Response DTO for cancel task endpoint."""
44
- task_id: TaskId
45
- message: str = "Cancellation request sent successfully"
46
- cancelled_at: datetime
47
-
48
-
49
- class TaskStatusResponse(BaseModel):
50
- """Response DTO for task status."""
51
- task: TaskResponse
52
-
53
-
54
- class TaskListResponse(BaseModel):
55
- """Response DTO for listing tasks."""
56
- tasks: List[TaskResponse]
57
- total_count: int
58
-
59
-
60
- class TaskErrorResponse(BaseModel):
61
- """Response DTO for task errors."""
62
- task_id: TaskId
63
- error_type: str
64
- error_message: str
65
- error_details: Optional[Dict[str, Any]] = None
66
- occurred_at: datetime
67
-
68
-
69
- class JSONRPCTaskResponse(BaseModel):
70
- """Response DTO matching the existing JSONRPC format."""
71
- result: Dict[str, Any]
72
- error: Optional[Dict[str, Any]] = None
73
- id: Optional[str] = None
74
- jsonrpc: str = "2.0"
@@ -1,3 +0,0 @@
1
- from .services.session_service import SessionService
2
-
3
- __all__ = ["SessionService"]
@@ -1,3 +0,0 @@
1
- from .session_service import SessionService
2
-
3
- __all__ = ["SessionService"]
@@ -1,135 +0,0 @@
1
- import uuid
2
- from datetime import datetime, timezone
3
-
4
- from solace_ai_connector.common.log import log
5
-
6
- from ...domain.entities.session import Message, Session, SessionHistory
7
- from ...domain.repositories.session_repository import (
8
- IMessageRepository,
9
- ISessionRepository,
10
- )
11
- from ...shared.enums import MessageType, SenderType, SessionStatus
12
- from ...shared.types import PaginationInfo, SessionId, UserId
13
-
14
-
15
- class SessionService:
16
- def __init__(
17
- self,
18
- session_repository: ISessionRepository,
19
- message_repository: IMessageRepository,
20
- ):
21
- self.session_repository = session_repository
22
- self.message_repository = message_repository
23
-
24
- def get_user_sessions(
25
- self, user_id: UserId, pagination: PaginationInfo | None = None
26
- ) -> list[Session]:
27
- return self.session_repository.get_by_user_id(user_id, pagination)
28
-
29
- def get_session(self, session_id: SessionId, user_id: UserId) -> Session | None:
30
- return self.session_repository.get_user_session(session_id, user_id)
31
-
32
- def get_session_history(
33
- self,
34
- session_id: SessionId,
35
- user_id: UserId,
36
- pagination: PaginationInfo | None = None,
37
- ) -> SessionHistory | None:
38
- session = self.session_repository.get_user_session(session_id, user_id)
39
- if not session:
40
- return None
41
-
42
- messages = self.message_repository.get_by_session_id(session_id, pagination)
43
-
44
- return SessionHistory(
45
- session=session,
46
- messages=messages,
47
- total_message_count=len(messages),
48
- )
49
-
50
- def create_session(
51
- self,
52
- user_id: UserId,
53
- name: str | None = None,
54
- agent_id: str | None = None,
55
- session_id: str | None = None,
56
- ) -> Session:
57
- if not user_id or user_id.strip() == "":
58
- raise ValueError(f"user_id cannot be None or empty. Received: {user_id}")
59
-
60
- if not session_id:
61
- session_id = str(uuid.uuid4())
62
-
63
- now = datetime.now(timezone.utc)
64
- session = Session(
65
- id=session_id,
66
- user_id=user_id,
67
- name=name,
68
- agent_id=agent_id,
69
- status=SessionStatus.ACTIVE,
70
- created_at=now,
71
- updated_at=now,
72
- last_activity=now,
73
- )
74
-
75
- return self.session_repository.create(session)
76
-
77
- def update_session_name(
78
- self, session_id: SessionId, user_id: UserId, name: str
79
- ) -> Session | None:
80
- session = self.session_repository.get_user_session(session_id, user_id)
81
- if not session:
82
- return None
83
-
84
- session.update_name(name)
85
- return self.session_repository.update(session)
86
-
87
- def delete_session(self, session_id: SessionId, user_id: UserId) -> bool:
88
- session = self.session_repository.get_user_session(session_id, user_id)
89
- if not session:
90
- return False
91
-
92
- if not session.can_be_deleted_by_user(user_id):
93
- return False
94
-
95
- self.message_repository.delete_by_session_id(session_id)
96
- return self.session_repository.delete(session_id, user_id)
97
-
98
- def add_message_to_session(
99
- self,
100
- session_id: SessionId,
101
- user_id: UserId,
102
- message: str,
103
- sender_type: SenderType,
104
- sender_name: str,
105
- agent_id: str | None = None,
106
- ) -> Message | None:
107
- if not user_id or user_id.strip() == "":
108
- raise ValueError(f"user_id cannot be None or empty. Received: {user_id}")
109
-
110
- session = self.session_repository.get_user_session(session_id, user_id)
111
- if not session:
112
- log.error(f"Session {session_id} not found for user {user_id}")
113
- return None
114
-
115
- if agent_id and not session.agent_id:
116
- session.agent_id = agent_id
117
- self.session_repository.update(session)
118
- log.info(f"Updated session {session_id} with agent_id: {agent_id}")
119
-
120
- message_entity = Message(
121
- id=str(uuid.uuid4()),
122
- session_id=session_id,
123
- message=message,
124
- sender_type=sender_type,
125
- sender_name=sender_name,
126
- message_type=MessageType.TEXT,
127
- created_at=datetime.now(timezone.utc),
128
- )
129
-
130
- message_entity.validate_message_content()
131
-
132
- session.mark_activity()
133
- self.session_repository.update(session)
134
-
135
- return self.message_repository.create(message_entity)
@@ -1,3 +0,0 @@
1
- from .session import Message, Session, SessionHistory
2
-
3
- __all__ = ["Session", "Message", "SessionHistory"]