feat: integrate ApiPilot tools and add light-theme voice UI

This commit is contained in:
2026-10-05 08:25:29 -04:00
parent 413ba08c21
commit 4d7c7a23df
9 changed files with 6930 additions and 171 deletions
+86 -1
View File
@@ -12,6 +12,78 @@ import math
import re
import tempfile
import unicodedata
# 2026-09-25 SANITIZE APIPILOT MARKERS PRIMA DI TTS (anti-leak pronunciato da Qwen3).
# Lato server S2S Python abbiamo gia' gate + sanitize ma come DOPPIA RETE
# di sicurezza sanitizziamo DIRETTAMENTE il testo che entra in Qwen3-TTS.
# Formati osservati reali oggi:
# (a) <<<APIPILOT_CALL:NAME=tool,ARGUMENTS=json>>> standard 3 parentesi
# (b) <APIPILOT_CALL:NAME=tool,ARGUMENTS={"format":"voice"}
# singolo < > (caso user oggi)
# (c) <<<APIPILOT_CALL:...}> \n>> spezzato piu' righe
# (d) >>>APIPILOT_CALL:...<<< invertito
# Regola: CALL -> placeholder spagnolo (frase naturale utente), RESULT -> strip.
_APIPILOT_CALL_NORMAL_RE = re.compile(
r"[<]{1,3}\s*APIPILOT_CALL:NAME=([A-Za-z0-9_.-]+),ARGUMENTS=(.*?)[>]{1,3}",
re.IGNORECASE | re.DOTALL,
)
_APIPILOT_CALL_INVERTED_RE = re.compile(
r"[>]{1,3}\s*APIPILOT_CALL:NAME=([A-Za-z0-9_.-]+),ARGUMENTS=(.*?)[<]{1,3}",
re.IGNORECASE | re.DOTALL,
)
_APIPILOT_RESULT_RE = re.compile(
r"[<]{1,3}\s*APIPILOT_RESULT(?::PASS2)?[^<>\n]{0,2000}[>]{1,3}",
re.IGNORECASE | re.DOTALL,
)
_APIPILOT_SPANISH_PLACEHOLDER = "Voy a consultar tus datos, un momento por favor."
def _tts_sanitize_apipilot_markers(text: str) -> tuple[str, bool, str]:
"""Rimuove marker APIPILOT dal testo TTS.
Returns (sanitized_text, found_any, log_tag).
"""
if not isinstance(text, str) or not text.strip():
return text, False, "empty"
found_call = False
found_result = False
tool_name = ""
# 1) marker CALL standard + invertito -> placeholder spagnolo
def _repl_call(m: re.Match) -> str:
nonlocal found_call, tool_name
found_call = True
try:
nm = m.group(1)
if isinstance(nm, str) and nm.strip():
tool_name = nm.strip()
except Exception:
pass
return f" {_APIPILOT_SPANISH_PLACEHOLDER} "
text2 = _APIPILOT_CALL_NORMAL_RE.sub(_repl_call, text)
if not found_call:
text2 = _APIPILOT_CALL_INVERTED_RE.sub(_repl_call, text2)
# 2) marker RESULT -> strip silenzioso (non sono pronunciabili)
if _APIPILOT_RESULT_RE.search(text2):
found_result = True
text2 = _APIPILOT_RESULT_RE.sub(" ", text2)
# 3) safety: FRAMMENTI marker singoli che non hanno wrapper completo
low = text2.lower()
if "apipilot" in low and ("call" in low or "result" in low):
import re as _re_sf
text2 = _re_sf.sub(r"(?i)\bAPIPILOT(?:_CALL|_RESULT)?\b", " ", text2)
text2 = _re_sf.sub(r"(?i)\b(?:NAME|ARGUMENTS|STATUS|PASS2)\s*[:=]", " ", text2)
found_call = True
if found_call or found_result:
text2 = re.sub(r"\s{2,}", " ", text2).strip()
tag = "-"
if found_call and found_result:
tag = f"CALL({tool_name or '?'})+RESULT"
elif found_call:
tag = f"CALL({tool_name or '?'})"
elif found_result:
tag = "RESULT"
return text2, (found_call or found_result), tag
from collections.abc import Callable
from pathlib import Path
from sys import platform
@@ -709,7 +781,20 @@ class Qwen3TTSHandler(BaseHandler[TTSIn, TTSOut]):
coalesced_text, _language_code, _saw_end_of_response = self._coalesce_pending_tts_input(tts_input)
text = coalesced_text or "Hello."
raw_text = coalesced_text or "Hello."
# 2026-09-25 SANITIZE APIPILOT PRIMA DI TTS (doppia rete sicurezza):
# Anche se lato server S2s Python abbiamo gia' gate audio drop + sanitize,
# Qwen3-TTS puo' ricevere marker per race / nuovi formati. Quindi QUI
# eliminiamo / sostituiamo con placeholder PRIMA di chiamare generate().
text, found_marker, sanitize_tag = _tts_sanitize_apipilot_markers(raw_text)
if found_marker:
logger.warning(
"[TTS-APIPILOT-SANITIZE] marker=%s rawLen=%d sanitizedLen=%d → sostituito con placeholder spagnolo / strip.",
sanitize_tag, len(raw_text or ""), len(text or ""),
)
if not text.strip():
# Safety se il marker era l'unica cosa: ripetiamo placeholder invece di Hello.
text = _APIPILOT_SPANISH_PLACEHOLDER
model_type = self._model_type()
self._apply_session_voice_override(model_type, runtime_config, response)