fix(voice): răspuns în engleză pe voice turns când vocea activă e pocket-tts

Instrucțiunea „engleză pentru audio" nu ajungea la model: VOICE_MODE.md nu
avea nicio regulă de limbă, iar condiția din TOOLS.md („când generez pentru
o voce pockettts") era neevaluabilă — modelul nu știa ce voce e activă.
Rezultat: mirroring pe română → filtrul de diacritice bloca sinteza → tăcere.

- tools/tts.py: helper public engine_for_voice() (catalog → tts.default_engine)
- src/router.py: injectează [tts-lang:en] în prefixul voice când vocea activă
  e pockettts (config citit fresh — vede swap-ul live /voice setvoice);
  anti-jailbreak strip extins la [tts-lang:...]
- personality/VOICE_MODE.md: regulă de limbă gated pe marker
- tests/test_router.py: 5 teste noi pentru marker + strip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 18:07:14 +00:00
parent 321bd49ce6
commit 00e0d1ad8c
5 changed files with 114 additions and 7 deletions

View File

@@ -32,10 +32,11 @@ log = logging.getLogger(__name__)
APPROVED_TASKS_FILE = Path(__file__).parent.parent / "approved-tasks.json"
# Anti-jailbreak: strip user-controlled leading [voice] / [speaker:...]
# tokens so they cannot impersonate the system-injected prefix on voice turns.
# Anti-jailbreak: strip user-controlled leading [voice] / [speaker:...] /
# [tts-lang:...] tokens so they cannot impersonate the system-injected
# prefix on voice turns.
_LEADING_VOICE_TOKEN_RE = re.compile(
r'^\s*(?:\[voice\]|\[speaker:[^\]]*\])\s*', re.IGNORECASE
r'^\s*(?:\[voice\]|\[speaker:[^\]]*\]|\[tts-lang:[^\]]*\])\s*', re.IGNORECASE
)
@@ -46,6 +47,24 @@ def _strip_leading_voice_tokens(text: str) -> str:
return text
text = stripped
def _voice_turn_lang_marker() -> str:
"""`'[tts-lang:en] '` 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ă
`/voice setvoice` și swap-ul in-band persistă `voice.default_voice` live,
printr-o altă instanță Config.
"""
try:
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] "
except Exception as e: # noqa: BLE001
log.warning("voice lang marker lookup failed: %s", e)
return ""
# Module-level config instance (lazy singleton)
_config: Config | None = None
@@ -177,7 +196,7 @@ def route_message(
voice_mode = adapter_name == "discord-voice"
if voice_mode:
user_name = _get_config().get("voice.user_name", "user") or "user"
claude_text = f"[voice] [speaker:{user_name}] {text}"
claude_text = f"[voice] [speaker:{user_name}] {_voice_turn_lang_marker()}{text}"
session_key = channel_id
try: