Skip to content

Commit 1e3dac5

Browse files
authored
Merge pull request #19 from damassi/damassi/fix/persist-aborted-stream
fix: Persist aborted stream output to chat history
2 parents a968e06 + 0b662cf commit 1e3dac5

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

src/__tests__/store.test.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,110 @@ describe("Store", () => {
387387

388388
expect(getState().isProcessing).toBe(false)
389389
})
390+
391+
test("abortRequest should save partially streamed message to chat history", () => {
392+
const { getState, actions } = setup()
393+
394+
const controller = new AbortController()
395+
actions.setAbortController(controller)
396+
actions.setIsProcessing(true)
397+
actions.setCurrentAssistantMessage("This is a partial response...")
398+
399+
actions.abortRequest()
400+
401+
const state = getState()
402+
403+
// Message should be saved to chat history
404+
expect(state.chatHistory).toHaveLength(1)
405+
expect(state.chatHistory[0]).toEqual({
406+
type: "message",
407+
role: "assistant",
408+
content: "This is a partial response...",
409+
})
410+
411+
// Current message should be cleared
412+
expect(state.currentAssistantMessage).toBe("")
413+
414+
// Stats should indicate abort
415+
expect(state.stats).toBe("User aborted the request.")
416+
417+
// Processing should be stopped
418+
expect(state.isProcessing).toBe(false)
419+
})
420+
421+
test("abortRequest should not save empty message to chat history", () => {
422+
const { getState, actions } = setup()
423+
424+
const controller = new AbortController()
425+
actions.setAbortController(controller)
426+
actions.setIsProcessing(true)
427+
actions.setCurrentAssistantMessage("")
428+
429+
actions.abortRequest()
430+
431+
const state = getState()
432+
433+
// No message should be added to history
434+
expect(state.chatHistory).toHaveLength(0)
435+
436+
// Current message should still be empty
437+
expect(state.currentAssistantMessage).toBe("")
438+
439+
// Stats should still indicate abort
440+
expect(state.stats).toBe("User aborted the request.")
441+
})
442+
443+
test("abortRequest should not save whitespace-only message to chat history", () => {
444+
const { getState, actions } = setup()
445+
446+
const controller = new AbortController()
447+
actions.setAbortController(controller)
448+
actions.setIsProcessing(true)
449+
actions.setCurrentAssistantMessage(" \n \t ")
450+
451+
actions.abortRequest()
452+
453+
const state = getState()
454+
455+
// No message should be added to history (whitespace is trimmed)
456+
expect(state.chatHistory).toHaveLength(0)
457+
458+
// Current message should still be empty
459+
expect(state.currentAssistantMessage).toBe("")
460+
})
461+
462+
test("abortRequest should preserve existing chat history when adding partial message", () => {
463+
const { getState, actions } = setup()
464+
465+
// Add existing history
466+
actions.addChatHistoryEntry({
467+
type: "message",
468+
role: "user",
469+
content: "Hello",
470+
})
471+
472+
const controller = new AbortController()
473+
actions.setAbortController(controller)
474+
actions.setIsProcessing(true)
475+
actions.setCurrentAssistantMessage("Partial response")
476+
477+
actions.abortRequest()
478+
479+
const state = getState()
480+
481+
// Should have both messages
482+
expect(state.chatHistory).toHaveLength(2)
483+
expect(state.chatHistory[0]).toEqual({
484+
type: "message",
485+
role: "user",
486+
content: "Hello",
487+
})
488+
expect(state.chatHistory[1]).toEqual({
489+
type: "message",
490+
role: "assistant",
491+
content: "Partial response",
492+
})
493+
})
390494
})
391495

392496
describe("reset action", () => {

src/store.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,18 @@ export const AgentStore = createContextStore<StoreModel>({
143143
abortRequest: action((state) => {
144144
state.abortController?.abort()
145145
state.abortController = undefined
146+
147+
if (state.currentAssistantMessage.trim()) {
148+
// Persist what has been generated so far
149+
state.chatHistory.push({
150+
type: "message",
151+
role: "assistant",
152+
content: state.currentAssistantMessage,
153+
})
154+
}
155+
146156
state.currentAssistantMessage = ""
157+
147158
state.stats = "User aborted the request."
148159
state.isProcessing = false
149160
}),

0 commit comments

Comments
 (0)