fix(insights): stop the daily briefing going blank on slow generations

The comprehensive insight that feeds both the daily briefing and the
insights trend narrative is a single reasoning-heavy generation over the
full feature snapshot. On a large account it runs past the provider
client's fixed 60 s timeout and is aborted mid-stream, so the chain
reports an all-failed outcome, no cached block is written, and the
surface falls back to its static placeholder on every visit. The warm
paths bounded the same generation tighter still (45 s) and counted the
failure with no observable signal, so the blank state was permanent and
silent.

Make the per-request upstream timeout configurable
(CompletionParams.timeoutMs, default 60 s for every existing caller) and
raise it to 120 s for the comprehensive surface only, across all three
generation paths: the on-demand regenerate, the on-demand warm, and the
nightly pre-generation. Raise the warm-pass budget to 130 s so the
provider's own timeout governs a genuine completion rather than clipping
it early. Annotate the comprehensive failure in both the generator and
the nightly loop so a generation that keeps failing is queryable instead
of leaving the surface empty without a trace.
This commit is contained in:
Marc-André Bombeck committed 2026-06-27 12:37:25 +02:00
1 parent 7d2852a312
commit 7e48e2c26d
12 files changed
+352 -16

No files matched your search

+7
View File
@@ -57,6 +57,7 @@ import {
singleUserTurn,
type InsightResult,
} from "@/lib/ai/types";
import { AI_BUDGETS } from "@/lib/ai/ai-budgets";
import { resolveProvider, resolveProviderChain } from "@/lib/ai/provider";
import {
AllProvidersFailedError,
@@ -700,6 +701,10 @@ export const POST = apiHandler((request: NextRequest) =>
user: userPrompt,
temperature: 0.3,
maxTokens: 1500,
// v1.21.5 — wider upstream budget so the reasoning-heavy briefing
// generation is not aborted at the client's 60 s default on large
// accounts (which returned an empty briefing + trend narrative).
timeoutMs: AI_BUDGETS.comprehensive.timeoutMs,
// The reply is parsed with `JSON.parse` below, so opt the OpenAI /
// Codex chains into their strict JSON mode (gated on this flag).
responseFormat: "json",
@@ -836,6 +841,8 @@ export const POST = apiHandler((request: NextRequest) =>
user: `${userPrompt}\n\n${buildBriefingGroundingCorrection(ungrounded)}`,
temperature: 0.3,
maxTokens: 1500,
// v1.21.5 — wider upstream budget; see the first generation call.
timeoutMs: AI_BUDGETS.comprehensive.timeoutMs,
// Parsed with `JSON.parse` below — opt OpenAI / Codex into JSON mode.
responseFormat: "json",
}),