chore: auto-commit from dashboard

This commit is contained in:
2026-07-10 15:12:22 +00:00
parent 7b44749e5a
commit 738dc0181a
13 changed files with 383 additions and 44 deletions

View File

@@ -787,6 +787,11 @@ def cmd_audio(args: list[str]) -> str:
if len(text) > _MAX_TTS_CHARS:
text = text[:_MAX_TTS_CHARS] + "..."
# Normalizare RO pentru TTS (numere, monedă, unități, abrevieri) —
# aceeași expandare ca voice mode live, fără trunchierea la 200 cuvinte
from src.voice.normalize import expand_for_tts
text = expand_for_tts(text)
# Apel TTS (import direct din tools/tts.py)
result = _tts_synthesize(text, voice)
if result.get("ok"):

View File

@@ -201,8 +201,12 @@ _CURRENCY_SUB = {
}
_CURRENCY_PATTERNS = [
# RON suffix (case-insensitive: RON, ron, lei)
(re.compile(r'(?<!\w)(\d+(?:\.\d+)?)\s+(?:RON|lei)\b', re.IGNORECASE), 'RON'),
# RON suffix (case-insensitive: RON, ron, lei). Accepts either a
# dot-decimal fraction ("12.50 lei") or, since Romanian accounting
# format uses comma for decimals ("53.600,00 lei" — the dot-thousands
# group is already collapsed by normalize_thousands upstream), a
# comma-decimal fraction in its own capture group.
(re.compile(r'(?<!\w)(\d+(?:\.\d+)?)(?:,(\d{1,2}))?\s+(?:RON|lei)\b', re.IGNORECASE), 'RON'),
# Prefix currencies
(re.compile(r'\$(\d+(?:\.\d+)?)'), 'USD'),
(re.compile(r'€(\d+(?:\.\d+)?)'), 'EUR'),
@@ -222,25 +226,28 @@ def _format_currency_unit(n: int, singular: str, plural: str) -> str:
return f"{word} {plural}"
def _format_currency(amount: str, code: str) -> str:
def _format_currency(amount: str, code: str, comma_frac: str | None = None) -> str:
main_sg, main_pl = _CURRENCY_MAIN[code]
if '.' in amount:
if comma_frac is not None:
whole_s, frac_s = amount, comma_frac
elif '.' in amount:
whole_s, frac_s = amount.split('.', 1)
# Normalize fractional part to 2 digits so "12.5 RON" reads as
# 50 bani, not 5 bani.
if len(frac_s) == 1:
frac_s = frac_s + '0'
elif len(frac_s) > 2:
frac_s = frac_s[:2]
whole = int(whole_s)
frac = int(frac_s)
whole_part = _format_currency_unit(whole, main_sg, main_pl)
if frac == 0:
return whole_part
sub_sg, sub_pl = _CURRENCY_SUB[code]
frac_part = _format_currency_unit(frac, sub_sg, sub_pl)
return f"{whole_part} și {frac_part}"
return _format_currency_unit(int(amount), main_sg, main_pl)
else:
return _format_currency_unit(int(amount), main_sg, main_pl)
# Normalize fractional part to 2 digits so "12.5 RON" reads as
# 50 bani, not 5 bani.
if len(frac_s) == 1:
frac_s = frac_s + '0'
elif len(frac_s) > 2:
frac_s = frac_s[:2]
whole = int(whole_s)
frac = int(frac_s)
whole_part = _format_currency_unit(whole, main_sg, main_pl)
if frac == 0:
return whole_part
sub_sg, sub_pl = _CURRENCY_SUB[code]
frac_part = _format_currency_unit(frac, sub_sg, sub_pl)
return f"{whole_part} și {frac_part}"
def expand_currency(text: str) -> str:
@@ -251,7 +258,10 @@ def expand_currency(text: str) -> str:
bani / cenți / pence).
"""
for pattern, code in _CURRENCY_PATTERNS:
text = pattern.sub(lambda m, c=code: _format_currency(m.group(1), c), text)
def _sub(m, c=code):
comma_frac = m.group(2) if m.re.groups >= 2 else None
return _format_currency(m.group(1), c, comma_frac)
text = pattern.sub(_sub, text)
return text
@@ -296,12 +306,13 @@ _MAX_WORDS = 200
_TRUNCATE_SUFFIX = "Restul l-am scris în chat."
def normalize_for_tts(text: str) -> str:
"""Apply the full normalization pipeline and truncate to 200 words.
def expand_for_tts(text: str) -> str:
"""Apply the full normalization pipeline (markdown strip, abbreviations,
numbers, currency, units, symbols) WITHOUT the 200-word truncation.
If the text exceeds 200 words, the first 200 are kept and the suffix
"Restul l-am scris în chat." is appended so the listener knows the
response continues in the text channel mirror.
Use this for one-shot TTS generation (e.g. /audio command) where the
"Restul l-am scris în chat." suffix from normalize_for_tts() would be
misleading (no live chat mirror exists for that flow).
"""
text = strip_markdown(text)
text = sanitize_punctuation(text)
@@ -312,6 +323,17 @@ def normalize_for_tts(text: str) -> str:
text = expand_units(text)
text = expand_numbers_ro(text)
text = expand_symbols(text)
return text.strip()
def normalize_for_tts(text: str) -> str:
"""Apply the full normalization pipeline and truncate to 200 words.
If the text exceeds 200 words, the first 200 are kept and the suffix
"Restul l-am scris în chat." is appended so the listener knows the
response continues in the text channel mirror.
"""
text = expand_for_tts(text)
words = text.split()
if len(words) > _MAX_WORDS:
text = ' '.join(words[:_MAX_WORDS]) + f" {_TRUNCATE_SUFFIX}"