solace-agent-mesh 1.4.2__py3-none-any.whl → 1.4.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 (84) hide show
  1. solace_agent_mesh/agent/adk/services.py +10 -3
  2. solace_agent_mesh/agent/sac/app.py +4 -1
  3. solace_agent_mesh/assets/docs/404.html +3 -3
  4. solace_agent_mesh/assets/docs/assets/js/beecea0d.8bbd852c.js +1 -0
  5. solace_agent_mesh/assets/docs/assets/js/{main.1a0c706b.js → main.66eec320.js} +2 -2
  6. solace_agent_mesh/assets/docs/assets/js/{runtime~main.f2b4ea70.js → runtime~main.355446b2.js} +1 -1
  7. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +3 -3
  8. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +3 -3
  9. 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
  10. solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +3 -3
  11. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +3 -3
  12. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +3 -3
  13. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +3 -3
  14. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +3 -3
  15. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +3 -3
  16. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +3 -3
  17. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +3 -3
  18. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +3 -3
  19. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +3 -3
  20. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +3 -3
  21. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +6 -3
  22. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +3 -3
  23. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  24. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +3 -3
  25. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +3 -3
  26. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +3 -3
  27. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +3 -3
  28. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +3 -3
  38. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +3 -3
  39. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +3 -3
  40. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +3 -3
  41. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +3 -3
  42. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +3 -3
  43. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +3 -3
  45. solace_agent_mesh/assets/docs/lunr-index-1758138634356.json +1 -0
  46. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  47. solace_agent_mesh/assets/docs/search-doc-1758138634356.json +1 -0
  48. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  49. solace_agent_mesh/cli/__init__.py +1 -1
  50. solace_agent_mesh/client/webui/frontend/static/assets/main-8xbvgfVK.css +1 -0
  51. solace_agent_mesh/client/webui/frontend/static/assets/main-Cv2k8j3R.js +339 -0
  52. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +13 -13
  53. solace_agent_mesh/client/webui/frontend/static/index.html +13 -13
  54. solace_agent_mesh/gateway/base/app.py +122 -71
  55. solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +342 -0
  56. solace_agent_mesh/gateway/http_sse/alembic.ini +4 -6
  57. solace_agent_mesh/gateway/http_sse/app.py +152 -90
  58. solace_agent_mesh/gateway/http_sse/dependencies.py +30 -24
  59. solace_agent_mesh/gateway/http_sse/repository/entities/message.py +3 -5
  60. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +7 -11
  61. solace_agent_mesh/gateway/http_sse/repository/message_repository.py +11 -7
  62. solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +7 -7
  63. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +10 -8
  64. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +18 -14
  65. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
  66. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +25 -23
  67. solace_agent_mesh/gateway/http_sse/routers/sessions.py +10 -19
  68. solace_agent_mesh/gateway/http_sse/services/session_service.py +5 -6
  69. solace_agent_mesh/gateway/http_sse/shared/__init__.py +17 -1
  70. solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
  71. solace_agent_mesh/gateway/http_sse/shared/types.py +23 -7
  72. {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.3.dist-info}/METADATA +1 -1
  73. {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.3.dist-info}/RECORD +79 -76
  74. solace_agent_mesh/assets/docs/assets/js/beecea0d.ae31f6a7.js +0 -1
  75. solace_agent_mesh/assets/docs/lunr-index-1758046853673.json +0 -1
  76. solace_agent_mesh/assets/docs/search-doc-1758046853673.json +0 -1
  77. solace_agent_mesh/client/webui/frontend/static/assets/main-B6BpuH9K.js +0 -339
  78. solace_agent_mesh/client/webui/frontend/static/assets/main-B9s_V9tJ.css +0 -1
  79. /solace_agent_mesh/assets/docs/assets/js/{main.1a0c706b.js.LICENSE.txt → main.66eec320.js.LICENSE.txt} +0 -0
  80. /solace_agent_mesh/gateway/http_sse/alembic/versions/{d5b3f8f2e9a0_create_initial_database.py → 20250910_d5b3f8f2e9a0_create_initial_database.py} +0 -0
  81. /solace_agent_mesh/gateway/http_sse/alembic/versions/{b1c2d3e4f5g6_add_database_indexes.py → 20250911_b1c2d3e4f5g6_add_database_indexes.py} +0 -0
  82. {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.3.dist-info}/WHEEL +0 -0
  83. {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.3.dist-info}/entry_points.txt +0 -0
  84. {solace_agent_mesh-1.4.2.dist-info → solace_agent_mesh-1.4.3.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.updated_at.desc()).all()
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 = self.db.query(SessionModel).filter(SessionModel.id == session.id).first()
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.updated_at = session.updated_at
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
- created_at=session.created_at,
63
- updated_at=session.updated_at,
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, session_id: SessionId, user_id: UserId, pagination: PaginationInfo | None = None
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.created_at.asc()).all()
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
- created_at=model.created_at,
123
- updated_at=model.updated_at,
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
- created_at=model.created_at,
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 typing import List, Optional
6
- from datetime import datetime
7
- from pydantic import BaseModel
5
+ from pydantic import BaseModel, ConfigDict, Field
8
6
 
9
- from ....shared.types import SessionId, UserId, MessageId, PaginationInfo
10
- from ....shared.enums import SenderType, MessageType
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(BaseModel):
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
- timestamp: datetime
22
- created_at: datetime
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(BaseModel):
25
+ class SessionResponse(BaseTimestampResponse):
27
26
  """Response DTO for a session."""
27
+
28
28
  id: SessionId
29
- user_id: UserId
30
- name: Optional[str] = None
31
- agent_id: Optional[str] = None
32
- created_at: datetime
33
- updated_at: Optional[datetime] = None
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
- sessions: List[SessionResponse]
40
- pagination: Optional[PaginationInfo] = None
41
- total_count: int
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, BackgroundTasks
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
- created_at=domain.created_at,
48
- updated_at=domain.updated_at,
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
- created_at=session_domain.created_at,
105
- updated_at=session_domain.updated_at,
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
- timestamp=message_domain.created_at,
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
- created_at=updated_domain.created_at,
238
- updated_at=updated_domain.updated_at,
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
- now = datetime.now(timezone.utc)
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
- created_at=now,
92
- updated_at=now,
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
- created_at=datetime.now(timezone.utc),
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__ = ["get_current_user"]
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 = Dict[str, Any]
18
- Headers = Dict[str, str]
19
- QueryParams = Dict[str, Union[str, List[str]]]
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: Optional[datetime] = None
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.2
3
+ Version: 1.4.3
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