fix: prevent assistant/reasoning accumulator id collisions (#929)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user