Fix SSE route dependency and align architecture docs
This commit is contained in:
5
backend/app/infrastructure/session/__init__.py
Normal file
5
backend/app/infrastructure/session/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Initialize the app.infrastructure.session package."""
|
||||
# Keep package boundaries explicit so backend imports stay predictable.
|
||||
|
||||
|
||||
__all__ = []
|
||||
@@ -0,0 +1,95 @@
|
||||
"""Implement infrastructure support for in memory conversation store."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from app.domain.conversation import ConversationMessage, ConversationSession, ConversationStore
|
||||
# Keep adapter behavior explicit so integration details remain easy to audit.
|
||||
|
||||
|
||||
|
||||
class InMemoryConversationStore(ConversationStore):
|
||||
"""Provide the In Memory Conversation Store store implementation."""
|
||||
def __init__(self, *, max_sessions: int = 100, timeout_minutes: int = 30) -> None:
|
||||
"""Initialize the In Memory Conversation Store instance."""
|
||||
self.max_sessions = max_sessions
|
||||
self.timeout_seconds = timeout_minutes * 60
|
||||
self.sessions: dict[str, ConversationSession] = {}
|
||||
|
||||
def _now(self) -> int:
|
||||
"""Handle now for this module for the In Memory Conversation Store instance."""
|
||||
return int(time.time())
|
||||
|
||||
def _cleanup_expired(self) -> None:
|
||||
"""Handle cleanup expired for this module for the In Memory Conversation Store instance."""
|
||||
now = self._now()
|
||||
expired = [
|
||||
session_id
|
||||
for session_id, session in self.sessions.items()
|
||||
if (now - session.updated_at) > self.timeout_seconds
|
||||
]
|
||||
for session_id in expired:
|
||||
self.sessions.pop(session_id, None)
|
||||
|
||||
def create_session(self, metadata: dict | None = None) -> ConversationSession:
|
||||
"""Create session for the In Memory Conversation Store instance."""
|
||||
self._cleanup_expired()
|
||||
if len(self.sessions) >= self.max_sessions:
|
||||
oldest = min(self.sessions.values(), key=lambda item: item.updated_at)
|
||||
self.sessions.pop(oldest.session_id, None)
|
||||
session_id = str(uuid.uuid4())[:8]
|
||||
session = ConversationSession(
|
||||
session_id=session_id,
|
||||
created_at=self._now(),
|
||||
updated_at=self._now(),
|
||||
metadata=metadata or {},
|
||||
)
|
||||
self.sessions[session_id] = session
|
||||
return session
|
||||
|
||||
def get_session(self, session_id: str) -> ConversationSession | None:
|
||||
"""Return session for the In Memory Conversation Store instance."""
|
||||
self._cleanup_expired()
|
||||
return self.sessions.get(session_id)
|
||||
|
||||
def save_message(
|
||||
self,
|
||||
session_id: str,
|
||||
*,
|
||||
role: str,
|
||||
content: str,
|
||||
sources: list[dict] | None = None,
|
||||
) -> ConversationSession | None:
|
||||
"""Save message for the In Memory Conversation Store instance."""
|
||||
session = self.get_session(session_id)
|
||||
if not session:
|
||||
return None
|
||||
session.messages.append(
|
||||
ConversationMessage(
|
||||
role=role,
|
||||
content=content,
|
||||
timestamp=self._now(),
|
||||
sources=sources or [],
|
||||
)
|
||||
)
|
||||
session.updated_at = self._now()
|
||||
return session
|
||||
|
||||
def delete_session(self, session_id: str) -> bool:
|
||||
"""Delete session for the In Memory Conversation Store instance."""
|
||||
return self.sessions.pop(session_id, None) is not None
|
||||
|
||||
def list_sessions(self) -> list[dict]:
|
||||
"""List sessions for the In Memory Conversation Store instance."""
|
||||
self._cleanup_expired()
|
||||
return [
|
||||
{
|
||||
"session_id": session.session_id,
|
||||
"message_count": len(session.messages),
|
||||
"created_at": session.created_at,
|
||||
"updated_at": session.updated_at,
|
||||
}
|
||||
for session in self.sessions.values()
|
||||
]
|
||||
Reference in New Issue
Block a user