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,90 +0,0 @@
1
- from datetime import datetime, timezone
2
-
3
- from pydantic import BaseModel
4
-
5
- from ...shared.enums import MessageType, SenderType, SessionStatus
6
- from ...shared.types import AgentId, MessageId, SessionId, UserId
7
-
8
-
9
- class Session(BaseModel):
10
- id: SessionId
11
- user_id: UserId
12
- name: str | None = None
13
- agent_id: AgentId | None = None
14
- status: SessionStatus = SessionStatus.ACTIVE
15
- created_at: datetime
16
- updated_at: datetime | None = None
17
- last_activity: datetime | None = None
18
-
19
- def update_name(self, new_name: str) -> None:
20
- if not new_name or len(new_name.strip()) == 0:
21
- raise ValueError("Session name cannot be empty")
22
- if len(new_name) > 255:
23
- raise ValueError("Session name cannot exceed 255 characters")
24
-
25
- self.name = new_name.strip()
26
- self.updated_at = datetime.now(timezone.utc)
27
-
28
- def mark_activity(self) -> None:
29
- self.last_activity = datetime.now(timezone.utc)
30
- self.updated_at = datetime.now(timezone.utc)
31
-
32
- def archive(self) -> None:
33
- self.status = SessionStatus.ARCHIVED
34
- self.updated_at = datetime.now(timezone.utc)
35
-
36
- def activate(self) -> None:
37
- self.status = SessionStatus.ACTIVE
38
- self.updated_at = datetime.now(timezone.utc)
39
-
40
- def can_be_deleted_by_user(self, user_id: UserId) -> bool:
41
- return self.user_id == user_id
42
-
43
- def can_be_accessed_by_user(self, user_id: UserId) -> bool:
44
- return self.user_id == user_id
45
-
46
-
47
- class Message(BaseModel):
48
- id: MessageId
49
- session_id: SessionId
50
- message: str
51
- sender_type: SenderType
52
- sender_name: str
53
- message_type: MessageType = MessageType.TEXT
54
- created_at: datetime
55
-
56
- def validate_message_content(self) -> None:
57
- if not self.message or len(self.message.strip()) == 0:
58
- raise ValueError("Message content cannot be empty")
59
- if len(self.message) > 10_000_000:
60
- raise ValueError("Message content exceeds maximum length (10MB)")
61
-
62
- def is_from_user(self) -> bool:
63
- return self.sender_type == SenderType.USER
64
-
65
- def is_from_agent(self) -> bool:
66
- return self.sender_type == SenderType.AGENT
67
-
68
- def is_system_message(self) -> bool:
69
- return self.sender_type == SenderType.SYSTEM
70
-
71
-
72
- class SessionHistory(BaseModel):
73
- session: Session
74
- messages: list[Message] = []
75
- total_message_count: int = 0
76
-
77
- def add_message(self, message: Message) -> None:
78
- if message.session_id != self.session.id:
79
- raise ValueError("Message does not belong to this session")
80
-
81
- message.validate_message_content()
82
- self.messages.append(message)
83
- self.total_message_count += 1
84
- self.session.mark_activity()
85
-
86
- def get_messages_by_sender_type(self, sender_type: SenderType) -> list[Message]:
87
- return [msg for msg in self.messages if msg.sender_type == sender_type]
88
-
89
- def get_latest_messages(self, count: int = 10) -> list[Message]:
90
- return sorted(self.messages, key=lambda x: x.created_at, reverse=True)[:count]
@@ -1,3 +0,0 @@
1
- from .session_repository import IMessageRepository, ISessionRepository
2
-
3
- __all__ = ["ISessionRepository", "IMessageRepository"]
@@ -1,54 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from ...shared.types import PaginationInfo, SessionId, UserId
4
- from ..entities.session import Message, Session
5
-
6
-
7
- class ISessionRepository(ABC):
8
- @abstractmethod
9
- def get_by_id(self, session_id: SessionId) -> Session | None:
10
- pass
11
-
12
- @abstractmethod
13
- def get_by_user_id(
14
- self, user_id: UserId, pagination: PaginationInfo | None = None
15
- ) -> list[Session]:
16
- pass
17
-
18
- @abstractmethod
19
- def get_user_session(
20
- self, session_id: SessionId, user_id: UserId
21
- ) -> Session | None:
22
- pass
23
-
24
- @abstractmethod
25
- def create(self, session: Session) -> Session:
26
- pass
27
-
28
- @abstractmethod
29
- def update(self, session: Session) -> Session | None:
30
- pass
31
-
32
- @abstractmethod
33
- def delete(self, session_id: SessionId, user_id: UserId) -> bool:
34
- pass
35
-
36
- @abstractmethod
37
- def exists(self, session_id: SessionId) -> bool:
38
- pass
39
-
40
-
41
- class IMessageRepository(ABC):
42
- @abstractmethod
43
- def get_by_session_id(
44
- self, session_id: SessionId, pagination: PaginationInfo | None = None
45
- ) -> list[Message]:
46
- pass
47
-
48
- @abstractmethod
49
- def create(self, message: Message) -> Message:
50
- pass
51
-
52
- @abstractmethod
53
- def delete_by_session_id(self, session_id: SessionId) -> bool:
54
- pass
@@ -1,4 +0,0 @@
1
- from .persistence.database_service import DatabaseService
2
- from .repositories.session_repository import SessionRepository, MessageRepository
3
-
4
- __all__ = ["DatabaseService", "SessionRepository", "MessageRepository"]
@@ -1,3 +0,0 @@
1
- from .container import ApplicationContainer, get_container, initialize_container
2
-
3
- __all__ = ["ApplicationContainer", "initialize_container", "get_container"]
@@ -1,123 +0,0 @@
1
- from collections.abc import Callable
2
- from typing import Any, TypeVar
3
-
4
- from ...application.services.session_service import SessionService
5
- from ...domain.repositories.session_repository import (
6
- IMessageRepository,
7
- ISessionRepository,
8
- )
9
- from ...infrastructure.persistence import database_service as db_service_module
10
- from ...infrastructure.persistence.database_service import DatabaseService
11
- from ...infrastructure.repositories.session_repository import (
12
- MessageRepository,
13
- SessionRepository,
14
- )
15
-
16
- T = TypeVar("T")
17
-
18
-
19
- class DIContainer:
20
- """Dependency injection container."""
21
-
22
- def __init__(self):
23
- self._services: dict[str, Any] = {}
24
- self._factories: dict[str, Callable] = {}
25
- self._singletons: dict[str, Any] = {}
26
-
27
- def register_singleton(self, service_type: type, instance: Any) -> None:
28
- self._singletons[service_type.__name__] = instance
29
-
30
- def register_factory(self, service_type: type, factory: Callable) -> None:
31
- self._factories[service_type.__name__] = factory
32
-
33
- def register_transient(self, service_type: type, implementation: type) -> None:
34
- """Register a transient service (new instance each time)."""
35
- self._services[service_type.__name__] = implementation
36
-
37
- def get(self, service_type: type) -> Any:
38
- """Get an instance of the requested service."""
39
- service_name = service_type.__name__
40
-
41
- # Check singletons first
42
- if service_name in self._singletons:
43
- return self._singletons[service_name]
44
-
45
- # Check factories
46
- if service_name in self._factories:
47
- return self._factories[service_name]()
48
-
49
- # Check transient services
50
- if service_name in self._services:
51
- implementation = self._services[service_name]
52
- return self._create_instance(implementation)
53
-
54
- raise ValueError(f"Service {service_name} is not registered")
55
-
56
- def _create_instance(self, implementation: type) -> Any:
57
- """Create an instance with dependency injection."""
58
- # This is a simplified implementation
59
- # In a production system, you'd use a more sophisticated approach
60
- # like inspecting constructor parameters and resolving them automatically
61
- return implementation()
62
-
63
-
64
- class ApplicationContainer:
65
- """Application-specific dependency injection container."""
66
-
67
- def __init__(self, database_url: str | None = None):
68
- self.container = DIContainer()
69
- self.database_url = database_url
70
- self.has_database = database_url is not None
71
- self._setup_dependencies()
72
-
73
- def _setup_dependencies(self) -> None:
74
- if self.has_database:
75
- database_service = DatabaseService(self.database_url)
76
- self.container.register_singleton(DatabaseService, database_service)
77
- db_service_module.database_service = database_service
78
-
79
- session_repository = SessionRepository(database_service)
80
- message_repository = MessageRepository(database_service)
81
-
82
- self.container.register_singleton(ISessionRepository, session_repository)
83
- self.container.register_singleton(IMessageRepository, message_repository)
84
-
85
- def session_service_factory():
86
- return SessionService(session_repository, message_repository)
87
-
88
- self.container.register_factory(SessionService, session_service_factory)
89
-
90
- def get_database_service(self) -> DatabaseService | None:
91
- if not self.has_database:
92
- return None
93
- return self.container.get(DatabaseService)
94
-
95
- def get_session_service(self) -> SessionService | None:
96
- if not self.has_database:
97
- return None
98
- return self.container.get(SessionService)
99
-
100
-
101
- # Global container instance
102
- _container: ApplicationContainer | None = None
103
-
104
-
105
- def initialize_container(database_url: str | None = None) -> ApplicationContainer:
106
- global _container
107
- _container = ApplicationContainer(database_url)
108
-
109
- # Only create tables if database is available
110
- if _container.has_database:
111
- database_service = _container.get_database_service()
112
- if database_service:
113
- database_service.create_tables()
114
-
115
- return _container
116
-
117
-
118
- def get_container() -> ApplicationContainer:
119
- if _container is None:
120
- raise RuntimeError(
121
- "Container not initialized. Call initialize_container() first."
122
- )
123
- return _container
@@ -1,4 +0,0 @@
1
- from .database_service import DatabaseService
2
- from .models import Base, SessionModel, MessageModel
3
-
4
- __all__ = ["DatabaseService", "Base", "SessionModel", "MessageModel"]
@@ -1,16 +0,0 @@
1
- from sqlalchemy.orm import sessionmaker
2
-
3
- from .database_service import DatabaseService
4
-
5
-
6
- class DatabasePersistenceService:
7
- def __init__(self, db_url: str):
8
- self.db_service = DatabaseService(db_url)
9
- self._session_factory = sessionmaker(bind=self.db_service.engine)
10
-
11
- def Session(self):
12
- return self._session_factory()
13
-
14
- @property
15
- def engine(self):
16
- return self.db_service.engine
@@ -1,119 +0,0 @@
1
- import logging
2
- from collections.abc import Generator
3
- from contextlib import contextmanager
4
-
5
- from sqlalchemy import create_engine, event
6
- from sqlalchemy.orm import Session, sessionmaker
7
-
8
- from .models import Base
9
-
10
-
11
- def get_database_type(database_url: str) -> str:
12
- """Get the database type from a database URL."""
13
- if database_url.startswith("sqlite"):
14
- return "sqlite"
15
- elif database_url.startswith("postgresql"):
16
- return "postgresql"
17
- else:
18
- return "unknown"
19
-
20
-
21
- class DatabaseService:
22
- def __init__(self, database_url: str):
23
- self.database_url = database_url
24
- self.logger = logging.getLogger(__name__)
25
-
26
- if database_url.startswith("sqlite"):
27
- self._setup_sqlite_engine(database_url)
28
- elif database_url.startswith("postgresql"):
29
- self._setup_postgresql_engine(database_url)
30
- else:
31
- # Fallback for other databases
32
- self._setup_generic_engine(database_url)
33
-
34
- self.SessionLocal = sessionmaker(
35
- autocommit=False, autoflush=False, bind=self.engine
36
- )
37
-
38
- def _setup_sqlite_engine(self, database_url: str):
39
- """Configure SQLite-specific engine settings."""
40
- self.engine = create_engine(
41
- database_url,
42
- echo=False,
43
- connect_args={
44
- "check_same_thread": False,
45
- },
46
- )
47
-
48
- @event.listens_for(self.engine, "connect")
49
- def set_sqlite_pragma(dbapi_connection, connection_record):
50
- cursor = dbapi_connection.cursor()
51
- cursor.execute("PRAGMA foreign_keys=ON")
52
- cursor.close()
53
-
54
- def _setup_postgresql_engine(self, database_url: str):
55
- """Configure PostgreSQL-specific engine settings."""
56
- try:
57
- import psycopg2
58
- except ImportError:
59
- raise ImportError(
60
- "PostgreSQL support requires psycopg2. Install with: "
61
- "pip install 'solace-agent-mesh[postgresql]'"
62
- )
63
-
64
- self.engine = create_engine(
65
- database_url,
66
- pool_size=10,
67
- max_overflow=20,
68
- pool_timeout=30,
69
- pool_recycle=3600,
70
- pool_pre_ping=True,
71
- echo=False,
72
- )
73
-
74
- def _setup_generic_engine(self, database_url: str):
75
- """Configure generic database engine settings."""
76
- self.engine = create_engine(
77
- database_url,
78
- pool_size=10,
79
- max_overflow=20,
80
- pool_timeout=30,
81
- pool_recycle=3600,
82
- pool_pre_ping=True,
83
- echo=False,
84
- )
85
-
86
- def create_tables(self):
87
- Base.metadata.create_all(bind=self.engine)
88
-
89
- @contextmanager
90
- def session_scope(self) -> Generator[Session, None, None]:
91
- session = self.SessionLocal()
92
- try:
93
- yield session
94
- session.commit()
95
- except Exception as e:
96
- session.rollback()
97
- self.logger.error(f"Database transaction failed: {e}")
98
- raise
99
- finally:
100
- session.close()
101
-
102
- @contextmanager
103
- def read_only_session(self) -> Generator[Session, None, None]:
104
- session = self.SessionLocal()
105
- try:
106
- yield session
107
- except Exception as e:
108
- session.rollback()
109
- self.logger.error(f"Database read operation failed: {e}")
110
- raise
111
- finally:
112
- session.close()
113
-
114
-
115
- database_service: DatabaseService = None
116
-
117
-
118
- def get_database_service() -> DatabaseService:
119
- return database_service
@@ -1,31 +0,0 @@
1
- from sqlalchemy import Column, DateTime, ForeignKey, String, Text
2
- from sqlalchemy.orm import declarative_base, relationship
3
- from sqlalchemy.sql import func
4
-
5
- Base = declarative_base()
6
-
7
-
8
- class SessionModel(Base):
9
- __tablename__ = "sessions"
10
- id = Column(String, primary_key=True)
11
- name = Column(String, nullable=True)
12
- user_id = Column(String, nullable=False)
13
- agent_id = Column(String, nullable=True)
14
- created_at = Column(DateTime, default=func.now())
15
- updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
16
- messages = relationship(
17
- "MessageModel", back_populates="session", cascade="all, delete-orphan"
18
- )
19
-
20
-
21
- class MessageModel(Base):
22
- __tablename__ = "chat_messages"
23
- id = Column(String, primary_key=True)
24
- session_id = Column(
25
- String, ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False
26
- )
27
- message = Column(Text, nullable=False)
28
- created_at = Column(DateTime, default=func.now())
29
- sender_type = Column(String(50))
30
- sender_name = Column(String(255))
31
- session = relationship("SessionModel", back_populates="messages")
@@ -1,12 +0,0 @@
1
- from .dependency_injection.container import ApplicationContainer
2
- from .persistence.database_service import DatabaseService
3
-
4
-
5
- class PersistenceService:
6
- def __init__(self, database_url: str):
7
- self.db_service = DatabaseService(database_url)
8
- self.container = ApplicationContainer(database_url)
9
-
10
- @property
11
- def engine(self):
12
- return self.db_service.engine
@@ -1,3 +0,0 @@
1
- from .session_repository import SessionRepository, MessageRepository
2
-
3
- __all__ = ["SessionRepository", "MessageRepository"]
@@ -1,174 +0,0 @@
1
- from ...domain.entities.session import Message, Session
2
- from ...domain.repositories.session_repository import (
3
- IMessageRepository,
4
- ISessionRepository,
5
- )
6
- from ...shared.enums import SenderType, SessionStatus
7
- from ...shared.types import PaginationInfo, SessionId, UserId
8
- from ..persistence.database_service import DatabaseService
9
- from ..persistence.models import MessageModel, SessionModel
10
-
11
-
12
- class SessionRepository(ISessionRepository):
13
- def __init__(self, db_service: DatabaseService):
14
- self.db_service = db_service
15
-
16
- def get_by_id(self, session_id: SessionId) -> Session | None:
17
- with self.db_service.read_only_session() as session:
18
- model = (
19
- session.query(SessionModel)
20
- .filter(SessionModel.id == session_id)
21
- .first()
22
- )
23
- return self._model_to_entity(model) if model else None
24
-
25
- def get_by_user_id(
26
- self, user_id: UserId, pagination: PaginationInfo | None = None
27
- ) -> list[Session]:
28
- with self.db_service.read_only_session() as session:
29
- query = session.query(SessionModel).filter(SessionModel.user_id == user_id)
30
-
31
- if pagination:
32
- offset = (pagination.page - 1) * pagination.page_size
33
- query = query.offset(offset).limit(pagination.page_size)
34
-
35
- models = query.order_by(SessionModel.updated_at.desc()).all()
36
- return [self._model_to_entity(model) for model in models]
37
-
38
- def get_user_session(
39
- self, session_id: SessionId, user_id: UserId
40
- ) -> Session | None:
41
- with self.db_service.read_only_session() as session:
42
- model = (
43
- session.query(SessionModel)
44
- .filter(SessionModel.id == session_id, SessionModel.user_id == user_id)
45
- .first()
46
- )
47
- return self._model_to_entity(model) if model else None
48
-
49
- def create(self, session_entity: Session) -> Session:
50
- with self.db_service.session_scope() as session:
51
- model = SessionModel(
52
- id=session_entity.id,
53
- user_id=session_entity.user_id,
54
- name=session_entity.name,
55
- agent_id=session_entity.agent_id,
56
- created_at=session_entity.created_at,
57
- updated_at=session_entity.updated_at,
58
- )
59
- session.add(model)
60
- session.flush()
61
- session.refresh(model)
62
- return self._model_to_entity(model)
63
-
64
- def update(self, session_entity: Session) -> Session | None:
65
- with self.db_service.session_scope() as session:
66
- model = (
67
- session.query(SessionModel)
68
- .filter(
69
- SessionModel.id == session_entity.id,
70
- SessionModel.user_id == session_entity.user_id,
71
- )
72
- .first()
73
- )
74
-
75
- if not model:
76
- return None
77
-
78
- model.name = session_entity.name
79
- model.agent_id = session_entity.agent_id
80
- model.updated_at = session_entity.updated_at
81
-
82
- session.flush()
83
- session.refresh(model)
84
- return self._model_to_entity(model)
85
-
86
- def delete(self, session_id: SessionId, user_id: UserId) -> bool:
87
- with self.db_service.session_scope() as session:
88
- model = (
89
- session.query(SessionModel)
90
- .filter(SessionModel.id == session_id, SessionModel.user_id == user_id)
91
- .first()
92
- )
93
-
94
- if not model:
95
- return False
96
-
97
- session.delete(model)
98
- return True
99
-
100
- def exists(self, session_id: SessionId) -> bool:
101
- with self.db_service.read_only_session() as session:
102
- return (
103
- session.query(SessionModel)
104
- .filter(SessionModel.id == session_id)
105
- .first()
106
- is not None
107
- )
108
-
109
- def _model_to_entity(self, model: SessionModel) -> Session:
110
- return Session(
111
- id=model.id,
112
- user_id=model.user_id,
113
- name=model.name,
114
- agent_id=model.agent_id,
115
- status=SessionStatus.ACTIVE,
116
- created_at=model.created_at,
117
- updated_at=model.updated_at,
118
- last_activity=model.updated_at,
119
- )
120
-
121
-
122
- class MessageRepository(IMessageRepository):
123
- def __init__(self, db_service: DatabaseService):
124
- self.db_service = db_service
125
-
126
- def get_by_session_id(
127
- self, session_id: SessionId, pagination: PaginationInfo | None = None
128
- ) -> list[Message]:
129
- with self.db_service.read_only_session() as session:
130
- query = session.query(MessageModel).filter(
131
- MessageModel.session_id == session_id
132
- )
133
- query = query.order_by(MessageModel.created_at.asc())
134
-
135
- if pagination:
136
- offset = (pagination.page - 1) * pagination.page_size
137
- query = query.offset(offset).limit(pagination.page_size)
138
-
139
- models = query.all()
140
- return [self._model_to_entity(model) for model in models]
141
-
142
- def create(self, message_entity: Message) -> Message:
143
- with self.db_service.session_scope() as session:
144
- model = MessageModel(
145
- id=message_entity.id,
146
- session_id=message_entity.session_id,
147
- message=message_entity.message,
148
- sender_type=message_entity.sender_type.value,
149
- sender_name=message_entity.sender_name,
150
- created_at=message_entity.created_at,
151
- )
152
- session.add(model)
153
- session.flush()
154
- session.refresh(model)
155
- return self._model_to_entity(model)
156
-
157
- def delete_by_session_id(self, session_id: SessionId) -> bool:
158
- with self.db_service.session_scope() as session:
159
- deleted_count = (
160
- session.query(MessageModel)
161
- .filter(MessageModel.session_id == session_id)
162
- .delete()
163
- )
164
- return deleted_count > 0
165
-
166
- def _model_to_entity(self, model: MessageModel) -> Message:
167
- return Message(
168
- id=model.id,
169
- session_id=model.session_id,
170
- message=model.message,
171
- sender_type=SenderType(model.sender_type),
172
- sender_name=model.sender_name,
173
- created_at=model.created_at,
174
- )