1
+ import {
2
+ saveMessage ,
3
+ deleteAllMessages ,
4
+ deleteMessagesAfter ,
5
+ getMessages ,
6
+ updateMessage ,
7
+ } from './db' ;
8
+
1
9
import type { ToolResponseContent , ToolCalls , ServiceResponse , Metadata } from './executor/types' ;
2
10
3
11
export type AssistantMessageContentType = 'text' | 'image' ;
@@ -18,13 +26,14 @@ export type UserAttachment = {
18
26
value : string ;
19
27
} ;
20
28
21
- type BaseMessage = {
29
+ export type BaseMessage = {
22
30
id : string ;
23
31
hidden : boolean ;
24
32
ctx : Record < string , unknown > ;
25
33
toolCalls ?: undefined ;
26
34
contexts ?: undefined ;
27
35
attachments ?: undefined ;
36
+ init ?: boolean | undefined ;
28
37
} ;
29
38
30
39
export type ToolResponseMessage = BaseMessage & {
@@ -58,6 +67,7 @@ export type Message = UserMessage | AssistantMessage | ToolResponseMessage;
58
67
type Namespace = {
59
68
history : Message [ ] ;
60
69
idRef : Record < string , Message > ;
70
+ persist : boolean ;
61
71
onAddMessage : < M extends Message = Message > ( msg : M ) => UpdatableHTMLElement < M > | undefined ;
62
72
} ;
63
73
@@ -95,6 +105,10 @@ const addUserMessage = (
95
105
}
96
106
97
107
msgObject . el = namespace . onAddMessage ( msgObject ) ;
108
+
109
+ if ( namespace . persist ) {
110
+ void saveMessage ( key , msgObject ) ;
111
+ }
98
112
} ;
99
113
100
114
const addToolResponseMessage = (
@@ -123,6 +137,10 @@ const addToolResponseMessage = (
123
137
}
124
138
125
139
msgObject . el = namespace . onAddMessage ( msgObject ) ;
140
+
141
+ if ( namespace . persist ) {
142
+ void saveMessage ( key , msgObject ) ;
143
+ }
126
144
} ;
127
145
128
146
const addAssistantMessage = ( key : string , data : ServiceResponse , hidden : boolean = false ) => {
@@ -156,6 +174,10 @@ const addAssistantMessage = (key: string, data: ServiceResponse, hidden: boolean
156
174
}
157
175
158
176
msgObject . el = namespace . onAddMessage ( msgObject ) ;
177
+
178
+ if ( namespace . persist ) {
179
+ void saveMessage ( key , msgObject ) ;
180
+ }
159
181
} ;
160
182
161
183
const updateAssistantMessage = ( key : string , data : ServiceResponse ) => {
@@ -180,6 +202,10 @@ const updateAssistantMessage = (key: string, data: ServiceResponse) => {
180
202
if ( msg . __type === 'AssistantMessage' && msg . el && msg . el . update ) {
181
203
msg . el . update ( msg ) ;
182
204
}
205
+
206
+ if ( namespace . persist ) {
207
+ void updateMessage ( msg ) ;
208
+ }
183
209
} ;
184
210
185
211
const getMessage = ( key : string , id : string ) => {
@@ -191,6 +217,91 @@ const getMessage = (key: string, id: string) => {
191
217
return namespace . idRef [ id ] ;
192
218
} ;
193
219
220
+ const loadFromDB = async ( key : string , onInitDone ?: ( ) => void ) => {
221
+ const messages = await getMessages ( key ) ;
222
+ for ( const message of messages ) {
223
+ const namespace = _namespace [ message . category ] ;
224
+ if ( ! namespace ) {
225
+ continue ;
226
+ }
227
+
228
+ if ( message . __type === 'UserMessage' ) {
229
+ const msgObject : UserMessage = {
230
+ init : true ,
231
+ __type : 'UserMessage' ,
232
+ content : message . content as string ,
233
+ contexts : message . contexts ,
234
+ attachments : message . attachments ,
235
+ role : 'user' ,
236
+ id : message . id ,
237
+ hidden : message . hidden ,
238
+ ctx : { } ,
239
+ } ;
240
+
241
+ const index = namespace . history . push ( msgObject ) - 1 ;
242
+ namespace . idRef [ message . id ] = namespace . history [ index ] ;
243
+
244
+ msgObject . el = namespace . onAddMessage ( msgObject ) ;
245
+
246
+ continue ;
247
+ }
248
+
249
+ if ( message . __type === 'AssistantMessage' ) {
250
+ const msgObject : AssistantMessage = {
251
+ init : true ,
252
+ __type : 'AssistantMessage' ,
253
+ content : undefined ,
254
+ toolCalls : undefined ,
255
+ contentType : 'text' ,
256
+ role : 'assistant' ,
257
+ id : message . id ,
258
+ metadata : message . metadata ,
259
+ hidden : message . hidden ,
260
+ ctx : message . ctx ,
261
+ attachments : message . attachments ,
262
+ contexts : message . contexts ,
263
+ } ;
264
+
265
+ if ( message . contentType === 'image' ) {
266
+ msgObject . content = message . content ;
267
+ msgObject . contentType = 'image' ;
268
+ } else {
269
+ msgObject . content = message . content ;
270
+ msgObject . toolCalls = message . toolCalls ;
271
+ }
272
+
273
+ const index = namespace . history . push ( msgObject ) - 1 ;
274
+ namespace . idRef [ message . id ] = namespace . history [ index ] ;
275
+
276
+ msgObject . el = namespace . onAddMessage ( msgObject ) ;
277
+ continue ;
278
+ }
279
+
280
+ if ( message . __type === 'ToolResponseMessage' ) {
281
+ const msgObject : ToolResponseMessage = {
282
+ init : true ,
283
+ __type : 'ToolResponseMessage' ,
284
+ content : message . content ,
285
+ role : 'tool' ,
286
+ id : message . id ,
287
+ hidden : message . hidden ,
288
+ ctx : { } ,
289
+ } ;
290
+
291
+ const index = namespace . history . push ( msgObject ) - 1 ;
292
+ if ( message . id ) {
293
+ namespace . idRef [ message . id ] = namespace . history [ index ] ;
294
+ }
295
+
296
+ msgObject . el = namespace . onAddMessage ( msgObject ) ;
297
+ }
298
+ }
299
+
300
+ if ( onInitDone ) {
301
+ onInitDone ( ) ;
302
+ }
303
+ } ;
304
+
194
305
export type ChatHistory = {
195
306
addUserMessage : (
196
307
content :
@@ -206,6 +317,7 @@ export type ChatHistory = {
206
317
hidden ?: boolean ,
207
318
) => void ;
208
319
updateAssistantMessage : ( data : ServiceResponse ) => void ;
320
+ syncMessage : ( id : string ) => void ;
209
321
getAssistantMessage : ( id : string ) => Message | undefined ;
210
322
getMessages : ( ) => Message [ ] ;
211
323
getMessagesHistory : ( ) => Pick <
@@ -217,31 +329,41 @@ export type ChatHistory = {
217
329
} ;
218
330
219
331
export const chatHistory = {
220
- init : ( key : string , onAddMessage : Namespace [ 'onAddMessage' ] ) : ChatHistory => {
221
- if ( ! _namespace [ key ] ) {
222
- _namespace [ key ] = {
332
+ init : ( config : {
333
+ key : string ;
334
+ persist ?: boolean ;
335
+ onAddMessage : Namespace [ 'onAddMessage' ] ;
336
+ onInitDone ?: ( ) => void ;
337
+ } ) : ChatHistory => {
338
+ if ( ! _namespace [ config . key ] ) {
339
+ _namespace [ config . key ] = {
223
340
history : [ ] ,
224
341
idRef : { } ,
225
- onAddMessage,
342
+ persist : config . persist ?? false ,
343
+ onAddMessage : config . onAddMessage ,
226
344
} ;
345
+
346
+ if ( config . persist ) {
347
+ void loadFromDB ( config . key , config . onInitDone ) ;
348
+ }
227
349
}
228
350
229
- if ( onAddMessage ) {
230
- _namespace [ key ] . onAddMessage = onAddMessage ;
351
+ if ( config . onAddMessage ) {
352
+ _namespace [ config . key ] . onAddMessage = config . onAddMessage ;
231
353
}
232
354
233
355
return {
234
356
addUserMessage : ( content , hidden = false ) => {
235
357
const id = 'user-msg-' + Date . now ( ) + Math . round ( Math . random ( ) * 1000 ) ;
236
- addUserMessage ( key , id , content , hidden ) ;
358
+ addUserMessage ( config . key , id , content , hidden ) ;
237
359
} ,
238
360
addAssistantMessage : ( data , hidden = false ) => {
239
- addAssistantMessage ( key , data , hidden ) ;
361
+ addAssistantMessage ( config . key , data , hidden ) ;
240
362
} ,
241
363
242
364
addToolCallsMessage : ( toolCalls , hidden = false ) => {
243
365
addAssistantMessage (
244
- key ,
366
+ config . key ,
245
367
{
246
368
__type : 'ToolsData' ,
247
369
id : crypto . randomUUID ( ) ,
@@ -256,19 +378,31 @@ export const chatHistory = {
256
378
) ;
257
379
} ,
258
380
addToolResponseMessage : ( id , content , hidden = false ) => {
259
- addToolResponseMessage ( key , id , content , hidden ) ;
381
+ addToolResponseMessage ( config . key , id , content , hidden ) ;
260
382
} ,
261
383
updateAssistantMessage : ( data ) => {
262
- updateAssistantMessage ( key , data ) ;
384
+ updateAssistantMessage ( config . key , data ) ;
385
+ } ,
386
+ syncMessage : ( id ) => {
387
+ const namespace = _namespace [ config . key ] ;
388
+ if ( ! namespace ) {
389
+ return ;
390
+ }
391
+
392
+ if ( ! namespace . idRef [ id ] ) {
393
+ return ;
394
+ }
395
+
396
+ void updateMessage ( namespace . idRef [ id ] ) ;
263
397
} ,
264
398
getAssistantMessage : ( id ) => {
265
- return getMessage ( key , id ) ;
399
+ return getMessage ( config . key , id ) ;
266
400
} ,
267
401
getMessages : ( ) => {
268
- return _namespace [ key ] . history ;
402
+ return _namespace [ config . key ] . history ;
269
403
} ,
270
404
getMessagesHistory : ( ) => {
271
- return _namespace [ key ] . history . map ( ( m ) => ( {
405
+ return _namespace [ config . key ] . history . map ( ( m ) => ( {
272
406
role : m . role ,
273
407
content : m . content ,
274
408
toolCalls : m . toolCalls ,
@@ -277,22 +411,26 @@ export const chatHistory = {
277
411
} ) ) ;
278
412
} ,
279
413
clearHistory : ( ) => {
280
- _namespace [ key ] . history . forEach ( ( msg ) => {
414
+ _namespace [ config . key ] . history . forEach ( ( msg ) => {
281
415
msg . el ?. remove ( ) ;
282
416
} ) ;
283
- _namespace [ key ] . history = [ ] ;
417
+ _namespace [ config . key ] . history = [ ] ;
418
+
419
+ void deleteAllMessages ( config . key ) ;
284
420
} ,
285
421
clearHistoryFrom : ( id : string ) => {
286
- const startIndex = _namespace [ key ] . history . findIndex ( ( obj ) => obj . id === id ) ;
422
+ const startIndex = _namespace [ config . key ] . history . findIndex ( ( obj ) => obj . id === id ) ;
287
423
288
424
if ( startIndex !== - 1 ) {
289
- for ( let i = startIndex ; i < _namespace [ key ] . history . length ; i ++ ) {
290
- const obj = _namespace [ key ] . history [ i ] ;
425
+ for ( let i = startIndex ; i < _namespace [ config . key ] . history . length ; i ++ ) {
426
+ const obj = _namespace [ config . key ] . history [ i ] ;
291
427
obj . el ?. remove ( ) ;
292
428
}
293
429
294
- _namespace [ key ] . history . splice ( startIndex ) ;
430
+ _namespace [ config . key ] . history . splice ( startIndex ) ;
295
431
}
432
+
433
+ void deleteMessagesAfter ( config . key , id ) ;
296
434
} ,
297
435
} ;
298
436
} ,
0 commit comments