solace-agent-mesh 1.3.0__py3-none-any.whl → 1.3.2__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/setup.py +141 -34
- solace_agent_mesh/agent/protocol/event_handlers.py +91 -0
- solace_agent_mesh/agent/sac/app.py +3 -2
- solace_agent_mesh/agent/tools/__init__.py +1 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +362 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/483cef9a.03d5dceb.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.071e2d94.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.4adc477a.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.cf0229ea.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +29 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html } +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html } +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +19 -27
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- 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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- 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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- 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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- 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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- 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 +5 -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 +63 -0
- 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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1757704179464.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1757704179464.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/client/webui/frontend/static/assets/{authCallback-vY5eu2lI.js → authCallback-CAX9u8a7.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-BeBkzgWW.js → client-DXU9SPI5.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{main-Bjys1KQs.js → main-DjoMeldu.js} +26 -26
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CE0AeXyK.js → vendor-B0BEKoAR.js} +69 -74
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
- solace_agent_mesh/common/a2a/__init__.py +4 -0
- solace_agent_mesh/common/a2a/protocol.py +20 -0
- solace_agent_mesh/common/sac/sam_component_base.py +29 -9
- solace_agent_mesh/common/sam_events/__init__.py +9 -0
- solace_agent_mesh/common/sam_events/event_service.py +207 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +1 -1
- solace_agent_mesh/gateway/http_sse/component.py +45 -35
- solace_agent_mesh/gateway/http_sse/dependencies.py +129 -66
- solace_agent_mesh/gateway/http_sse/main.py +22 -35
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +37 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +41 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +45 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +16 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +64 -0
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +78 -0
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +27 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +27 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +139 -0
- solace_agent_mesh/gateway/http_sse/routers/{agents.py → agent_cards.py} +7 -7
- solace_agent_mesh/gateway/http_sse/routers/config.py +1 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +20 -0
- solace_agent_mesh/gateway/http_sse/{api → routers}/dto/requests/session_requests.py +1 -8
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +16 -0
- solace_agent_mesh/gateway/http_sse/{api → routers}/dto/responses/session_responses.py +3 -30
- solace_agent_mesh/gateway/http_sse/{api/controllers/session_controller.py → routers/sessions.py} +20 -77
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +42 -49
- solace_agent_mesh/gateway/http_sse/{api/controllers/user_controller.py → routers/users.py} +1 -1
- solace_agent_mesh/gateway/http_sse/services/{agent_service.py → agent_card_service.py} +19 -19
- solace_agent_mesh/gateway/http_sse/services/session_service.py +245 -0
- solace_agent_mesh/gateway/http_sse/session_manager.py +0 -3
- solace_agent_mesh/gateway/http_sse/shared/enums.py +0 -5
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/METADATA +1 -1
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/RECORD +109 -110
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/75384d09.bf78fbdb.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757433031159.json +0 -1
- solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +0 -676
- solace_agent_mesh/gateway/http_sse/api/__init__.py +0 -11
- solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +0 -9
- solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +0 -279
- solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +0 -37
- solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +0 -66
- solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +0 -43
- solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +0 -74
- solace_agent_mesh/gateway/http_sse/application/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/application/services/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/application/services/session_service.py +0 -135
- solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/domain/entities/session.py +0 -90
- solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +0 -54
- solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +0 -4
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +0 -123
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +0 -4
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +0 -16
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +0 -119
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +0 -31
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +0 -12
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +0 -3
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +0 -174
- /solace_agent_mesh/assets/docs/assets/js/{main.08d30374.js.LICENSE.txt → main.4adc477a.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/gateway/http_sse/{api → routers}/dto/__init__.py +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.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,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,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
|
solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py
DELETED
|
@@ -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,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
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|