diff --git a/backend/app/api/endpoints/ai_builder.py b/backend/app/api/endpoints/ai_builder.py index 6ea2c935..5ec8d55a 100644 --- a/backend/app/api/endpoints/ai_builder.py +++ b/backend/app/api/endpoints/ai_builder.py @@ -80,6 +80,7 @@ async def get_quota( account_id=current_user.account_id, db=db, billing_anchor=current_user.ai_billing_cycle_anchor_at, + is_super_admin=current_user.is_super_admin, ) return AIQuotaStatusResponse( **quota_status, @@ -99,12 +100,13 @@ async def start_conversation( """Stage 1: Create a new AI wizard conversation with foundation metadata.""" _require_ai_enabled() - # Check daily quota (anti-abuse) + # Check daily quota (anti-abuse) — super admins bypass allowed, quota_status = await check_ai_quota( user_id=current_user.id, account_id=current_user.account_id, db=db, billing_anchor=current_user.ai_billing_cycle_anchor_at, + is_super_admin=current_user.is_super_admin, ) if not allowed: reset_key = ( diff --git a/backend/app/core/ai_quota_service.py b/backend/app/core/ai_quota_service.py index c4d26b9e..67eed9e5 100644 --- a/backend/app/core/ai_quota_service.py +++ b/backend/app/core/ai_quota_service.py @@ -85,12 +85,14 @@ async def check_ai_quota( account_id: UUID, db: AsyncSession, billing_anchor: Optional[datetime] = None, + is_super_admin: bool = False, ) -> tuple[bool, dict]: """Check if user can make an AI generation. Returns (allowed, quota_status_dict). Monthly counts only rows with counts_toward_quota=True. Daily counts only rows with generation_type in ('scaffold', 'branch_detail'). + Super admins bypass all limits. """ plan = await get_user_plan(account_id, db) monthly_limit, daily_limit = await _get_effective_limits(account_id, plan, db) @@ -120,6 +122,10 @@ async def check_ai_quota( allowed = True deny_reason = None + if is_super_admin: + # Super admins bypass all limits + monthly_limit = None + daily_limit = None if monthly_limit is not None and monthly_count >= monthly_limit: allowed = False deny_reason = "monthly"