Files
HealthLog/src/lib/ai/effective-timeout.ts
T
Marc-André Bombeck 556f442724 fix(insights): honor the per-user AI timeout and keep the last good narrative
The daily briefing and the per-metric status assessments ignored the
per-user response-timeout setting: they hardcoded their per-surface
AI_BUDGETS default, so raising the timeout for a slow self-hosted or
local backend had no effect and the surfaces still aborted early. Only
the coach threaded the setting through.

Resolve the effective timeout from User.aiResponseTimeoutSeconds in one
shared helper and thread it onto every non-coach generation call:

- runStatusCompletion is the single chokepoint every status / reference
  surface funnels through (per-metric cards, the batched assessment, the
  derived assessments, the period narratives), so resolving it there
  applies the setting to all of them at once — on both the upstream
  call's own timeout and the outer wall-clock cap that previously clipped
  a raised value at 60 s.
- The comprehensive briefing generator and the user-initiated regenerate
  route thread it onto the main generation, the JSON retry, the grounding
  retry, and the paragraph re-roll.

On a failed generation the briefing keeps its last good text — no cache
row is written, so the previous payload stays intact — and records a
dated, append-only failure marker (no migration; reuses the audit-log
JSON pattern). The read path compares the marker against the last
successful generation so it can tell a held briefing from a current one.
2026-06-28 12:42:35 +02:00

26 lines
1.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Resolve the upstream provider timeout (ms) for a non-chat generation surface.
*
* The per-user `User.aiResponseTimeoutSeconds` setting (Settings → AI) is the
* operator's lever for slow self-hosted / local backends whose first request
* loads the model. Coach already threads it onto its provider call; the
* generated-narrative surfaces (daily briefing, status cards, period
* narratives, …) historically hardcoded their per-surface `AI_BUDGETS.*`
* default and ignored the setting, so raising the timeout had no effect on
* them. This helper is the one place that converts the stored seconds into the
* milliseconds a `CompletionParams.timeoutMs` expects, falling back to the
* surface's budget default when the user has not set a value.
*
* Semantics mirror Coach: a positive stored value wins (seconds → ms), and any
* unset / non-positive value yields the surface default. The setting is bounded
* to 10–600 s at write-time, so no read-time clamp is needed here.
*/
export function resolveEffectiveTimeoutMs(
aiResponseTimeoutSeconds: number | null | undefined,
budgetDefaultMs: number,
): number {
return aiResponseTimeoutSeconds != null && aiResponseTimeoutSeconds > 0
? aiResponseTimeoutSeconds * 1000
: budgetDefaultMs;
}