@@ -380,6 +380,7 @@ final class AIChatViewModel {
380380
381381 // Capture value types on main actor before detaching
382382 let chatMessages = Array ( messages. dropLast ( ) )
383+ let assistantIndex = messages. count - 1
383384
384385 streamingTask = Task . detached ( priority: . userInitiated) { [ weak self] in
385386 do {
@@ -389,17 +390,56 @@ final class AIChatViewModel {
389390 systemPrompt: systemPrompt
390391 )
391392
393+ // Batch tokens off the main actor, flush on interval
394+ var pendingContent = " "
395+ var pendingUsage : AITokenUsage ?
396+ let flushInterval : ContinuousClock . Duration = . milliseconds( 80 )
397+ var lastFlushTime : ContinuousClock . Instant = . now
398+
392399 for try await event in stream {
393400 guard !Task. isCancelled else { break }
401+ switch event {
402+ case . text( let token) :
403+ pendingContent += token
404+ case . usage( let usage) :
405+ pendingUsage = usage
406+ }
407+
408+ if ContinuousClock . now - lastFlushTime >= flushInterval {
409+ let content = pendingContent
410+ let usage = pendingUsage
411+ pendingContent = " "
412+ pendingUsage = nil
413+ await MainActor . run { [ weak self] in
414+ guard let self,
415+ assistantIndex < self . messages. count,
416+ self . messages [ assistantIndex] . id == assistantID
417+ else { return }
418+ if !content. isEmpty {
419+ self . messages [ assistantIndex] . content += content
420+ }
421+ if let usage {
422+ self . messages [ assistantIndex] . usage = usage
423+ }
424+ }
425+ lastFlushTime = . now
426+ }
427+ }
428+
429+ // Final flush — deliver remaining buffered tokens
430+ if !Task. isCancelled, !pendingContent. isEmpty || pendingUsage != nil {
431+ let content = pendingContent
432+ let usage = pendingUsage
394433 await MainActor . run { [ weak self] in
395434 guard let self,
396- let idx = self . messages. firstIndex ( where: { $0. id == assistantID } )
435+ assistantIndex < self . messages. count,
436+ self . messages [ assistantIndex] . id == assistantID
397437 else { return }
398- switch event {
399- case . text ( let token ) :
400- self . messages [ idx ] . content += token
401- case . usage ( let usage) :
402- self . messages [ idx ] . usage = usage
438+ if !content . isEmpty {
439+ self . messages [ assistantIndex ] . content += content
440+ }
441+ if let usage {
442+ self . messages [ assistantIndex ] . usage = usage
403443 }
404444 }
405445 }
0 commit comments