"""SQLAlchemy-backed per-user preferences repository.""" from __future__ import annotations from datetime import UTC, datetime from typing import Any from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from deerflow.persistence.user_preferences.model import UserPreferencesRow class UserPreferencesRepository: def __init__(self, session_factory: async_sessionmaker[AsyncSession]) -> None: self._sf = session_factory async def get_for_user(self, user_id: str) -> dict[str, Any]: """Return the user's stored preferences (empty dict when none).""" async with self._sf() as session: row = await session.get(UserPreferencesRow, user_id) if row is None or not isinstance(row.preferences, dict): return {} return dict(row.preferences) async def merge_for_user(self, user_id: str, patch: dict[str, Any]) -> dict[str, Any]: """Shallow-merge ``patch`` into the user's preferences and return the result.""" now = datetime.now(UTC) async with self._sf() as session: row = await session.get(UserPreferencesRow, user_id) if row is None: merged = dict(patch) row = UserPreferencesRow(user_id=user_id, preferences=merged, created_at=now, updated_at=now) session.add(row) else: merged = dict(row.preferences or {}) merged.update(patch) # Reassign so SQLAlchemy detects the change on the JSON column. row.preferences = merged row.updated_at = now await session.commit() return dict(merged)