"""Allowlisted, plain-text failures. Never return a raw model exception or HTML.""" import re import time from dataclasses import dataclass from fastapi import HTTPException @dataclass(frozen=True) class Failure: code: str message: str retry_at: float | None = None def generation_failure(error, phase): title = getattr(error, 'title', '') if phase == 'inference' and title == 'ZeroGPU quota exceeded': text = str(getattr(error, 'message', ''))[:4096] message = 'Hugging Face could not allocate a GPU because your ZeroGPU quota is currently insufficient.' quota = re.search(r'\((\d{1,5})s requested vs\. (\d{1,5}(?:\.\d{1,3})?)s left\)', text) if quota: message += f' This run requests {quota[1]} seconds; {quota[2]} seconds remain.' wait = re.search(r'Try again in (?:(\d{1,2}) days?, )?(\d{1,3}):(\d{2}):(\d{2})(?:\.\d+)?\.', text) retry_at = None if wait: seconds = int(wait[1] or 0)*86400 + int(wait[2])*3600 + int(wait[3])*60 + int(wait[4]) if 0 < seconds <= 7*86400: retry_at = time.time()+seconds message += f' Try again in about {max(1, (seconds+59)//60)} minutes.' message += ' Your input and settings are saved. Open this app on Hugging Face and sign in to use your account quota.' return Failure('gpu_quota', message, retry_at) if phase == 'inference' and title in ('ZeroGPU queue timeout', 'ZeroGPU client error', 'ZeroGPU pending credits exceeded'): return Failure('gpu_unavailable', 'A GPU is not available right now. Your input and settings are saved; try again shortly.') if phase == 'inference' and title == 'ZeroGPU illegal duration': return Failure('gpu_duration', 'This run exceeds the GPU duration allowed by your Hugging Face quota. Your input and settings are saved.') if isinstance(error, HTTPException) and error.status_code == 401: return Failure('session_expired', 'Your Hugging Face session has expired. Sign in again, then resume your saved input and settings.') if phase == 'session': return Failure('session_unavailable', 'Hugging Face could not verify your session. Your input and settings are saved; try again shortly.') return Failure('generation_failed', 'Generation could not be completed. Your input and settings are saved; try again.')