feat(quotas): enforce user quotas on chat, agent, scheduled and webhook runs

check_usage now checks the billable user's quota on every request, before
the per-agent 24h limits, which keep applying to traffic through an agent.
Until now a request without an agent key skipped every limit. A refusal is
a 429 with Retry-After and a body naming the budget, usage, limit, the
layer the limit came from and when it resets.

Headless runs check the agent owner's quota before starting. A refused
scheduled run is recorded as budget_exceeded; a refused webhook run returns
a quota_exceeded result instead of raising, so Celery does not retry it.
This commit is contained in:
Alex committed 2026-09-21 12:11:11 +01:00
1 parent 6826313b60
commit 5406550bca
13 files changed
+238 -9

No files matched your search

+7
View File
@@ -2122,6 +2122,7 @@ def agent_webhook_worker(self, agent_id, payload):
try:
# Shared headless path with the scheduler; approval-gated tools auto-deny.
from docsgpt.agents.headless_runner import run_agent_headless
from docsgpt.quotas.service import QuotaExceededError
outcome = run_agent_headless(
agent_config,
@@ -2135,6 +2136,12 @@ def agent_webhook_worker(self, agent_id, payload):
"tool_calls": outcome.get("tool_calls", []),
"thought": outcome.get("thought", ""),
}
except QuotaExceededError as e:
# Returned, not raised: retrying cannot succeed before the quota resets.
logging.warning(
f"Webhook skipped for agent {agent_id}: {e}", extra={"agent_id": agent_id}
)
return {"status": "quota_exceeded", "error": str(e)}
except Exception as e:
logging.error(f"Error running agent logic: {e}", exc_info=True)
raise