fix: prevent post-cancel chunks from rendering after ESC interrupt (#416)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-29 13:57:41 -08:00
committed by GitHub
parent 3e4212d5f2
commit 1dc703e532
3 changed files with 17 additions and 2 deletions

View File

@@ -1095,8 +1095,13 @@ export default function App({
sessionStatsRef.current.endTurn(apiDurationMs);
sessionStatsRef.current.updateUsageFromBuffers(buffersRef.current);
// Immediate refresh after stream completes to show final state
refreshDerived();
const wasInterrupted = !!buffersRef.current.interrupted;
// Immediate refresh after stream completes to show final state unless
// the user already cancelled (handleInterrupt rendered the UI).
if (!wasInterrupted) {
refreshDerived();
}
// Case 1: Turn ended normally
if (stopReason === "end_turn") {

View File

@@ -219,6 +219,13 @@ function extractTextPart(v: unknown): string {
// Feed one SDK chunk; mutate buffers in place.
export function onChunk(b: Buffers, chunk: LettaStreamingResponse) {
// Skip processing if stream was interrupted mid-turn. handleInterrupt already
// rendered the cancellation state, so we should ignore any buffered chunks
// that arrive before drainStream exits.
if (b.interrupted) {
return;
}
// TODO remove once SDK v1 has proper typing for in-stream errors
// Check for streaming error objects (not typed in SDK but emitted by backend)
// Note: Error handling moved to catch blocks in App.tsx and headless.ts

View File

@@ -71,6 +71,9 @@ export async function drainStream(
} else if (abortSignal?.aborted) {
// Already aborted before we started
abortedViaListener = true;
if (stream.controller && !stream.controller.signal.aborted) {
stream.controller.abort();
}
}
try {