diff --git a/openless-all/app/src-tauri/src/asr/bailian.rs b/openless-all/app/src-tauri/src/asr/bailian.rs index 484a9125..dba2742a 100644 --- a/openless-all/app/src-tauri/src/asr/bailian.rs +++ b/openless-all/app/src-tauri/src/asr/bailian.rs @@ -5,6 +5,7 @@ //! matches OpenLess' recorder output directly. The Qwen OpenAI Realtime line is //! a different protocol and is intentionally left for a follow-up provider. +use std::collections::BTreeMap; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -94,7 +95,9 @@ struct SyncState { start: Option, final_tx: Option>>, send_tx: Option>, - final_segments: Vec, + /// sentence_id → text,按 sentence_id 排序拼接得到最终文本。 + /// 同一 sentence_id 的后到结果覆盖前一个,消除累积文本导致的重复。 + final_segments: BTreeMap, last_result_text: String, } @@ -367,11 +370,22 @@ impl BailianRealtimeASR { if trimmed.is_empty() { return; } - let is_sentence_final = sentence.get("end_time").is_some(); + // end_time 为 0 或缺失时是 interim 结果;仅正数才是真正完成的句子。 + let end_time = sentence + .get("end_time") + .and_then(Value::as_i64) + .unwrap_or(0); + let is_sentence_final = end_time > 0; + let sentence_id = sentence + .get("sentence_id") + .and_then(Value::as_i64) + .unwrap_or(0); let mut st = self.state.lock(); st.last_result_text = trimmed.to_string(); - if is_sentence_final && st.final_segments.last().map(|s| s.as_str()) != Some(trimmed) { - st.final_segments.push(trimmed.to_string()); + if is_sentence_final && sentence_id > 0 { + // 同一 sentence_id 后到覆盖前到:API 对同一句话的累积更新 + // ("你"→"你好"→"你好吗")只保留最终版本。 + st.final_segments.insert(sentence_id, trimmed.to_string()); } } @@ -386,7 +400,7 @@ impl BailianRealtimeASR { let text = if st.final_segments.is_empty() { st.last_result_text.clone() } else { - st.final_segments.join("") + st.final_segments.values().cloned().collect::>().join("") }; let duration_ms = if st.bytes_received > 0 { st.bytes_received / BYTES_PER_MS