From 761953b50edecbb25d616dfdd41642a4924b029e Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Sun, 22 Feb 2026 13:35:45 +0000 Subject: [PATCH] feat(chat): persist model per thread and auto-load on thread switch --- .../frontend/src/features/chat/chat-page.tsx | 37 ++++++++++++++++++- studio/frontend/src/features/chat/db.ts | 14 +++++++ .../src/features/chat/runtime-provider.tsx | 3 ++ studio/frontend/src/features/chat/types.ts | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/studio/frontend/src/features/chat/chat-page.tsx b/studio/frontend/src/features/chat/chat-page.tsx index 986a3ee642..c363704d61 100644 --- a/studio/frontend/src/features/chat/chat-page.tsx +++ b/studio/frontend/src/features/chat/chat-page.tsx @@ -358,6 +358,41 @@ export function ChatPage(): ReactElement { setViewBeforeCompare(null); }, [viewBeforeCompare]); + const handleThreadSelect = useCallback( + (nextView: ChatView) => { + setView(nextView); + + const threadId = + nextView.mode === "single" ? nextView.threadId : undefined; + const pairId = + nextView.mode === "compare" ? nextView.pairId : undefined; + + void (async () => { + let thread: import("./types").ThreadRecord | undefined; + if (threadId) { + thread = await db.threads.get(threadId); + } else if (pairId) { + thread = await db.threads + .where("pairId") + .equals(pairId) + .first(); + } + const threadModelId = thread?.modelId; + if (!threadModelId) return; + + const currentCheckpoint = + useChatRuntimeStore.getState().params.checkpoint; + if (threadModelId === currentCheckpoint) return; + + if (currentCheckpoint) { + await ejectModel(); + } + await selectModel({ id: threadModelId }); + })(); + }, + [ejectModel, selectModel], + ); + const models = useMemo( () => modelsFromStore.map((model) => ({ @@ -500,7 +535,7 @@ export function ChatPage(): ReactElement { tx.table("messages").clear()); +db.version(3) + .stores({ + threads: "id, modelType, pairId, archived, createdAt", + messages: "id, threadId, createdAt", + }) + .upgrade((tx) => + tx + .table("threads") + .toCollection() + .modify((thread) => { + if (!thread.modelId) thread.modelId = ""; + }), + ); + export { db }; export function useLiveQuery( diff --git a/studio/frontend/src/features/chat/runtime-provider.tsx b/studio/frontend/src/features/chat/runtime-provider.tsx index 9f99d309c7..ce553dcdbf 100644 --- a/studio/frontend/src/features/chat/runtime-provider.tsx +++ b/studio/frontend/src/features/chat/runtime-provider.tsx @@ -323,10 +323,13 @@ function createDexieAdapter( }, async initialize(threadId: string) { + const currentModelId = + useChatRuntimeStore.getState().params.checkpoint ?? ""; await db.threads.add({ id: threadId, title: "New Chat", modelType, + modelId: currentModelId, pairId, archived: false, createdAt: Date.now(), diff --git a/studio/frontend/src/features/chat/types.ts b/studio/frontend/src/features/chat/types.ts index 01fa4fe200..a60bcbe657 100644 --- a/studio/frontend/src/features/chat/types.ts +++ b/studio/frontend/src/features/chat/types.ts @@ -8,6 +8,7 @@ export interface ThreadRecord { id: string; title: string; modelType: ModelType; + modelId?: string; pairId?: string; archived: boolean; createdAt: number;