chore(llm): regional hygiene — narrative prompt-hash cache + brief retry-budget cap#4911
Conversation
…try-budget cap Two of the #4896 waste patterns: 1. Narrative-before-dedup: generateRegionalNarrative fired its ~900-token call before any content-identity check — byte-identical world state (7 regions x 4 bundle runs/day) regenerated the same narrative every 6h, and same-15min-bucket re-runs burned it just for persistSnapshot's idempotency guard to discard the result. The generator now caches the parsed narrative under a hash of the exact prompt (whose only volatile input is a day-granular date), 24h TTL, injectable for tests, best-effort on Redis failure. Only VALID parsed narratives are cached. 2. Coverage-fail retry storm: the #2989 cooldown bypass re-ran ALL region briefs on every 6h tick for as long as an outage lasted (~28 LLM calls/day, timed exactly to provider incidents) because each failed attempt refreshes seed-meta fetchedAt. A rolling 24h retry budget (INCR+EXPIRE NX, 2 bypasses/window) keeps the fast transient self-heal — first retry on the next tick, one more after — then pauses until the window expires. Fails OPEN on Redis errors so a hiccup can never block self-healing; the scheduled weekly cadence never consults the budget. Refs #4896 Claude-Session: https://claude.ai/code/session_01YTm4GsLG2M7ZuaHF9MpZih
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
| const resp = await fetch(`${url}/get/${encodeURIComponent(key)}`, { | ||
| headers: { Authorization: `Bearer ${token}` }, | ||
| signal: AbortSignal.timeout(3_000), | ||
| }); | ||
| if (!resp.ok) return null; | ||
| const data = await resp.json(); | ||
| return data?.result ? JSON.parse(data.result) : null; | ||
| }, | ||
| async set(key, value, ttlSeconds) { | ||
| const { url, token } = getRedisCredentials(); | ||
| await fetch(url, { | ||
| method: 'POST', | ||
| headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(['SET', key, JSON.stringify(value), 'EX', String(ttlSeconds)]), | ||
| signal: AbortSignal.timeout(3_000), | ||
| }); |
There was a problem hiding this comment.
Missing
User-Agent header in new Redis fetch calls
AGENTS.md mandates "Always include User-Agent header in server-side fetch calls." Both the get (line 475) and set (line 485) methods in defaultNarrativeCache omit it. The same gap appears in consumeBriefRetryBudget in seed-bundle-regional.mjs (line 57). Upstash itself doesn't enforce this, but the convention exists for consistent server-side request attribution — the LLM provider calls in this same file already include the User-Agent via provider.headers(key), so the Redis paths are now inconsistent with the rest of the module.
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…creds are absent getRedisCredentials() exits the process on missing env — the default narrative cache killed the CI unit runner (and any credless local run) the moment generateRegionalNarrative touched it without an injected cache. Read env directly and no-op without creds: no cache, generation proceeds exactly as before the cache existed. Caught by the unit job on this PR; reproduced locally with UPSTASH_* unset. Claude-Session: https://claude.ai/code/session_01YTm4GsLG2M7ZuaHF9MpZih
…ket-brief context quantization, forecast prompt-hash cache, whyMatters v8 cross-read (#4914) (#4916) * chore(llm): cache-identity churn batch 2 — stop paid regeneration of byte-identical prompts (#4914) - summary cache key: dedup identical (headline, body) pairs before the top-5 slice (v6→v7) — the server prompt path dedups AFTER the key was computed, so the same unique story set with a different duplicate composition minted distinct keys for an identical prompt - daily market brief: quantize every float interpolated into the summarizer geoContext (index moves 0.25pp, VIX 1pt, HY 10bps, 2s10s 5bps, yields 0.1pp, sector 0.5pp) — raw 5-min-tick floats became the key's :g segment and defeated the 24h-class TTL per tick and per user - forecast narratives: key the combined/scenario caches on the exact prompt text (the #4911 prompt-hash pattern) instead of raw floats the prompt never renders; TTL 1h→24h (prompt-hash keys self-invalidate, and the old TTL expired between hourly runs by construction) - whyMatters: the cron fallback now reads the analyst endpoint's v8 envelope (same hashBriefStory identity) before paying a direct-Gemini generation; read-only — legacy output stays in the v5 namespace Closes #4914 🤖 Generated with Claude Code Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP * test: update summarize-reasoning CACHE_VERSION pin v6→v7 (missed in the #4914 bump) Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
What ships (#4896 items 1–2)
1. Narrative prompt-hash cache.
generateRegionalNarrativefired its ~900-token LLM call before any content-identity check: byte-identical world state (7 regions × 4seed-bundle-regionalruns/day) regenerated the same narrative every 6h, and same-15-min-bucket re-runs burned the call just forpersistSnapshot's idempotency guard to discard the result. The generator now caches the parsed narrative underintelligence:narrative-cache:v1:<region>:<sha16(prompt)>(24h TTL — the prompt's only volatile input is a day-granular date). Injectableopts.cachefor tests; best-effort on Redis failure; only valid parsed narratives are cached so a garbage response can't be pinned.2. Retry-budget cap on the weekly-brief coverage-fail bypass. The #2989 self-heal bypass re-ran all 7 region briefs on every 6h tick for as long as an OpenRouter outage lasted (~28 LLM calls/day, timed exactly to provider incidents), because each failed attempt refreshes
fetchedAt. A rolling 24h budget (INCR+EXPIRE NX, 2 bypasses/window) keeps the fast transient self-heal — first retry on the next tick, one more after — then pauses until the window expires. Fails open on Redis errors (a hiccup can never block self-healing); the scheduled weekly cadence never consults the budget.Tests
tests/regional-narrative-cache.test.mjs(5): cache hit servesprovider=cachewith the producing model and zero LLM calls; changed content misses; invalid generations never cached; throwing cache never blocks generation;globalregion short-circuits untouched.tests/seed-bundle-regional-briefs-cooldown.test.mjs+4: bypass fires with budget room, suppressed at exhaustion, fails open on pipeline errors, and the weekly cadence never consults the budget. Full regional suites 115/115 ✓, biome ✓.Refs #4896 (items 3–5 dispositioned on the issue)
https://claude.ai/code/session_01YTm4GsLG2M7ZuaHF9MpZih