Skip to content

Commit 6720aba

Browse files
committed
fix: throttle scroll and reduce forced renders during streaming
1 parent e6b8fa3 commit 6720aba

1 file changed

Lines changed: 69 additions & 51 deletions

File tree

src/lib/components/chat/Chat.svelte

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111
let autoScroll = true;
112112
let processing = '';
113113
let messagesContainerElement: HTMLDivElement;
114+
let scrollRafId = null;
115+
let lastTtsProcessTime = 0;
114116
115117
let navbarElement;
116118
@@ -671,6 +673,9 @@
671673
672674
onDestroy(() => {
673675
try {
676+
if (scrollRafId) {
677+
cancelAnimationFrame(scrollRafId);
678+
}
674679
pageSubscribe();
675680
showControlsSubscribe();
676681
selectedFolderSubscribe();
@@ -1175,14 +1180,20 @@
11751180
}
11761181
};
11771182
1178-
const scrollToBottom = async (behavior = 'auto') => {
1179-
await tick();
1180-
if (messagesContainerElement) {
1181-
messagesContainerElement.scrollTo({
1182-
top: messagesContainerElement.scrollHeight,
1183-
behavior
1184-
});
1183+
const scrollToBottom = (behavior = 'auto') => {
1184+
if (scrollRafId) {
1185+
return;
11851186
}
1187+
scrollRafId = requestAnimationFrame(async () => {
1188+
scrollRafId = null;
1189+
await tick();
1190+
if (messagesContainerElement) {
1191+
messagesContainerElement.scrollTo({
1192+
top: messagesContainerElement.scrollHeight,
1193+
behavior
1194+
});
1195+
}
1196+
});
11861197
};
11871198
const chatCompletedHandler = async (_chatId, modelId, responseMessageId, messages) => {
11881199
const res = await chatCompleted(localStorage.token, {
@@ -1471,35 +1482,39 @@
14711482
// Stream response
14721483
let value = choices[0]?.delta?.content ?? '';
14731484
if (message.content == '' && value == '\n') {
1474-
console.log('Empty response');
1485+
// Skip leading newlines
14751486
} else {
14761487
message.content += value;
14771488
14781489
if (navigator.vibrate && ($settings?.hapticFeedback ?? false)) {
14791490
navigator.vibrate(5);
14801491
}
14811492
1482-
// Emit chat event for TTS
1483-
const messageContentParts = getMessageContentParts(
1484-
removeAllDetails(message.content),
1485-
$config?.audio?.tts?.split_on ?? 'punctuation'
1486-
);
1487-
messageContentParts.pop();
1493+
// Throttle TTS processing to avoid re-parsing entire content on every token
1494+
const now = Date.now();
1495+
if (now - lastTtsProcessTime >= 150) {
1496+
lastTtsProcessTime = now;
14881497
1489-
// dispatch only last sentence and make sure it hasn't been dispatched before
1490-
if (
1491-
messageContentParts.length > 0 &&
1492-
messageContentParts[messageContentParts.length - 1] !== message.lastSentence
1493-
) {
1494-
message.lastSentence = messageContentParts[messageContentParts.length - 1];
1495-
eventTarget.dispatchEvent(
1496-
new CustomEvent('chat', {
1497-
detail: {
1498-
id: message.id,
1499-
content: messageContentParts[messageContentParts.length - 1]
1500-
}
1501-
})
1498+
const messageContentParts = getMessageContentParts(
1499+
removeAllDetails(message.content),
1500+
$config?.audio?.tts?.split_on ?? 'punctuation'
15021501
);
1502+
messageContentParts.pop();
1503+
1504+
if (
1505+
messageContentParts.length > 0 &&
1506+
messageContentParts[messageContentParts.length - 1] !== message.lastSentence
1507+
) {
1508+
message.lastSentence = messageContentParts[messageContentParts.length - 1];
1509+
eventTarget.dispatchEvent(
1510+
new CustomEvent('chat', {
1511+
detail: {
1512+
id: message.id,
1513+
content: messageContentParts[messageContentParts.length - 1]
1514+
}
1515+
})
1516+
);
1517+
}
15031518
}
15041519
}
15051520
}
@@ -1513,27 +1528,31 @@
15131528
navigator.vibrate(5);
15141529
}
15151530
1516-
// Emit chat event for TTS
1517-
const messageContentParts = getMessageContentParts(
1518-
removeAllDetails(message.content),
1519-
$config?.audio?.tts?.split_on ?? 'punctuation'
1520-
);
1521-
messageContentParts.pop();
1531+
// Throttle TTS processing
1532+
const now = Date.now();
1533+
if (now - lastTtsProcessTime >= 150) {
1534+
lastTtsProcessTime = now;
15221535
1523-
// dispatch only last sentence and make sure it hasn't been dispatched before
1524-
if (
1525-
messageContentParts.length > 0 &&
1526-
messageContentParts[messageContentParts.length - 1] !== message.lastSentence
1527-
) {
1528-
message.lastSentence = messageContentParts[messageContentParts.length - 1];
1529-
eventTarget.dispatchEvent(
1530-
new CustomEvent('chat', {
1531-
detail: {
1532-
id: message.id,
1533-
content: messageContentParts[messageContentParts.length - 1]
1534-
}
1535-
})
1536+
const messageContentParts = getMessageContentParts(
1537+
removeAllDetails(message.content),
1538+
$config?.audio?.tts?.split_on ?? 'punctuation'
15361539
);
1540+
messageContentParts.pop();
1541+
1542+
if (
1543+
messageContentParts.length > 0 &&
1544+
messageContentParts[messageContentParts.length - 1] !== message.lastSentence
1545+
) {
1546+
message.lastSentence = messageContentParts[messageContentParts.length - 1];
1547+
eventTarget.dispatchEvent(
1548+
new CustomEvent('chat', {
1549+
detail: {
1550+
id: message.id,
1551+
content: messageContentParts[messageContentParts.length - 1]
1552+
}
1553+
})
1554+
);
1555+
}
15371556
}
15381557
}
15391558
@@ -1597,11 +1616,10 @@
15971616
);
15981617
}
15991618
1600-
console.log(data);
1601-
await tick();
1602-
1603-
if (autoScroll) {
1604-
scrollToBottom();
1619+
if (!done) {
1620+
if (autoScroll) {
1621+
scrollToBottom();
1622+
}
16051623
}
16061624
};
16071625

0 commit comments

Comments
 (0)