fix(practice): guard lang variables; add furigana support for JA in prompts and UI; fix f-string JSON construction
This commit is contained in:
@@ -98,10 +98,16 @@ async def start_scenario(callback: CallbackQuery, state: FSMContext):
|
|||||||
await state.set_state(PracticeStates.in_conversation)
|
await state.set_state(PracticeStates.in_conversation)
|
||||||
|
|
||||||
# Формируем сообщение (перевод скрыт, доступен по кнопке)
|
# Формируем сообщение (перевод скрыт, доступен по кнопке)
|
||||||
|
ai_msg = conversation_start.get('message', '')
|
||||||
|
if learn_lang.lower() == 'ja':
|
||||||
|
fg = conversation_start.get('furigana')
|
||||||
|
if fg:
|
||||||
|
ai_msg = f"{ai_msg} ({fg})"
|
||||||
|
|
||||||
text = (
|
text = (
|
||||||
f"<b>{SCENARIOS[scenario].get(ui_lang, SCENARIOS[scenario]['ru'])}</b>\n\n"
|
f"<b>{SCENARIOS[scenario].get(ui_lang, SCENARIOS[scenario]['ru'])}</b>\n\n"
|
||||||
f"📝 <i>{conversation_start.get('context', '')}</i>\n\n"
|
f"📝 <i>{conversation_start.get('context', '')}</i>\n\n"
|
||||||
f"<b>AI:</b> {conversation_start.get('message', '')}\n\n"
|
f"<b>AI:</b> {ai_msg}\n\n"
|
||||||
f"{t(ui_lang, 'practice.hints')}\n"
|
f"{t(ui_lang, 'practice.hints')}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -181,8 +187,14 @@ async def handle_conversation(message: Message, state: FSMContext):
|
|||||||
level = data.get('level', 'B1')
|
level = data.get('level', 'B1')
|
||||||
message_count = data.get('message_count', 0)
|
message_count = data.get('message_count', 0)
|
||||||
|
|
||||||
|
# Определяем языки пользователя для ответа (до показа индикатора)
|
||||||
|
async with async_session_maker() as session:
|
||||||
|
user2 = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
||||||
|
ui_lang2 = (user2.language_interface if user2 else 'ru') or 'ru'
|
||||||
|
learn_lang2 = (user2.learning_language if user2 else 'en') or 'en'
|
||||||
|
|
||||||
# Показываем индикатор
|
# Показываем индикатор
|
||||||
thinking_msg = await message.answer(t((user2.language_interface if user2 else 'ru') or 'ru', 'practice.thinking'))
|
thinking_msg = await message.answer(t(ui_lang2, 'practice.thinking'))
|
||||||
|
|
||||||
# Добавляем сообщение пользователя в историю
|
# Добавляем сообщение пользователя в историю
|
||||||
conversation_history.append({
|
conversation_history.append({
|
||||||
@@ -190,12 +202,6 @@ async def handle_conversation(message: Message, state: FSMContext):
|
|||||||
"content": user_message
|
"content": user_message
|
||||||
})
|
})
|
||||||
|
|
||||||
# Определяем языки пользователя для ответа
|
|
||||||
async with async_session_maker() as session:
|
|
||||||
user2 = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
|
||||||
ui_lang2 = (user2.language_interface if user2 else 'ru') or 'ru'
|
|
||||||
learn_lang2 = (user2.learning_language if user2 else 'en') or 'en'
|
|
||||||
|
|
||||||
# Получаем ответ от AI
|
# Получаем ответ от AI
|
||||||
ai_response = await ai_service.continue_conversation(
|
ai_response = await ai_service.continue_conversation(
|
||||||
conversation_history=conversation_history,
|
conversation_history=conversation_history,
|
||||||
@@ -233,10 +239,13 @@ async def handle_conversation(message: Message, state: FSMContext):
|
|||||||
if feedback.get('comment'):
|
if feedback.get('comment'):
|
||||||
text += f"💬 {feedback['comment']}\n\n"
|
text += f"💬 {feedback['comment']}\n\n"
|
||||||
|
|
||||||
# Ответ AI
|
# Ответ AI (с фуриганой для японского)
|
||||||
text += (
|
resp = ai_response.get('response', '')
|
||||||
f"<b>AI:</b> {ai_response.get('response', '')}\n\n"
|
if learn_lang2.lower() == 'ja':
|
||||||
)
|
fg = ai_response.get('furigana')
|
||||||
|
if fg:
|
||||||
|
resp = f"{resp} ({fg})"
|
||||||
|
text += f"<b>AI:</b> {resp}\n\n"
|
||||||
|
|
||||||
# Подсказки
|
# Подсказки
|
||||||
suggestions = ai_response.get('suggestions', [])
|
suggestions = ai_response.get('suggestions', [])
|
||||||
@@ -253,8 +262,8 @@ async def handle_conversation(message: Message, state: FSMContext):
|
|||||||
|
|
||||||
# Кнопки
|
# Кнопки
|
||||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[InlineKeyboardButton(text=t(lang2, 'practice.show_translation_btn'), callback_data=f"show_tr_{this_idx}")],
|
[InlineKeyboardButton(text=t(ui_lang2, 'practice.show_translation_btn'), callback_data=f"show_tr_{this_idx}")],
|
||||||
[InlineKeyboardButton(text=t(lang2, 'practice.stop_btn'), callback_data="stop_practice")]
|
[InlineKeyboardButton(text=t(ui_lang2, 'practice.stop_btn'), callback_data="stop_practice")]
|
||||||
])
|
])
|
||||||
|
|
||||||
await message.answer(text, reply_markup=keyboard)
|
await message.answer(text, reply_markup=keyboard)
|
||||||
@@ -263,25 +272,26 @@ async def handle_conversation(message: Message, state: FSMContext):
|
|||||||
@router.callback_query(F.data.startswith("show_tr_"), PracticeStates.in_conversation)
|
@router.callback_query(F.data.startswith("show_tr_"), PracticeStates.in_conversation)
|
||||||
async def show_translation(callback: CallbackQuery, state: FSMContext):
|
async def show_translation(callback: CallbackQuery, state: FSMContext):
|
||||||
"""Показать перевод для конкретного сообщения AI"""
|
"""Показать перевод для конкретного сообщения AI"""
|
||||||
|
# Определяем язык интерфейса пользователя
|
||||||
|
async with async_session_maker() as session:
|
||||||
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||||
|
lang = (user.language_interface if user else 'ru') or 'ru'
|
||||||
try:
|
try:
|
||||||
idx = int(callback.data.split("_")[-1])
|
idx = int(callback.data.split("_")[-1])
|
||||||
except Exception:
|
except Exception:
|
||||||
await callback.answer(t((user.language_interface if user else 'ru') or 'ru', 'practice.translation_unavailable'), show_alert=True)
|
await callback.answer(t(lang, 'practice.translation_unavailable'), show_alert=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
data = await state.get_data()
|
data = await state.get_data()
|
||||||
translations = data.get('translations', {}) or {}
|
translations = data.get('translations', {}) or {}
|
||||||
tr_text = translations.get(idx)
|
tr_text = translations.get(idx)
|
||||||
if not tr_text:
|
if not tr_text:
|
||||||
await callback.answer(t((user.language_interface if user else 'ru') or 'ru', 'practice.translation_unavailable'), show_alert=True)
|
await callback.answer(t(lang, 'practice.translation_unavailable'), show_alert=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Вставляем перевод в существующее сообщение
|
# Вставляем перевод в существующее сообщение
|
||||||
orig = callback.message.text or ""
|
orig = callback.message.text or ""
|
||||||
# Определяем язык
|
# Определяем язык (уже вычислен выше)
|
||||||
async with async_session_maker() as session:
|
|
||||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
|
||||||
lang = (user.language_interface if user else 'ru') or 'ru'
|
|
||||||
marker = t(lang, 'common.translation') + ":"
|
marker = t(lang, 'common.translation') + ":"
|
||||||
if marker in orig:
|
if marker in orig:
|
||||||
await callback.answer(t(lang, 'practice.translation_already'))
|
await callback.answer(t(lang, 'practice.translation_already'))
|
||||||
|
|||||||
@@ -340,6 +340,10 @@ class AIService:
|
|||||||
|
|
||||||
scenario_desc = scenarios.get(scenario, "повседневный разговор")
|
scenario_desc = scenarios.get(scenario, "повседневный разговор")
|
||||||
|
|
||||||
|
extra_fields = ''
|
||||||
|
if learning_lang.lower() == 'ja':
|
||||||
|
extra_fields = ",\n \"furigana\": \"фуригана (кана над/после иероглифов для поля message)\""
|
||||||
|
|
||||||
prompt = f"""Ты - собеседник для практики языка {learning_lang} уровня {level}.
|
prompt = f"""Ты - собеседник для практики языка {learning_lang} уровня {level}.
|
||||||
Начни диалог в сценарии: {scenario_desc} на {learning_lang}.
|
Начни диалог в сценарии: {scenario_desc} на {learning_lang}.
|
||||||
|
|
||||||
@@ -348,7 +352,7 @@ class AIService:
|
|||||||
"message": "твоя первая реплика на {learning_lang}",
|
"message": "твоя первая реплика на {learning_lang}",
|
||||||
"translation": "перевод на {translation_lang}",
|
"translation": "перевод на {translation_lang}",
|
||||||
"context": "краткое описание ситуации на {translation_lang}",
|
"context": "краткое описание ситуации на {translation_lang}",
|
||||||
"suggestions": ["подсказка 1", "подсказка 2", "подсказка 3"]
|
"suggestions": ["подсказка 1", "подсказка 2", "подсказка 3"]{extra_fields}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
Требования:
|
Требования:
|
||||||
@@ -408,6 +412,10 @@ class AIService:
|
|||||||
for msg in conversation_history[-6:] # Последние 6 сообщений
|
for msg in conversation_history[-6:] # Последние 6 сообщений
|
||||||
])
|
])
|
||||||
|
|
||||||
|
extra_fields_resp = ''
|
||||||
|
if learning_lang.lower() == 'ja':
|
||||||
|
extra_fields_resp = ",\n \"furigana\": \"фуригана (кана над/после иероглифов для поля response)\""
|
||||||
|
|
||||||
prompt = f"""Ты ведешь диалог на языке {learning_lang} уровня {level} в сценарии "{scenario}".
|
prompt = f"""Ты ведешь диалог на языке {learning_lang} уровня {level} в сценарии "{scenario}".
|
||||||
|
|
||||||
История диалога:
|
История диалога:
|
||||||
@@ -423,7 +431,7 @@ User: {user_message}
|
|||||||
"corrections": "исправления ошибок пользователя (если есть)",
|
"corrections": "исправления ошибок пользователя (если есть)",
|
||||||
"comment": "краткий комментарий об ответе пользователя"
|
"comment": "краткий комментарий об ответе пользователя"
|
||||||
}},
|
}},
|
||||||
"suggestions": ["подсказка 1 для следующего ответа", "подсказка 2"]
|
"suggestions": ["подсказка 1 для следующего ответа", "подсказка 2"]{extra_fields_resp}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
Требования:
|
Требования:
|
||||||
|
|||||||
Reference in New Issue
Block a user