summaryrefslogtreecommitdiffhomepage
path: root/packages/frontend/src/lib
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-03 14:49:04 +0900
committerAdam Malczewski <[email protected]>2026-06-03 14:49:04 +0900
commit656aad2752991ff32e98fed270fa330355650c17 (patch)
treeef6ca33b8bf988f6790c19eb021432c34e7ae798 /packages/frontend/src/lib
parente87e6b39285c8001045d1ebdac873b182c0f7868 (diff)
downloaddispatch-656aad2752991ff32e98fed270fa330355650c17.tar.gz
dispatch-656aad2752991ff32e98fed270fa330355650c17.zip
fix: warm the SAME Anthropic message-cache bucket as real turns
Root cause of the 'first warmup misses' + 'switch to chat misses' bugs: Anthropic keys the MESSAGE-level prompt cache on `tool_choice` AND the extended-thinking parameters (both rows in their cache-invalidation table mark the messages cache as invalidated on change). The original warmCache() sent toolChoice:'none' and NO thinking providerOptions, while real turns send toolChoice:'auto' + thinking config for the effort. So warming and chat wrote TWO different message-cache buckets: - warmup #1 missed (no warm-only bucket existed yet), every later warmup hit its own bucket; - the next real chat message read the OTHER bucket → miss. Fix: extract a shared buildStreamOptions() that produces the cache-affecting params (toolChoice + thinking providerOptions + maxOutputTokens). Both run() and warmCache() now call it with the SAME resolved reasoning effort, so the warming replay refreshes the exact cache the next real message reads. The trivial probe turn is still appended AFTER the last cache breakpoint, so it never disturbs the cached prefix. Threaded the per-tab reasoning effort (per-model -> per-tab selector -> default, mirroring processMessage) from the frontend resolver through POST /chat/warm to warmCacheForTab to warmCache. Tests: updated the warmCache toolChoice test to assert it MATCHES a real turn, added an invariant test driving run() and warmCache() and asserting identical cache-affecting params, and assert effort forwarding in the frontend store. check / test (780) / frontend build / typecheck all green.
Diffstat (limited to 'packages/frontend/src/lib')
-rw-r--r--packages/frontend/src/lib/cache-warming.svelte.ts7
-rw-r--r--packages/frontend/src/lib/tabs.svelte.ts7
2 files changed, 13 insertions, 1 deletions
diff --git a/packages/frontend/src/lib/cache-warming.svelte.ts b/packages/frontend/src/lib/cache-warming.svelte.ts
index cda3fd1..0253c08 100644
--- a/packages/frontend/src/lib/cache-warming.svelte.ts
+++ b/packages/frontend/src/lib/cache-warming.svelte.ts
@@ -41,6 +41,12 @@ export interface WarmRequestParams {
keyId: string | null;
modelId: string | null;
agentModels: AgentModelEntry[] | null;
+ /**
+ * The SAME reasoning effort the next real turn would use. It drives the
+ * Anthropic thinking providerOptions, which is a message-cache key — warming
+ * must match it so it refreshes the bucket the real message reads.
+ */
+ reasoningEffort: string | null;
}
/** Reactive, per-tab warming UI state (read by the Chat Settings debug strip). */
@@ -177,6 +183,7 @@ export function createCacheWarmingStore() {
...(params?.keyId ? { keyId: params.keyId } : {}),
...(params?.modelId ? { modelId: params.modelId } : {}),
...(params?.agentModels ? { agentModels: params.agentModels } : {}),
+ ...(params?.reasoningEffort ? { reasoningEffort: params.reasoningEffort } : {}),
}),
});
// A newer cancel/fire superseded this request — drop its result so it
diff --git a/packages/frontend/src/lib/tabs.svelte.ts b/packages/frontend/src/lib/tabs.svelte.ts
index a0125ef..ca04e62 100644
--- a/packages/frontend/src/lib/tabs.svelte.ts
+++ b/packages/frontend/src/lib/tabs.svelte.ts
@@ -251,7 +251,12 @@ export function createTabStore() {
cacheWarming.setRequestResolver((tabId) => {
const t = getTabById(tabId);
if (!t) return null;
- return { keyId: t.keyId, modelId: t.modelId, agentModels: t.agentModels };
+ return {
+ keyId: t.keyId,
+ modelId: t.modelId,
+ agentModels: t.agentModels,
+ reasoningEffort: t.reasoningEffort,
+ };
});
$effect.root(() => {