Улучшенный будильник, таймер, перевод
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
# Обрабатывает запросы пользователя и переводы.
|
||||
|
||||
import requests
|
||||
import re
|
||||
from .config import PERPLEXITY_API_KEY, PERPLEXITY_MODEL, PERPLEXITY_API_URL
|
||||
|
||||
|
||||
@@ -21,7 +22,9 @@ SYSTEM_PROMPT = """Ты — Александр, умный голосовой а
|
||||
# Требует возвращать ТОЛЬКО перевод, без лишних слов ("Конечно, вот перевод...").
|
||||
TRANSLATION_SYSTEM_PROMPT = """You are a translation engine.
|
||||
Translate from {source} to {target}.
|
||||
Return only the translated text, without quotes, comments, or explanations.
|
||||
Return 2-3 short translation variants only.
|
||||
No explanations, no quotes, no comments.
|
||||
Separate variants with " / " (space slash space).
|
||||
Keep the translation максимально кратким и естественным, без лишних слов."""
|
||||
|
||||
|
||||
@@ -178,8 +181,22 @@ def translate_text(text: str, source_lang: str, target_lang: str) -> str:
|
||||
|
||||
response = _send_request(
|
||||
messages,
|
||||
max_tokens=400,
|
||||
max_tokens=160,
|
||||
temperature=0.2, # Низкая температура для точности перевода
|
||||
error_text="Произошла ошибка при переводе. Попробуйте ещё раз.",
|
||||
)
|
||||
return response.strip()
|
||||
cleaned = response.strip()
|
||||
if not cleaned:
|
||||
return cleaned
|
||||
|
||||
# Normalize to 2-3 variants separated by " / "
|
||||
parts = []
|
||||
for chunk in re.split(r"(?:\s*/\s*|\n|;|\|)", cleaned):
|
||||
item = chunk.strip(" \t-•")
|
||||
if item:
|
||||
parts.append(item)
|
||||
if not parts:
|
||||
return cleaned
|
||||
|
||||
parts = parts[:3]
|
||||
return " / ".join(parts)
|
||||
|
||||
@@ -91,6 +91,9 @@ MONTHS_GENITIVE = [
|
||||
"декабря",
|
||||
]
|
||||
|
||||
# Леммы единиц времени (для корректного падежа числительных)
|
||||
TIME_UNIT_LEMMAS = {"час", "минута", "секунда"}
|
||||
|
||||
|
||||
def get_case_from_preposition(prep_token):
|
||||
"""Определяет падеж по предлогу."""
|
||||
@@ -127,7 +130,6 @@ def numbers_to_words(text: str) -> str:
|
||||
|
||||
# 1. Обработка годов: "в 1999 году", "2024 год"
|
||||
def replace_year_match(match):
|
||||
full_str = match.group(0)
|
||||
prep = match.group(1) # Предлог (в, с, к...)
|
||||
year_str = match.group(2) # Само число
|
||||
year_word = match.group(3) # Слово "год", "году" и т.д.
|
||||
@@ -207,9 +209,10 @@ def numbers_to_words(text: str) -> str:
|
||||
|
||||
case = "nominative"
|
||||
gender = "m"
|
||||
prep_clean = prep.strip().lower() if prep else None
|
||||
|
||||
if prep:
|
||||
morph_case = get_case_from_preposition(prep.strip())
|
||||
if prep_clean:
|
||||
morph_case = get_case_from_preposition(prep_clean)
|
||||
if morph_case:
|
||||
case = PYMORPHY_TO_NUM2WORDS.get(morph_case, "nominative")
|
||||
|
||||
@@ -221,6 +224,16 @@ def numbers_to_words(text: str) -> str:
|
||||
morph_gender = parsed.tag.gender
|
||||
gender = PYMORPHY_TO_GENDER.get(morph_gender, "m")
|
||||
|
||||
# Спец-случай: "на 1 час" -> "на один час" (не "одного")
|
||||
# Для неодушевленных муж./ср. рода в винительном падеже
|
||||
# числительные должны совпадать с именительным.
|
||||
if (
|
||||
prep_clean == "на"
|
||||
and parsed.normal_form in TIME_UNIT_LEMMAS
|
||||
and parsed.tag.gender in ("masc", "neut")
|
||||
):
|
||||
case = "nominative"
|
||||
|
||||
words = convert_number(
|
||||
num_str, context_type="cardinal", case=case, gender=gender
|
||||
)
|
||||
|
||||
66
app/core/commands.py
Normal file
66
app/core/commands.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
Command parsing helpers.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
_STOP_WORDS_STRICT = {
|
||||
"стоп",
|
||||
"хватит",
|
||||
"перестань",
|
||||
"замолчи",
|
||||
"прекрати",
|
||||
"тихо",
|
||||
"stop",
|
||||
}
|
||||
|
||||
_STOP_PATTERNS_LENIENT = [
|
||||
r"\bстоп\w*\b",
|
||||
r"\bstop\b",
|
||||
r"\bхватит\b",
|
||||
r"\bперестан\w*\b",
|
||||
r"\bпрекрат\w*\b",
|
||||
r"\bзамолч\w*\b",
|
||||
r"\bтише\b",
|
||||
r"\bтихо\b",
|
||||
r"\bвыключ\w*\b",
|
||||
r"\bотключ\w*\b",
|
||||
r"\bостанов\w*\b",
|
||||
r"\bотмен\w*\b",
|
||||
r"\bпауза\b",
|
||||
r"\bдостаточно\b",
|
||||
]
|
||||
_STOP_PATTERNS_LENIENT_COMPILED = [re.compile(p) for p in _STOP_PATTERNS_LENIENT]
|
||||
|
||||
|
||||
def _normalize_text(text: str) -> str:
|
||||
text = text.lower().replace("ё", "е")
|
||||
text = re.sub(r"[^\w\s]+", " ", text, flags=re.UNICODE)
|
||||
text = re.sub(r"\s+", " ", text, flags=re.UNICODE).strip()
|
||||
return text
|
||||
|
||||
|
||||
def is_stop_command(text: str, mode: str = "strict") -> bool:
|
||||
"""
|
||||
Detect stop commands in text.
|
||||
|
||||
mode:
|
||||
- "strict": only exact stop words.
|
||||
- "lenient": broader patterns for noisy recognition.
|
||||
"""
|
||||
if not text:
|
||||
return False
|
||||
|
||||
normalized = _normalize_text(text)
|
||||
if not normalized:
|
||||
return False
|
||||
|
||||
if mode == "strict":
|
||||
words = normalized.split()
|
||||
return any(word in _STOP_WORDS_STRICT for word in words)
|
||||
|
||||
for pattern in _STOP_PATTERNS_LENIENT_COMPILED:
|
||||
if pattern.search(normalized):
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -7,6 +7,7 @@ Loads environment variables from .env file.
|
||||
# Он загружает настройки из файла .env (переменные окружения) и определяет константы.
|
||||
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -40,7 +41,6 @@ CHANNELS = 1
|
||||
|
||||
# --- Настройка времени ---
|
||||
# Устанавливаем часовой пояс на Москву, чтобы будильник работал корректно
|
||||
import time
|
||||
|
||||
os.environ["TZ"] = "Europe/Moscow"
|
||||
time.tzset()
|
||||
|
||||
Reference in New Issue
Block a user