fix: preserve assistant partial stream during reasoning

This commit is contained in:
Ayaan Zaidi
2026-02-19 10:45:06 +05:30
committed by Vignesh
parent 2cbf15eb66
commit 221d50bc18
4 changed files with 105 additions and 18 deletions

View File

@@ -82,6 +82,30 @@ export function handleMessageUpdate(
: undefined;
const evtType = typeof assistantRecord?.type === "string" ? assistantRecord.type : "";
if (evtType === "thinking_start" || evtType === "thinking_delta" || evtType === "thinking_end") {
const thinkingDelta = typeof assistantRecord?.delta === "string" ? assistantRecord.delta : "";
const thinkingContent =
typeof assistantRecord?.content === "string" ? assistantRecord.content : "";
appendRawStream({
ts: Date.now(),
event: "assistant_thinking_stream",
runId: ctx.params.runId,
sessionId: (ctx.params.session as { id?: string }).id,
evtType,
delta: thinkingDelta,
content: thinkingContent,
});
if (ctx.state.streamReasoning) {
// Prefer full partial-message thinking when available; fall back to event payloads.
const partialThinking = extractAssistantThinking(msg);
ctx.emitReasoningStream(partialThinking || thinkingContent || thinkingDelta);
}
if (evtType === "thinking_end") {
void ctx.params.onReasoningEnd?.();
}
return;
}
if (evtType !== "text_delta" && evtType !== "text_start" && evtType !== "text_end") {
return;
}

View File

@@ -201,6 +201,56 @@ describe("subscribeEmbeddedPiSession", () => {
},
);
it("streams native thinking_delta events and signals reasoning end", () => {
let handler: ((evt: unknown) => void) | undefined;
const session: StubSession = {
subscribe: (fn) => {
handler = fn;
return () => {};
},
};
const onReasoningStream = vi.fn();
const onReasoningEnd = vi.fn();
subscribeEmbeddedPiSession({
session: session as unknown as Parameters<typeof subscribeEmbeddedPiSession>[0]["session"],
runId: "run",
reasoningMode: "stream",
onReasoningStream,
onReasoningEnd,
});
handler?.({
type: "message_update",
message: {
role: "assistant",
content: [{ type: "thinking", thinking: "Checking files" }],
},
assistantMessageEvent: {
type: "thinking_delta",
delta: "Checking files",
},
});
handler?.({
type: "message_update",
message: {
role: "assistant",
content: [{ type: "thinking", thinking: "Checking files done" }],
},
assistantMessageEvent: {
type: "thinking_end",
},
});
const streamTexts = onReasoningStream.mock.calls
.map((call) => call[0]?.text)
.filter((value): value is string => typeof value === "string");
expect(streamTexts.at(-1)).toBe("Reasoning:\n_Checking files done_");
expect(onReasoningEnd).toHaveBeenCalledTimes(1);
});
it("emits delta chunks in agent events for streaming assistant text", () => {
const { emit, onAgentEvent } = createAgentEventHarness();