Skip to content

Commit c1c17ed

Browse files
committed
perf: widen safe brain prefetch reuse for stt drift
1 parent 6ebeea5 commit c1c17ed

2 files changed

Lines changed: 111 additions & 2 deletions

File tree

internal/voice/orchestrator.go

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,19 +371,23 @@ func (o *Orchestrator) RunConnection(ctx context.Context, s *session.Session, in
371371
if brainPrefetchResultVal == nil {
372372
return nil
373373
}
374-
if brainPrefetchResultVal.canonicalInput != canonical {
374+
if !brainPrefetchCanonicalCompatible(brainPrefetchResultVal.canonicalInput, canonical) {
375375
return nil
376376
}
377377
if brainPrefetchReadyAt.IsZero() || time.Since(brainPrefetchReadyAt) > brainPrefetchFresh {
378378
return nil
379379
}
380+
wasExactMatch := brainPrefetchResultVal.canonicalInput == canonical
380381
out := &brainPrefetchResult{
381382
canonicalInput: brainPrefetchResultVal.canonicalInput,
382383
deltas: append([]string(nil), brainPrefetchResultVal.deltas...),
383384
finalText: brainPrefetchResultVal.finalText,
384385
}
385386
brainPrefetchResultVal = nil
386387
brainPrefetchReadyAt = time.Time{}
388+
if !wasExactMatch {
389+
o.metrics.SessionEvents.WithLabelValues("brain_prefetch_fuzzy_hit").Inc()
390+
}
387391
return out
388392
}
389393
if ready := getReady(); ready != nil {
@@ -392,7 +396,7 @@ func (o *Orchestrator) RunConnection(ctx context.Context, s *session.Session, in
392396

393397
var done chan struct{}
394398
brainPrefetchMu.Lock()
395-
if brainPrefetchInFlight && brainPrefetchCanonical == canonical {
399+
if brainPrefetchInFlight && brainPrefetchCanonicalCompatible(brainPrefetchCanonical, canonical) {
396400
done = brainPrefetchDone
397401
}
398402
brainPrefetchMu.Unlock()
@@ -2101,6 +2105,63 @@ func shouldSpeculateBrainCanonical(canonical string) bool {
21012105
return words >= brainPrefetchMinWords
21022106
}
21032107

2108+
func brainPrefetchCanonicalCompatible(prefetchedCanonical, committedCanonical string) bool {
2109+
prefetchedCanonical = strings.TrimSpace(prefetchedCanonical)
2110+
committedCanonical = strings.TrimSpace(committedCanonical)
2111+
if prefetchedCanonical == "" || committedCanonical == "" {
2112+
return false
2113+
}
2114+
if prefetchedCanonical == committedCanonical {
2115+
return true
2116+
}
2117+
// Common case: one canonical transcript is an incremental extension/rollback of the other.
2118+
if canonicalIsProgressiveContinuation(prefetchedCanonical, committedCanonical) ||
2119+
canonicalIsProgressiveContinuation(committedCanonical, prefetchedCanonical) {
2120+
return true
2121+
}
2122+
// Guarded fuzzy match for tiny STT corrections:
2123+
// require a strong shared word prefix and only allow one trailing mismatch.
2124+
pWords := strings.Fields(prefetchedCanonical)
2125+
cWords := strings.Fields(committedCanonical)
2126+
minWords := len(pWords)
2127+
if len(cWords) < minWords {
2128+
minWords = len(cWords)
2129+
}
2130+
if minWords < 4 {
2131+
return false
2132+
}
2133+
if absInt(len(pWords)-len(cWords)) > 2 {
2134+
return false
2135+
}
2136+
shared := sharedWordPrefixCount(pWords, cWords)
2137+
return shared >= minWords-1 && shared >= 3
2138+
}
2139+
2140+
func sharedWordPrefixCount(a, b []string) int {
2141+
if len(a) == 0 || len(b) == 0 {
2142+
return 0
2143+
}
2144+
limit := len(a)
2145+
if len(b) < limit {
2146+
limit = len(b)
2147+
}
2148+
count := 0
2149+
for i := 0; i < limit; i++ {
2150+
if a[i] != b[i] {
2151+
break
2152+
}
2153+
count++
2154+
}
2155+
return count
2156+
}
2157+
2158+
func absInt(v int) int {
2159+
if v < 0 {
2160+
return -v
2161+
}
2162+
return v
2163+
}
2164+
21042165
func shouldStartBrainPrefetchEarly(partialText, canonical string, utteranceAge time.Duration) bool {
21052166
words := wordsInCanonical(canonical)
21062167
normalized := normalizeSemanticEndpointText(partialText)

internal/voice/orchestrator_prefetch_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,51 @@ func TestShouldStartBrainPrefetchEarly(t *testing.T) {
9393
})
9494
}
9595
}
96+
97+
func TestBrainPrefetchCanonicalCompatible(t *testing.T) {
98+
cases := []struct {
99+
name string
100+
prefetch string
101+
committed string
102+
want bool
103+
}{
104+
{
105+
name: "exact match",
106+
prefetch: "build api endpoint with auth",
107+
committed: "build api endpoint with auth",
108+
want: true,
109+
},
110+
{
111+
name: "progressive continuation match",
112+
prefetch: "build api endpoint",
113+
committed: "build api endpoint with auth and tests",
114+
want: true,
115+
},
116+
{
117+
name: "tiny trailing correction still matches",
118+
prefetch: "build api endpoint with auth middleware",
119+
committed: "build api endpoint with auth middlewares",
120+
want: true,
121+
},
122+
{
123+
name: "small unrelated tail rewrite does not match",
124+
prefetch: "build api endpoint with auth middleware",
125+
committed: "build api endpoint for markdown parser",
126+
want: false,
127+
},
128+
{
129+
name: "short text is too risky for fuzzy match",
130+
prefetch: "build api now",
131+
committed: "build api tomorrow",
132+
want: false,
133+
},
134+
}
135+
for _, tc := range cases {
136+
tc := tc
137+
t.Run(tc.name, func(t *testing.T) {
138+
if got := brainPrefetchCanonicalCompatible(tc.prefetch, tc.committed); got != tc.want {
139+
t.Fatalf("brainPrefetchCanonicalCompatible(%q, %q) = %v, want %v", tc.prefetch, tc.committed, got, tc.want)
140+
}
141+
})
142+
}
143+
}

0 commit comments

Comments
 (0)