Funk-lab/stt/client/stt/voice/asr/whisper.py
alkatrazz f23c52b694 feat(stt-client): ASR enfichable — backend onnx Parakeet/Nemotron en plus de whisper
Phase 0 (abstraction) + Phase 1 (backend onnx), additif et rétrocompatible.

- voice/asr/ : protocole ASRBackend (load + transcribe) + factory make_backend
  pilotée par cfg["asr_engine"]. Imports lourds lazy → stt --help/config OK sans
  aucun moteur installé.
- whisper.py : faster-whisper extrait tel quel (défaut, comportement inchangé).
- onnx_parakeet.py : onnx-asr (Parakeet/Canary/Nemotron ONNX, multilingue FR,
  ~600M, CPU rapide / GPU AMD via onnxruntime-rocm). Providers configurables ;
  message d'install actionnable si onnx-asr absent.
- engine.py : délègue load()/_transcribe() au backend (le reste — VAD, wake word,
  TTS — inchangé).
- config : [voice] asr_engine="whisper" (défaut), onnx_model, onnx_providers.
- pyproject : extras optionnels onnx / onnx-rocm ; bump 0.6.1 → 0.7.0.

Bascule à chaud par config, rollback = asr_engine="whisper". Streaming = phase 2
(spike séparé). admin/ia/stt.md mis à jour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 14:17:31 +02:00

36 lines
1 KiB
Python

"""Backend ASR par défaut : faster-whisper (CTranslate2), CPU int8.
Extrait tel quel de l'ancien `VoiceEngine.load()`/`_transcribe()` — comportement
inchangé (c'est le défaut tant que `asr_engine != "onnx"`).
"""
from __future__ import annotations
from typing import Any
class WhisperBackend:
def __init__(self, cfg: dict[str, Any]):
self.cfg = cfg
self._model = None
def load(self) -> None:
from faster_whisper import WhisperModel
self._model = WhisperModel(
self.cfg["whisper_model"], device="cpu", compute_type="int8"
)
def transcribe(self, audio_int16) -> str:
import numpy as np
audio_f32 = audio_int16.astype(np.float32) / 32768.0
segments, _ = self._model.transcribe(
audio_f32,
language=self.cfg.get("language", "fr"),
task="transcribe",
beam_size=3,
vad_filter=True,
initial_prompt="Transcription en français.",
)
return " ".join(seg.text for seg in segments).strip()