Skip to content

Commit 837b7bc

Browse files
committed
webui: preserve partial content when streaming errors occur
1 parent 73e53dc commit 837b7bc

File tree

1 file changed

+75
-20
lines changed

1 file changed

+75
-20
lines changed

tools/server/webui/src/components/ChatScreen.tsx

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,39 @@ export default function ChatScreen() {
121121
scrollToBottom(false, 1);
122122
}, [currConvId]);
123123

124-
const onChunk: CallbackGeneratedChunk = (currLeafNodeId?: Message['id']) => {
124+
// Enhanced onChunk callback with error handling
125+
const onChunk: CallbackGeneratedChunk = (currLeafNodeId?: Message['id'], error?: Error) => {
126+
if (error) {
127+
// Handle streaming error - preserve partial content and show error indicator
128+
console.error('Streaming error occurred:', error);
129+
130+
// Find the current pending message and add error indicator
131+
const currentPendingMsg = pendingMessages[currConvId ?? ''];
132+
if (currentPendingMsg && currentPendingMsg.content !== null) {
133+
// The partial content is already preserved in the pending message
134+
// We just need to add an error indicator to the UI
135+
toast.error('Connection interrupted. Partial response preserved.', {
136+
duration: 4000,
137+
style: {
138+
background: '#fff3cd',
139+
color: '#856404',
140+
border: '1px solid #ffeaa7',
141+
},
142+
});
143+
} else {
144+
// No partial content was generated
145+
toast.error('Connection failed. Please try again.', {
146+
duration: 4000,
147+
style: {
148+
background: '#f8d7da',
149+
color: '#721c24',
150+
border: '1px solid #f5c6cb',
151+
},
152+
});
153+
}
154+
return;
155+
}
156+
125157
if (currLeafNodeId) {
126158
setCurrNodeId(currLeafNodeId);
127159
}
@@ -139,18 +171,27 @@ export default function ChatScreen() {
139171
setCurrNodeId(-1);
140172
// get the last message node
141173
const lastMsgNodeId = messages.at(-1)?.msg.id ?? null;
142-
if (
143-
!(await sendMessage(
174+
175+
try {
176+
const success = await sendMessage(
144177
currConvId,
145178
lastMsgNodeId,
146179
lastInpMsg,
147180
extraContext.items,
148181
onChunk
149-
))
150-
) {
182+
);
183+
184+
if (!success) {
185+
// restore the input message if failed
186+
textarea.setValue(lastInpMsg);
187+
}
188+
} catch (error) {
189+
console.error('Send message error:', error);
151190
// restore the input message if failed
152191
textarea.setValue(lastInpMsg);
192+
// The onChunk callback should have already handled the error UI
153193
}
194+
154195
// OK
155196
extraContext.clearItems();
156197
};
@@ -162,13 +203,20 @@ export default function ChatScreen() {
162203
if (!viewingChat) return;
163204
setCurrNodeId(msg.id);
164205
scrollToBottom(false);
165-
await replaceMessageAndGenerate(
166-
viewingChat.conv.id,
167-
msg.parent,
168-
content,
169-
msg.extra,
170-
onChunk
171-
);
206+
207+
try {
208+
await replaceMessageAndGenerate(
209+
viewingChat.conv.id,
210+
msg.parent,
211+
content,
212+
msg.extra,
213+
onChunk
214+
);
215+
} catch (error) {
216+
console.error('Edit message error:', error);
217+
// The onChunk callback should handle the error UI
218+
}
219+
172220
setCurrNodeId(-1);
173221
scrollToBottom(false);
174222
};
@@ -177,13 +225,20 @@ export default function ChatScreen() {
177225
if (!viewingChat) return;
178226
setCurrNodeId(msg.parent);
179227
scrollToBottom(false);
180-
await replaceMessageAndGenerate(
181-
viewingChat.conv.id,
182-
msg.parent,
183-
null,
184-
msg.extra,
185-
onChunk
186-
);
228+
229+
try {
230+
await replaceMessageAndGenerate(
231+
viewingChat.conv.id,
232+
msg.parent,
233+
null,
234+
msg.extra,
235+
onChunk
236+
);
237+
} catch (error) {
238+
console.error('Regenerate message error:', error);
239+
// The onChunk callback should handle the error UI
240+
}
241+
187242
setCurrNodeId(-1);
188243
scrollToBottom(false);
189244
};
@@ -456,4 +511,4 @@ function ChatInput({
456511
</Dropzone>
457512
</div>
458513
);
459-
}
514+
}

0 commit comments

Comments
 (0)