@@ -30,7 +30,6 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
30
30
downloadModel: (e) => _downloadModel (e, emit),
31
31
deleteModel: (e) => _deleteModel (e, emit),
32
32
setPercentDownloaded: (e) => _setPercentDownloaded (e, emit),
33
- setTranscript: (e) => _setTranscript (e, emit),
34
33
updateTemperature: (e) => _updateTemperature (e, emit),
35
34
updateTopK: (e) => _updateTopK (e, emit),
36
35
updateMaxTokens: (e) => _updateMaxTokens (e, emit),
@@ -39,9 +38,6 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
39
38
);
40
39
},
41
40
);
42
- for (final model in LlmModel .values) {
43
- transcript[model] = < ChatMessage > [];
44
- }
45
41
final cacheDirFuture = path_provider.getApplicationCacheDirectory ();
46
42
modelProvider.ready.then ((_) async {
47
43
cacheDir = (await cacheDirFuture).absolute.path;
@@ -54,13 +50,6 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
54
50
/// Utility which knows how to download and store the model file.
55
51
ModelLocationProvider modelProvider;
56
52
57
- /// Container for one message log per [LlmModel] . Mutable data type.
58
- /// Initialized to contain empty lists for each [LlmModel] . At any given time,
59
- /// one of these lists should be mirrored in [state.transcript] . Which one
60
- /// depends on the [model] value attached to the most recent model-specific
61
- /// event.
62
- final Map <LlmModel , List <ChatMessage >> transcript = {};
63
-
64
53
/// Constructor for the inference engine.
65
54
final LlmInferenceEngine Function (LlmInferenceOptions ) engineBuilder;
66
55
@@ -88,10 +77,6 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
88
77
}
89
78
}
90
79
91
- void _setTranscript (SetTranscript event, Emit emit) {
92
- emit (state.copyWith (transcript: transcript[event.model]! ));
93
- }
94
-
95
80
ModelInfo _getInfo (LlmModel model) {
96
81
final existingPath = modelProvider.pathFor (model);
97
82
return existingPath != null
@@ -125,9 +110,6 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
125
110
/// However, if you have already made other modifications to a state object
126
111
/// (via copying), consider passing that copied value to the [overrideState]
127
112
/// parameter.
128
- ///
129
- /// See also:
130
- /// * [_emitTranscript] for the transcript version of this method.
131
113
void _emitNewModelInfo (
132
114
LlmModel model,
133
115
ModelInfo info,
@@ -220,6 +202,7 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
220
202
);
221
203
await Future .delayed (const Duration (milliseconds: 100 ));
222
204
emit (state.copyWith (error: null ));
205
+ return ;
223
206
}
224
207
225
208
if (downloadStream != null ) {
@@ -239,18 +222,12 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
239
222
add (CheckForModel (modelToDelete));
240
223
}
241
224
242
- void _completeResponse (CompleteResponse e, emit) {
243
- transcript[e.model]! .last = transcript[e.model]! .last.complete ();
244
- final newState = state.copyWith (
245
- isLlmTyping: false ,
246
- transcript: transcript[e.model]! ,
247
- );
248
- _emitTranscript (emit, e.model, newState);
249
- }
225
+ void _completeResponse (CompleteResponse e, emit) =>
226
+ emit (state.copyWith (isLlmTyping: false ).completeMessage (e.model));
250
227
251
228
Future <void > _addMessage (AddMessage event, Emit emit) async {
252
229
// Value equality will detect that this state is "new"
253
- addMessageToTranscript ( event, emit );
230
+ emit (state. addMessage ( event.message, event.model) );
254
231
if (state.engine == null ) {
255
232
await _queueMessageForLlm (event, emit);
256
233
} else {
@@ -269,16 +246,20 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
269
246
AddMessage event,
270
247
Emit emit,
271
248
) async {
272
- final formattedChatHistory = _formatChatHistoryForLlm (state.transcript);
249
+ final formattedChatHistory =
250
+ _formatChatHistoryForLlm (state.transcript[event.model]! );
273
251
final responseStream = state.engine! .generateResponse (formattedChatHistory);
274
252
275
253
// Add a blank response for the LLM into which we can write its answer.
276
254
// Create a synthetic event just to pass to this helper method, but don't
277
255
// route it through the `add` method.
278
- addMessageToTranscript (AddMessage (ChatMessage .llm ('' ), event.model), emit);
279
- emit (state.copyWith (isLlmTyping: true ));
256
+ emit (
257
+ state
258
+ .copyWith (isLlmTyping: true )
259
+ .addMessage (ChatMessage .llm ('' ), event.model),
260
+ );
280
261
281
- final messageIndex = transcript[event.model]! .length - 1 ;
262
+ final messageIndex = state. transcript[event.model]! .length - 1 ;
282
263
bool first = true ;
283
264
await for (final String chunk in responseStream) {
284
265
add (
@@ -297,46 +278,22 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
297
278
chunk: '' ,
298
279
model: event.model,
299
280
index: messageIndex,
300
- first: false ,
281
+ first: first ,
301
282
last: true ,
302
283
),
303
284
);
304
285
add (CompleteResponse (event.model));
305
286
}
306
287
307
- void _extendMessage (ExtendMessage event, Emit emit) {
308
- final message = transcript[event.model]! [event.index];
309
- transcript[event.model]! [event.index] = message.copyWith (
310
- body: '${message .body }${event .chunk }' .sanitize (event.first, event.last),
311
- );
312
- _emitTranscript (emit, event.model);
313
- }
314
-
315
- void addMessageToTranscript (AddMessage event, Emit emit) {
316
- transcript[event.model]! .add (event.message);
317
- _emitTranscript (emit, event.model);
318
- }
319
-
320
- /// Re-emits the current state with an updated copy of the transcript from the
321
- /// Bloc's own storage. However, if you have already made other modifications
322
- /// to a state object (via copying), consider passing that copied value to
323
- /// the [overrideState] parameter.
324
- ///
325
- /// See also:
326
- /// * [_emitNewModelInfo] for the [ModelInfo] version of this method.
327
- void _emitTranscript (
328
- Emit emit,
329
- LlmModel model, [
330
- TranscriptState ? overrideState,
331
- ]) {
332
- // It would be nice to not copy the whole list every time.
333
- final newState = (overrideState ?? state).copyWith (
334
- transcript: List <ChatMessage >.from (
335
- transcript[model]! ,
336
- ),
337
- );
338
- emit (newState);
339
- }
288
+ void _extendMessage (ExtendMessage event, Emit emit) => emit (
289
+ state.extendMessage (
290
+ event.chunk,
291
+ model: event.model,
292
+ index: event.index,
293
+ first: event.first,
294
+ last: event.last,
295
+ ),
296
+ );
340
297
341
298
static const _begin = '<begin_transmission>' ;
342
299
static const _end = '<end_transmission>' ;
@@ -368,13 +325,13 @@ class TranscriptBloc extends Bloc<TranscriptEvent, TranscriptState> {
368
325
}
369
326
}
370
327
371
- @Freezed ()
328
+ @Freezed (makeCollectionsUnmodifiable : false )
372
329
class TranscriptState with _$TranscriptState {
373
330
TranscriptState ._();
374
331
factory TranscriptState ({
375
332
/// Log of messages for the [selectedModel] . Other models may have other
376
333
/// message logs found on the [TranscriptBloc] .
377
- @Default ( < ChatMessage > []) List <ChatMessage > transcript,
334
+ required Map < LlmModel , List <ChatMessage > > transcript,
378
335
379
336
/// True only after the [ModelLocationProvider] has sorted out the initial
380
337
/// state.
@@ -407,17 +364,63 @@ class TranscriptState with _$TranscriptState {
407
364
408
365
factory TranscriptState .initial ([int ? seed]) {
409
366
final modelInfoMap = < LlmModel , ModelInfo > {};
367
+ final transcript = < LlmModel , List <ChatMessage >> {};
410
368
for (final model in LlmModel .values) {
411
369
modelInfoMap[model] = const ModelInfo ();
370
+ transcript[model] = < ChatMessage > [];
412
371
}
413
372
return TranscriptState (
414
- transcript: < ChatMessage > [] ,
373
+ transcript: transcript ,
415
374
randomSeed: seed ?? Random ().nextInt (1 << 32 ),
416
375
modelInfoMap: modelInfoMap,
417
376
);
418
377
}
419
378
420
379
int get sequenceBatchSize => 20 ;
380
+
381
+ Map <LlmModel , List <ChatMessage >> _copyTranscript () {
382
+ final newTranscript = < LlmModel , List <ChatMessage >> {};
383
+ for (final key in transcript.keys) {
384
+ newTranscript[key] = List <ChatMessage >.from (transcript[key]! );
385
+ }
386
+ return newTranscript;
387
+ }
388
+
389
+ TranscriptState addMessage (ChatMessage message, LlmModel model) {
390
+ final newTranscript = _copyTranscript ();
391
+ newTranscript[model]! .add (message);
392
+ return copyWith (transcript: newTranscript);
393
+ }
394
+
395
+ TranscriptState extendMessage (
396
+ String chunk, {
397
+ required int index,
398
+ required LlmModel model,
399
+ required bool first,
400
+ required bool last,
401
+ }) {
402
+ final newTranscript = _copyTranscript ();
403
+ assert (() {
404
+ if (newTranscript[model]! .length < index + 1 ) {
405
+ throw Exception ('Tried to add to index $index , but length is '
406
+ 'only ${newTranscript [model ]!.length } for $model ' );
407
+ }
408
+ return true ;
409
+ }());
410
+ final oldMessage = newTranscript[model]! [index];
411
+ final newMessage = oldMessage.copyWith (
412
+ body: '${oldMessage .body }$chunk ' .sanitize (first, last),
413
+ );
414
+ newTranscript[model]! [index] = newMessage;
415
+ return copyWith (transcript: newTranscript);
416
+ }
417
+
418
+ TranscriptState completeMessage (LlmModel model) {
419
+ final newTranscript = _copyTranscript ();
420
+ newTranscript[model]! .last =
421
+ newTranscript[model]! .last.copyWith (isComplete: true );
422
+ return copyWith (transcript: newTranscript);
423
+ }
421
424
}
422
425
423
426
@Freezed ()
@@ -435,7 +438,6 @@ class TranscriptEvent with _$TranscriptEvent {
435
438
UpdateTemperature ;
436
439
const factory TranscriptEvent .updateTopK (int value) = UpdateTopK ;
437
440
const factory TranscriptEvent .updateMaxTokens (int value) = UpdateMaxTokens ;
438
- const factory TranscriptEvent .setTranscript (LlmModel model) = SetTranscript ;
439
441
const factory TranscriptEvent .addMessage (
440
442
ChatMessage message, LlmModel model) = AddMessage ;
441
443
const factory TranscriptEvent .extendMessage ({
0 commit comments