Source code for lalandre_rag.retrieval.result_cache

"""
Redis-backed result cache for the retrieval service.
"""

import dataclasses
import hashlib
import json
import logging
from typing import Any, Dict, List, Optional

from .result import RetrievalResult

logger = logging.getLogger(__name__)


[docs] class RetrievalCache: """Thin wrapper around Redis for caching retrieval results.""" def __init__(self, redis_client: Any, ttl: int) -> None: self._redis = redis_client self._ttl = ttl @property def enabled(self) -> bool: """Return whether Redis caching is currently active.""" return self._redis is not None and self._ttl > 0
[docs] @staticmethod def cache_key( query: str, top_k: int, score_threshold: Optional[float], filters: Optional[Dict[str, Any]], granularity: Optional[str], collections: Optional[List[str]], embedding_preset: Optional[str] = None, ) -> str: """Build a stable cache key for one retrieval request shape.""" payload = json.dumps( { "q": query, "k": top_k, "thr": score_threshold, "f": sorted(filters.items()) if filters else [], "g": granularity, "c": sorted(collections or []), "ep": embedding_preset or "", }, sort_keys=True, ) return f"retrieval:{hashlib.sha256(payload.encode()).hexdigest()[:24]}"
[docs] def get(self, key: str) -> Optional[List[RetrievalResult]]: """Fetch cached retrieval results for *key*, if present.""" if not self.enabled: return None try: raw = self._redis.get(key) if not raw: return None return [RetrievalResult(**item) for item in json.loads(raw)] except Exception as exc: logger.warning("Retrieval cache GET failed for %s: %s", key[:40], exc) return None
[docs] def set(self, key: str, results: List[RetrievalResult]) -> None: """Store retrieval results for *key* with the configured TTL.""" if not self.enabled or not results: return try: payload = json.dumps([dataclasses.asdict(r) for r in results]) self._redis.setex(key, self._ttl, payload) except Exception as exc: logger.warning("Retrieval cache SET failed for %s: %s", key[:40], exc)