fix(tts): pocket-tts refuză text cu diacritice RO în loc să-l pronunțe greșit
Kyutai pocket-tts nu suportă română (vezi eval anterior). Codul rula oricum sinteza pe orice text primit; acum _synthesize_pockettts respinge diacriticele RO înainte de request, fără fallback silențios pe altă voce. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,7 @@
|
||||
#### TTS: engine pocket-tts + Supertonic
|
||||
- **Motoare:** `supertonic` (voci predefinite M1-M5/F1-F5, server local `:7788`) și `pockettts` (Kyutai pocket-tts, voice cloning din sample WAV, server local `:7789`, config `tts.pockettts_url`).
|
||||
- **Rutare (`tools/tts.py::synthesize`):** engine-ul se decide din catalogul `tts_voices.json` — fiecare voce are un `engine` asociat. Voce necatalogată → cade pe `tts.default_engine` din `config.json`. Dacă pocket-tts e indisponibil tehnic (connect error / 5xx) → fallback automat pe Supertonic pentru cererea curentă; erorile de conținut (voce inexistentă, fișier `.safetensors` lipsă) NU declanșează fallback, se raportează direct.
|
||||
- **pocket-tts e engleză-only:** modelul nu suportă română. `tools/tts.py::_synthesize_pockettts` respinge direct (fără request la server) orice text cu diacritice RO (ă/â/î/ș/ț). Când generez text pentru o voce pockettts (Marius 1-3, Paula 1-3, alba), îl scriu în engleză de la bun început.
|
||||
- **Catalog voci:** `tts_voices.json` — sursă live pentru autocomplete pe `/audio` și `/voice setvoice` (o voce nouă adăugată apare imediat, fără redeploy). Intrare pocket-tts: `{"engine": "pockettts", "state_path": "models/voices/<slug>.safetensors"}`. Intrare supertonic: `{"engine": "supertonic"}`.
|
||||
- **`/voice engine <pockettts|Supertonic>`:** schimbă `tts.default_engine` în config.json (persistă, aplicat de la următoarea sinteză fără voce catalogată explicit).
|
||||
- **`/voice setvoice <voce>`:** schimbă vocea implicită (autocomplete din `tts_voices.json`); dacă sunt deja în voice channel, swap live pe sesiunea curentă.
|
||||
|
||||
@@ -128,6 +128,24 @@ class TestPockettsFallback:
|
||||
assert result["ok"] is False
|
||||
assert "400" in result["error"]
|
||||
|
||||
@patch("httpx.post")
|
||||
def test_romanian_diacritics_rejected_without_calling_server(self, mock_post):
|
||||
result = _synthesize_pockettts("Salut, cum îți este ziua?", {})
|
||||
assert result["ok"] is False
|
||||
assert "engleză" in result["error"]
|
||||
mock_post.assert_not_called()
|
||||
|
||||
@patch("tools.tts._load_voice_catalog")
|
||||
@patch("httpx.post")
|
||||
def test_romanian_text_does_not_fall_back_to_supertonic(self, mock_post, mock_catalog):
|
||||
mock_catalog.return_value = {"Marius 1": {"engine": "pockettts"}}
|
||||
|
||||
result = synthesize("Bună dimineața!", voice="Marius 1")
|
||||
|
||||
mock_post.assert_not_called()
|
||||
assert result["ok"] is False
|
||||
assert result["engine_used"] == "pockettts"
|
||||
|
||||
|
||||
class TestSupertonicLangNaRetry:
|
||||
@patch("tools.tts._load_voice_catalog")
|
||||
|
||||
15
tools/tts.py
15
tools/tts.py
@@ -53,6 +53,15 @@ class _PocketTTSUnavailable(Exception):
|
||||
"""pocket-tts server e nereachable sau eșuează server-side (5xx) — caller-ul trebuie să facă fallback pe Supertonic."""
|
||||
|
||||
|
||||
# pocket-tts e monolingv engleză per deployment — diacriticele RO sunt un semnal
|
||||
# sigur că textul n-a fost tradus (vezi personality/TOOLS.md).
|
||||
_RO_DIACRITICS = set("ăâîșțĂÂÎȘȚşţŞŢ")
|
||||
|
||||
|
||||
def _looks_romanian(text: str) -> bool:
|
||||
return any(ch in _RO_DIACRITICS for ch in text)
|
||||
|
||||
|
||||
def sanitize_for_supertonic(text: str) -> str:
|
||||
"""Replace Unicode punctuation and strip chars that crash Supertonic's ONNX model."""
|
||||
for src, dst in _TTS_PUNCT_MAP.items():
|
||||
@@ -172,6 +181,12 @@ def _synthesize_pockettts(text: str, entry: dict) -> dict:
|
||||
Raises:
|
||||
_PocketTTSUnavailable: eșec tehnic (connect error/timeout/5xx) — caller-ul face fallback.
|
||||
"""
|
||||
if _looks_romanian(text):
|
||||
return {
|
||||
"ok": False,
|
||||
"error": "pocket-tts nu suportă română — textul trebuie să fie în engleză.",
|
||||
}
|
||||
|
||||
state_path = entry.get("state_path")
|
||||
voice_url = entry.get("voice_url")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user