feat: batch-генерация слов дня, кнопка "Слово дня" в статистике
- Оптимизирована генерация слов дня: 2 запроса к AI вместо 11 - Добавлена кнопка "Слово дня" в /stats для быстрого доступа - Локализация для ru/en/ja 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1371,74 +1371,76 @@ User: {user_message}
|
||||
}
|
||||
]
|
||||
|
||||
async def generate_word_of_day(
|
||||
async def generate_words_of_day_batch(
|
||||
self,
|
||||
level: str,
|
||||
learning_lang: str = "en",
|
||||
language: str,
|
||||
levels: List[str],
|
||||
translation_lang: str = "ru",
|
||||
excluded_words: List[str] = None,
|
||||
user_id: Optional[int] = None
|
||||
) -> Optional[Dict]:
|
||||
excluded_words: Dict[str, List[str]] = None
|
||||
) -> Optional[Dict[str, Dict]]:
|
||||
"""
|
||||
Генерация слова дня.
|
||||
Генерация слов дня для всех уровней одного языка за один запрос.
|
||||
|
||||
Args:
|
||||
level: Уровень пользователя (A1-C2 или N5-N1)
|
||||
learning_lang: Язык изучения
|
||||
language: Язык изучения (en/ja)
|
||||
levels: Список уровней (A1-C2 или N5-N1)
|
||||
translation_lang: Язык перевода
|
||||
excluded_words: Список слов для исключения (уже были)
|
||||
user_id: ID пользователя для выбора модели
|
||||
excluded_words: Dict {level: [excluded_words]} для исключения
|
||||
|
||||
Returns:
|
||||
Dict с полями: word, transcription, translation, examples, synonyms, etymology
|
||||
Dict {level: word_data} или None при ошибке
|
||||
"""
|
||||
language_names = {
|
||||
"en": "английский",
|
||||
"ja": "японский"
|
||||
}
|
||||
language_name = language_names.get(learning_lang, "английский")
|
||||
language_names = {"en": "английский", "ja": "японский"}
|
||||
language_name = language_names.get(language, "английский")
|
||||
|
||||
translation_names = {
|
||||
"ru": "русский",
|
||||
"en": "английский",
|
||||
"ja": "японский"
|
||||
}
|
||||
translation_names = {"ru": "русский", "en": "английский", "ja": "японский"}
|
||||
translation_name = translation_names.get(translation_lang, "русский")
|
||||
|
||||
excluded_str = ""
|
||||
# Формируем список исключений по уровням
|
||||
excluded_info = ""
|
||||
if excluded_words:
|
||||
excluded_str = f"\n\nНЕ используй эти слова (уже были): {', '.join(excluded_words[:20])}"
|
||||
excluded_parts = []
|
||||
for level, words in excluded_words.items():
|
||||
if words:
|
||||
excluded_parts.append(f"- {level}: {', '.join(words[:15])}")
|
||||
if excluded_parts:
|
||||
excluded_info = "\n\nНЕ используй эти слова (уже были недавно):\n" + "\n".join(excluded_parts)
|
||||
|
||||
prompt = f"""Сгенерируй интересное "слово дня" для изучающего {language_name} язык на уровне {level}.
|
||||
levels_str = ", ".join(levels)
|
||||
|
||||
Требования:
|
||||
prompt = f"""Сгенерируй "слово дня" для изучающих {language_name} язык на каждом из уровней: {levels_str}.
|
||||
|
||||
Требования для каждого слова:
|
||||
- Слово должно быть полезным и интересным
|
||||
- Подходящее для уровня {level}
|
||||
- НЕ слишком простое и НЕ слишком сложное
|
||||
- Желательно с интересной этимологией или фактом{excluded_str}
|
||||
- Строго соответствовать указанному уровню сложности
|
||||
- Желательно с интересной этимологией или фактом
|
||||
- Все слова должны быть РАЗНЫМИ{excluded_info}
|
||||
|
||||
Верни JSON:
|
||||
Верни JSON объект, где ключи - уровни ({levels_str}):
|
||||
{{
|
||||
"word": "слово на {language_name}",
|
||||
"transcription": "транскрипция (IPA для английского, хирагана для японского)",
|
||||
"translation": "перевод на {translation_name}",
|
||||
"examples": [
|
||||
{{"sentence": "пример предложения", "translation": "перевод примера"}},
|
||||
{{"sentence": "второй пример", "translation": "перевод"}}
|
||||
],
|
||||
"synonyms": "синоним1, синоним2, синоним3",
|
||||
"etymology": "краткий интересный факт о слове или его происхождении (1-2 предложения)"
|
||||
"{levels[0]}": {{
|
||||
"word": "слово на {language_name}",
|
||||
"transcription": "транскрипция (IPA для английского, хирагана для японского)",
|
||||
"translation": "перевод на {translation_name}",
|
||||
"examples": [
|
||||
{{"sentence": "пример предложения", "translation": "перевод примера"}},
|
||||
{{"sentence": "второй пример", "translation": "перевод"}}
|
||||
],
|
||||
"synonyms": "синоним1, синоним2",
|
||||
"etymology": "краткий интересный факт (1-2 предложения)"
|
||||
}},
|
||||
... (для каждого уровня)
|
||||
}}"""
|
||||
|
||||
try:
|
||||
logger.info(f"[AI Request] generate_word_of_day: level='{level}', lang='{learning_lang}'")
|
||||
logger.info(f"[AI Request] generate_words_of_day_batch: lang='{language}', levels={levels}")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - опытный лингвист, который подбирает интересные слова для изучения."},
|
||||
{"role": "system", "content": "Ты - опытный лингвист. Отвечай только валидным JSON без markdown."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
model_name, provider = await self._get_active_model(user_id)
|
||||
model_name, provider = await self._get_active_model(None)
|
||||
|
||||
if provider == AIProvider.google:
|
||||
response_data = await self._make_google_request(messages, temperature=0.8, model=model_name)
|
||||
@@ -1449,11 +1451,11 @@ User: {user_message}
|
||||
content = self._strip_markdown_code_block(content)
|
||||
result = json.loads(content)
|
||||
|
||||
logger.info(f"[AI Response] generate_word_of_day: word='{result.get('word', 'N/A')}'")
|
||||
logger.info(f"[AI Response] generate_words_of_day_batch: generated {len(result)} words")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[AI Error] generate_word_of_day: {type(e).__name__}: {str(e)}")
|
||||
logger.error(f"[AI Error] generate_words_of_day_batch: {type(e).__name__}: {str(e)}")
|
||||
return None
|
||||
|
||||
async def generate_mini_story(
|
||||
|
||||
Reference in New Issue
Block a user