Files
2026-04-27 00:37:51 -04:00

63 lines
1.9 KiB
Python

"""
Global AI feature flags stored in Firestore at config/ai_features.
Defaults to all-on when the document does not exist yet. Uses a short
in-memory TTL cache so flag reads don't add a Firestore round-trip to every
call upload.
"""
import time
from typing import Any
from app.internal.logger import logger
from app.internal import firestore as fstore
_COLLECTION = "config"
_DOC_ID = "ai_features"
_TTL = 30.0 # seconds before re-reading from Firestore
_DEFAULTS: dict[str, bool] = {
"stt_enabled": True,
"correlation_enabled": True,
"summaries_enabled": True,
"vocabulary_learning_enabled": True,
}
_cache: dict[str, Any] = {}
_cache_ts: float = 0.0
async def get_flags() -> dict[str, bool]:
"""Return the current feature flags, using the TTL cache when fresh."""
global _cache, _cache_ts
now = time.monotonic()
if _cache and (now - _cache_ts) < _TTL:
return dict(_cache)
try:
doc = await fstore.doc_get(_COLLECTION, _DOC_ID)
if doc:
merged = {**_DEFAULTS, **{k: bool(v) for k, v in doc.items() if k in _DEFAULTS}}
else:
merged = dict(_DEFAULTS)
except Exception as e:
logger.warning(f"Feature flags: could not read from Firestore ({e}), using defaults")
merged = dict(_DEFAULTS)
_cache = merged
_cache_ts = now
return dict(_cache)
async def set_flags(updates: dict[str, bool]) -> dict[str, bool]:
"""Write flag updates to Firestore and invalidate the cache."""
global _cache, _cache_ts
clean = {k: bool(v) for k, v in updates.items() if k in _DEFAULTS}
if not clean:
raise ValueError(f"No recognised flag keys in update: {list(updates)}")
await fstore.doc_set(_COLLECTION, _DOC_ID, clean)
_cache_ts = 0.0 # force re-read on next get_flags()
logger.info(f"Feature flags updated: {clean}")
return await get_flags()