From 27a949f1ff1828a2c24c6b8dec7557d5c29205c5 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Thu, 28 May 2026 09:13:12 +0200 Subject: [PATCH] Convert conversation audio to 16kHz for STT (fixes whisperx alignment crash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STT models (Whisper, WhisperX VAD, wav2vec2 alignment) all expect 16kHz. Sending 24kHz caused whisperx's VAD to miss speech segments, leaving alignment with None inputs → 'NoneType has no attribute to' crash. Added _to_wav_16k() and use it in conversation/turn and transcribe-bytes endpoints. Health check probe also uses 16kHz silence for consistency. Co-Authored-By: Claude Sonnet 4.6 --- server.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/server.py b/server.py index 4c79eb1..0347998 100644 --- a/server.py +++ b/server.py @@ -906,6 +906,15 @@ def _to_wav_24k(src: Path) -> Path: return out +def _to_wav_16k(src: Path) -> Path: + """16kHz mono WAV — required by Whisper/WhisperX VAD and wav2vec2 alignment.""" + out = TEMP_DIR / f"{src.stem}_16k.wav" + seg = AudioSegment.from_file(str(src)) + seg = seg.set_frame_rate(16000).set_channels(1).set_sample_width(2) + seg.export(str(out), format="wav") + return out + + def _trim(src: Path, start_s: float, end_s: float) -> Path: seg = AudioSegment.from_file(str(src)) trimmed = seg[int(start_s * 1000):int(end_s * 1000)] @@ -1903,9 +1912,8 @@ def _stt_backend_api_key(settings: dict, backend: str) -> str: return settings.get("whisper_api_key", "").strip() -def _make_minimal_wav(duration_ms: int = 500) -> bytes: - """Minimal WAV: mono 16-bit 16kHz silence of given duration.""" - sample_rate = 16000 +def _make_minimal_wav(duration_ms: int = 500, sample_rate: int = 16000) -> bytes: + """Minimal WAV: mono 16-bit silence of given duration at given sample rate.""" num_frames = sample_rate * duration_ms // 1000 data = b"\x00\x00" * num_frames header = struct.pack( @@ -2131,7 +2139,7 @@ async def transcribe_bytes( with tmp.open("wb") as f: _copy_limited(file.file, f, _MAX_UPLOAD_BYTES) if suffix != ".wav": - wav_tmp = _to_wav_24k(tmp) + wav_tmp = _to_wav_16k(tmp) settings = _load_settings() stt_backend = _clean_stt_backend(backend) text, used_backend = await asyncio.to_thread(_transcribe_audio, wav_tmp, settings, stt_backend) @@ -5377,7 +5385,8 @@ async def conversation_turn( try: with tmp.open("wb") as f: _copy_limited(audio.file, f, _MAX_UPLOAD_BYTES) - wav_tmp = tmp if suffix == ".wav" else _to_wav_24k(tmp) + # STT models (Whisper, WhisperX, wav2vec2 alignment) all expect 16kHz + wav_tmp = _to_wav_16k(tmp) except Exception as e: tmp.unlink(missing_ok=True) raise HTTPException(400, f"Audio upload failed: {e}")