forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLiveNotesAccumulator.swift
More file actions
130 lines (108 loc) · 4.56 KB
/
Copy pathLiveNotesAccumulator.swift
File metadata and controls
130 lines (108 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import Foundation
struct LiveNotesGenerationRequest: Equatable {
let recentText: String
let existingNotesText: String
let segmentStartOrder: Int
let segmentEndOrder: Int
}
struct LiveNotesAccumulator {
let wordThreshold: Int
let maxWordBufferSize: Int
let maxExistingNotesContext: Int
private(set) var wordBuffer: [String] = []
private(set) var existingNotesContext: [String] = []
private(set) var currentSegmentOrder: Int = 0
private var processedSegmentWordCounts: [String: Int] = [:]
private var wordsSinceLastGeneration = 0
/// Number of words included in the most recent generation request's window.
/// `markGenerationSucceeded` decrements the unsummarized counter by exactly
/// this, so words that arrived while the generation was in flight are kept.
private var wordsInFlightForGeneration = 0
init(
wordThreshold: Int = 50,
maxWordBufferSize: Int = 500,
maxExistingNotesContext: Int = 20
) {
self.wordThreshold = wordThreshold
self.maxWordBufferSize = maxWordBufferSize
self.maxExistingNotesContext = maxExistingNotesContext
}
mutating func reset() {
wordBuffer = []
existingNotesContext = []
currentSegmentOrder = 0
processedSegmentWordCounts = [:]
wordsSinceLastGeneration = 0
wordsInFlightForGeneration = 0
}
mutating func seedExistingNotes(_ notes: [String]) {
existingNotesContext = trimmedContext(notes)
}
mutating func appendExistingNote(_ note: String) {
existingNotesContext.append(note)
trimExistingNotesContext()
}
mutating func handleSegmentsUpdate(
_ segments: [SpeakerSegment],
isGenerating: Bool
) -> LiveNotesGenerationRequest? {
currentSegmentOrder = segments.count
let currentSegmentIds = Set(segments.map(\.id))
processedSegmentWordCounts = processedSegmentWordCounts.filter { currentSegmentIds.contains($0.key) }
let newWords = segments.flatMap { segment in
let words = segment.text.split(separator: " ").map(String.init)
let processedCount = processedSegmentWordCounts[segment.id] ?? 0
processedSegmentWordCounts[segment.id] = words.count
guard words.count > processedCount else { return [String]() }
return Array(words.dropFirst(processedCount))
}
guard !newWords.isEmpty else { return nil }
wordBuffer.append(contentsOf: newWords)
wordsSinceLastGeneration += newWords.count
trimWordBuffer()
// A word can't be "unsummarized" if it was already trimmed out of the
// buffer, so cap the counter at the buffered word count. Without this, an
// extreme burst larger than the buffer would leave a residual counter that
// re-summarizes the same tail forever.
wordsSinceLastGeneration = min(wordsSinceLastGeneration, wordBuffer.count)
guard wordsSinceLastGeneration >= wordThreshold, !isGenerating else {
return nil
}
// Summarize ALL unsummarized words, not just the last `wordThreshold`. When
// a single update (or accumulation while a prior generation was in flight)
// brings more than `wordThreshold` new words, a fixed suffix(wordThreshold)
// window dropped the middle span and re-summarized the tail.
let generationWindow = wordsSinceLastGeneration
wordsInFlightForGeneration = generationWindow
return LiveNotesGenerationRequest(
recentText: wordBuffer.suffix(generationWindow).joined(separator: " "),
existingNotesText: existingNotesText(),
segmentStartOrder: max(0, currentSegmentOrder - 3),
segmentEndOrder: currentSegmentOrder
)
}
mutating func markGenerationSucceeded(noteText: String) {
// Decrement only by the window that was actually summarized, so words that
// arrived while this generation was in flight remain unsummarized.
wordsSinceLastGeneration = max(0, wordsSinceLastGeneration - wordsInFlightForGeneration)
wordsInFlightForGeneration = 0
appendExistingNote(noteText)
}
private mutating func trimWordBuffer() {
guard wordBuffer.count > maxWordBufferSize else { return }
wordBuffer.removeFirst(wordBuffer.count - maxWordBufferSize)
}
private mutating func trimExistingNotesContext() {
existingNotesContext = trimmedContext(existingNotesContext)
}
private func trimmedContext(_ notes: [String]) -> [String] {
guard notes.count > maxExistingNotesContext else { return notes }
return Array(notes.suffix(maxExistingNotesContext))
}
private func existingNotesText() -> String {
if existingNotesContext.isEmpty {
return "No existing notes yet."
}
return "Existing notes:\n" + existingNotesContext.map { "- \($0)" }.joined(separator: "\n")
}
}