Кончился free-tier провайдера
Когда перебираешь провайдеров на этапе прототипа, у каждого свои триальные
кредиты — и заканчиваются они молча. Внешне это выглядит как «модель вдруг
перестала отвечать»: код падает в insufficient_quota или 402 Payment Required, а вы полчаса думаете, что сломали промпт.
Отделите эту ошибку от обычных сбоев и пришлите push ровно один раз — с именем провайдера, у которого кончилось.
Ловим именно «кончилась квота», а не любую ошибку
Заголовок раздела «Ловим именно «кончилась квота», а не любую ошибку»Важно не путать «кончились кредиты» (лечится пополнением) с 429 (лечится ожиданием). У провайдеров это разные коды/типы — проверяем точно.
import os, requestsimport openai, anthropic
NOTIFLY_URL = os.environ["NOTIFLY_URL"]NOTIFLY_TOKEN = os.environ["NOTIFLY_TOKEN"]
def notify(title, msg, prio): requests.post(f"{NOTIFLY_URL}/message", params={"token": NOTIFLY_TOKEN}, json={"title": title, "message": msg, "priority": prio}, timeout=5)
def _alerted_once(provider): """Флаг на провайдера: алёрт про кончившийся триал шлём один раз.""" flag = f"/tmp/free-tier-{provider}.flag" if os.path.exists(flag): return True open(flag, "w").close() return False
def is_quota_exhausted(exc): """True только для «кончились кредиты/квота», не для rate-limit.""" # OpenAI: 429 c code == "insufficient_quota" (да, тот же статус, что и rate-limit) if isinstance(exc, openai.RateLimitError): code = (getattr(exc, "code", None) or (exc.body or {}).get("code") if getattr(exc, "body", None) else None) return code == "insufficient_quota" # Anthropic и многие OpenAI-совместимые API: 402 Payment Required status = getattr(exc, "status_code", None) return status == 402
def guard(provider, call): """Выполнить вызов; при исчерпании квоты — push один раз и проброс.""" try: return call() except Exception as e: if is_quota_exhausted(e) and not _alerted_once(provider): notify( f"🪙 {provider}: кончился free-tier", f"Ошибка квоты у {provider}. Пополните счёт или переключите провайдера.", priority=7, ) raise
# использованиеoai = openai.OpenAI()resp = guard("openai", lambda: oai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "ping"}]))insufficient_quota у OpenAI приезжает под тем же статусом 429, что и
rate-limit, — поэтому недостаточно смотреть на HTTP-код, нужен именно
code. Flag-файл на провайдера гарантирует, что при пополнении вы не
получите один и тот же алёрт сотню раз подряд.
Полезно при переборе провайдеров
Заголовок раздела «Полезно при переборе провайдеров»Если вы гоняете один и тот же промпт по нескольким API сразу, оберните
каждый своим guard(provider, ...) — и первый же push скажет, у кого
именно кончилось, без раскопок в логах. При пополнении удалите flag-файл
(rm /tmp/free-tier-openai.flag), чтобы алёрт снова стал возможен.
Что положить в текст алёрта
Заголовок раздела «Что положить в текст алёрта»- имя провайдера и модель;
- точный код ошибки (
insufficient_quota/402) — чтобы отличить от 429; - прямую ссылку на страницу billing этого провайдера;
- напоминание удалить flag-файл после пополнения.