Skip to content

Commit d9110b7

Browse files
committed
fix: adaptive throttle for markdown parsing during streaming
Fixes open-webui#20878 Implements adaptive throttling (50-300ms) based on actual render time: - Starts at 50ms for smooth initial streaming - Automatically backs off to 300ms if rendering is slow - Returns to faster updates when load decreases This provides smooth UX for short responses while protecting against freezes during heavy tool call scenarios.
1 parent 4aacaeb commit d9110b7

1 file changed

Lines changed: 50 additions & 6 deletions

File tree

src/lib/components/chat/Messages/Markdown.svelte

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script>
2+
import { onDestroy } from 'svelte';
23
import { marked } from 'marked';
34
import { replaceTokens, processResponseContent } from '$lib/utils';
45
import { user } from '$lib/stores';
@@ -49,13 +50,56 @@
4950
extensions: [mentionExtension({ triggerChar: '@' }), mentionExtension({ triggerChar: '#' })]
5051
});
5152
52-
$: (async () => {
53-
if (content) {
54-
tokens = marked.lexer(
55-
replaceTokens(processResponseContent(content), model?.name, $user?.name)
56-
);
53+
// Adaptive throttle: starts fast, backs off if rendering is slow
54+
let throttleId = null;
55+
let pendingContent = null;
56+
let throttleMs = 50;
57+
const MIN_THROTTLE = 50;
58+
const MAX_THROTTLE = 300;
59+
const TARGET_RENDER_TIME = 30;
60+
61+
function parseMarkdown(text) {
62+
return marked.lexer(replaceTokens(processResponseContent(text), model?.name, $user?.name));
63+
}
64+
65+
function doUpdate() {
66+
if (!pendingContent) return;
67+
68+
const start = performance.now();
69+
tokens = parseMarkdown(pendingContent);
70+
const renderTime = performance.now() - start;
71+
72+
// Adapt throttle based on render time
73+
if (renderTime > TARGET_RENDER_TIME * 2) {
74+
throttleMs = Math.min(throttleMs * 1.5, MAX_THROTTLE);
75+
} else if (renderTime < TARGET_RENDER_TIME && throttleMs > MIN_THROTTLE) {
76+
throttleMs = Math.max(throttleMs * 0.9, MIN_THROTTLE);
77+
}
78+
79+
throttleId = null;
80+
}
81+
82+
$: if (content) {
83+
if (done) {
84+
if (throttleId) {
85+
clearTimeout(throttleId);
86+
throttleId = null;
87+
}
88+
throttleMs = MIN_THROTTLE;
89+
tokens = parseMarkdown(content);
90+
} else {
91+
pendingContent = content;
92+
if (!throttleId) {
93+
throttleId = setTimeout(doUpdate, throttleMs);
94+
}
5795
}
58-
})();
96+
}
97+
98+
onDestroy(() => {
99+
if (throttleId) {
100+
clearTimeout(throttleId);
101+
}
102+
});
59103
</script>
60104
61105
{#key id}

0 commit comments

Comments
 (0)