diff --git a/src/lib/ai/ai-budgets.ts b/src/lib/ai/ai-budgets.ts index d97544066..6735cff37 100644 --- a/src/lib/ai/ai-budgets.ts +++ b/src/lib/ai/ai-budgets.ts @@ -61,8 +61,20 @@ export const AI_BUDGETS = { * generation runs off the request hot path (nightly warm + the on-demand * background warm), and the explicit regenerate is rate-limited, so the * wider ceiling is bounded. + * + * v1.25.12 — raised again to 180 s. The same "operation aborted due to + * timeout" hop (no upstream status) recurred at exactly the 120 s ceiling + * once the codex default slug moved to the heavier-reasoning `gpt-5.5` + * line: a wide account (full-history briefing, raw signals included) spends + * longer in the reasoning channel before the first visible token than the + * 120 s budget allowed, so the single-provider chain timed out with nothing + * to fall back to and the briefing rendered its greeting with no body. The + * per-user `aiResponseTimeoutSeconds` setting still overrides this default + * for operators on even slower backends; this only lifts the floor for + * accounts that never set it. The worker cap in `insight-pregenerate.ts` + * derives from this value plus fixed headroom, so it tracks automatically. */ - comprehensive: { temperature: 0.3, maxTokens: 1500, timeoutMs: 120_000 }, + comprehensive: { temperature: 0.3, maxTokens: 1500, timeoutMs: 180_000 }, /** * Per-metric status assessment cards — output is a single diff --git a/src/lib/jobs/__tests__/insight-pregenerate.test.ts b/src/lib/jobs/__tests__/insight-pregenerate.test.ts index 5dea6830d..219204e0b 100644 --- a/src/lib/jobs/__tests__/insight-pregenerate.test.ts +++ b/src/lib/jobs/__tests__/insight-pregenerate.test.ts @@ -304,10 +304,10 @@ describe("runInsightPregenerate — outcome tally", () => { observedSignal = opts.signal; setTimeout( () => resolve({ status: "generated", providerType: "x" }), - // v1.25.3 — above the default warm budget (~150 s = the 120 s + // v1.25.12 — above the default warm budget (~210 s = the 180 s // comprehensive provider budget + headroom) so the bounded budget // fires before this "slow" generation settles. - 200_000, + 260_000, ); }), ); @@ -323,7 +323,7 @@ describe("runInsightPregenerate — outcome tally", () => { statusGenerators, warmGenericMetrics, }); - await vi.advanceTimersByTimeAsync(160_000); + await vi.advanceTimersByTimeAsync(230_000); const result = await promise; // The timeout fired, aborted the still-running generation (so its @@ -798,10 +798,10 @@ describe("forceWarmUser — on-demand single-user warm (v1.8.7.1)", () => { // advance past it so the warm continues. setTimeout( () => resolve({ status: "generated", providerType: "x" }), - // v1.25.3 — above the default warm budget (~150 s = the 120 s + // v1.25.12 — above the default warm budget (~210 s = the 180 s // comprehensive provider budget + headroom) so the bounded budget // fires before this "slow" generation settles. - 200_000, + 260_000, ); }), ); @@ -818,7 +818,7 @@ describe("forceWarmUser — on-demand single-user warm (v1.8.7.1)", () => { warmGenericMetrics, }); // Advance past the comprehensive budget so withTimeout fires. - await vi.advanceTimersByTimeAsync(160_000); + await vi.advanceTimersByTimeAsync(230_000); const result = await promise; for (const g of statusGenerators) { @@ -916,10 +916,10 @@ describe("forceWarmUser — on-demand single-user warm (v1.8.7.1)", () => { observedSignal = opts.signal; setTimeout( () => resolve({ status: "generated", providerType: "x" }), - // v1.25.3 — above the default warm budget (~150 s = the 120 s + // v1.25.12 — above the default warm budget (~210 s = the 180 s // comprehensive provider budget + headroom) so the bounded budget // fires before this "slow" generation settles. - 200_000, + 260_000, ); }), ); @@ -935,7 +935,7 @@ describe("forceWarmUser — on-demand single-user warm (v1.8.7.1)", () => { statusGenerators, warmGenericMetrics, }); - await vi.advanceTimersByTimeAsync(160_000); + await vi.advanceTimersByTimeAsync(230_000); const result = await promise; // The timeout fired and aborted the still-running generation.