fix: defer the jobs settle-sync until the turn is over (coalescing follow-up 2)
With continuation coalescing, a job flips done at CLAIM time — while the learner's own answer turn is still streaming. The poller's settle re-sync fired on the flip, swapping in a mid-turn record snapshot mid-stream; the steer after the assistant message broke the SDK's in-place continuation merge, and the streaming response landed as a SECOND message with the same id — duplicate React keys in the live transcript (the user's live test hit it; the record itself stayed clean). A settle during a busy turn (local stream OR server turnInFlight) now marks a pending sync; the first idle poll tick adopts the finished record. This also fixes the latent multi-job race: steer B settling while steer A's wake turn is in flight would have grabbed the same mid-turn snapshot. Out-of-lane surgical touch (flagged for master review): the wave-1 poller block in components/chat.tsx.
This commit is contained in:
+3
-2
@@ -27,6 +27,7 @@ import assert from "node:assert/strict";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { after, describe, it } from "node:test";
|
||||
import type { UIMessage } from "ai";
|
||||
import { MockLanguageModelV4, simulateReadableStream } from "ai/test";
|
||||
import type { LanguageModelV4StreamPart } from "@ai-sdk/provider";
|
||||
import {
|
||||
@@ -771,8 +772,8 @@ describe("persistTurn id hygiene", () => {
|
||||
// response copy under one id — the record must keep only the merge.
|
||||
const chat = chatStore.createChat("Test: dedupe continuation copies");
|
||||
trackChat(chat.id);
|
||||
const short = { id: "msg_dup_1", role: "assistant", parts: [{ type: "text", text: "step 1" }] };
|
||||
const long = { id: "msg_dup_1", role: "assistant", parts: [{ type: "text", text: "step 1" }, { type: "text", text: "step 2 (continued)" }] };
|
||||
const short: UIMessage = { id: "msg_dup_1", role: "assistant", parts: [{ type: "text", text: "step 1" }] };
|
||||
const long: UIMessage = { id: "msg_dup_1", role: "assistant", parts: [{ type: "text", text: "step 1" }, { type: "text", text: "step 2 (continued)" }] };
|
||||
persistTurn(chatStore, chat.id, [
|
||||
{ id: "u1", role: "user", parts: [{ type: "text", text: "hi" }] },
|
||||
short,
|
||||
|
||||
Reference in New Issue
Block a user