fix: prevent assistant/reasoning accumulator id collisions (#929)

This commit is contained in:
Charles Packer
2026-02-12 11:13:42 -08:00
committed by GitHub
parent 82fd71bdf0
commit 0ada2db7d7
2 changed files with 55 additions and 2 deletions

View File

@@ -451,6 +451,18 @@ function extractTextPart(v: unknown): string {
return "";
}
function resolveLineIdForKind(
b: Buffers,
canonicalId: string,
kind: "assistant" | "reasoning",
): string {
const existing = b.byId.get(canonicalId);
if (!existing || existing.kind === kind) return canonicalId;
// Avoid cross-kind collisions when providers reuse the same id/otid.
return `${kind}:${canonicalId}`;
}
function resolveAssistantLineId(
b: Buffers,
chunk: LettaStreamingResponse & { id?: string; otid?: string },
@@ -499,7 +511,13 @@ function resolveAssistantLineId(
b.assistantCanonicalByOtid.set(otid, canonical);
}
return canonical;
const lineId = resolveLineIdForKind(b, canonical, "assistant");
if (lineId !== canonical) {
if (messageId) b.assistantCanonicalByMessageId.set(messageId, lineId);
if (otid) b.assistantCanonicalByOtid.set(otid, lineId);
}
return lineId;
}
function resolveReasoningLineId(
@@ -549,7 +567,13 @@ function resolveReasoningLineId(
b.reasoningCanonicalByOtid.set(otid, canonical);
}
return canonical;
const lineId = resolveLineIdForKind(b, canonical, "reasoning");
if (lineId !== canonical) {
if (messageId) b.reasoningCanonicalByMessageId.set(messageId, lineId);
if (otid) b.reasoningCanonicalByOtid.set(otid, lineId);
}
return lineId;
}
/**

View File

@@ -237,4 +237,33 @@ describe("accumulator usage statistics", () => {
expect(line && "text" in line ? line.text : "").toBe("Think through it");
expect(buffers.byId.get("reasoning-msg-2")).toBeUndefined();
});
test("separates reasoning and assistant lines when ids overlap", () => {
const buffers = createBuffers();
onChunk(buffers, {
message_type: "reasoning_message",
id: "shared-stream-id",
reasoning: "Thinking... ",
} as unknown as LettaStreamingResponse);
onChunk(buffers, {
message_type: "assistant_message",
id: "shared-stream-id",
content: [{ type: "text", text: "Final answer" }],
} as unknown as LettaStreamingResponse);
const reasoning = buffers.byId.get("shared-stream-id");
const assistant = buffers.byId.get("assistant:shared-stream-id");
expect(reasoning?.kind).toBe("reasoning");
expect(reasoning && "text" in reasoning ? reasoning.text : "").toBe(
"Thinking... ",
);
expect(assistant?.kind).toBe("assistant");
expect(assistant && "text" in assistant ? assistant.text : "").toBe(
"Final answer",
);
});
});