[实时字幕端点]:完成无新文字快速结束录音,包含1.5秒停滞配置、Capture端点和回归测试

This commit is contained in:
mkbk
2026-06-18 11:23:20 +08:00
parent 39da9dd09f
commit 1e956f5eb6
13 changed files with 151 additions and 5 deletions
+23 -4
View File
@@ -163,6 +163,7 @@ class TurnController:
self.vad_recorder.provider.reset()
self.audio_preprocessor.reset()
realtime_session = self._start_realtime_transcript()
last_partial_ms: int | None = None
self._event(CAPTURE_STARTED, PipelineState.RECORDING, state_message, turn_id=turn_id)
while True:
frames = self.transport.read_frames(timeout_ms=100)
@@ -180,7 +181,8 @@ class TurnController:
if isinstance(result, ProviderError):
return result
if self.vad_recorder.started and realtime_session is not None:
self._emit_realtime_transcript(realtime_session, frame, turn_id)
if self._emit_realtime_transcript(realtime_session, frame, turn_id):
last_partial_ms = frame.timestamp_ms
if isinstance(result, AudioSegment):
if realtime_session is not None:
self._finish_realtime_transcript(realtime_session, turn_id)
@@ -192,6 +194,18 @@ class TurnController:
payload={"end_reason": result.metadata.get("end_reason", "")},
)
return result
if self._should_end_after_realtime_idle(last_partial_ms, frame.timestamp_ms):
if realtime_session is not None:
self._finish_realtime_transcript(realtime_session, turn_id)
result = self.vad_recorder.finish("partial_transcript_idle")
self._event(
SPEECH_ENDED,
PipelineState.RECORDING,
"用户语音结束",
turn_id=turn_id,
payload={"end_reason": result.metadata.get("end_reason", "")},
)
return result
def _start_realtime_transcript(self) -> RealtimeTranscriptSession | None:
if not self.config.realtime_transcript_enabled or self.realtime_stt is None:
@@ -203,14 +217,19 @@ class TurnController:
realtime_session: RealtimeTranscriptSession,
frame: AudioFrame,
turn_id: int,
) -> None:
) -> bool:
transcript = realtime_session.accept_frame(frame)
if transcript is None:
return
return False
text = transcript.normalized_text
if not is_valid_transcript_text(text):
return
return False
self._event(TRANSCRIPT_PARTIAL, PipelineState.RECORDING, "", turn_id=turn_id, payload={"text": text})
return True
def _should_end_after_realtime_idle(self, last_partial_ms: int | None, current_ms: int) -> bool:
timeout_ms = self.config.realtime_transcript_idle_timeout_ms
return timeout_ms > 0 and last_partial_ms is not None and current_ms - last_partial_ms >= timeout_ms
def _finish_realtime_transcript(
self,