fix(ai): distinguish automatic prompt-cache lifetimes - #835
Draft
Altairpaca wants to merge 3 commits into
Draft
Conversation
Altairpaca
force-pushed
the
fix/831-deepseek-cache-lifetime
branch
from
August 14, 2026 08:13
6a7bb25 to
0b0432a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Direct DeepSeek API sessions are currently treated as if they had a deterministic 5-minute prompt-cache TTL. That propagates into Goal monitor scheduling (a continuation every 270s — 5m minus the default 30s safety buffer, or the 240s fallback) and into TUI copy claiming the wake stays inside a "5m prompt-cache TTL" and keeps tokens warm with estimated savings.
DeepSeek's official context cache is automatic and best-effort: enabled by default for all users, no client-visible TTL contract, and unused entries are cleared only after hours to days (api-docs.deepseek.com/guides/kv_cache). This PR fixes the cache-lifetime classification rather than tuning DeepSeek-specific numbers.
Root cause
resolvePromptCacheTtlSeconds()(packages/ai/src/utils/prompt-cache-ttl.ts) returned 300s for everyopenai-completionsmodel without Anthropic-style cache control — including direct DeepSeek. PR #767 then consumed that budget:resolvePromptCacheSafeWaitSeconds()derived 270s, and the Goal monitor scheduled its continuation there. Returningundefinedalone would not fix it: Goal maps unknown budgets to the legacy 240s fallback, which is still a cache-preservation framing.Behavior before/after
Before (direct DeepSeek, Goal with a live wake source):
300s(fabricated 5m TTL)4m 30s(or the 240s fallback)After:
PromptCacheLifetimesemantic:fixed(ttlSeconds) | automatic | disabled | unknown, resolved byresolvePromptCacheLifetime();resolvePromptCacheTtlSeconds()becomes a backwards-compatible wrapper (public signature unchanged, every non-DeepSeek result identical).automatic— no fixed TTL (300 or 3600) is ever reported, including underPI_CACHE_RETENTION=long.promptCache.goalBackstopMaxSeconds, default 3570s → 59m30s wake) instead of the 270s/240s cache-preservation wakes.cacheLifetime: "automatic", omitttlSeconds/estimatedSavedUsd, and render neutrally: "Continuation expected ready ... - provider caching is automatic; the timed wake only keeps the goal alive." / "~120K tokens cached after the prior turn".Rebuilt on latest main (2026-08-14)
This PR was force-pushed with a fresh implementation based on current
main(0f651bd4a, 387 commits past the previous basec67dd6906), not a mechanical rebase:prompt-cache-ttl.tsonly gainedchatTemplateArgs/supportsThinkingTokenBudgetcompat fields). The discriminated-union design is the minimal shape, and it reuses the existingdetectOpenAICompletionsCompatDeepSeek detection convention (provider === "deepseek" || baseUrl.includes("deepseek.com")).prompt_cache_retention: "24h"behavior withcacheRetention: "long"on DeepSeek is a separate finding, deliberately NOT fixed here (own follow-up).RED -> GREEN
All captures under
local-ignore/qa-evidence/20260814-issue-831-deepseek-cache-lifetime-rebuild/:red-01-ai-prompt-cache-lifetime.txt: 6 failing new cases —resolvePromptCacheLifetime is not a function(API contract absent)red-02-prompt-cache-budget.txt:expected undefined to be 270— DeepSeek had a 270s budgetred-03-issue-831-regression.txt: 4 failing — 240s fallback delay; "~120K tokens kept warm" / "stayed warm" copygreen-01..04: ai lifetime+TTL 38/38, ai adjacent cache suites 58 passed/12 skipped (1 pre-existing live E2E billing failure, see below), goal/budget suites 72/72, cache-keepalive 7/7Verification
npm run check— Biome + pinned-deps + ts-imports + shrinkwrap + install-lock + claude-sdk-platform-lock + tsc + browser-smoke, exit 0npm test— workspace suites pass; the only failure is a pre-existing release-tooling script test (scripts/prepare-senpi-bundled-workspaces.test.mjs"issue 446 ...",npm pack --dry-runoutput parsing on npm 12) that fails identically on pristinemain— reproduced and recordedgit diff --checkclean; packageCHANGELOG.mduntouched (maintainer-owned per CONTRIBUTING.md); requiredchanges.mdentries added (packages/ai/src, coding-agent core, goal)Scope/non-goals
openai-completionslanes stay 300s).cacheRetention: "long",openai-completionsbuildParamsmay sendprompt_cache_keyandprompt_cache_retention: "24h"to DeepSeek (its compat allows long retention). Default short retention sends neither field. This deserves its own focused follow-up.Evidence
local-ignore/qa-evidence/20260814-issue-831-deepseek-cache-lifetime-rebuild/— RED and GREEN vitest captures, npm check/test receipts, senpi-qa channel outputs. No credentials or environment dumps are included.Fixes #831
Maintainer note (CI)
no-changeloglabel or maintainer-added CHANGELOG entries: this PR intentionally does not editCHANGELOG.md(CONTRIBUTING.md: "Do not edit CHANGELOG.md. Changelog entries are added by maintainers."), andchanges.mdentries are provided at the fork contract.