fix(voxtral): willkuerliche Abbrueche — semantischen Endpoint + Live-Partials raus
Ursache: Voxtral-3B transkribiert den ganzen wachsenden Buffer (~5-6s bei langen Aufnahmen). Diese Partial-Latenz war groesser als der semantische Endpoint-Timeout (4.8s) → 'Text waechst nicht mehr' feuerte faelschlich → Abbruch nach 20-40s. Fix: keine Live-Partials mehr, kein semantischer Endpoint — Turn-Ende rein akustisch (Stille-VAD), transkribiert wird nur EINMAL im _finalize. max_new_tokens 512->4096 (512 schnitt lange Diktate ab). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+12
-30
@@ -121,7 +121,9 @@ class VoxtralRunner:
|
||||
)
|
||||
inputs = inputs.to(VOXTRAL_DEVICE, dtype=torch.bfloat16)
|
||||
with torch.no_grad():
|
||||
outputs = model.generate(**inputs, max_new_tokens=512)
|
||||
# hoch genug fuer lange Diktate (stoppt eh am EOS); 512 hat
|
||||
# mehrminutige Aufnahmen abgeschnitten.
|
||||
outputs = model.generate(**inputs, max_new_tokens=4096)
|
||||
trimmed = outputs[:, inputs.input_ids.shape[1]:]
|
||||
text = proc.batch_decode(trimmed, skip_special_tokens=True)
|
||||
return (text[0] if text else "").strip()
|
||||
@@ -284,40 +286,20 @@ class SessionManager:
|
||||
return
|
||||
if self._buffer_ms(sess) < STREAM_MIN_AUDIO_MS:
|
||||
return
|
||||
# adaptive akustische Sprach-Aktivitaet
|
||||
# Adaptive akustische Sprach-Aktivitaet (M0.1). KEINE Live-Partials mehr:
|
||||
# Voxtral-3B transkribiert den ganzen WACHSENDEN Buffer und braucht dafuer
|
||||
# bei langen Aufnahmen 5-6 s — zu langsam fuer Live-Text, UND diese Latenz
|
||||
# hat den semantischen Endpoint faelschlich ausgeloest (Partial-Latenz >
|
||||
# Timeout → willkuerliche Abbrueche nach 20-40 s). Deshalb: Turn-Ende rein
|
||||
# AKUSTISCH, transkribiert wird nur EINMAL im _finalize.
|
||||
rms = self._tail_rms(sess)
|
||||
if rms >= self._voice_threshold(sess):
|
||||
sess.last_voice_at = now
|
||||
else:
|
||||
self._update_noise_floor(sess, rms)
|
||||
# Endpoint-Entscheidung, sobald Text erkannt wurde
|
||||
if sess.last_growth_at > 0.0:
|
||||
ac_sil = (now - sess.last_voice_at) * 1000.0 if sess.last_voice_at > 0 else 0.0
|
||||
se_sil = (now - sess.last_growth_at) * 1000.0
|
||||
ac_done = sess.last_voice_at > 0 and ac_sil >= sess.endpoint_ms
|
||||
se_done = se_sil >= sess.endpoint_ms * STREAM_SEMANTIC_BACKUP_FACTOR
|
||||
if ac_done or se_done:
|
||||
await self._finalize(sess, "endpoint" if ac_done else "endpoint_semantic")
|
||||
return
|
||||
# Partial-Transkription (throttled)
|
||||
if (now - sess.last_transcribe_at) * 1000.0 < STREAM_TRANSCRIBE_INTERVAL_MS:
|
||||
return
|
||||
sess.last_transcribe_at = now
|
||||
audio = pcm_s16le_to_float32(bytes(sess.pcm_buffer))
|
||||
try:
|
||||
text = (await self.runner.transcribe(audio, sess.language)).strip()
|
||||
except Exception:
|
||||
logger.exception("Stream %s: Partial-Transcribe crashed", sess.request_id[:8])
|
||||
return
|
||||
if text and text != sess.last_partial:
|
||||
sess.last_partial = text
|
||||
sess.last_growth_at = now
|
||||
if self._ws is not None:
|
||||
await _send(self._ws, "stt_partial", {
|
||||
"requestId": sess.request_id,
|
||||
"audioRequestId": sess.audio_request_id,
|
||||
"text": text,
|
||||
})
|
||||
# Endpoint: hat der User schon gesprochen UND ist es seit endpoint_ms still?
|
||||
if sess.last_voice_at > 0 and (now - sess.last_voice_at) * 1000.0 >= sess.endpoint_ms:
|
||||
await self._finalize(sess, "endpoint")
|
||||
|
||||
async def _finalize(self, sess: StreamSession, reason: str) -> None:
|
||||
if sess.endpoint_sent:
|
||||
|
||||
Reference in New Issue
Block a user