mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-13 08:45:24 +00:00
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""
|
|
In-memory TTL cache for RAG responses.
|
|
Keyed on (collection_name, normalised query).
|
|
Only used for stateless queries (no conversation history).
|
|
"""
|
|
import hashlib
|
|
import time
|
|
from typing import Any, Optional
|
|
|
|
_store: dict[str, tuple[Any, float]] = {}
|
|
_index: dict[str, set[str]] = {} # collection_name → set of cache keys
|
|
_MAX_ENTRIES = 500
|
|
TTL = 6 * 3600 # 6 hours
|
|
_set_ctor = set # save builtin before it's shadowed by our module-level `set` function
|
|
|
|
|
|
def _make_key(collection_name: str, query: str) -> str:
|
|
raw = f"{collection_name}::{query.lower().strip()}"
|
|
return hashlib.sha256(raw.encode()).hexdigest()
|
|
|
|
|
|
def get(collection_name: str, query: str) -> Optional[Any]:
|
|
k = _make_key(collection_name, query)
|
|
entry = _store.get(k)
|
|
if entry is None:
|
|
return None
|
|
value, expires_at = entry
|
|
if time.monotonic() > expires_at:
|
|
_store.pop(k, None)
|
|
_index.get(collection_name, _set_ctor()).discard(k)
|
|
return None
|
|
return value
|
|
|
|
|
|
def set(collection_name: str, query: str, value: Any) -> None:
|
|
if len(_store) >= _MAX_ENTRIES:
|
|
now = time.monotonic()
|
|
expired = [k for k, (_, exp) in _store.items() if exp < now]
|
|
for k in expired:
|
|
_store.pop(k, None)
|
|
if len(_store) >= _MAX_ENTRIES:
|
|
oldest = min(_store, key=lambda k: _store[k][1])
|
|
_store.pop(oldest, None)
|
|
k = _make_key(collection_name, query)
|
|
_store[k] = (value, time.monotonic() + TTL)
|
|
_index.setdefault(collection_name, _set_ctor()).add(k)
|
|
|
|
|
|
def invalidate(collection_name: str) -> int:
|
|
"""Drop all cached entries for a collection — call after any KB update."""
|
|
keys = _index.pop(collection_name, _set_ctor())
|
|
for k in keys:
|
|
_store.pop(k, None)
|
|
return len(keys)
|