Skip to content

Commit 6b8b3ec

Browse files
feat: implement fantastic streaming smoothing 🤩
1 parent d74647c commit 6b8b3ec

File tree

1 file changed

+54
-6
lines changed

1 file changed

+54
-6
lines changed

src/components/Generator.tsx

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ export default () => {
227227
}),
228228
}),
229229
signal: controller.signal,
230-
headers: { authorization: `Bearer ${localStorage.getItem('apiKey')}` },
230+
headers: localStorage.getItem('apiKey') ? { authorization: `Bearer ${localStorage.getItem('apiKey')}` } : {},
231231
})
232232
if (!response.ok) {
233233
const error = await response.json()
@@ -243,25 +243,73 @@ export default () => {
243243
const decoder = new TextDecoder('utf-8')
244244
let done = false
245245

246+
let realValue = ''
247+
let displayValue = ''
248+
let lastTime = Date.now()
249+
const intervals = [0]
250+
251+
const N = 5
252+
const MAX = 500
253+
const FACTOR = 50
254+
255+
const getProperInterval = () => {
256+
const slidingWindowMean = intervals.slice(-N).reduce((a, b) => a + b, 0) / Math.min(intervals.length, N)
257+
return Math.min(slidingWindowMean, MAX / (realValue.length - displayValue.length))
258+
}
259+
260+
const update = async() => {
261+
if (!streaming()) return fastForward()
262+
263+
const distance = realValue.length - displayValue.length
264+
if (!done) {
265+
const num = Math.round(distance / FACTOR) || 1
266+
displayValue = realValue.slice(0, displayValue.length + num)
267+
setCurrentAssistantMessage(displayValue)
268+
//
269+
realValue !== displayValue ? setTimeout(update, Math.round(getProperInterval())) : requestAnimationFrame(update)
270+
}
271+
}
272+
273+
const fastForward = () => {
274+
const distance = realValue.length - displayValue.length
275+
if (distance) {
276+
const num = Math.round(distance / FACTOR) || 1
277+
displayValue = realValue.slice(0, displayValue.length + num)
278+
setCurrentAssistantMessage(displayValue)
279+
//
280+
if (realValue === displayValue) return archiveCurrentMessage()
281+
282+
const interval = Math.floor(10 / (realValue.length - displayValue.length))
283+
284+
interval ? setTimeout(fastForward, interval) : requestAnimationFrame(fastForward)
285+
} else {
286+
return archiveCurrentMessage()
287+
}
288+
}
289+
290+
update()
291+
246292
while (!done) {
247293
const { value, done: readerDone } = await reader.read()
248294
if (value) {
249295
const char = decoder.decode(value)
250296
if (char === '\n' && currentAssistantMessage().endsWith('\n'))
251297
continue
252298

253-
if (char)
254-
setCurrentAssistantMessage(currentAssistantMessage() + char)
299+
if (char) {
300+
realValue += char
301+
intervals.push(Date.now() - lastTime)
302+
lastTime = Date.now()
303+
}
255304
}
256305
done = readerDone
306+
done && fastForward()
257307
}
258308
} catch (e) {
259309
console.error(e)
260310
setStreaming(false)
261311
setController(null)
262-
return
263312
}
264-
archiveCurrentMessage()
265313
}
266314

267315
const archiveCurrentMessage = () => {
@@ -293,7 +341,7 @@ export default () => {
293341
const stopStreamFetch = () => {
294342
if (controller()) {
295343
controller()!.abort()
296-
archiveCurrentMessage()
344+
setStreaming(false)
297345
}
298346
}
299347

0 commit comments

Comments
 (0)