Ajoute le moteur faster-whisper, pour les machines sans Apple Silicon
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
5cf8f448ed
commit
6974a3406c
@ -59,4 +59,12 @@ def choisir_transcripteur(nom, modele, langue, amorce):
|
|||||||
if nom == "mlx":
|
if nom == "mlx":
|
||||||
from moteur.mlx import TranscripteurMLX
|
from moteur.mlx import TranscripteurMLX
|
||||||
return TranscripteurMLX(modele, langue, amorce)
|
return TranscripteurMLX(modele, langue, amorce)
|
||||||
|
if nom in ("faster-cpu", "faster-gpu"):
|
||||||
|
# C'est ICI, et nulle part dans TranscripteurFasterWhisper, que le
|
||||||
|
# peripherique et le type de calcul sont decides : la classe reste
|
||||||
|
# testable sans jamais sonder de vrai materiel.
|
||||||
|
from moteur.faster import TranscripteurFasterWhisper
|
||||||
|
peripherique = "cuda" if nom == "faster-gpu" else "cpu"
|
||||||
|
type_calcul = "float16" if nom == "faster-gpu" else "int8"
|
||||||
|
return TranscripteurFasterWhisper(modele, langue, amorce, peripherique, type_calcul)
|
||||||
raise NotImplementedError(f"Moteur non pris en charge à cette étape : {nom}")
|
raise NotImplementedError(f"Moteur non pris en charge à cette étape : {nom}")
|
||||||
|
|||||||
48
moteur/faster.py
Normal file
48
moteur/faster.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""Transcription par faster-whisper (CTranslate2) — tourne partout : Mac Intel,
|
||||||
|
Windows avec ou sans GPU. Complement de moteur/mlx.py, qui lui est reserve a
|
||||||
|
l'Apple Silicon.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from core import clean_transcript
|
||||||
|
|
||||||
|
|
||||||
|
class TranscripteurFasterWhisper:
|
||||||
|
"""Implementation du contrat Transcripteur pour faster-whisper.
|
||||||
|
|
||||||
|
Le peripherique (`cpu`/`cuda`) et le type de calcul (`int8`/`float16`...)
|
||||||
|
sont recus en parametres plutot que devines ici : cette classe ne sonde
|
||||||
|
jamais le materiel elle-meme. C'est `moteur/__init__.py::choisir_transcripteur`
|
||||||
|
qui tranche, d'apres `_cuda_disponible` — garder la decision hors de cette
|
||||||
|
classe la rend testable sans machine CUDA ni GPU.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, modele, langue, amorce, peripherique, type_calcul):
|
||||||
|
self.modele = modele
|
||||||
|
self.langue = langue
|
||||||
|
self.amorce = amorce
|
||||||
|
self.peripherique = peripherique
|
||||||
|
self.type_calcul = type_calcul
|
||||||
|
self._instance = None # modele CTranslate2 charge paresseusement
|
||||||
|
|
||||||
|
def warmup(self):
|
||||||
|
"""Charge le modele une fois, sur une seconde de silence."""
|
||||||
|
self.transcrire(np.zeros(16000, dtype="float32"))
|
||||||
|
|
||||||
|
def transcrire(self, audio):
|
||||||
|
# Import differe dans la methode, jamais au niveau module : sinon toute
|
||||||
|
# machine sans faster-whisper installe casserait a l'import de ce
|
||||||
|
# fichier, y compris pour les tests qui n'exercent que la fabrique
|
||||||
|
# (meme raison que moteur/mlx.py pour mlx_whisper).
|
||||||
|
from faster_whisper import WhisperModel
|
||||||
|
|
||||||
|
if self._instance is None:
|
||||||
|
self._instance = WhisperModel(
|
||||||
|
self.modele, device=self.peripherique, compute_type=self.type_calcul
|
||||||
|
)
|
||||||
|
segments, _info = self._instance.transcribe(
|
||||||
|
audio, language=self.langue, initial_prompt=self.amorce
|
||||||
|
)
|
||||||
|
# faster-whisper rend un generateur de segments, pas un texte unique :
|
||||||
|
# il faut le consommer entierement avant de pouvoir le joindre.
|
||||||
|
return clean_transcript("".join(segment.text for segment in segments))
|
||||||
@ -1,4 +1,9 @@
|
|||||||
|
# mlx-whisper : GPU Apple Silicon uniquement (moteur "mlx", machine actuelle).
|
||||||
mlx-whisper>=0.4.3
|
mlx-whisper>=0.4.3
|
||||||
|
# faster-whisper : coeur CTranslate2, tourne partout (Mac Intel, Windows avec ou
|
||||||
|
# sans GPU). Les deux coexistent car aucun des deux ne couvre a lui seul toutes
|
||||||
|
# les plateformes visees par le portage — cf moteur/__init__.py::nom_moteur_pour.
|
||||||
|
faster-whisper>=1.0
|
||||||
sounddevice>=0.5.5
|
sounddevice>=0.5.5
|
||||||
numpy>=2.4
|
numpy>=2.4
|
||||||
pynput>=1.8.2
|
pynput>=1.8.2
|
||||||
|
|||||||
@ -64,3 +64,23 @@ def test_cuda_sonde_ne_leve_jamais_meme_si_le_chargeur_explose():
|
|||||||
def charger(nom):
|
def charger(nom):
|
||||||
raise RuntimeError("chargeur casse, pas seulement OSError")
|
raise RuntimeError("chargeur casse, pas seulement OSError")
|
||||||
assert _cuda_disponible(plateforme="win32", charger=charger) is False
|
assert _cuda_disponible(plateforme="win32", charger=charger) is False
|
||||||
|
|
||||||
|
|
||||||
|
# --- faster-whisper : meme garantie que MLX ci-dessus — la fabrique rend un
|
||||||
|
# transcripteur complet sans jamais importer faster_whisper au niveau module,
|
||||||
|
# pour rester executable sur une machine qui ne l'a pas installe.
|
||||||
|
|
||||||
|
def test_la_fabrique_rend_un_transcripteur_faster_cpu_complet():
|
||||||
|
t = choisir_transcripteur(nom="faster-cpu", modele="Systran/faster-whisper-small",
|
||||||
|
langue="fr", amorce="")
|
||||||
|
assert callable(t.warmup) and callable(t.transcrire)
|
||||||
|
assert t.peripherique == "cpu"
|
||||||
|
assert t.type_calcul == "int8"
|
||||||
|
|
||||||
|
|
||||||
|
def test_la_fabrique_rend_un_transcripteur_faster_gpu_complet():
|
||||||
|
t = choisir_transcripteur(nom="faster-gpu", modele="Systran/faster-whisper-small",
|
||||||
|
langue="fr", amorce="")
|
||||||
|
assert callable(t.warmup) and callable(t.transcrire)
|
||||||
|
assert t.peripherique == "cuda"
|
||||||
|
assert t.type_calcul == "float16"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user