Skip to content

Commit d528444

Browse files
webui: preserve partial response on streaming error (ggml-org#23090)
1 parent 91e84fe commit d528444

3 files changed

Lines changed: 17 additions & 21 deletions

File tree

tools/server/webui/src/lib/constants/agentic.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ export const ATTACHMENT_SAVED_REGEX = /\[Attachment saved: ([^\]]+)\]/;
44

55
export const NEWLINE_SEPARATOR = '\n';
66

7-
export const LLM_ERROR_BLOCK_START = '\n\n```\nUpstream LLM error:\n';
8-
export const LLM_ERROR_BLOCK_END = '\n```\n';
9-
107
export const DEFAULT_AGENTIC_CONFIG: AgenticConfig = {
118
enabled: true,
129
maxTurns: 100,

tools/server/webui/src/lib/stores/agentic.svelte.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@ import { ToolSource, ToolPermissionDecision } from '$lib/enums';
3030
import { SvelteMap } from 'svelte/reactivity';
3131
import { ToolsService } from '$lib/services/tools.service';
3232
import { isAbortError } from '$lib/utils';
33-
import {
34-
DEFAULT_AGENTIC_CONFIG,
35-
NEWLINE_SEPARATOR,
36-
LLM_ERROR_BLOCK_START,
37-
LLM_ERROR_BLOCK_END
38-
} from '$lib/constants';
33+
import { DEFAULT_AGENTIC_CONFIG, NEWLINE_SEPARATOR } from '$lib/constants';
3934
import {
4035
IMAGE_MIME_TO_EXTENSION,
4136
DATA_URI_BASE64_REGEX,
@@ -640,10 +635,9 @@ class AgenticStore {
640635
return;
641636
}
642637
const normalizedError = error instanceof Error ? error : new Error('LLM stream error');
643-
// Save error as content in the current turn
644-
onChunk?.(`${LLM_ERROR_BLOCK_START}${normalizedError.message}${LLM_ERROR_BLOCK_END}`);
638+
// preserve partial output as is, the outer error dialog informs the user separately
645639
await onAssistantTurnComplete?.(
646-
turnContent + `${LLM_ERROR_BLOCK_START}${normalizedError.message}${LLM_ERROR_BLOCK_END}`,
640+
turnContent,
647641
turnReasoningContent || undefined,
648642
this.buildFinalTimings(capturedTimings, agenticTimings),
649643
undefined

tools/server/webui/src/lib/stores/chat.svelte.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ class ChatStore {
814814
);
815815
}
816816
},
817-
onError: (error: Error) => {
817+
onError: async (error: Error) => {
818818
this.setStreamingActive(false);
819819
if (isAbortError(error)) {
820820
cleanupStreamingState();
@@ -826,13 +826,10 @@ class ChatStore {
826826
return;
827827
}
828828
console.error('Streaming error:', error);
829+
// keep whatever was streamed so far, the message stays in memory and in DB
830+
await this.savePartialResponseIfNeeded(convId);
829831
cleanupStreamingState();
830832
this.clearPendingMessage(convId);
831-
const idx = conversationsStore.findMessageIndex(assistantMessage.id);
832-
if (idx !== -1) {
833-
const failedMessage = conversationsStore.removeMessageAtIndex(idx);
834-
if (failedMessage) DatabaseService.deleteMessage(failedMessage.id).catch(console.error);
835-
}
836833
const contextInfo = (
837834
error as Error & { contextInfo?: { n_prompt_tokens: number; n_ctx: number } }
838835
).contextInfo;
@@ -1389,9 +1386,17 @@ class ChatStore {
13891386
}
13901387

13911388
console.error('Continue generation error:', error);
1392-
conversationsStore.updateMessageAtIndex(idx, { content: originalContent });
1393-
1394-
await DatabaseService.updateMessage(msg.id, { content: originalContent });
1389+
// keep whatever was appended so far, the message stays in memory and in DB
1390+
await DatabaseService.updateMessage(msg.id, {
1391+
content: originalContent + appendedContent,
1392+
reasoningContent: originalReasoning + appendedReasoning || undefined,
1393+
timestamp: Date.now()
1394+
});
1395+
conversationsStore.updateMessageAtIndex(idx, {
1396+
content: originalContent + appendedContent,
1397+
reasoningContent: originalReasoning + appendedReasoning || undefined,
1398+
timestamp: Date.now()
1399+
});
13951400

13961401
this.setChatLoading(msg.convId, false);
13971402
this.clearChatStreaming(msg.convId);

0 commit comments

Comments
 (0)