Remplace la voicebank générée par Kokoro (timbre anglais sur français phonémisé -> accent que Qwen3 clonait) par 41 vraies voix FR issues de CML-TTS (livres audio studio) : 1 narrateur dédié, 18F/14M nommées, 4F/4M anonymes réservées. - scripts/import_voices.py : import multi-shards parquet, 1 clip/locuteur (le plus propre via levenshtein), genre estimé par F0 (YIN, anti-octave), filtre débit de parole (ref_text aligné sur l'audio). - VoiceEntry.anonymous + assign_voices : les figurants « anonyme (...) » tirent dans un pool réservé, jamais mélangé avec les voix nommées ; narrateur dédié (fr_narrator remplace fr_f_siwis). - dedup._anon_attrs : genre/âge déduits du nom anonyme (bon genre de voix). - tts/qwen3.py : garde-fou anti-dérive (rejette/réessaie les sorties en boucle ou coupées en estimant la durée plausible du chunk). Limite connue : Qwen3 ne sait pas synthétiser les fragments d'1-2 mots (incises, titres) -> trous ; à traiter (repli Kokoro ou fusion des incises). Inclut aussi du travail en cours antérieur (refacto backend LLM pluggable mlx/lmstudio, benchmark, ajustements frontend/API). Claude-Session: https://claude.ai/code/session_01XSVvcy1mfb4k1xDgib9vVU
37 lines
1.1 KiB
Python
37 lines
1.1 KiB
Python
"""Selection du backend LLM par nom (pluggable).
|
|
|
|
Calque de `tts/factory.py` : cache par (nom, reference de modele). Une
|
|
sauvegarde des reglages (settings.save_settings) appelle `reset_llm_cache()`
|
|
pour que les changements de backend/modele prennent effet sans redemarrage.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
|
|
from .base import LLMBackend
|
|
|
|
BACKENDS = ("mlx", "lmstudio")
|
|
|
|
|
|
@lru_cache(maxsize=4)
|
|
def get_llm_backend(backend: str = "mlx", model_ref: str = "") -> LLMBackend:
|
|
backend = backend.lower()
|
|
if backend == "mlx":
|
|
from .mlx_backend import MLXBackend
|
|
return MLXBackend(model_ref)
|
|
if backend == "lmstudio":
|
|
from .lmstudio_backend import LMStudioBackend
|
|
return LMStudioBackend(model_ref)
|
|
raise ValueError(
|
|
f"Backend LLM inconnu: {backend!r} (dispo: {', '.join(BACKENDS)})")
|
|
|
|
|
|
def reset_llm_cache() -> None:
|
|
"""Vide les instances de backend et le cache de chargement mlx."""
|
|
get_llm_backend.cache_clear()
|
|
try:
|
|
from .mlx_backend import _load
|
|
_load.cache_clear()
|
|
except Exception: # noqa: BLE001
|
|
pass
|