From 20e66adae39c86bcbcc10679250dd9c22905a03f Mon Sep 17 00:00:00 2001 From: Roland Tannous <115670425+rolandtannous@users.noreply.github.com> Date: Mon, 18 May 2026 00:42:30 +0400 Subject: [PATCH] studio/chat: persist Anthropic container id on first turn of new thread (#5526) * studio/chat: persist Anthropic container id on first turn of new thread The container_ready SSE handler updated thread record via db.threads.update, which silently affects 0 rows when the Dexie row hasn't been inserted yet. On the first turn of a brand-new thread the SSE event can arrive before assistant-ui persists the row, so the new container id was dropped, and the next turn re-read null and let Anthropic auto-create a fresh container instead of reusing the one from turn 1. Call ensureThreadRecord first so the row exists before the update lands. * studio/chat: temp diag log for container persistence races * studio/chat: drop temp diag logging * studio/chat: retry container persist instead of forcing thread row Drop ensureThreadRecord + circular runtime-provider import. Retry the update for up to 500ms so assistant-ui's DexieAdapter.initialize wins the race and creates the thread row with the right modelType (base / lora / model1 / model2), then our update lands on a subsequent attempt. Addresses gemini-code-assist review on #5526. --- .../src/features/chat/api/chat-adapter.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/studio/frontend/src/features/chat/api/chat-adapter.ts b/studio/frontend/src/features/chat/api/chat-adapter.ts index 427f12c074..aeca357f1c 100644 --- a/studio/frontend/src/features/chat/api/chat-adapter.ts +++ b/studio/frontend/src/features/chat/api/chat-adapter.ts @@ -1280,11 +1280,29 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter { externalProvider?.providerType === "anthropic" ? "anthropicCodeExecContainerId" : "openaiCodeExecContainerId"; - void db.threads - .update(resolvedThreadId, { - [field]: newContainerId, - }) - .catch(() => {}); + // On the first turn of a brand-new thread the row + // may not be in Dexie yet when this SSE event + // fires — db.threads.update silently affects 0 + // rows, the next turn re-reads null, and Anthropic + // auto-creates a fresh container. Retry briefly so + // assistant-ui's own DexieAdapter.initialize lands + // the row first (with the correct modelType for + // base / lora / compare contexts) and our update + // sticks on a subsequent attempt. + try { + for (let attempt = 0; attempt < 10; attempt++) { + const affected = await db.threads.update( + resolvedThreadId, + { [field]: newContainerId }, + ); + if (affected > 0) break; + await new Promise((resolve) => + setTimeout(resolve, 50), + ); + } + } catch { + /* best-effort: container reuse is an optimization */ + } } continue; }