refactor: streamline TUI stream assembly updates

This commit is contained in:
Peter Steinberger
2026-01-20 08:45:07 +00:00
parent c17c7b4e24
commit 32a668e4d9
3 changed files with 48 additions and 20 deletions

View File

@@ -87,4 +87,14 @@ describe("extractContentFromMessage", () => {
expect(text).toBe("hello");
});
it("renders error text when stopReason is error and content is not an array", () => {
const text = extractContentFromMessage({
role: "assistant",
stopReason: "error",
errorMessage: '429 {"error":{"message":"rate limit"}}',
});
expect(text).toContain("HTTP 429");
});
});

View File

@@ -65,4 +65,29 @@ describe("TuiStreamAssembler", () => {
expect(finalText).toBe("Streamed");
});
it("returns null when delta text is unchanged", () => {
const assembler = new TuiStreamAssembler();
const first = assembler.ingestDelta(
"run-4",
{
role: "assistant",
content: [{ type: "text", text: "Repeat" }],
},
false,
);
expect(first).toBe("Repeat");
const second = assembler.ingestDelta(
"run-4",
{
role: "assistant",
content: [{ type: "text", text: "Repeat" }],
},
false,
);
expect(second).toBeNull();
});
});

View File

@@ -27,10 +27,9 @@ export class TuiStreamAssembler {
return state;
}
ingestDelta(runId: string, message: unknown, showThinking: boolean): string | null {
private updateRunState(state: RunStreamState, message: unknown, showThinking: boolean) {
const thinkingText = extractThinkingFromMessage(message);
const contentText = extractContentFromMessage(message);
const state = this.getOrCreateRun(runId);
if (thinkingText) {
state.thinkingText = thinkingText;
@@ -45,29 +44,23 @@ export class TuiStreamAssembler {
showThinking,
});
if (!displayText || displayText === state.displayText) return null;
state.displayText = displayText;
return displayText;
}
ingestDelta(runId: string, message: unknown, showThinking: boolean): string | null {
const state = this.getOrCreateRun(runId);
const previousDisplayText = state.displayText;
this.updateRunState(state, message, showThinking);
if (!state.displayText || state.displayText === previousDisplayText) return null;
return state.displayText;
}
finalize(runId: string, message: unknown, showThinking: boolean): string {
const state = this.getOrCreateRun(runId);
const thinkingText = extractThinkingFromMessage(message);
const contentText = extractContentFromMessage(message);
if (thinkingText) {
state.thinkingText = thinkingText;
}
if (contentText) {
state.contentText = contentText;
}
const finalComposed = composeThinkingAndContent({
thinkingText: state.thinkingText,
contentText: state.contentText,
showThinking,
});
this.updateRunState(state, message, showThinking);
const finalComposed = state.displayText;
const finalText = resolveFinalAssistantText({
finalText: finalComposed,
streamedText: state.displayText,