No alerting when an AI provider account runs dry or a model is retired #14

Closed
opened 2026-08-19 22:06:55 -04:00 by logan · 1 comment
Owner

Three separate outages in one night, all with the same shape: a dependency died, the code fell back gracefully, and nothing said so.

  1. corr_cheap_model / corr_smart_model pointed at retired Gemini model IDs. Every correlation call 404'd for an unknown number of days. The LLM tier and the consensus tiebreak were dead in production while correlation was being tuned against rules-only output.
  2. Once the IDs were corrected, every call returned 429 "prepayment credits are depleted".
  3. Transcription has the same structure: transcription.py returns None on any failure and the pipeline carries on, so an unpayable OpenAI account would silently store every call with no transcript.

All three now log once at ERROR naming the fix, which is a floor, not a solution — nobody reads container logs continuously. What is missing is an actual alert (Discord webhook to the operator, or a health endpoint that reports degraded AI tiers) so a dead dependency surfaces without someone happening to grep for it.

The deeper pattern worth fixing: every AI call site fails soft, which is right for a transient blip and wrong for a permanent condition. Those two cases need to be distinguishable everywhere, not just where they have been patched reactively.

Three separate outages in one night, all with the same shape: a dependency died, the code fell back gracefully, and nothing said so. 1. `corr_cheap_model` / `corr_smart_model` pointed at retired Gemini model IDs. Every correlation call 404'd for an unknown number of days. The LLM tier and the consensus tiebreak were dead in production while correlation was being tuned against rules-only output. 2. Once the IDs were corrected, every call returned 429 "prepayment credits are depleted". 3. Transcription has the same structure: `transcription.py` returns `None` on any failure and the pipeline carries on, so an unpayable OpenAI account would silently store every call with no transcript. All three now log once at ERROR naming the fix, which is a floor, not a solution — nobody reads container logs continuously. What is missing is an actual alert (Discord webhook to the operator, or a health endpoint that reports degraded AI tiers) so a dead dependency surfaces without someone happening to grep for it. The deeper pattern worth fixing: every AI call site fails soft, which is right for a transient blip and wrong for a permanent condition. Those two cases need to be distinguishable everywhere, not just where they have been patched reactively.
logan closed this issue 2026-08-20 03:14:40 -04:00
Author
Owner

Implemented in a250c29.

  • New app/internal/ai_health.py: a per-tier degradation registry (transcription, correlation_cheap, correlation_smart, extraction). classify(text) is now the single place that disambiguates a depleted balance from an ordinary rate limit — both arrive as 429 — moved out of llm_correlator.py so it cannot drift into two copies.
  • report_degraded(..., permanent=) alerts immediately for a permanent condition (retired model, dead billing) and only after 5 consecutive failures for a transient one. Once per episode, never per call — this runs at radio-traffic volume.
  • report_healthy(tier) resets and posts a recovery notice, so a tier that comes back does not stay flagged.
  • GET /health/ai exposes the snapshot. Deliberately separate from /health so the CI deploy's curl -f /health liveness check keeps its existing meaning.
  • Alerts POST to ai_alert_webhook_url (new in config.py); empty means silently skip, since not every deployment will set one.
  • transcription.py and llm_correlator.py now report through the registry on both failure and success; the ERROR logs are kept alongside, not replaced.

14 new tests in tests/test_ai_health.py. Suite: 107 passed, 0 failed.

Set AI_ALERT_WEBHOOK_URL on the server — without it this only improves /health/ai, and the whole point of the issue was getting the signal out of the box.

Note the adjacent gap found the same night: deploys were failing silently for two days with nobody notified either (server-26#21). Same shape of problem, different subsystem.

Implemented in `a250c29`. - New `app/internal/ai_health.py`: a per-tier degradation registry (`transcription`, `correlation_cheap`, `correlation_smart`, `extraction`). `classify(text)` is now the single place that disambiguates a depleted balance from an ordinary rate limit — both arrive as 429 — moved out of `llm_correlator.py` so it cannot drift into two copies. - `report_degraded(..., permanent=)` alerts immediately for a permanent condition (retired model, dead billing) and only after 5 consecutive failures for a transient one. Once per episode, never per call — this runs at radio-traffic volume. - `report_healthy(tier)` resets and posts a recovery notice, so a tier that comes back does not stay flagged. - `GET /health/ai` exposes the snapshot. Deliberately separate from `/health` so the CI deploy's `curl -f /health` liveness check keeps its existing meaning. - Alerts POST to `ai_alert_webhook_url` (new in `config.py`); empty means silently skip, since not every deployment will set one. - `transcription.py` and `llm_correlator.py` now report through the registry on both failure and success; the ERROR logs are kept alongside, not replaced. 14 new tests in `tests/test_ai_health.py`. Suite: **107 passed, 0 failed**. **Set `AI_ALERT_WEBHOOK_URL` on the server** — without it this only improves `/health/ai`, and the whole point of the issue was getting the signal out of the box. Note the adjacent gap found the same night: deploys were failing silently for two days with nobody notified either (server-26#21). Same shape of problem, different subsystem.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#14