Arag / app /services /session_store.py
AuthorBot
Restructure project for HF Spaces deployment
772f852
Raw
History Blame Contribute Delete
2.8 kB
"""Author RAG — Session Store Service.
Reader session CRUD using Redis for hot state and JSON files for persistence.
Per implementation plan.
"""
import json
import uuid
from pathlib import Path
import structlog
from app.config import get_settings
logger = structlog.get_logger(__name__)
cfg = get_settings()
SESSIONS_DIR = Path("/data/sessions") if Path("/data").exists() else Path("data/sessions")
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
SESSION_TTL = cfg.RAG_SESSION_TTL_MINUTES * 60
class SessionStore:
"""Manages reader sessions in Redis (hot) + JSON files (cold)."""
def __init__(self, redis=None):
self.redis = redis
def _redis_key(self, session_id: str, author_id: str) -> str:
return f"session:{author_id}:{session_id}"
def _file_path(self, session_id: str) -> Path:
return SESSIONS_DIR / f"{session_id}.json"
async def create(self, author_id: str) -> str:
"""Create a new empty session. Returns session_id."""
session_id = str(uuid.uuid4())
data = {"session_id": session_id, "author_id": author_id, "history": [], "turn_count": 0}
await self.save(session_id, author_id, data)
return session_id
async def load(self, session_id: str, author_id: str) -> dict:
"""Load session from Redis or file fallback."""
key = self._redis_key(session_id, author_id)
if self.redis:
try:
raw = await self.redis.get(key)
if raw:
return json.loads(raw)
except Exception as e:
logger.warning("Redis session load failed", error=str(e))
# File fallback
path = self._file_path(session_id)
if path.exists():
return json.loads(path.read_text())
return {"session_id": session_id, "author_id": author_id, "history": [], "turn_count": 0}
async def save(self, session_id: str, author_id: str, data: dict) -> None:
"""Save session to Redis + file."""
key = self._redis_key(session_id, author_id)
raw = json.dumps(data)
if self.redis:
try:
await self.redis.setex(key, SESSION_TTL, raw)
except Exception as e:
logger.warning("Redis session save failed", error=str(e))
# Always persist to file
self._file_path(session_id).write_text(raw)
async def delete(self, session_id: str, author_id: str) -> None:
"""Delete session from Redis and file."""
key = self._redis_key(session_id, author_id)
if self.redis:
try:
await self.redis.delete(key)
except Exception:
pass
path = self._file_path(session_id)
if path.exists():
path.unlink()