chore: auto-commit from dashboard
This commit is contained in:
@@ -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"):
|
||||
|
||||
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user