feat(chat): Implement real-time SSE streaming with reasoning steps and improved UI indicators.

This commit is contained in:
Yunxiao Xu
2026-02-13 00:00:50 -08:00
parent af731413af
commit 339f69a2a3
14 changed files with 777 additions and 17 deletions

View File

@@ -0,0 +1,67 @@
import { describe, it, expect, vi } from "vitest"
import { ChatService, type ChatEvent } from "./chat"
describe("ChatService SSE Parsing", () => {
it("should correctly parse a text stream chunk", () => {
const rawChunk = `data: {"type": "on_chat_model_stream", "name": "summarizer", "data": {"chunk": "Hello"}}\n\n`
const events = ChatService.parseSSEChunk(rawChunk)
expect(events).toHaveLength(1)
expect(events[0]).toEqual({
type: "on_chat_model_stream",
name: "summarizer",
data: { chunk: "Hello" }
})
})
it("should handle multiple events in one chunk", () => {
const rawChunk =
`data: {"type": "on_chat_model_stream", "name": "summarizer", "data": {"chunk": "Hello"}}\n\n` +
`data: {"type": "on_chat_model_stream", "name": "summarizer", "data": {"chunk": " World"}}\n\n`
const events = ChatService.parseSSEChunk(rawChunk)
expect(events).toHaveLength(2)
expect(events[1].data.chunk).toBe(" World")
})
it("should parse encoded plots from executor node", () => {
const rawChunk = `data: {"type": "on_chain_end", "name": "executor", "data": {"encoded_plots": ["base64data"]}}\n\n`
const events = ChatService.parseSSEChunk(rawChunk)
expect(events[0].data.encoded_plots).toEqual(["base64data"])
})
it("should identify the done event", () => {
const rawChunk = `data: {"type": "done"}\n\n`
const events = ChatService.parseSSEChunk(rawChunk)
expect(events[0].type).toBe("done")
})
})
describe("ChatService Message State Management", () => {
it("should append text chunks to the last message content", () => {
const messages = [{ id: "1", role: "assistant", content: "Initial", created_at: new Date().toISOString() }]
const event: ChatEvent = {
type: "on_chat_model_stream",
node: "summarizer",
data: { chunk: { content: " text" } }
}
const updatedMessages = ChatService.updateMessagesWithEvent(messages as any, event)
expect(updatedMessages[0].content).toBe("Initial text")
})
it("should add plots to the message state", () => {
const messages = [{ id: "1", role: "assistant", content: "Analysis", created_at: new Date().toISOString(), plots: [] }]
const event: ChatEvent = {
type: "on_chain_end",
name: "executor",
data: { encoded_plots: ["plot1"] }
}
const updatedMessages = ChatService.updateMessagesWithEvent(messages as any, event)
expect(updatedMessages[0].plots).toEqual(["plot1"])
})
})