@@ -10,15 +10,15 @@ import { BASE_URL, CONFIG_DEFAULT, isDev } from '../Config';
1010import { matchPath , useLocation } from 'react-router' ;
1111
1212interface AppContextValue {
13- isGenerating : boolean ;
1413 viewingConversation : Conversation | null ;
15- pendingMessage : PendingMessage | null ;
14+ pendingMessages : Record < Conversation [ 'id' ] , PendingMessage > ;
15+ isGenerating : ( convId : string ) => boolean ;
1616 sendMessage : (
1717 convId : string ,
1818 content : string ,
1919 onChunk ?: CallbackGeneratedChunk
2020 ) => Promise < boolean > ;
21- stopGenerating : ( ) => void ;
21+ stopGenerating : ( convId : string ) => void ;
2222 replaceMessageAndGenerate : (
2323 convId : string ,
2424 origMsgId : Message [ 'id' ] ,
@@ -45,13 +45,14 @@ export const AppContextProvider = ({
4545 const params = matchPath ( '/chat/:convId' , pathname ) ;
4646 const convId = params ?. params ?. convId ;
4747
48- const [ isGenerating , setIsGenerating ] = useState ( false ) ;
4948 const [ viewingConversation , setViewingConversation ] =
5049 useState < Conversation | null > ( null ) ;
51- const [ pendingMessage , setPendingMessage ] = useState < PendingMessage | null > (
52- null
53- ) ;
54- const [ abortController , setAbortController ] = useState ( new AbortController ( ) ) ;
50+ const [ pendingMessages , setPendingMessages ] = useState <
51+ Record < Conversation [ 'id' ] , PendingMessage >
52+ > ( { } ) ;
53+ const [ aborts , setAborts ] = useState <
54+ Record < Conversation [ 'id' ] , AbortController >
55+ > ( { } ) ;
5556 const [ config , setConfig ] = useState ( StorageUtils . getConfig ( ) ) ;
5657
5758 useEffect ( ( ) => {
@@ -66,11 +67,41 @@ export const AppContextProvider = ({
6667 } ;
6768 } , [ convId ] ) ;
6869
70+ const setPending = ( convId : string , pendingMsg : PendingMessage | null ) => {
71+ // if pendingMsg is null, remove the key from the object
72+ if ( ! pendingMsg ) {
73+ setPendingMessages ( ( prev ) => {
74+ const newState = { ...prev } ;
75+ delete newState [ convId ] ;
76+ return newState ;
77+ } ) ;
78+ } else {
79+ setPendingMessages ( ( prev ) => ( { ...prev , [ convId ] : pendingMsg } ) ) ;
80+ }
81+ } ;
82+
83+ const setAbort = ( convId : string , controller : AbortController | null ) => {
84+ if ( ! controller ) {
85+ setAborts ( ( prev ) => {
86+ const newState = { ...prev } ;
87+ delete newState [ convId ] ;
88+ return newState ;
89+ } ) ;
90+ } else {
91+ setAborts ( ( prev ) => ( { ...prev , [ convId ] : controller } ) ) ;
92+ }
93+ } ;
94+
95+ ////////////////////////////////////////////////////////////////////////
96+ // public functions
97+
98+ const isGenerating = ( convId : string ) => ! ! pendingMessages [ convId ] ;
99+
69100 const generateMessage = async (
70101 convId : string ,
71102 onChunk ?: CallbackGeneratedChunk
72103 ) => {
73- if ( isGenerating ) return ;
104+ if ( isGenerating ( convId ) ) return ;
74105
75106 const config = StorageUtils . getConfig ( ) ;
76107 const currConversation = StorageUtils . getOneConversation ( convId ) ;
@@ -79,16 +110,14 @@ export const AppContextProvider = ({
79110 }
80111
81112 const abortController = new AbortController ( ) ;
82- setIsGenerating ( true ) ;
83- setAbortController ( abortController ) ;
113+ setAbort ( convId , abortController ) ;
84114
85115 let pendingMsg : PendingMessage = {
86- convId,
87116 id : Date . now ( ) + 1 ,
88117 role : 'assistant' ,
89118 content : null ,
90119 } ;
91- setPendingMessage ( pendingMsg ) ;
120+ setPending ( convId , pendingMsg ) ;
92121
93122 try {
94123 // prepare messages for API
@@ -157,7 +186,6 @@ export const AppContextProvider = ({
157186 const lastContent = pendingMsg . content || '' ;
158187 if ( addedContent ) {
159188 pendingMsg = {
160- convId,
161189 id : pendingMsg . id ,
162190 role : 'assistant' ,
163191 content : lastContent + addedContent ,
@@ -173,18 +201,15 @@ export const AppContextProvider = ({
173201 predicted_ms : timings . predicted_ms ,
174202 } ;
175203 }
176- setPendingMessage ( pendingMsg ) ;
204+ setPending ( convId , pendingMsg ) ;
177205 onChunk ?.( ) ;
178206 }
179207 } catch ( err ) {
180- console . error ( err ) ;
181- setPendingMessage ( null ) ;
182- setIsGenerating ( false ) ;
208+ setPending ( convId , null ) ;
183209 if ( ( err as Error ) . name === 'AbortError' ) {
184210 // user stopped the generation via stopGeneration() function
185211 // we can safely ignore this error
186212 } else {
187- setIsGenerating ( false ) ;
188213 console . error ( err ) ;
189214 // eslint-disable-next-line @typescript-eslint/no-explicit-any
190215 alert ( ( err as any ) ?. message ?? 'Unknown error' ) ;
@@ -200,8 +225,7 @@ export const AppContextProvider = ({
200225 timings : pendingMsg . timings ,
201226 } ) ;
202227 }
203- setPendingMessage ( null ) ;
204- setIsGenerating ( false ) ;
228+ setPending ( convId , null ) ;
205229 onChunk ?.( ) ; // trigger scroll to bottom
206230 } ;
207231
@@ -210,7 +234,7 @@ export const AppContextProvider = ({
210234 content : string ,
211235 onChunk ?: CallbackGeneratedChunk
212236 ) : Promise < boolean > => {
213- if ( isGenerating || content . trim ( ) . length === 0 ) return false ;
237+ if ( isGenerating ( convId ) || content . trim ( ) . length === 0 ) return false ;
214238
215239 StorageUtils . appendMsg ( convId , {
216240 id : Date . now ( ) ,
@@ -228,10 +252,9 @@ export const AppContextProvider = ({
228252 return false ;
229253 } ;
230254
231- const stopGenerating = ( ) => {
232- setIsGenerating ( false ) ;
233- setPendingMessage ( null ) ;
234- abortController . abort ( ) ;
255+ const stopGenerating = ( convId : string ) => {
256+ setPending ( convId , null ) ;
257+ aborts [ convId ] ?. abort ( ) ;
235258 } ;
236259
237260 // if content is undefined, we remove last assistant message
@@ -241,7 +264,7 @@ export const AppContextProvider = ({
241264 content ?: string ,
242265 onChunk ?: CallbackGeneratedChunk
243266 ) => {
244- if ( isGenerating ) return ;
267+ if ( isGenerating ( convId ) ) return ;
245268
246269 StorageUtils . filterAndKeepMsgs ( convId , ( msg ) => msg . id < origMsgId ) ;
247270 if ( content ) {
@@ -265,7 +288,7 @@ export const AppContextProvider = ({
265288 value = { {
266289 isGenerating,
267290 viewingConversation,
268- pendingMessage ,
291+ pendingMessages ,
269292 sendMessage,
270293 stopGenerating,
271294 replaceMessageAndGenerate,
0 commit comments