From 60eef2fc6734f88df06e2f85db9b9368cc8c227f Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 Jan 2025 15:21:05 -0500 Subject: [PATCH] fix: buffer incomplete JSON objects from AI stream - simplify ndjson stream to get complete lines instead of chunks - add buffering for incomplete lines --- src/puter-js/src/lib/utils.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/puter-js/src/lib/utils.js b/src/puter-js/src/lib/utils.js index 212ee6be..34f7ab52 100644 --- a/src/puter-js/src/lib/utils.js +++ b/src/puter-js/src/lib/utils.js @@ -282,7 +282,14 @@ async function driverCall_( let signal_stream_update = null; let lastLength = 0; let response_complete = false; - const parts_received = []; + + let buffer = ''; + + // NOTE: linked-list technically would perform better, + // but in practice there are at most 2-3 lines + // buffered so this does not matter. + const lines_received = []; + xhr.onreadystatechange = () => { if ( xhr.readyState === 2 ) { if ( xhr.getResponseHeader("Content-Type") !== @@ -295,13 +302,10 @@ async function driverCall_( signal_stream_update = tp.resolve.bind(tp); await tp; if ( response_complete ) break; - while ( parts_received.length > 0 ) { - const value = parts_received.pop(); - const parts = value.split('\n'); - for ( const part of parts ) { - if ( part.trim() === '' ) continue; - yield JSON.parse(part); - } + while ( lines_received.length > 0 ) { + const line = lines_received.shift(); + if ( line.trim() === '' ) continue; + yield JSON.parse(line); } } } @@ -322,8 +326,19 @@ async function driverCall_( const newText = xhr.responseText.slice(lastLength); lastLength = xhr.responseText.length; // Update lastLength to the current length - parts_received.push(newText); - signal_stream_update(); + let hasUpdates = false; + for ( let i = 0; i < newText.length; i++ ) { + buffer += newText[i]; + if ( newText[i] === '\n' ) { + hasUpdates = true; + lines_received.push(buffer); + buffer = ''; + } + } + + if ( hasUpdates ) { + signal_stream_update(); + } }; // ========================