fix(flow): drain trailing audio and harden chunked ASR pipeline
- P0/P1: tail-drain state machine in FlowContinuousCapture with converter flush; FlowSessionManager awaits drain before finalize - P2: FlowCaptureTailDrain policy/tracker, pipeline diagnostics, unit tests - P3: short final chunk merged re-transcription in ChunkedUtterancePipeline - P4: UtteranceTranscriptStitcher composedSafely fallback; preview path parity in LiveDictationController Co-authored-by: Rocky <hkgood@users.noreply.github.com>
This commit is contained in:
@@ -11,4 +11,8 @@ enum FlowDiagnostics {
|
||||
static func log(_ message: String) {
|
||||
OSGLog.flow.info("\(message, privacy: .public)")
|
||||
}
|
||||
|
||||
static func logDrain(_ report: FlowCaptureDrainReport) {
|
||||
FlowPipelineDiagnostics.logDrain(report)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,20 +485,20 @@ final class FlowSessionManager: ObservableObject {
|
||||
|
||||
// Close the mic gate first, then mark processing before dropping the
|
||||
// recording flag so the poll loop cannot start a second utterance.
|
||||
capture.endUtterance()
|
||||
FlowSessionBridge.setRecordingState(.processing)
|
||||
isUtteranceRecording = false
|
||||
isUtteranceProcessing = true
|
||||
|
||||
// Do NOT cancel `asrTask` or `asr` — the preview pipeline relies on
|
||||
// the consumer staying alive until `.final` lands (see
|
||||
// `PreviewASRControllerStateTests`).
|
||||
// Do NOT cancel `asrTask` or `asr` — drain trailing PCM, then finalize.
|
||||
|
||||
finalizeTask?.cancel()
|
||||
finalizeTask = Task { @MainActor [weak self] in
|
||||
await self?.finalizeUtterance()
|
||||
guard let self else { return }
|
||||
let drainReport = await self.capture.endUtteranceAndDrain()
|
||||
FlowDiagnostics.logDrain(drainReport)
|
||||
await self.finalizeUtterance()
|
||||
}
|
||||
debug("utterance stopped, finalizing")
|
||||
debug("utterance stopped, draining tail")
|
||||
}
|
||||
|
||||
private func abortUtterance() {
|
||||
|
||||
Reference in New Issue
Block a user