solace-agent-mesh 1.4.2__py3-none-any.whl → 1.4.4__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/services.py +10 -3
- solace_agent_mesh/agent/sac/app.py +5 -1
- solace_agent_mesh/agent/sac/component.py +7 -3
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +17 -0
- solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +4 -0
- solace_agent_mesh/agent/tools/tool_definition.py +8 -0
- solace_agent_mesh/agent/tools/web_tools.py +5 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{ae0e903d.abca774a.js → ae0e903d.ac3b9419.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/beecea0d.8bbd852c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.1a0c706b.js → main.03fb0598.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/{runtime~main.f2b4ea70.js → runtime~main.4ee39c6f.js} +1 -1
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +6 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1758293998763.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1758293998763.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-8xbvgfVK.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Cv2k8j3R.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +13 -13
- solace_agent_mesh/client/webui/frontend/static/index.html +13 -13
- solace_agent_mesh/gateway/base/app.py +122 -71
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +342 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +4 -6
- solace_agent_mesh/gateway/http_sse/app.py +152 -90
- solace_agent_mesh/gateway/http_sse/dependencies.py +30 -24
- solace_agent_mesh/gateway/http_sse/main.py +19 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +3 -5
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +7 -11
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +11 -7
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +7 -7
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +10 -8
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +18 -14
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +25 -23
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +10 -19
- solace_agent_mesh/gateway/http_sse/services/session_service.py +5 -6
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +17 -1
- solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
- solace_agent_mesh/gateway/http_sse/shared/types.py +23 -7
- {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.4.dist-info}/METADATA +1 -1
- {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.4.dist-info}/RECORD +86 -83
- solace_agent_mesh/assets/docs/assets/js/beecea0d.ae31f6a7.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1758046853673.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1758046853673.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-B6BpuH9K.js +0 -339
- solace_agent_mesh/client/webui/frontend/static/assets/main-B9s_V9tJ.css +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.1a0c706b.js.LICENSE.txt → main.03fb0598.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/gateway/http_sse/alembic/versions/{d5b3f8f2e9a0_create_initial_database.py → 20250910_d5b3f8f2e9a0_create_initial_database.py} +0 -0
- /solace_agent_mesh/gateway/http_sse/alembic/versions/{b1c2d3e4f5g6_add_database_indexes.py → 20250911_b1c2d3e4f5g6_add_database_indexes.py} +0 -0
- {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.4.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.4.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,7 +12,7 @@ from .models import MessageModel, SessionModel
|
|
|
12
12
|
|
|
13
13
|
class SessionRepository(ISessionRepository):
|
|
14
14
|
"""SQLAlchemy implementation of session repository."""
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def __init__(self, db: DBSession):
|
|
17
17
|
self.db = db
|
|
18
18
|
|
|
@@ -26,7 +26,7 @@ class SessionRepository(ISessionRepository):
|
|
|
26
26
|
offset = (pagination.page - 1) * pagination.page_size
|
|
27
27
|
query = query.offset(offset).limit(pagination.page_size)
|
|
28
28
|
|
|
29
|
-
models = query.order_by(SessionModel.
|
|
29
|
+
models = query.order_by(SessionModel.updated_time.desc()).all()
|
|
30
30
|
return [self._model_to_entity(model) for model in models]
|
|
31
31
|
|
|
32
32
|
def find_user_session(
|
|
@@ -45,13 +45,15 @@ class SessionRepository(ISessionRepository):
|
|
|
45
45
|
|
|
46
46
|
def save(self, session: Session) -> Session:
|
|
47
47
|
"""Save or update a session."""
|
|
48
|
-
model =
|
|
48
|
+
model = (
|
|
49
|
+
self.db.query(SessionModel).filter(SessionModel.id == session.id).first()
|
|
50
|
+
)
|
|
49
51
|
|
|
50
52
|
if model:
|
|
51
53
|
# Update existing
|
|
52
54
|
model.name = session.name
|
|
53
55
|
model.agent_id = session.agent_id
|
|
54
|
-
model.
|
|
56
|
+
model.updated_time = session.updated_time
|
|
55
57
|
else:
|
|
56
58
|
# Create new
|
|
57
59
|
model = SessionModel(
|
|
@@ -59,8 +61,8 @@ class SessionRepository(ISessionRepository):
|
|
|
59
61
|
name=session.name,
|
|
60
62
|
user_id=session.user_id,
|
|
61
63
|
agent_id=session.agent_id,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
created_time=session.created_time,
|
|
65
|
+
updated_time=session.updated_time,
|
|
64
66
|
)
|
|
65
67
|
self.db.add(model)
|
|
66
68
|
|
|
@@ -82,7 +84,10 @@ class SessionRepository(ISessionRepository):
|
|
|
82
84
|
return result > 0
|
|
83
85
|
|
|
84
86
|
def find_user_session_with_messages(
|
|
85
|
-
self,
|
|
87
|
+
self,
|
|
88
|
+
session_id: SessionId,
|
|
89
|
+
user_id: UserId,
|
|
90
|
+
pagination: PaginationInfo | None = None,
|
|
86
91
|
) -> tuple[Session, list[Message]] | None:
|
|
87
92
|
"""Find a session with its messages."""
|
|
88
93
|
session_model = (
|
|
@@ -105,7 +110,7 @@ class SessionRepository(ISessionRepository):
|
|
|
105
110
|
offset = (pagination.page - 1) * pagination.page_size
|
|
106
111
|
message_query = message_query.offset(offset).limit(pagination.page_size)
|
|
107
112
|
|
|
108
|
-
message_models = message_query.order_by(MessageModel.
|
|
113
|
+
message_models = message_query.order_by(MessageModel.created_time.asc()).all()
|
|
109
114
|
|
|
110
115
|
session = self._model_to_entity(session_model)
|
|
111
116
|
messages = [self._message_model_to_entity(model) for model in message_models]
|
|
@@ -119,15 +124,14 @@ class SessionRepository(ISessionRepository):
|
|
|
119
124
|
user_id=model.user_id,
|
|
120
125
|
name=model.name,
|
|
121
126
|
agent_id=model.agent_id,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
last_activity=model.updated_at,
|
|
127
|
+
created_time=model.created_time,
|
|
128
|
+
updated_time=model.updated_time,
|
|
125
129
|
)
|
|
126
130
|
|
|
127
131
|
def _message_model_to_entity(self, model: MessageModel) -> Message:
|
|
128
132
|
"""Convert SQLAlchemy message model to domain entity."""
|
|
129
133
|
from ..shared.enums import MessageType, SenderType
|
|
130
|
-
|
|
134
|
+
|
|
131
135
|
return Message(
|
|
132
136
|
id=model.id,
|
|
133
137
|
session_id=model.session_id,
|
|
@@ -135,5 +139,5 @@ class SessionRepository(ISessionRepository):
|
|
|
135
139
|
sender_type=SenderType(model.sender_type),
|
|
136
140
|
sender_name=model.sender_name,
|
|
137
141
|
message_type=MessageType.TEXT, # Default for now
|
|
138
|
-
|
|
139
|
-
)
|
|
142
|
+
created_time=model.created_time,
|
|
143
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base response classes with automatic timestamp serialization.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
|
|
9
|
+
from ....shared import epoch_ms_to_iso8601
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseTimestampResponse(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Base class for responses that include standard timestamp fields.
|
|
15
|
+
|
|
16
|
+
Timestamp fields are stored as epoch milliseconds (int) internally.
|
|
17
|
+
Automatically converts created_time and updated_time to ISO strings in JSON output.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
21
|
+
|
|
22
|
+
def model_dump(self, **kwargs) -> dict[str, Any]:
|
|
23
|
+
kwargs.setdefault('by_alias', True)
|
|
24
|
+
"""Override model_dump to convert timestamp fields to ISO strings."""
|
|
25
|
+
data = super().model_dump(**kwargs)
|
|
26
|
+
|
|
27
|
+
# Convert timestamp fields to ISO strings for JSON output
|
|
28
|
+
# Check both snake_case (internal) and camelCase (API) names
|
|
29
|
+
timestamp_fields = ["created_time", "updated_time", "createdTime", "updatedTime"]
|
|
30
|
+
for field in timestamp_fields:
|
|
31
|
+
if field in data and data[field] is not None:
|
|
32
|
+
data[field] = epoch_ms_to_iso8601(data[field])
|
|
33
|
+
|
|
34
|
+
return data
|
|
35
|
+
|
|
36
|
+
def model_dump_json(self, **kwargs) -> str:
|
|
37
|
+
"""Override model_dump_json to use our timestamp conversion."""
|
|
38
|
+
# Get the converted data first, then serialize to JSON
|
|
39
|
+
converted_data = self.model_dump(**kwargs)
|
|
40
|
+
import json
|
|
41
|
+
|
|
42
|
+
return json.dumps(converted_data, default=str)
|
|
@@ -2,40 +2,42 @@
|
|
|
2
2
|
Session-related response DTOs.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
6
|
|
|
9
|
-
from ....shared.
|
|
10
|
-
from ....shared.
|
|
7
|
+
from ....shared.enums import MessageType, SenderType
|
|
8
|
+
from ....shared.types import MessageId, PaginationInfo, SessionId, UserId
|
|
9
|
+
from .base_responses import BaseTimestampResponse
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class MessageResponse(
|
|
12
|
+
class MessageResponse(BaseTimestampResponse):
|
|
14
13
|
"""Response DTO for a chat message."""
|
|
14
|
+
|
|
15
15
|
id: MessageId
|
|
16
|
-
session_id: SessionId
|
|
16
|
+
session_id: SessionId = Field(alias="sessionId")
|
|
17
17
|
message: str
|
|
18
|
-
sender_type: SenderType
|
|
19
|
-
sender_name: str
|
|
20
|
-
message_type: MessageType = MessageType.TEXT
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
updated_at: Optional[datetime] = None
|
|
18
|
+
sender_type: SenderType = Field(alias="senderType")
|
|
19
|
+
sender_name: str = Field(alias="senderName")
|
|
20
|
+
message_type: MessageType = Field(default=MessageType.TEXT, alias="messageType")
|
|
21
|
+
created_time: int = Field(alias="createdTime")
|
|
22
|
+
updated_time: int | None = Field(default=None, alias="updatedTime")
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
class SessionResponse(
|
|
25
|
+
class SessionResponse(BaseTimestampResponse):
|
|
27
26
|
"""Response DTO for a session."""
|
|
27
|
+
|
|
28
28
|
id: SessionId
|
|
29
|
-
user_id: UserId
|
|
30
|
-
name:
|
|
31
|
-
agent_id:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
last_activity: Optional[datetime] = None
|
|
29
|
+
user_id: UserId = Field(alias="userId")
|
|
30
|
+
name: str | None = None
|
|
31
|
+
agent_id: str | None = Field(default=None, alias="agentId")
|
|
32
|
+
created_time: int = Field(alias="createdTime")
|
|
33
|
+
updated_time: int | None = Field(default=None, alias="updatedTime")
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
class SessionListResponse(BaseModel):
|
|
38
37
|
"""Response DTO for a list of sessions."""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
|
|
39
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
40
|
+
|
|
41
|
+
sessions: list[SessionResponse]
|
|
42
|
+
pagination: PaginationInfo | None = None
|
|
43
|
+
total_count: int = Field(alias="totalCount")
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
from fastapi import APIRouter, Body, Depends, HTTPException, status
|
|
1
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, status
|
|
2
2
|
from solace_ai_connector.common.log import log
|
|
3
3
|
|
|
4
|
+
from ..dependencies import get_session_business_service
|
|
4
5
|
from ..services.session_service import SessionService
|
|
5
|
-
from ..dependencies import (
|
|
6
|
-
get_session_business_service,
|
|
7
|
-
)
|
|
8
6
|
from ..shared.auth_utils import get_current_user
|
|
9
7
|
from .dto.requests.session_requests import (
|
|
10
|
-
DeleteSessionRequest,
|
|
11
8
|
GetSessionHistoryRequest,
|
|
12
9
|
GetSessionRequest,
|
|
13
10
|
GetSessionsRequest,
|
|
@@ -44,9 +41,8 @@ async def get_all_sessions(
|
|
|
44
41
|
user_id=domain.user_id,
|
|
45
42
|
name=domain.name,
|
|
46
43
|
agent_id=domain.agent_id,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
last_activity=domain.last_activity,
|
|
44
|
+
created_time=domain.created_time,
|
|
45
|
+
updated_time=domain.updated_time,
|
|
50
46
|
)
|
|
51
47
|
session_responses.append(session_response)
|
|
52
48
|
|
|
@@ -101,9 +97,8 @@ async def get_session(
|
|
|
101
97
|
user_id=session_domain.user_id,
|
|
102
98
|
name=session_domain.name,
|
|
103
99
|
agent_id=session_domain.agent_id,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
last_activity=session_domain.last_activity,
|
|
100
|
+
created_time=session_domain.created_time,
|
|
101
|
+
updated_time=session_domain.updated_time,
|
|
107
102
|
)
|
|
108
103
|
|
|
109
104
|
except HTTPException:
|
|
@@ -170,8 +165,7 @@ async def get_session_history(
|
|
|
170
165
|
sender_type=message_domain.sender_type,
|
|
171
166
|
sender_name=message_domain.sender_name,
|
|
172
167
|
message_type=message_domain.message_type,
|
|
173
|
-
|
|
174
|
-
created_at=message_domain.created_at,
|
|
168
|
+
created_time=message_domain.created_time,
|
|
175
169
|
)
|
|
176
170
|
message_responses.append(message_response)
|
|
177
171
|
|
|
@@ -234,9 +228,8 @@ async def update_session_name(
|
|
|
234
228
|
user_id=updated_domain.user_id,
|
|
235
229
|
name=updated_domain.name,
|
|
236
230
|
agent_id=updated_domain.agent_id,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
last_activity=updated_domain.last_activity,
|
|
231
|
+
created_time=updated_domain.created_time,
|
|
232
|
+
updated_time=updated_domain.updated_time,
|
|
240
233
|
)
|
|
241
234
|
|
|
242
235
|
except HTTPException:
|
|
@@ -282,9 +275,7 @@ async def delete_session(
|
|
|
282
275
|
|
|
283
276
|
except ValueError as e:
|
|
284
277
|
log.warning("Validation error deleting session %s: %s", session_id, e)
|
|
285
|
-
raise HTTPException(
|
|
286
|
-
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
|
|
287
|
-
)
|
|
278
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
288
279
|
except Exception as e:
|
|
289
280
|
log.error(
|
|
290
281
|
"Error deleting session %s for user %s: %s",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
from datetime import datetime, timezone
|
|
3
2
|
from typing import TYPE_CHECKING
|
|
4
3
|
|
|
5
4
|
from solace_ai_connector.common.log import log
|
|
@@ -13,6 +12,7 @@ from ..repository import (
|
|
|
13
12
|
)
|
|
14
13
|
from ..shared.enums import MessageType, SenderType
|
|
15
14
|
from ..shared.types import PaginationInfo, SessionId, UserId
|
|
15
|
+
from ..shared import now_epoch_ms
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from ..component import WebUIBackendComponent
|
|
@@ -82,15 +82,14 @@ class SessionService:
|
|
|
82
82
|
|
|
83
83
|
# Leave name as None/empty - frontend will generate display name if needed
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
now_ms = now_epoch_ms()
|
|
86
86
|
session = Session(
|
|
87
87
|
id=session_id,
|
|
88
88
|
user_id=user_id,
|
|
89
89
|
name=name,
|
|
90
90
|
agent_id=agent_id,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
last_activity=now,
|
|
91
|
+
created_time=now_ms,
|
|
92
|
+
updated_time=now_ms,
|
|
94
93
|
)
|
|
95
94
|
|
|
96
95
|
created_session = self.session_repository.save(session)
|
|
@@ -185,7 +184,7 @@ class SessionService:
|
|
|
185
184
|
sender_type=sender_type,
|
|
186
185
|
sender_name=sender_name,
|
|
187
186
|
message_type=message_type,
|
|
188
|
-
|
|
187
|
+
created_time=now_epoch_ms(),
|
|
189
188
|
)
|
|
190
189
|
|
|
191
190
|
saved_message = self.message_repository.save(message_entity)
|
|
@@ -5,5 +5,21 @@ Contains common utilities, constants, enums, and types used across layers.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from .auth_utils import get_current_user
|
|
8
|
+
from .timestamp_utils import (
|
|
9
|
+
datetime_to_epoch_ms,
|
|
10
|
+
epoch_ms_to_datetime,
|
|
11
|
+
epoch_ms_to_iso8601,
|
|
12
|
+
iso8601_to_epoch_ms,
|
|
13
|
+
now_epoch_ms,
|
|
14
|
+
validate_epoch_ms,
|
|
15
|
+
)
|
|
8
16
|
|
|
9
|
-
__all__ = [
|
|
17
|
+
__all__ = [
|
|
18
|
+
"get_current_user",
|
|
19
|
+
"now_epoch_ms",
|
|
20
|
+
"epoch_ms_to_iso8601",
|
|
21
|
+
"iso8601_to_epoch_ms",
|
|
22
|
+
"datetime_to_epoch_ms",
|
|
23
|
+
"epoch_ms_to_datetime",
|
|
24
|
+
"validate_epoch_ms",
|
|
25
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Epoch timestamp utilities for database portability and timezone handling.
|
|
3
|
+
|
|
4
|
+
Matches the Java backend pattern:
|
|
5
|
+
- Store as epoch milliseconds in database
|
|
6
|
+
- Return as ISO 8601 strings via API
|
|
7
|
+
- UI handles timezone conversion
|
|
8
|
+
|
|
9
|
+
This provides a consistent approach across the platform.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def now_epoch_ms() -> int:
|
|
17
|
+
"""
|
|
18
|
+
Get current time as milliseconds since epoch (matches Java pattern).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Current time as epoch milliseconds
|
|
22
|
+
"""
|
|
23
|
+
return int(time.time() * 1000)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def epoch_ms_to_iso8601(epoch_ms: int) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Convert epoch milliseconds to ISO 8601 string (matches Java epochLongToIso8601String).
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
epoch_ms: Time in milliseconds since epoch
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
ISO 8601 formatted string in UTC timezone
|
|
35
|
+
"""
|
|
36
|
+
return datetime.fromtimestamp(epoch_ms / 1000, tz=timezone.utc).isoformat()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def iso8601_to_epoch_ms(iso8601_string: str) -> int:
|
|
40
|
+
"""
|
|
41
|
+
Convert ISO 8601 string to epoch milliseconds (matches Java iso8601StringToEpochLong).
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
iso8601_string: ISO 8601 formatted datetime string
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Time as epoch milliseconds
|
|
48
|
+
"""
|
|
49
|
+
dt = datetime.fromisoformat(iso8601_string.replace("Z", "+00:00"))
|
|
50
|
+
return int(dt.timestamp() * 1000)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def datetime_to_epoch_ms(dt: datetime) -> int:
|
|
54
|
+
"""
|
|
55
|
+
Convert datetime object to epoch milliseconds.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dt: Python datetime object
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Time as epoch milliseconds
|
|
62
|
+
"""
|
|
63
|
+
return int(dt.timestamp() * 1000)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def epoch_ms_to_datetime(epoch_ms: int) -> datetime:
|
|
67
|
+
"""
|
|
68
|
+
Convert epoch milliseconds to datetime object.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
epoch_ms: Time in milliseconds since epoch
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Python datetime object in UTC
|
|
75
|
+
"""
|
|
76
|
+
return datetime.fromtimestamp(epoch_ms / 1000, tz=timezone.utc)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def validate_epoch_ms(epoch_ms: int | None) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Validate that an epoch milliseconds value is reasonable.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
epoch_ms: Time in milliseconds since epoch
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if the timestamp appears valid
|
|
88
|
+
"""
|
|
89
|
+
if epoch_ms is None:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
# Check if timestamp is in reasonable range
|
|
93
|
+
# After 1970-01-01 and before year 2100
|
|
94
|
+
min_epoch_ms = 0
|
|
95
|
+
max_epoch_ms = 4102444800000 # 2100-01-01 00:00:00 UTC
|
|
96
|
+
|
|
97
|
+
return min_epoch_ms <= epoch_ms <= max_epoch_ms
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Custom types and type aliases used throughout the application.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Dict, Any, List, Optional, Union
|
|
6
5
|
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
7
8
|
from pydantic import BaseModel
|
|
8
9
|
|
|
9
10
|
# Basic type aliases
|
|
@@ -14,18 +15,29 @@ TaskId = str
|
|
|
14
15
|
AgentId = str
|
|
15
16
|
|
|
16
17
|
# Dictionary types
|
|
17
|
-
JsonDict =
|
|
18
|
-
Headers =
|
|
19
|
-
QueryParams =
|
|
18
|
+
JsonDict = dict[str, Any]
|
|
19
|
+
Headers = dict[str, str]
|
|
20
|
+
QueryParams = dict[str, str | list[str]]
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
# Common data structures
|
|
22
24
|
class Timestamp(BaseModel):
|
|
23
|
-
"""Standardized timestamp representation."""
|
|
25
|
+
"""Standardized timestamp representation using epoch milliseconds."""
|
|
26
|
+
|
|
27
|
+
created_time: int # Epoch milliseconds
|
|
28
|
+
updated_time: int | None = None # Epoch milliseconds
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LegacyTimestamp(BaseModel):
|
|
32
|
+
"""Legacy timestamp representation (deprecated - use Timestamp instead)."""
|
|
33
|
+
|
|
24
34
|
created_at: datetime
|
|
25
|
-
updated_at:
|
|
35
|
+
updated_at: datetime | None = None
|
|
36
|
+
|
|
26
37
|
|
|
27
38
|
class PaginationInfo(BaseModel):
|
|
28
39
|
"""Pagination information for list responses."""
|
|
40
|
+
|
|
29
41
|
page: int
|
|
30
42
|
page_size: int
|
|
31
43
|
total_items: int
|
|
@@ -33,13 +45,17 @@ class PaginationInfo(BaseModel):
|
|
|
33
45
|
has_next: bool
|
|
34
46
|
has_previous: bool
|
|
35
47
|
|
|
48
|
+
|
|
36
49
|
class SortInfo(BaseModel):
|
|
37
50
|
"""Sorting information for list requests."""
|
|
51
|
+
|
|
38
52
|
field: str
|
|
39
53
|
direction: str = "asc" # asc or desc
|
|
40
54
|
|
|
55
|
+
|
|
41
56
|
class FilterInfo(BaseModel):
|
|
42
57
|
"""Filtering information for list requests."""
|
|
58
|
+
|
|
43
59
|
field: str
|
|
44
60
|
operator: str # eq, ne, gt, lt, gte, lte, contains, in
|
|
45
|
-
value: Any
|
|
61
|
+
value: Any
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solace-agent-mesh
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.4
|
|
4
4
|
Summary: Solace Agent Mesh is an open-source framework for building event-driven, multi-agent AI systems where specialized agents collaborate on complex tasks.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SolaceLabs/solace-agent-mesh
|
|
6
6
|
Project-URL: Repository, https://github.com/SolaceLabs/solace-agent-mesh
|