fix(asr): ASR 数据不丢失三件套 — partial 缓存 + 末帧排队 + Whisper 失败保留 (closes #54 #55 #56)#74
Merged
Conversation
closes #54, #55, #56 三件事,同一目标:用户的口述内容不能因为网络抖动 / 服务端关连接 / 凭证错误 而消失。 ## #54 partial cache (volcengine.rs) - SyncState 加 last_partial_text;每次非 final result 缓存当前累积文本 - 服务端 close(无 final 帧)和 receive loop Err(网络中断)路径不再直接 NoFinalResult - 新增 fallback_to_partial_or_error:有 partial 就 signal_success(RawTranscript), 没有才 signal_error;外层 await_final_result 拿到的是已识别那段文字而非空 - 实测命中:网络抖动断线,用户至少拿到断线前那段已识别的话 ## #55 audio finalize race (volcengine.rs) - 新增 pending_sends: AtomicUsize + send_done: Notify - consume_pcm_chunk 每个 spawn fetch_add(1),spawn 内发送完毕 fetch_sub(1) + notify - send_last_frame 进函数先 await pending_sends == 0(800ms 上限避网络极端场景) 再发 leftover + NegativeSequence 末帧 - 防止:fire-and-forget 的 chunk-send 与末帧并发,末帧先到服务端 → 后续 chunk 被当成"流已结束"后多余帧丢弃 → 尾句吞掉 ## #56 Whisper buffer 保留 (whisper.rs) - 之前 transcribe 一进函数就 mem::take 把 buffer 清空,凭证错 / 网络挂 / 解析失败 都让 PCM 直接消失,无法重试也无法 fallback - 改为先 clone (~960 KB / 30s 音频) → transcribe_inner → 成功才 clear buffer - 失败时 buffer 保持,给上层"再试一次"或"留失败历史记录"留余地
Reviewer's GuideAdds robustness to ASR pipelines to ensure recognized speech is not lost by caching partial transcripts in Volcengine streaming ASR, enforcing send ordering for the last frame, and preserving Whisper batch PCM buffers on failures, with minimal API surface changes. Sequence diagram for Volcengine fallback_to_partial_or_error on close or network errorsequenceDiagram
participant Service as VolcengineASRService
participant VolcengineStreamingASR as Volcengine
participant Coordinator
loop partial_results
Service-->>Volcengine: partial_transcript(text, has_final=false)
Volcengine->>Volcengine: on_transcript_message(text, has_final=false)
Volcengine->>Volcengine: state.last_partial_text = text
end
alt normal_final
Service-->>Volcengine: final_transcript(text, has_final=true)
Volcengine->>Volcengine: on_transcript_message(text, has_final=true)
Volcengine->>Coordinator: signal_success(RawTranscript{text, duration_ms})
else close_without_final
Service-->>Volcengine: websocket_close
Volcengine->>Volcengine: fallback_to_partial_or_error(NoFinalResult)
alt last_partial_text is not empty
Volcengine->>Coordinator: signal_success(RawTranscript{last_partial_text, duration_ms})
else no_partial_cached
Volcengine->>Coordinator: signal_error(NoFinalResult)
end
else network_error
Service--xVolcengine: connection_error(e)
Volcengine->>Volcengine: fallback_to_partial_or_error(ConnectionFailed)
alt last_partial_text is not empty
Volcengine->>Coordinator: signal_success(RawTranscript{last_partial_text, duration_ms})
else no_partial_cached
Volcengine->>Coordinator: signal_error(ConnectionFailed)
end
end
Class diagram for updated ASR components (VolcengineStreamingASR, SyncState, WhisperBatchASR)classDiagram
class SyncState {
+bool is_connected
+Option~Sender_RawTranscript_or_VolcengineASRError~~ final_tx
+Option~Handle~ runtime
+Option~Instant~ start
+String last_partial_text
}
class VolcengineStreamingASR {
+ParkingMutex~SyncState~ state
+SharedWriter writer
+ParkingMutex~Option~Receiver_RawTranscript_or_VolcengineASRError~~ final_rx
+Arc~AtomicUsize~ pending_sends
+Arc~Notify~ send_done
+start_stream() Result
+handle_incoming() void
+send_last_frame() Result
+on_transcript_message(full_text String, has_final bool) void
+signal_success(transcript RawTranscript) void
+signal_error(err VolcengineASRError) void
-fallback_to_partial_or_error(err VolcengineASRError) void
}
class WhisperBatchASR {
+Mutex~Vec_u8~ buffer
+String base_url
+Option~String~ api_key
+transcribe() Result~RawTranscript~
-transcribe_inner(pcm BytesRef) Result~RawTranscript~
}
class AudioConsumer {
<<trait>>
+consume_pcm_chunk(pcm Vec_u8, seq u64) void
+send_last_frame() Result
}
VolcengineStreamingASR ..|> AudioConsumer
VolcengineStreamingASR --> SyncState
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
This was referenced Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
单一原则:用户的话不能丢
三个独立但同源的丢字 bug,都在 ASR 边界上。集中一个 PR 因为它们共享 "asr/" 文件域、共享测试场景、互不冲突。
#54 partial 缓存兜底(volcengine.rs)
服务端在 final 帧前关连接 / 网络中断时,原本 `signal_error(NoFinalResult)` → 上层拿到空 → 已识别的话全丢。
新增 `last_partial_text`,关连接路径走 `fallback_to_partial_or_error`:有 partial 就 success(partial),没有才 error。
#55 末帧排队(volcengine.rs)
`consume_pcm_chunk` fire-and-forget spawn 发 chunk;`send_last_frame` 直接发 NegativeSequence 末帧 → 末帧可能先于尾部 chunk 到服务端 → 后续 chunk 被当成 "stream 已结束" 之后的多余数据丢弃 → 尾句吞掉。
`pending_sends: AtomicUsize` + `Notify`:末帧前等所有 chunk 发送完成(800ms 上限)。
#56 Whisper 失败保留(whisper.rs)
`transcribe` 一进函数就 `mem::take` 清空 buffer,凭证错 / 网络挂 / 解析失败都让 PCM 直接消失,无法重试。
改为 clone → `transcribe_inner` → 成功才 clear。
Test plan
Summary by Sourcery
Ensure ASR never silently drops recognized or recorded user speech by adding fallbacks and send-order safeguards to Volcengine streaming ASR and preserving audio on Whisper batch failures.
Bug Fixes:
Enhancements: