solace-agent-mesh 1.4.6__py3-none-any.whl → 1.4.8__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.
- solace_agent_mesh/agent/adk/runner.py +24 -8
- solace_agent_mesh/agent/sac/component.py +13 -1
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{0e682baa.da822665.js → 0e682baa.d054e1d8.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/166ab619.e27886d9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{1c6e87d2.43771adc.js → 1c6e87d2.e056b7e0.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/453a82a6.3c6bb61d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/483cef9a.4736f2d8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{4c2787c2.fc6804f2.js → 4c2787c2.c1290a40.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/{5b4258a4.dff11eca.js → 5b4258a4.fdfd2325.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/{75384d09.abdf9cf9.js → 75384d09.c19e8b51.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/85387663.be2bc838.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/945fb41e.16e00776.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a12a4955.25fbed32.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.af35e313.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.5fe5203f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/bac0be12.17de4316.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cee5d587.47904f5e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/d6a81ee7.829198f1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.ed8dd236.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f897a61a.126663fe.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{fbfa3e75.aca209c9.js → fbfa3e75.e144b16c.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/main.86924c42.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.0d2ff2b6.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/rbac-setup-guilde/index.html +201 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +12 -13
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/litellm_models/index.html +49 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +7 -7
- solace_agent_mesh/assets/docs/lunr-index-1759246102819.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1759246102819.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -3
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +3 -3
- solace_agent_mesh/cli/commands/init_cmd/broker_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +4 -4
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +12 -1
- solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +5 -5
- solace_agent_mesh/client/webui/frontend/static/assets/main-B0PHV3hm.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-bFMKlzKf.js → _index-BNuqpWDc.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-89db7c30.js → manifest-44d62be6.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +112 -42
- solace_agent_mesh/gateway/http_sse/app.py +0 -28
- solace_agent_mesh/gateway/http_sse/component.py +21 -15
- solace_agent_mesh/gateway/http_sse/dependencies.py +84 -88
- solace_agent_mesh/gateway/http_sse/main.py +37 -15
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +3 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +3 -1
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +5 -0
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +25 -23
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +12 -4
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +46 -42
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +199 -59
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +1 -6
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +3 -17
- solace_agent_mesh/gateway/http_sse/routers/people.py +4 -37
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +33 -68
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +54 -28
- solace_agent_mesh/gateway/http_sse/services/session_service.py +60 -28
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +122 -1
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +278 -0
- solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
- solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
- solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
- solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
- solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
- solace_agent_mesh/templates/plugin_agent_config_template.yaml +1 -1
- solace_agent_mesh/templates/plugin_custom_config_template.yaml +1 -1
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +1 -1
- solace_agent_mesh/templates/shared_config.yaml +1 -1
- {solace_agent_mesh-1.4.6.dist-info → solace_agent_mesh-1.4.8.dist-info}/METADATA +34 -35
- {solace_agent_mesh-1.4.6.dist-info → solace_agent_mesh-1.4.8.dist-info}/RECORD +123 -110
- solace_agent_mesh/assets/docs/assets/js/166ab619.e8f3a7c7.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/483cef9a.8d318c2f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/85387663.6bf41934.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/945fb41e.abf2be91.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.1d029b81.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.c786e887.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/bac0be12.27ee2c26.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/beecea0d.8bbd852c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cee5d587.f1e1ca86.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.2b2f5048.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f897a61a.bc634a3e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.72d74e33.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.3dcfaf51.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1758893005563.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1758893005563.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BKIoiLSu.js +0 -339
- /solace_agent_mesh/assets/docs/assets/js/{main.72d74e33.js.LICENSE.txt → main.86924c42.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.4.6.dist-info → solace_agent_mesh-1.4.8.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.4.6.dist-info → solace_agent_mesh-1.4.8.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.4.6.dist-info → solace_agent_mesh-1.4.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,94 +1,51 @@
|
|
|
1
|
-
from fastapi import APIRouter, Body, Depends, HTTPException, status
|
|
2
|
-
from fastapi import Request as FastAPIRequest
|
|
1
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query, status
|
|
3
2
|
from solace_ai_connector.common.log import log
|
|
3
|
+
from sqlalchemy.orm import Session
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from ..dependencies import get_session_business_service, get_session_manager
|
|
5
|
+
from ..dependencies import get_session_business_service, get_db
|
|
7
6
|
from ..services.session_service import SessionService
|
|
8
|
-
from ..session_manager import SessionManager
|
|
9
7
|
from ..shared.auth_utils import get_current_user
|
|
8
|
+
from ..shared.pagination import DataResponse, PaginatedResponse, PaginationParams
|
|
9
|
+
from ..shared.response_utils import create_data_response
|
|
10
10
|
from .dto.requests.session_requests import (
|
|
11
11
|
GetSessionHistoryRequest,
|
|
12
12
|
GetSessionRequest,
|
|
13
|
-
GetSessionsRequest,
|
|
14
13
|
UpdateSessionRequest,
|
|
15
14
|
)
|
|
16
|
-
from .dto.responses.session_responses import
|
|
17
|
-
MessageResponse,
|
|
18
|
-
SessionListResponse,
|
|
19
|
-
SessionResponse,
|
|
20
|
-
)
|
|
21
|
-
from ....common.a2a import create_generic_success_response
|
|
15
|
+
from .dto.responses.session_responses import MessageResponse, SessionResponse
|
|
22
16
|
|
|
23
17
|
router = APIRouter()
|
|
24
18
|
|
|
25
19
|
|
|
26
|
-
@router.post("/sessions/new", response_model=JSONRPCSuccessResponse)
|
|
27
|
-
async def create_new_session(
|
|
28
|
-
request: FastAPIRequest,
|
|
29
|
-
user: dict = Depends(get_current_user),
|
|
30
|
-
session_manager: SessionManager = Depends(get_session_manager),
|
|
31
|
-
session_service: SessionService = Depends(get_session_business_service),
|
|
32
|
-
):
|
|
33
|
-
"""Creates a new session on-demand and returns its ID."""
|
|
34
|
-
user_id = user.get("id")
|
|
35
|
-
log.info("User %s requesting new session", user_id)
|
|
36
|
-
try:
|
|
37
|
-
new_session_id = session_manager.create_new_session_id(request)
|
|
38
|
-
log.info("Created new session ID: %s for user %s", new_session_id, user_id)
|
|
39
|
-
|
|
40
|
-
# Attempt to create the session record in the DB.
|
|
41
|
-
# The service will handle the check for whether persistence is enabled.
|
|
42
|
-
session_service.create_session(
|
|
43
|
-
user_id=user_id,
|
|
44
|
-
agent_id=None, # Agent is not known at this point
|
|
45
|
-
name=None,
|
|
46
|
-
session_id=new_session_id,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
return create_generic_success_response(
|
|
50
|
-
result={"id": new_session_id}, request_id=None
|
|
51
|
-
)
|
|
52
|
-
except Exception as e:
|
|
53
|
-
log.error("Error creating new session for user %s: %s", user_id, e)
|
|
54
|
-
raise HTTPException(
|
|
55
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
56
|
-
detail="Failed to create a new session",
|
|
57
|
-
)
|
|
58
|
-
|
|
59
20
|
|
|
60
|
-
@router.get("/sessions", response_model=
|
|
21
|
+
@router.get("/sessions", response_model=PaginatedResponse[SessionResponse])
|
|
61
22
|
async def get_all_sessions(
|
|
23
|
+
page_number: int = Query(default=1, ge=1, alias="pageNumber"),
|
|
24
|
+
page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
|
|
25
|
+
db: Session = Depends(get_db),
|
|
62
26
|
user: dict = Depends(get_current_user),
|
|
63
27
|
session_service: SessionService = Depends(get_session_business_service),
|
|
64
28
|
):
|
|
65
29
|
user_id = user.get("id")
|
|
66
|
-
log.info("
|
|
30
|
+
log.info(f"User '{user_id}' is listing sessions with pagination (page={page_number}, size={page_size})")
|
|
67
31
|
|
|
68
32
|
try:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
session_domains = session_service.get_user_sessions(
|
|
72
|
-
user_id=request_dto.user_id, pagination=request_dto.pagination
|
|
73
|
-
)
|
|
33
|
+
pagination = PaginationParams(page_number=page_number, page_size=page_size)
|
|
34
|
+
paginated_response = session_service.get_user_sessions(db, user_id, pagination)
|
|
74
35
|
|
|
75
36
|
session_responses = []
|
|
76
|
-
for
|
|
37
|
+
for session_domain in paginated_response.data:
|
|
77
38
|
session_response = SessionResponse(
|
|
78
|
-
id=
|
|
79
|
-
user_id=
|
|
80
|
-
name=
|
|
81
|
-
agent_id=
|
|
82
|
-
created_time=
|
|
83
|
-
updated_time=
|
|
39
|
+
id=session_domain.id,
|
|
40
|
+
user_id=session_domain.user_id,
|
|
41
|
+
name=session_domain.name,
|
|
42
|
+
agent_id=session_domain.agent_id,
|
|
43
|
+
created_time=session_domain.created_time,
|
|
44
|
+
updated_time=session_domain.updated_time,
|
|
84
45
|
)
|
|
85
46
|
session_responses.append(session_response)
|
|
86
47
|
|
|
87
|
-
return
|
|
88
|
-
sessions=session_responses,
|
|
89
|
-
total_count=len(session_responses),
|
|
90
|
-
pagination=request_dto.pagination,
|
|
91
|
-
)
|
|
48
|
+
return PaginatedResponse(data=session_responses, meta=paginated_response.meta)
|
|
92
49
|
|
|
93
50
|
except Exception as e:
|
|
94
51
|
log.error("Error fetching sessions for user %s: %s", user_id, e)
|
|
@@ -98,9 +55,10 @@ async def get_all_sessions(
|
|
|
98
55
|
)
|
|
99
56
|
|
|
100
57
|
|
|
101
|
-
@router.get("/sessions/{session_id}", response_model=SessionResponse)
|
|
58
|
+
@router.get("/sessions/{session_id}", response_model=DataResponse[SessionResponse])
|
|
102
59
|
async def get_session(
|
|
103
60
|
session_id: str,
|
|
61
|
+
db: Session = Depends(get_db),
|
|
104
62
|
user: dict = Depends(get_current_user),
|
|
105
63
|
session_service: SessionService = Depends(get_session_business_service),
|
|
106
64
|
):
|
|
@@ -120,7 +78,7 @@ async def get_session(
|
|
|
120
78
|
request_dto = GetSessionRequest(session_id=session_id, user_id=user_id)
|
|
121
79
|
|
|
122
80
|
session_domain = session_service.get_session_details(
|
|
123
|
-
session_id=request_dto.session_id, user_id=request_dto.user_id
|
|
81
|
+
db=db, session_id=request_dto.session_id, user_id=request_dto.user_id
|
|
124
82
|
)
|
|
125
83
|
|
|
126
84
|
if not session_domain:
|
|
@@ -130,7 +88,7 @@ async def get_session(
|
|
|
130
88
|
|
|
131
89
|
log.info("User %s authorized. Fetching session_id: %s", user_id, session_id)
|
|
132
90
|
|
|
133
|
-
|
|
91
|
+
session_response = SessionResponse(
|
|
134
92
|
id=session_domain.id,
|
|
135
93
|
user_id=session_domain.user_id,
|
|
136
94
|
name=session_domain.name,
|
|
@@ -139,6 +97,8 @@ async def get_session(
|
|
|
139
97
|
updated_time=session_domain.updated_time,
|
|
140
98
|
)
|
|
141
99
|
|
|
100
|
+
return create_data_response(session_response)
|
|
101
|
+
|
|
142
102
|
except HTTPException:
|
|
143
103
|
raise
|
|
144
104
|
except Exception as e:
|
|
@@ -157,6 +117,7 @@ async def get_session(
|
|
|
157
117
|
@router.get("/sessions/{session_id}/messages")
|
|
158
118
|
async def get_session_history(
|
|
159
119
|
session_id: str,
|
|
120
|
+
db: Session = Depends(get_db),
|
|
160
121
|
user: dict = Depends(get_current_user),
|
|
161
122
|
session_service: SessionService = Depends(get_session_business_service),
|
|
162
123
|
):
|
|
@@ -178,6 +139,7 @@ async def get_session_history(
|
|
|
178
139
|
request_dto = GetSessionHistoryRequest(session_id=session_id, user_id=user_id)
|
|
179
140
|
|
|
180
141
|
history_domain = session_service.get_session_history(
|
|
142
|
+
db=db,
|
|
181
143
|
session_id=request_dto.session_id,
|
|
182
144
|
user_id=request_dto.user_id,
|
|
183
145
|
pagination=request_dto.pagination,
|
|
@@ -228,6 +190,7 @@ async def get_session_history(
|
|
|
228
190
|
async def update_session_name(
|
|
229
191
|
session_id: str,
|
|
230
192
|
name: str = Body(..., embed=True),
|
|
193
|
+
db: Session = Depends(get_db),
|
|
231
194
|
user: dict = Depends(get_current_user),
|
|
232
195
|
session_service: SessionService = Depends(get_session_business_service),
|
|
233
196
|
):
|
|
@@ -249,6 +212,7 @@ async def update_session_name(
|
|
|
249
212
|
)
|
|
250
213
|
|
|
251
214
|
updated_domain = session_service.update_session_name(
|
|
215
|
+
db=db,
|
|
252
216
|
session_id=request_dto.session_id,
|
|
253
217
|
user_id=request_dto.user_id,
|
|
254
218
|
name=request_dto.name,
|
|
@@ -293,6 +257,7 @@ async def update_session_name(
|
|
|
293
257
|
@router.delete("/sessions/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
294
258
|
async def delete_session(
|
|
295
259
|
session_id: str,
|
|
260
|
+
db: Session = Depends(get_db),
|
|
296
261
|
user: dict = Depends(get_current_user),
|
|
297
262
|
session_service: SessionService = Depends(get_session_business_service),
|
|
298
263
|
):
|
|
@@ -301,7 +266,7 @@ async def delete_session(
|
|
|
301
266
|
|
|
302
267
|
try:
|
|
303
268
|
deleted = session_service.delete_session_with_notifications(
|
|
304
|
-
session_id=session_id, user_id=user_id
|
|
269
|
+
db=db, session_id=session_id, user_id=user_id
|
|
305
270
|
)
|
|
306
271
|
|
|
307
272
|
if not deleted:
|
|
@@ -15,6 +15,7 @@ from solace_ai_connector.common.log import log
|
|
|
15
15
|
|
|
16
16
|
from ....gateway.http_sse.session_manager import SessionManager
|
|
17
17
|
from ....gateway.http_sse.services.task_service import TaskService
|
|
18
|
+
from ....gateway.http_sse.services.session_service import SessionService
|
|
18
19
|
|
|
19
20
|
from a2a.types import (
|
|
20
21
|
CancelTaskRequest,
|
|
@@ -29,6 +30,7 @@ from ....gateway.http_sse.dependencies import (
|
|
|
29
30
|
get_session_manager,
|
|
30
31
|
get_sac_component,
|
|
31
32
|
get_task_service,
|
|
33
|
+
get_session_business_service,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
from typing import TYPE_CHECKING
|
|
@@ -45,6 +47,7 @@ async def _submit_task(
|
|
|
45
47
|
session_manager: SessionManager,
|
|
46
48
|
component: "WebUIBackendComponent",
|
|
47
49
|
is_streaming: bool,
|
|
50
|
+
session_service: SessionService | None = None,
|
|
48
51
|
):
|
|
49
52
|
"""Helper to submit a task, handling both streaming and non-streaming cases."""
|
|
50
53
|
log_prefix = f"[POST /api/v1/message:{'stream' if is_streaming else 'send'}] "
|
|
@@ -77,7 +80,7 @@ async def _submit_task(
|
|
|
77
80
|
|
|
78
81
|
client_id = session_manager.get_a2a_client_id(request)
|
|
79
82
|
|
|
80
|
-
# Use session ID from frontend request (contextId) instead of cookie-based session
|
|
83
|
+
# Use session ID from frontend request (contextId per A2A spec) instead of cookie-based session
|
|
81
84
|
# Handle various falsy values: None, empty string, whitespace-only string
|
|
82
85
|
frontend_session_id = None
|
|
83
86
|
if (
|
|
@@ -88,13 +91,16 @@ async def _submit_task(
|
|
|
88
91
|
if isinstance(context_id, str) and context_id.strip():
|
|
89
92
|
frontend_session_id = context_id.strip()
|
|
90
93
|
|
|
94
|
+
user_id = user_identity.get("id")
|
|
95
|
+
from ....gateway.http_sse.dependencies import SessionLocal
|
|
96
|
+
|
|
91
97
|
if frontend_session_id:
|
|
92
98
|
session_id = frontend_session_id
|
|
93
99
|
log.info(
|
|
94
100
|
"%sUsing session ID from frontend request: %s", log_prefix, session_id
|
|
95
101
|
)
|
|
96
102
|
else:
|
|
97
|
-
# Create new session when frontend doesn't provide one
|
|
103
|
+
# Create new session when frontend doesn't provide one
|
|
98
104
|
session_id = session_manager.create_new_session_id(request)
|
|
99
105
|
log.info(
|
|
100
106
|
"%sNo valid session ID from frontend, created new session: %s",
|
|
@@ -102,43 +108,60 @@ async def _submit_task(
|
|
|
102
108
|
session_id,
|
|
103
109
|
)
|
|
104
110
|
|
|
111
|
+
# Immediately create session in database if persistence is enabled
|
|
112
|
+
# This ensures the session exists before any other operations (like artifact listing)
|
|
113
|
+
if SessionLocal is not None and session_service is not None:
|
|
114
|
+
db = SessionLocal()
|
|
115
|
+
try:
|
|
116
|
+
session_service.create_session(
|
|
117
|
+
db=db,
|
|
118
|
+
user_id=user_id,
|
|
119
|
+
agent_id=agent_name,
|
|
120
|
+
session_id=session_id,
|
|
121
|
+
)
|
|
122
|
+
db.commit()
|
|
123
|
+
log.info("%sCreated session in database: %s", log_prefix, session_id)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
db.rollback()
|
|
126
|
+
log.warning("%sFailed to create session in database: %s", log_prefix, e)
|
|
127
|
+
finally:
|
|
128
|
+
db.close()
|
|
129
|
+
|
|
105
130
|
log.info(
|
|
106
131
|
"%sUsing ClientID: %s, SessionID: %s", log_prefix, client_id, session_id
|
|
107
132
|
)
|
|
108
133
|
|
|
109
134
|
# Store message in persistence layer if available
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if is_streaming and SessionLocal is not None:
|
|
135
|
+
if is_streaming and SessionLocal is not None and session_service is not None:
|
|
136
|
+
db = SessionLocal()
|
|
114
137
|
try:
|
|
115
|
-
from ....gateway.http_sse.dependencies import (
|
|
116
|
-
create_session_service_with_transaction,
|
|
117
|
-
)
|
|
118
138
|
from ....gateway.http_sse.shared.enums import SenderType
|
|
119
139
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
message_text = ""
|
|
141
|
+
if payload.params and payload.params.message:
|
|
142
|
+
parts = a2a.get_parts_from_message(payload.params.message)
|
|
143
|
+
for part in parts:
|
|
144
|
+
if hasattr(part, "text"):
|
|
145
|
+
message_text = part.text
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
session_service.add_message_to_session(
|
|
149
|
+
db=db,
|
|
150
|
+
session_id=session_id,
|
|
151
|
+
user_id=user_id,
|
|
152
|
+
message=message_text or "Task submitted",
|
|
153
|
+
sender_type=SenderType.USER,
|
|
154
|
+
sender_name=user_id or "user",
|
|
155
|
+
agent_id=agent_name,
|
|
156
|
+
)
|
|
157
|
+
db.commit()
|
|
138
158
|
except Exception as e:
|
|
159
|
+
db.rollback()
|
|
139
160
|
log.error(
|
|
140
161
|
"%sFailed to store message in session service: %s", log_prefix, e
|
|
141
162
|
)
|
|
163
|
+
finally:
|
|
164
|
+
db.close()
|
|
142
165
|
else:
|
|
143
166
|
log.debug(
|
|
144
167
|
"%sNo persistence available or non-streaming - skipping message storage",
|
|
@@ -216,6 +239,7 @@ async def send_task_to_agent(
|
|
|
216
239
|
session_manager=session_manager,
|
|
217
240
|
component=component,
|
|
218
241
|
is_streaming=False,
|
|
242
|
+
session_service=None,
|
|
219
243
|
)
|
|
220
244
|
|
|
221
245
|
|
|
@@ -225,6 +249,7 @@ async def subscribe_task_from_agent(
|
|
|
225
249
|
payload: SendStreamingMessageRequest,
|
|
226
250
|
session_manager: SessionManager = Depends(get_session_manager),
|
|
227
251
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
252
|
+
session_service: SessionService = Depends(get_session_business_service),
|
|
228
253
|
):
|
|
229
254
|
"""
|
|
230
255
|
Submits a streaming task request to the specified agent.
|
|
@@ -237,6 +262,7 @@ async def subscribe_task_from_agent(
|
|
|
237
262
|
session_manager=session_manager,
|
|
238
263
|
component=component,
|
|
239
264
|
is_streaming=True,
|
|
265
|
+
session_service=session_service,
|
|
240
266
|
)
|
|
241
267
|
|
|
242
268
|
|
|
@@ -302,4 +328,4 @@ async def cancel_agent_task(
|
|
|
302
328
|
raise HTTPException(
|
|
303
329
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
304
330
|
detail=error_resp.model_dump(exclude_none=True),
|
|
305
|
-
)
|
|
331
|
+
)
|
|
@@ -2,6 +2,7 @@ import uuid
|
|
|
2
2
|
from typing import TYPE_CHECKING, Optional
|
|
3
3
|
|
|
4
4
|
from solace_ai_connector.common.log import log
|
|
5
|
+
from sqlalchemy.orm import Session as DbSession
|
|
5
6
|
|
|
6
7
|
from ..repository import (
|
|
7
8
|
IMessageRepository,
|
|
@@ -13,6 +14,7 @@ from ..repository import (
|
|
|
13
14
|
from ..shared.enums import MessageType, SenderType
|
|
14
15
|
from ..shared.types import PaginationInfo, SessionId, UserId
|
|
15
16
|
from ..shared import now_epoch_ms
|
|
17
|
+
from ..shared.pagination import PaginationParams, PaginatedResponse, get_pagination_or_default
|
|
16
18
|
|
|
17
19
|
if TYPE_CHECKING:
|
|
18
20
|
from ..component import WebUIBackendComponent
|
|
@@ -21,38 +23,65 @@ if TYPE_CHECKING:
|
|
|
21
23
|
class SessionService:
|
|
22
24
|
def __init__(
|
|
23
25
|
self,
|
|
24
|
-
session_repository: ISessionRepository,
|
|
25
|
-
message_repository: IMessageRepository,
|
|
26
26
|
component: "WebUIBackendComponent" = None,
|
|
27
27
|
):
|
|
28
|
-
self.session_repository = session_repository
|
|
29
|
-
self.message_repository = message_repository
|
|
30
28
|
self.component = component
|
|
31
29
|
|
|
30
|
+
def _get_repositories(self, db: DbSession):
|
|
31
|
+
"""Create repositories for the given database session."""
|
|
32
|
+
from ..repository import SessionRepository, MessageRepository
|
|
33
|
+
session_repository = SessionRepository(db)
|
|
34
|
+
message_repository = MessageRepository(db)
|
|
35
|
+
return session_repository, message_repository
|
|
36
|
+
|
|
32
37
|
def is_persistence_enabled(self) -> bool:
|
|
33
38
|
"""Checks if the service is configured with a persistent backend."""
|
|
34
|
-
# The presence of a database_url on the component is the source of truth
|
|
35
|
-
# for whether SQL persistence is enabled.
|
|
36
39
|
return self.component and self.component.database_url is not None
|
|
37
40
|
|
|
38
41
|
def get_user_sessions(
|
|
39
|
-
self,
|
|
40
|
-
|
|
42
|
+
self,
|
|
43
|
+
db: DbSession,
|
|
44
|
+
user_id: UserId,
|
|
45
|
+
pagination: PaginationParams | None = None
|
|
46
|
+
) -> PaginatedResponse[Session]:
|
|
47
|
+
"""
|
|
48
|
+
Get paginated sessions for a user with full metadata.
|
|
49
|
+
|
|
50
|
+
Uses default pagination if none provided (page 1, size 20).
|
|
51
|
+
Returns paginated response with pageNumber, pageSize, nextPage, totalPages, totalCount.
|
|
52
|
+
"""
|
|
41
53
|
if not user_id or user_id.strip() == "":
|
|
42
54
|
raise ValueError("User ID cannot be empty")
|
|
43
55
|
|
|
44
|
-
|
|
56
|
+
pagination = get_pagination_or_default(pagination)
|
|
57
|
+
session_repository, _ = self._get_repositories(db)
|
|
58
|
+
|
|
59
|
+
pagination_info = PaginationInfo(
|
|
60
|
+
page=pagination.page_number,
|
|
61
|
+
page_size=pagination.page_size,
|
|
62
|
+
total_items=0,
|
|
63
|
+
total_pages=0,
|
|
64
|
+
has_next=False,
|
|
65
|
+
has_previous=False,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
sessions = session_repository.find_by_user(user_id, pagination_info)
|
|
69
|
+
total_count = session_repository.count_by_user(user_id)
|
|
70
|
+
|
|
71
|
+
return PaginatedResponse.create(sessions, total_count, pagination)
|
|
45
72
|
|
|
46
73
|
def get_session_details(
|
|
47
|
-
self, session_id: SessionId, user_id: UserId
|
|
74
|
+
self, db: DbSession, session_id: SessionId, user_id: UserId
|
|
48
75
|
) -> Session | None:
|
|
49
76
|
if not self._is_valid_session_id(session_id):
|
|
50
77
|
return None
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
session_repository, _ = self._get_repositories(db)
|
|
80
|
+
return session_repository.find_user_session(session_id, user_id)
|
|
53
81
|
|
|
54
82
|
def get_session_history(
|
|
55
83
|
self,
|
|
84
|
+
db: DbSession,
|
|
56
85
|
session_id: SessionId,
|
|
57
86
|
user_id: UserId,
|
|
58
87
|
pagination: PaginationInfo | None = None,
|
|
@@ -60,7 +89,8 @@ class SessionService:
|
|
|
60
89
|
if not self._is_valid_session_id(session_id):
|
|
61
90
|
return None
|
|
62
91
|
|
|
63
|
-
|
|
92
|
+
session_repository, _ = self._get_repositories(db)
|
|
93
|
+
result = session_repository.find_user_session_with_messages(
|
|
64
94
|
session_id, user_id, pagination
|
|
65
95
|
)
|
|
66
96
|
if not result:
|
|
@@ -75,6 +105,7 @@ class SessionService:
|
|
|
75
105
|
|
|
76
106
|
def create_session(
|
|
77
107
|
self,
|
|
108
|
+
db: DbSession,
|
|
78
109
|
user_id: UserId,
|
|
79
110
|
name: str | None = None,
|
|
80
111
|
agent_id: str | None = None,
|
|
@@ -90,8 +121,6 @@ class SessionService:
|
|
|
90
121
|
if not session_id:
|
|
91
122
|
session_id = str(uuid.uuid4())
|
|
92
123
|
|
|
93
|
-
# Leave name as None/empty - frontend will generate display name if needed
|
|
94
|
-
|
|
95
124
|
now_ms = now_epoch_ms()
|
|
96
125
|
session = Session(
|
|
97
126
|
id=session_id,
|
|
@@ -102,10 +131,8 @@ class SessionService:
|
|
|
102
131
|
updated_time=now_ms,
|
|
103
132
|
)
|
|
104
133
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
created_session = self.session_repository.save(session)
|
|
134
|
+
session_repository, _ = self._get_repositories(db)
|
|
135
|
+
created_session = session_repository.save(session)
|
|
109
136
|
log.info("Created new session %s for user %s", created_session.id, user_id)
|
|
110
137
|
|
|
111
138
|
if not created_session:
|
|
@@ -114,7 +141,7 @@ class SessionService:
|
|
|
114
141
|
return created_session
|
|
115
142
|
|
|
116
143
|
def update_session_name(
|
|
117
|
-
self, session_id: SessionId, user_id: UserId, name: str
|
|
144
|
+
self, db: DbSession, session_id: SessionId, user_id: UserId, name: str
|
|
118
145
|
) -> Session | None:
|
|
119
146
|
if not self._is_valid_session_id(session_id):
|
|
120
147
|
raise ValueError("Invalid session ID")
|
|
@@ -125,23 +152,25 @@ class SessionService:
|
|
|
125
152
|
if len(name.strip()) > 255:
|
|
126
153
|
raise ValueError("Session name cannot exceed 255 characters")
|
|
127
154
|
|
|
128
|
-
|
|
155
|
+
session_repository, _ = self._get_repositories(db)
|
|
156
|
+
session = session_repository.find_user_session(session_id, user_id)
|
|
129
157
|
if not session:
|
|
130
158
|
return None
|
|
131
159
|
|
|
132
160
|
session.update_name(name)
|
|
133
|
-
updated_session =
|
|
161
|
+
updated_session = session_repository.save(session)
|
|
134
162
|
|
|
135
163
|
log.info("Updated session %s name to '%s'", session_id, name)
|
|
136
164
|
return updated_session
|
|
137
165
|
|
|
138
166
|
def delete_session_with_notifications(
|
|
139
|
-
self, session_id: SessionId, user_id: UserId
|
|
167
|
+
self, db: DbSession, session_id: SessionId, user_id: UserId
|
|
140
168
|
) -> bool:
|
|
141
169
|
if not self._is_valid_session_id(session_id):
|
|
142
170
|
raise ValueError("Invalid session ID")
|
|
143
171
|
|
|
144
|
-
|
|
172
|
+
session_repository, _ = self._get_repositories(db)
|
|
173
|
+
session = session_repository.find_user_session(session_id, user_id)
|
|
145
174
|
if not session:
|
|
146
175
|
log.warning(
|
|
147
176
|
"Attempted to delete non-existent session %s by user %s",
|
|
@@ -158,7 +187,7 @@ class SessionService:
|
|
|
158
187
|
)
|
|
159
188
|
return False
|
|
160
189
|
|
|
161
|
-
deleted =
|
|
190
|
+
deleted = session_repository.delete(session_id, user_id)
|
|
162
191
|
if not deleted:
|
|
163
192
|
return False
|
|
164
193
|
|
|
@@ -171,6 +200,7 @@ class SessionService:
|
|
|
171
200
|
|
|
172
201
|
def add_message_to_session(
|
|
173
202
|
self,
|
|
203
|
+
db: DbSession,
|
|
174
204
|
session_id: SessionId,
|
|
175
205
|
user_id: UserId,
|
|
176
206
|
message: str,
|
|
@@ -185,9 +215,11 @@ class SessionService:
|
|
|
185
215
|
if not message or message.strip() == "":
|
|
186
216
|
raise ValueError("Message cannot be empty")
|
|
187
217
|
|
|
188
|
-
|
|
218
|
+
session_repository, message_repository = self._get_repositories(db)
|
|
219
|
+
session = session_repository.find_user_session(session_id, user_id)
|
|
189
220
|
if not session:
|
|
190
221
|
session = self.create_session(
|
|
222
|
+
db=db,
|
|
191
223
|
user_id=user_id,
|
|
192
224
|
agent_id=agent_id,
|
|
193
225
|
session_id=session_id,
|
|
@@ -203,10 +235,10 @@ class SessionService:
|
|
|
203
235
|
created_time=now_epoch_ms(),
|
|
204
236
|
)
|
|
205
237
|
|
|
206
|
-
saved_message =
|
|
238
|
+
saved_message = message_repository.save(message_entity)
|
|
207
239
|
|
|
208
240
|
session.mark_activity()
|
|
209
|
-
|
|
241
|
+
session_repository.save(session)
|
|
210
242
|
|
|
211
243
|
log.info("Added message to session %s from %s", session_id, sender_name)
|
|
212
244
|
return saved_message
|
|
@@ -257,4 +289,4 @@ class SessionService:
|
|
|
257
289
|
"Failed to publish session deletion event to agent %s: %s",
|
|
258
290
|
agent_id,
|
|
259
291
|
e,
|
|
260
|
-
)
|
|
292
|
+
)
|