prompt-engine / backend /core /usage.py
siddhm11
Deploy the audit remediation backend
bd719d5
Raw History Blame Contribute Delete
2.28 kB
"""
Daily-usage accounting that survives a database outage.
check_daily_limit() used to initialise `count = 0`, run a Mongo
count_documents(), and swallow any exception. A read failure therefore
returned "0 used" and every caller got an unlimited allowance — and because
the shared Groq key is ~100 enhancements/day for the whole user base, a single
Atlas free-tier pause could drain the org quota in minutes and surface to
everyone as a total outage.
Failing closed instead is not an improvement on its own: it turns a transient
blip into "nobody can use the product". So Mongo stays authoritative when it
answers, and this module keeps a per-process shadow tally to fall back on.
Scope and limits, stated plainly: the tally lives in this process's memory. The
Space runs a single uvicorn worker with no --workers flag, so today that is the
whole picture. It resets on restart, which is why a degraded read also clamps
to DEGRADED_LIMIT rather than trusting a tally that may have just been born.
Move this to an atomic Mongo $inc on a per-day document before running more
than one worker.
"""
import threading
from datetime import date
_lock = threading.Lock()
_counts: dict = {} # user_id -> [iso_date, count]
# Ceiling applied when the datastore is unreachable and the shadow tally cannot
# be trusted to be complete (e.g. the process restarted mid-outage).
DEGRADED_LIMIT = 3
def _today() -> str:
return date.today().isoformat()
def record(user_id: str) -> None:
"""Count one billable enhancement for today."""
if not user_id:
return
today = _today()
with _lock:
entry = _counts.get(user_id)
if entry is None or entry[0] != today:
_counts[user_id] = [today, 1]
else:
entry[1] += 1
def get(user_id: str) -> int:
"""Enhancements this process has seen from this user today."""
with _lock:
entry = _counts.get(user_id)
if entry is None or entry[0] != _today():
return 0
return entry[1]
def prune() -> None:
"""Drop entries from previous days. Cheap; safe to call opportunistically."""
today = _today()
with _lock:
for uid in [u for u, e in _counts.items() if e[0] != today]:
_counts.pop(uid, None)