"""Per-user usage metering with configurable caps and the at-cap policy. Skill requirements met here: metering enforced per-user and accurately; the cap is the plan's effective cap (base × multiplier); the at-cap behavior is the plan's configurable CapPolicy, not a hardcoded branch. """ from __future__ import annotations from backend.billing import store from backend.billing.plans import CapPolicy, Feature, Plan from backend.billing.subscription import effective_plan # Reset window for the model-call meter. Monthly-ish; a config value, not magic. MODEL_CALL_WINDOW_SECONDS = 30 * 24 * 3600 MODEL_CALL_METER = "model_calls" def usage_status(user_id: str) -> dict: plan = effective_plan(user_id) cap = plan.effective_model_cap used, window_start = store.get_usage(user_id, MODEL_CALL_METER, MODEL_CALL_WINDOW_SECONDS) remaining = None if cap is None else max(0, cap - used) return { "plan_id": plan.id, "meter": MODEL_CALL_METER, "used": used, "cap": cap, # None = unmetered "remaining": remaining, "cap_policy": plan.cap_policy.value, "window_start": window_start, "window_seconds": MODEL_CALL_WINDOW_SECONDS, } def check_and_consume(user_id: str, feature: Feature = Feature.AR_MODEL_GEN) -> dict: """Call BEFORE doing metered work. Returns {allowed, reason, status}. On allowed=True it has already incremented the counter (reserve-then-do). Unmetered plans (cap None) always allow.""" plan = effective_plan(user_id) if not plan.grants(feature): return {"allowed": False, "reason": "feature_not_in_plan", "status": usage_status(user_id)} cap = plan.effective_model_cap if cap is None: return {"allowed": True, "reason": "unmetered", "status": usage_status(user_id)} used, _ = store.get_usage(user_id, MODEL_CALL_METER, MODEL_CALL_WINDOW_SECONDS) if used >= cap: return {"allowed": False, "reason": f"cap_reached:{plan.cap_policy.value}", "status": usage_status(user_id)} store.increment_usage(user_id, MODEL_CALL_METER, MODEL_CALL_WINDOW_SECONDS, 1) return {"allowed": True, "reason": "ok", "status": usage_status(user_id)}