@@ -13,6 +13,8 @@ import { patchCompletedMessage } from '../patch-completed-message';
1313
1414// NOTE: We get the source info only in the first chunk, so we need to save it until the AI response is fully generated.
1515const sourcesStore : Record < string , ChatCompletionChunk [ 'sources' ] > = { } ;
16+ const flushScheduled : Record < string , boolean > = { } ;
17+ const contentBuffer : Record < string , string > = { } ;
1618
1719export const handleChatCompletionEvent = async ( socketResponse : ChatEventBase ) : Promise < void > => {
1820 const sessionId = socketService . socketSessionId ;
@@ -25,15 +27,24 @@ export const handleChatCompletionEvent = async (socketResponse: ChatEventBase):
2527 }
2628
2729 if ( ! chatCompletionData ?. content ) return ;
30+ contentBuffer [ chatId ] = chatCompletionData . content ;
2831
2932 const queryKey = chatQueriesKeys . get ( chatId ) . queryKey ;
3033 const chatData = queryClient . getQueryData < ChatResponse > ( queryKey ) ;
3134 const storedSources = sourcesStore [ chatId ] ;
3235
33- if ( chatData ) {
34- queryClient . setQueryData ( queryKey , ( oldData : ChatResponse ) =>
35- patchChatMessagesWithCompletion ( oldData , chatCompletionData . content , storedSources ) ,
36- ) ;
36+ // NOTE: Limit updates to once per frame (~16ms) because frequent streaming updates
37+ // can cause UI unresponsiveness on low-end Android devices.
38+ if ( ! flushScheduled [ chatId ] && chatData ) {
39+ flushScheduled [ chatId ] = true ;
40+
41+ requestAnimationFrame ( ( ) => {
42+ flushScheduled [ chatId ] = false ;
43+
44+ queryClient . setQueryData ( queryKey , ( oldData : ChatResponse ) =>
45+ patchChatMessagesWithCompletion ( oldData , contentBuffer [ chatId ] , storedSources ) ,
46+ ) ;
47+ } ) ;
3748 }
3849
3950 if ( chatCompletionData . done ) {
0 commit comments