fix(voice): instrucțiune de limbă inline + fallback Supertonic pe română
Markerul opac [tts-lang:en] depindea de regula din VOICE_MODE.md, aflată la ~30k caractere distanță în system prompt — modelul o rata pe ~1 din 5 turnuri. Răspunsul în română era apoi respins integral de pocket-tts (English-only) și turnul rămânea fără audio. - src/router.py: markerul poartă instrucțiunea inline (un singur token, acoperit în continuare de strip-ul anti-jailbreak); 5/5 EN la repro - src/voice/tts_stream.py: dacă modelul tot scapă română pe o voce pockettts, blocul cade pe Supertonic (M2, ro) în loc de tăcere; coada TTS transportă (clause, voice, lang) per clauză - tools/tts.py: looks_romanian public (folosit de tts_stream) - personality/VOICE_MODE.md: regula gated pe prefixul [tts-lang:en - tests: 4 teste noi fallback + expectații marker actualizate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,8 +48,18 @@ def _strip_leading_voice_tokens(text: str) -> str:
|
||||
text = stripped
|
||||
|
||||
|
||||
# Instrucțiunea de limbă călătorește inline cu turnul: regula din VOICE_MODE.md
|
||||
# singură (la ~30k caractere distanță în system prompt) e ratată de model pe
|
||||
# ~1 din 5 turnuri (observat 2026-07-11). Fără `]` interior, deci acoperită de
|
||||
# _LEADING_VOICE_TOKEN_RE.
|
||||
_TTS_LANG_EN_MARKER = (
|
||||
"[tts-lang:en — reply entirely in English: the active TTS voice "
|
||||
"cannot speak Romanian] "
|
||||
)
|
||||
|
||||
|
||||
def _voice_turn_lang_marker() -> str:
|
||||
"""`'[tts-lang:en] '` dacă vocea activă de voice mode e pe un engine
|
||||
"""`_TTS_LANG_EN_MARKER` dacă vocea activă de voice mode e pe un engine
|
||||
English-only (pocket-tts), altfel `''`.
|
||||
|
||||
Citește config fresh de pe disc (nu singleton-ul modulului) pentru că
|
||||
@@ -60,7 +70,7 @@ def _voice_turn_lang_marker() -> str:
|
||||
from tools.tts import engine_for_voice
|
||||
voice = Config().get("voice.default_voice", "M2") or "M2"
|
||||
if engine_for_voice(voice) == "pockettts":
|
||||
return "[tts-lang:en] "
|
||||
return _TTS_LANG_EN_MARKER
|
||||
except Exception as e: # noqa: BLE001
|
||||
log.warning("voice lang marker lookup failed: %s", e)
|
||||
return ""
|
||||
|
||||
@@ -22,7 +22,7 @@ from typing import Iterator, List, Optional
|
||||
import discord
|
||||
|
||||
from src.voice.normalize import normalize_for_tts
|
||||
from tools.tts import engine_for_voice, synthesize
|
||||
from tools.tts import DEFAULT_VOICE, engine_for_voice, looks_romanian, synthesize
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -208,13 +208,25 @@ class TTSQueue:
|
||||
# pocket-tts is English-only, so RO number/time/currency expansion
|
||||
# must be skipped or the normalizer injects Romanian diacritics that
|
||||
# pocket-tts refuses to speak (silent dropped clause).
|
||||
lang = "en" if engine_for_voice(self.voice_id) == "pockettts" else "ro"
|
||||
voice = self.voice_id
|
||||
english_only = engine_for_voice(voice) == "pockettts"
|
||||
if english_only and looks_romanian(text):
|
||||
# Claude a încălcat regula [tts-lang:en] — pocket-tts ar respinge
|
||||
# fiecare clauză și turnul ar fi tăcere totală. Mai bine vocea
|
||||
# Supertonic implicită în română decât nimic.
|
||||
log.warning(
|
||||
"pocket-tts voice %r got Romanian text — falling back to "
|
||||
"Supertonic %r for this block", voice, DEFAULT_VOICE,
|
||||
)
|
||||
voice = DEFAULT_VOICE
|
||||
english_only = False
|
||||
lang = "en" if english_only else "ro"
|
||||
cleaned = normalize_for_tts(text, lang=lang)
|
||||
n = 0
|
||||
for clause in clause_segments(cleaned):
|
||||
clause = clause.strip()
|
||||
if clause:
|
||||
self._text_queue.put(clause)
|
||||
self._text_queue.put((clause, voice, lang))
|
||||
n += 1
|
||||
log.info("ttsq.push_text: input %d chars → %d clauses queued", len(text), n)
|
||||
|
||||
@@ -257,11 +269,12 @@ class TTSQueue:
|
||||
continue
|
||||
if item is _POISON:
|
||||
break
|
||||
if not isinstance(item, str):
|
||||
if not isinstance(item, tuple):
|
||||
continue
|
||||
preview = item[:60]
|
||||
clause, voice, lang = item
|
||||
preview = clause[:60]
|
||||
try:
|
||||
result = synthesize(item, voice=self.voice_id, lang=self.lang)
|
||||
result = synthesize(clause, voice=voice, lang=lang)
|
||||
except Exception as e:
|
||||
log.warning("TTS synth raised for %r: %s", preview, e)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user