fix(insights): derive briefing warm budget from the response timeout

The comprehensive briefing warm/cron path wrapped generation in a fixed
130 s outer abort, but the per-user response-timeout setting accepts
10-600 s and the AI settings tell users to raise it for slow local
models. Any account whose response timeout was >= ~130 s had every
comprehensive warm clipped by the warm bound, not the provider, so the
generation never landed: no cached briefing row was written and the
dashboard fell to the hard empty state.

Size the warm outer abort from the resolved effective timeout
(`aiResponseTimeoutSeconds * 1000`, falling back to the comprehensive
surface budget) plus a fixed headroom for feature extraction, prompt
assembly, and the cache write. A raised setting now extends the warm and
nightly budgets instead of being silently capped. An absolute ceiling
(the 600 s write-time maximum plus the headroom) keeps a wedged call from
pinning the worker while staying above the largest value a user can set.
Both the nightly loop and the forced on-visit warm read the per-user
setting and use the derived budget.

Make the empty state more useful: record the upstream HTTP status on a
failure marker and classify it (timeout / auth / rate-limit / provider /
format), so the "couldn't generate" state points at the right lever —
raise the response timeout for a timeout, re-check the provider for an
auth rejection — instead of one generic line. Keep-last-good behaviour is
unchanged.

Align the response-timeout field copy with the actual default budget
(~120 s, not ~180 s) across the placeholder, the OpenAPI description, and
the inline note.
This commit is contained in:
Marc-André Bombeck committed 2026-06-29 15:08:04 +02:00
1 parent 4266203d3a
commit 4ad4707be9
18 files changed
+426 -41

No files matched your search

@@ -7,7 +7,8 @@
* call (`CompletionParams.timeoutMs`). Surfaced here mainly for local /
* self-hosted backends: an MLX/exo server can take >60 s on the first request
* while it loads the model, which the legacy 60 s default timed out before the
* first token landed. Empty = the generous built-in default (~180 s).
* first token landed. Empty = the built-in comprehensive-briefing default
* (~120 s, `AI_BUDGETS.comprehensive.timeoutMs`).
* ──────────────────────────────────────────────────────────────── */
import { useState } from "react";