Funk-lab/stt/client/stt/voice/asr/__init__.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.2 KiB
Python

"""Backends ASR enfichables.
L'`VoiceEngine` ne connaît que l'interface `ASRBackend` (`load()` + `transcribe()`) :
le choix du moteur est piloté par `cfg["asr_engine"]`. Les imports lourds
(faster-whisper, onnx-asr) restent dans les sous-modules, chargés à la volée par la
factory → `stt --help` et la config marchent sans aucun moteur installé.
"""
from __future__ import annotations
from typing import Any, Protocol, runtime_checkable
@runtime_checkable
class ASRBackend(Protocol):
"""Transcripteur d'un énoncé entier (audio `np.int16` @16 kHz mono → texte)."""
def load(self) -> None: ...
def transcribe(self, audio_int16) -> str: ...
def make_backend(cfg: dict[str, Any]) -> "ASRBackend":
"""Construit le backend ASR d'après `cfg["asr_engine"]` (défaut : whisper)."""
engine = (cfg.get("asr_engine") or "whisper").lower()
if engine in ("whisper", "faster-whisper"):
from .whisper import WhisperBackend
return WhisperBackend(cfg)
if engine in ("onnx", "onnx-asr", "parakeet", "nemotron", "canary"):
from .onnx_parakeet import OnnxAsrBackend
return OnnxAsrBackend(cfg)
raise ValueError(
f"asr_engine inconnu : {engine!r} (attendu 'whisper' ou 'onnx')"
)