import logging import httpx from openai import AsyncOpenAI from config.settings import settings from typing import Dict, List logger = logging.getLogger(__name__) class AIService: """Сервис для работы с OpenAI API через Cloudflare Gateway""" def __init__(self): self.api_key = settings.openai_api_key # Проверяем, настроен ли Cloudflare AI Gateway if settings.cloudflare_account_id: # Используем Cloudflare AI Gateway с прямыми HTTP запросами self.base_url = ( f"https://gateway.ai.cloudflare.com/v1/" f"{settings.cloudflare_account_id}/" f"{settings.cloudflare_gateway_id}/" f"openai" ) self.use_cloudflare = True logger.info(f"AI Service initialized with Cloudflare Gateway: {self.base_url}") else: # Прямое подключение к OpenAI self.base_url = "https://api.openai.com/v1" self.use_cloudflare = False logger.info("AI Service initialized with direct OpenAI connection") # HTTP клиент для всех запросов self.http_client = httpx.AsyncClient( timeout=httpx.Timeout(60.0, connect=10.0), limits=httpx.Limits(max_keepalive_connections=5, max_connections=10) ) async def _make_openai_request(self, messages: list, temperature: float = 0.3, model: str = "gpt-4o-mini") -> dict: """Выполнить запрос к OpenAI API (через Cloudflare или напрямую)""" url = f"{self.base_url}/chat/completions" headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" } payload = { "model": model, "messages": messages, "temperature": temperature, "response_format": {"type": "json_object"} } response = await self.http_client.post(url, headers=headers, json=payload) response.raise_for_status() return response.json() async def translate_word(self, word: str, source_lang: str = "en", translation_lang: str = "ru") -> Dict: """ Перевести слово и получить дополнительную информацию Args: word: Слово для перевода source_lang: Язык исходного слова (ISO2) translation_lang: Язык перевода (ISO2) Returns: Dict с переводом, транскрипцией и примерами """ prompt = f"""Переведи слово/фразу "{word}" с языка {source_lang} на {translation_lang}. Верни ответ строго в формате JSON: {{ "word": "исходное слово на {source_lang}", "translation": "перевод на {translation_lang}", "transcription": "транскрипция в IPA (если применимо)", "examples": [ {{"{source_lang}": "пример на языке обучения", "{translation_lang}": "перевод примера"}} ], "category": "категория слова (работа, еда, путешествия и т.д.)", "difficulty": "уровень сложности (A1/A2/B1/B2/C1/C2)" }} Важно: верни только JSON, без дополнительного текста.""" try: logger.info(f"[GPT Request] translate_word: word='{word}', source='{source_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - помощник для изучения языков. Отвечай только в формате JSON."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.3) import json result = json.loads(response_data['choices'][0]['message']['content']) logger.info(f"[GPT Response] translate_word: success, translation='{result.get('translation', 'N/A')}'") return result except Exception as e: logger.error(f"[GPT Error] translate_word: {type(e).__name__}: {str(e)}") # Fallback в случае ошибки return { "word": word, "translation": "Ошибка перевода", "transcription": "", "examples": [], "category": "unknown", "difficulty": "A1" } async def translate_word_with_contexts( self, word: str, source_lang: str = "en", translation_lang: str = "ru", max_translations: int = 3 ) -> Dict: """ Перевести слово и получить несколько переводов с контекстами Args: word: Слово для перевода source_lang: Язык исходного слова (ISO2) translation_lang: Язык перевода (ISO2) max_translations: Максимальное количество переводов Returns: Dict с переводами, каждый с примером предложения """ prompt = f"""Переведи слово/фразу "{word}" с языка {source_lang} на {translation_lang}. Если у слова есть несколько значений в разных контекстах, дай до {max_translations} разных переводов. Для каждого перевода дай пример предложения, показывающий это значение. Верни ответ строго в формате JSON: {{ "word": "исходное слово на {source_lang}", "transcription": "транскрипция в IPA (если применимо)", "category": "основная категория слова", "difficulty": "уровень сложности (A1/A2/B1/B2/C1/C2)", "translations": [ {{ "translation": "перевод 1 на {translation_lang}", "context": "пример предложения на {source_lang}, показывающий это значение", "context_translation": "перевод примера на {translation_lang}", "is_primary": true }}, {{ "translation": "перевод 2 на {translation_lang} (если есть другое значение)", "context": "пример предложения на {source_lang}", "context_translation": "перевод примера на {translation_lang}", "is_primary": false }} ] }} Важно: - Первый перевод должен быть самым распространённым (is_primary: true) - Давай разные переводы только если слово реально имеет разные значения - Примеры должны чётко показывать конкретное значение слова - Верни только JSON, без дополнительного текста""" try: logger.info(f"[GPT Request] translate_word_with_contexts: word='{word}', source='{source_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - помощник для изучения языков. Отвечай только в формате JSON."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.3) import json content = response_data['choices'][0]['message']['content'] # Убираем markdown обёртку если есть if content.startswith('```'): content = content.split('\n', 1)[1] if '\n' in content else content[3:] if content.endswith('```'): content = content[:-3] content = content.strip() result = json.loads(content) translations_count = len(result.get('translations', [])) logger.info(f"[GPT Response] translate_word_with_contexts: success, {translations_count} translations") return result except Exception as e: logger.error(f"[GPT Error] translate_word_with_contexts: {type(e).__name__}: {str(e)}") # Fallback в случае ошибки return { "word": word, "transcription": "", "category": "unknown", "difficulty": "A1", "translations": [{ "translation": "Ошибка перевода", "context": "", "context_translation": "", "is_primary": True }] } async def translate_words_batch( self, words: List[str], source_lang: str = "en", translation_lang: str = "ru" ) -> List[Dict]: """ Перевести список слов пакетно Args: words: Список слов для перевода source_lang: Язык исходных слов (ISO2) translation_lang: Язык перевода (ISO2) Returns: List[Dict] с переводами, транскрипциями """ if not words: return [] words_list = "\n".join(f"- {w}" for w in words[:50]) # Максимум 50 слов за раз # Добавляем инструкцию для фуриганы если японский furigana_instruction = "" if source_lang == "ja": furigana_instruction = '\n "reading": "чтение хираганой (только для кандзи)",' prompt = f"""Переведи следующие слова/фразы с языка {source_lang} на {translation_lang}: {words_list} Верни ответ строго в формате JSON массива: [ {{ "word": "исходное слово", "translation": "перевод", "transcription": "транскрипция (IPA или ромадзи для японского)",{furigana_instruction} }}, ... ] Важно: - Верни только JSON массив, без дополнительного текста - Сохрани порядок слов как в исходном списке - Для каждого слова укажи точный перевод и транскрипцию""" try: logger.info(f"[GPT Request] translate_words_batch: {len(words)} words, {source_lang} -> {translation_lang}") messages = [ {"role": "system", "content": "Ты - помощник для изучения языков. Отвечай только в формате JSON."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.3) import json content = response_data['choices'][0]['message']['content'] # Убираем markdown обёртку если есть if content.startswith('```'): content = content.split('\n', 1)[1] if '\n' in content else content[3:] if content.endswith('```'): content = content[:-3] content = content.strip() result = json.loads(content) # Если вернулся dict с ключом типа "words" или "translations" — извлекаем список if isinstance(result, dict): for key in ['words', 'translations', 'result', 'data']: if key in result and isinstance(result[key], list): result = result[key] break if not isinstance(result, list): logger.warning(f"[GPT Warning] translate_words_batch: unexpected format, got {type(result)}") return [{"word": w, "translation": "", "transcription": ""} for w in words] logger.info(f"[GPT Response] translate_words_batch: success, got {len(result)} translations") return result except Exception as e: logger.error(f"[GPT Error] translate_words_batch: {type(e).__name__}: {str(e)}") # Возвращаем слова без перевода в случае ошибки return [{"word": w, "translation": "", "transcription": ""} for w in words] async def check_answer(self, question: str, correct_answer: str, user_answer: str) -> Dict: """ Проверить ответ пользователя с помощью ИИ Args: question: Вопрос задания correct_answer: Правильный ответ user_answer: Ответ пользователя Returns: Dict с результатом проверки и обратной связью """ prompt = f"""Проверь ответ пользователя на задание по английскому языку. Задание: {question} Правильный ответ: {correct_answer} Ответ пользователя: {user_answer} Верни ответ в формате JSON: {{ "is_correct": true/false, "feedback": "краткое объяснение (если ответ неверный, объясни ошибку и дай правильный вариант)", "score": 0-100 }} Учитывай возможные вариации ответа. Если смысл передан правильно, даже с небольшими грамматическими неточностями, засчитывай ответ.""" try: logger.info(f"[GPT Request] check_answer: user_answer='{user_answer[:30]}...'") messages = [ {"role": "system", "content": "Ты - преподаватель английского языка. Проверяй ответы справедливо, учитывая контекст."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.3) import json result = json.loads(response_data['choices'][0]['message']['content']) logger.info(f"[GPT Response] check_answer: is_correct={result.get('is_correct', False)}, score={result.get('score', 0)}") return result except Exception as e: logger.error(f"[GPT Error] check_answer: {type(e).__name__}: {str(e)}") return { "is_correct": False, "feedback": "Ошибка проверки ответа", "score": 0 } async def generate_fill_in_sentence(self, word: str, learning_lang: str = "en", translation_lang: str = "ru") -> Dict: """ Сгенерировать предложение с пропуском для заданного слова Args: word: Слово (на языке обучения), для которого нужно создать предложение learning_lang: Язык обучения (ISO2) translation_lang: Язык перевода предложения (ISO2) Returns: Dict с предложением и правильным ответом """ prompt = f"""Создай предложение на языке {learning_lang}, используя слово "{word}". Замени это слово на пропуск "___". Верни ответ в формате JSON: {{ "sentence": "предложение с пропуском ___", "answer": "{word}", "translation": "перевод предложения на {translation_lang}" }} Предложение должно быть простым и естественным. Контекст должен четко подсказывать правильное слово.""" try: logger.info(f"[GPT Request] generate_fill_in_sentence: word='{word}', lang='{learning_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - преподаватель иностранных языков. Создавай простые и понятные упражнения."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.7) import json result = json.loads(response_data['choices'][0]['message']['content']) logger.info(f"[GPT Response] generate_fill_in_sentence: success") return result except Exception as e: logger.error(f"[GPT Error] generate_fill_in_sentence: {type(e).__name__}: {str(e)}") return { "sentence": f"I like to ___ every day.", "answer": word, "translation": f"Мне нравится {word} каждый день." } async def generate_thematic_words( self, theme: str, level: str = "B1", count: int = 10, learning_lang: str = "en", translation_lang: str = "ru", exclude_words: List[str] = None ) -> List[Dict]: """ Сгенерировать подборку слов по теме Args: theme: Тема для подборки слов level: Уровень сложности (A1-C2) count: Количество слов learning_lang: Язык изучения translation_lang: Язык перевода exclude_words: Список слов для исключения (уже известные) Returns: Список словарей с информацией о словах """ exclude_instruction = "" exclude_words_set = set() if exclude_words: # Ограничиваем список до 100 слов чтобы не раздувать промпт words_sample = exclude_words[:100] exclude_words_set = set(w.lower() for w in exclude_words) exclude_instruction = f""" ⚠️ ЗАПРЕЩЁННЫЕ СЛОВА (НЕ ИСПОЛЬЗОВАТЬ!): {', '.join(words_sample)} Эти слова пользователь уже знает. ОБЯЗАТЕЛЬНО выбери ДРУГИЕ слова!""" prompt = f"""Создай подборку из {count} слов на языке {learning_lang} по теме "{theme}" для уровня {level}. Переводы дай на {translation_lang}. {exclude_instruction} Верни ответ в формате JSON: {{ "theme": "{theme}", "words": [ {{ "word": "слово на {learning_lang}", "translation": "перевод на {translation_lang}", "transcription": "транскрипция в IPA (если применимо)", "example": "пример использования на {learning_lang}", "example_translation": "перевод примера на {translation_lang}" }} ] }} Слова должны быть: - Полезными и часто используемыми - Соответствовать уровню {level} - Связаны с темой "{theme}" - Разнообразными (существительные, глаголы, прилагательные)""" try: logger.info(f"[GPT Request] generate_thematic_words: theme='{theme}', level='{level}', count={count}, learn='{learning_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - преподаватель иностранных языков. Подбирай полезные и актуальные слова."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.7) import json result = json.loads(response_data['choices'][0]['message']['content']) words = result.get('words', []) # Фильтруем слова которые AI мог вернуть несмотря на инструкцию if exclude_words_set: filtered_words = [ w for w in words if w.get('word', '').lower() not in exclude_words_set ] filtered_count = len(words) - len(filtered_words) if filtered_count > 0: logger.info(f"[GPT Response] generate_thematic_words: filtered out {filtered_count} excluded words") words = filtered_words logger.info(f"[GPT Response] generate_thematic_words: success, generated {len(words)} words") return words except Exception as e: logger.error(f"[GPT Error] generate_thematic_words: {type(e).__name__}: {str(e)}") return [] async def extract_words_from_text(self, text: str, level: str = "B1", max_words: int = 15, learning_lang: str = "en", translation_lang: str = "ru") -> List[Dict]: """ Извлечь ключевые слова из текста для изучения Args: text: Текст на английском языке level: Уровень пользователя (A1-C2) max_words: Максимальное количество слов для извлечения Returns: Список словарей с информацией о словах """ prompt = f"""Проанализируй следующий текст на языке {learning_lang} и извлеки из него до {max_words} самых полезных слов для изучения на уровне {level}. Переводы дай на {translation_lang}. Текст: {text} Верни ответ в формате JSON: {{ "words": [ {{ "word": "слово на {learning_lang} (в базовой форме)", "translation": "перевод на {translation_lang}", "transcription": "транскрипция в IPA (если применимо)", "context": "предложение из текста на {learning_lang}, где используется это слово" }} ] }} Критерии отбора слов: - Выбирай самые важные и полезные слова из текста - Слова должны быть интересны для уровня {level} - Не включай простейшие слова (a, the, is, и т.д.) - Слова должны быть в базовой форме (инфинитив для глаголов, ед.число для существительных) - Разнообразие: существительные, глаголы, прилагательные, устойчивые выражения""" try: text_preview = text[:100] + "..." if len(text) > 100 else text logger.info(f"[GPT Request] extract_words_from_text: text_length={len(text)}, level='{level}', max_words={max_words}, learn='{learning_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - преподаватель иностранных языков. Помогаешь извлекать полезные слова для изучения из текстов."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.5) import json result = json.loads(response_data['choices'][0]['message']['content']) words_count = len(result.get('words', [])) logger.info(f"[GPT Response] extract_words_from_text: success, extracted {words_count} words") return result.get('words', []) except Exception as e: logger.error(f"[GPT Error] extract_words_from_text: {type(e).__name__}: {str(e)}") return [] async def start_conversation(self, scenario: str, level: str = "B1", learning_lang: str = "en", translation_lang: str = "ru") -> Dict: """ Начать диалоговую практику с AI Args: scenario: Сценарий диалога (restaurant, shopping, travel, etc.) level: Уровень пользователя (A1-C2) Returns: Dict с начальной репликой и контекстом """ scenarios = { "restaurant": "ресторан - заказ еды", "shopping": "магазин - покупка одежды", "travel": "аэропорт/отель - путешествие", "work": "офис - рабочая встреча", "doctor": "клиника - визит к врачу", "casual": "повседневный разговор" } scenario_desc = scenarios.get(scenario, "повседневный разговор") extra_fields = '' if learning_lang.lower() == 'ja': # Для японского просим версию с фуриганой в скобках ТОЛЬКО для кандзи # Не добавляй фуригану к кана или латинским буквам extra_fields = ",\n \"message_annotated\": \"фраза на {learning_lang} с фуриганой в скобках ТОЛЬКО к кандзи (Так правильно: いらっしゃいませ!今日は何を注文(ちゅうもん)しますか?, Так неправильно: こんにちは(こんにちは)!今日ははどうですか?); к こんにちは не добовляй фурагану; не добавляй фуригану к катакане, фуригане, хирагане, частице и латинице\"" prompt = f"""Ты - собеседник для практики языка {learning_lang} уровня {level}. Начни диалог в сценарии: {scenario_desc} на {learning_lang}. Верни ответ в формате JSON: {{ "message": "твоя первая реплика на {learning_lang}", "translation": "перевод на {translation_lang}", "context": "краткое описание ситуации на {translation_lang}", "suggestions": [ {{"learn": "подсказка на {learning_lang}", "learn_annotated": "подсказка с фуриганой в скобках ТОЛЬКО к кандзи (Так правильно: いらっしゃいませ!今日は何を注文(ちゅうもん)しますか?, Так неправильно: こんにちは(こんにちは)!今日ははどうですか?); к こんにちは не добовляй фурагану; не добавляй фуригану к катакане, фуригане, хирагане, частице и латинице; {learning_lang})", "trans": "перевод подсказки на {translation_lang}"}}, {{"learn": "...", "learn_annotated": "...", "trans": "..."}}, {{"learn": "...", "learn_annotated": "...", "trans": "..."}} ]{extra_fields} }} Требования: - Говори естественно, используй уровень {level} - Создай интересную ситуацию - Задай вопрос или начни разговор - Подсказки должны помочь пользователю ответить""" try: logger.info(f"[GPT Request] start_conversation: scenario='{scenario}', level='{level}', learn='{learning_lang}', to='{translation_lang}'") messages = [ {"role": "system", "content": "Ты - дружелюбный собеседник для практики иностранных языков. Веди естественный диалог."}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.8) import json result = json.loads(response_data['choices'][0]['message']['content']) logger.info(f"[GPT Response] start_conversation: success, scenario='{scenario}'") return result except Exception as e: logger.error(f"[GPT Error] start_conversation: {type(e).__name__}: {str(e)}") return { "message": "Hello! How are you today?", "translation": "Привет! Как дела сегодня?", "context": "Повседневный разговор", "suggestions": ["I'm fine, thank you!", "Good, and you?", "Not bad!"] } async def continue_conversation( self, conversation_history: List[Dict], user_message: str, scenario: str, level: str = "B1", learning_lang: str = "en", translation_lang: str = "ru" ) -> Dict: """ Продолжить диалог и проверить ответ пользователя Args: conversation_history: История диалога user_message: Сообщение пользователя scenario: Сценарий диалога level: Уровень пользователя Returns: Dict с ответом AI, проверкой и подсказками """ # Формируем историю для контекста history_text = "\n".join([ f"{'AI' if msg['role'] == 'assistant' else 'User'}: {msg['content']}" for msg in conversation_history[-6:] # Последние 6 сообщений ]) extra_fields_resp = '' if learning_lang.lower() == 'ja': # Для японского просим версию ответа с фуриганой ТОЛЬКО для кандзи # Не добавляй фуригану к кана или латинским буквам extra_fields_resp = ",\n \"response_annotated\": \"ответ на {learning_lang} с фуриганой ТОЛЬКО для кандзи (напр.: 今日(きょう)); не добавляй фуригану к кана или латинице\"" prompt = f"""Ты ведешь диалог на языке {learning_lang} уровня {level} в сценарии "{scenario}". История диалога: {history_text} User: {user_message} Верни ответ в формате JSON: {{ "response": "твой ответ на {learning_lang}", "translation": "перевод твоего ответа на {translation_lang}", "feedback": {{ "has_errors": true/false, "corrections": "исправления ошибок пользователя (если есть)", "comment": "краткий комментарий об ответе пользователя" }}, "suggestions": [ {{"learn": "подсказка на {learning_lang}", "learn_annotated": "подсказка с фуриганой (ТОЛЬКО для кандзи; {learning_lang})", "trans": "перевод подсказки на {translation_lang}"}}, {{"learn": "...", "learn_annotated": "...", "trans": "..."}} ]{extra_fields_resp} }} Требования: - Продолжай естественный диалог - Если у пользователя есть грамматические или лексические ошибки, укажи их в corrections - Будь дружелюбным и поддерживающим - Используй лексику уровня {level}""" try: logger.info(f"[GPT Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}, learn='{learning_lang}', to='{translation_lang}'") # Формируем сообщения для API messages = [ {"role": "system", "content": f"Ты - дружелюбный собеседник для практики языка {learning_lang} уровня {level}. Веди естественный диалог и помогай исправлять ошибки."} ] # Добавляем историю for msg in conversation_history[-6:]: messages.append(msg) # Добавляем текущее сообщение пользователя messages.append({"role": "user", "content": user_message}) # Добавляем инструкцию для форматирования ответа messages.append({"role": "user", "content": prompt}) response_data = await self._make_openai_request(messages, temperature=0.8) import json result = json.loads(response_data['choices'][0]['message']['content']) has_errors = result.get('feedback', {}).get('has_errors', False) logger.info(f"[GPT Response] continue_conversation: success, has_errors={has_errors}") return result except Exception as e: logger.error(f"[GPT Error] continue_conversation: {type(e).__name__}: {str(e)}") return { "response": "I see. Tell me more about that.", "translation": "Понятно. Расскажи мне больше об этом.", "feedback": { "has_errors": False, "corrections": "", "comment": "Good!" }, "suggestions": ["Sure!", "Well...", "Actually..."] } async def generate_level_test(self, learning_language: str = "en") -> List[Dict]: """ Сгенерировать тест для определения уровня языка Args: learning_language: Язык изучения (en, es, de, fr, ja) Returns: Список из 7 вопросов разной сложности """ # Определяем систему уровней и язык для промпта if learning_language == "ja": level_system = "JLPT (N5-N1)" language_name = "японского" levels_req = """- Вопросы 1-2: уровень N5 (базовый) - Вопросы 3-4: уровень N4-N3 (элементарный-средний) - Вопросы 5-6: уровень N2 (продвинутый) - Вопрос 7: уровень N1 (профессиональный)""" level_example = "N5" else: level_system = "CEFR (A1-C2)" lang_names = {"en": "английского", "es": "испанского", "de": "немецкого", "fr": "французского"} language_name = lang_names.get(learning_language, "английского") levels_req = """- Вопросы 1-2: уровень A1 (базовый) - Вопросы 3-4: уровень A2-B1 (элементарный-средний) - Вопросы 5-6: уровень B2-C1 (продвинутый) - Вопрос 7: уровень C2 (профессиональный)""" level_example = "A1" prompt = f"""Создай тест из 7 вопросов для определения уровня {language_name} языка ({level_system}). Верни ответ в формате JSON: {{ "questions": [ {{ "question": "текст вопроса на изучаемом языке", "question_ru": "перевод вопроса на русский", "options": ["вариант A", "вариант B", "вариант C", "вариант D"], "correct": 0, "level": "{level_example}" }} ] }} Требования: {levels_req} - Каждый вопрос с 4 вариантами ответа - correct - индекс правильного ответа (0-3) - Вопросы на грамматику, лексику и понимание""" try: logger.info(f"[GPT Request] generate_level_test: generating 7 questions for {learning_language}") system_msg = f"Ты - эксперт по тестированию уровня {language_name} языка. Создавай объективные тесты." messages = [ {"role": "system", "content": system_msg}, {"role": "user", "content": prompt} ] response_data = await self._make_openai_request(messages, temperature=0.7) import json result = json.loads(response_data['choices'][0]['message']['content']) questions_count = len(result.get('questions', [])) logger.info(f"[GPT Response] generate_level_test: success, generated {questions_count} questions") return result.get('questions', []) except Exception as e: logger.error(f"[GPT Error] generate_level_test: {type(e).__name__}: {str(e)}, using fallback questions") # Fallback с базовыми вопросами if learning_language == "ja": return self._get_jlpt_fallback_questions() return self._get_cefr_fallback_questions() def _get_cefr_fallback_questions(self) -> List[Dict]: """Fallback вопросы для CEFR (английский и европейские языки)""" return [ { "question": "What is your name?", "question_ru": "Как тебя зовут?", "options": ["My name is", "I am name", "Name my is", "Is name my"], "correct": 0, "level": "A1" }, { "question": "I ___ to school every day.", "question_ru": "Я ___ в школу каждый день.", "options": ["go", "goes", "going", "went"], "correct": 0, "level": "A1" }, { "question": "She ___ been to Paris twice.", "question_ru": "Она ___ в Париже дважды.", "options": ["have", "has", "had", "having"], "correct": 1, "level": "A2" }, { "question": "If I ___ rich, I would travel the world.", "question_ru": "Если бы я был богат, я бы путешествовал по миру.", "options": ["am", "was", "were", "be"], "correct": 2, "level": "B1" }, { "question": "The project ___ by next Monday.", "question_ru": "Проект ___ к следующему понедельнику.", "options": ["will complete", "will be completed", "completes", "is completing"], "correct": 1, "level": "B2" }, { "question": "Had I known about the meeting, I ___ attended.", "question_ru": "Если бы я знал о встрече, я бы посетил.", "options": ["would have", "will have", "would", "will"], "correct": 0, "level": "C1" }, { "question": "The nuances of his argument were so ___ that few could grasp them.", "question_ru": "Нюансы его аргумента были настолько ___, что немногие могли их понять.", "options": ["subtle", "obvious", "simple", "clear"], "correct": 0, "level": "C2" } ] def _get_jlpt_fallback_questions(self) -> List[Dict]: """Fallback вопросы для JLPT (японский)""" return [ { "question": "これは ___です。", "question_ru": "Это ___.", "options": ["ほん", "本ん", "ぼん", "もと"], "correct": 0, "level": "N5" }, { "question": "私は毎日学校に___。", "question_ru": "Я каждый день хожу в школу.", "options": ["いきます", "いくます", "いきす", "いきました"], "correct": 0, "level": "N5" }, { "question": "昨日、映画を___から、今日は勉強します。", "question_ru": "Вчера я посмотрел фильм, поэтому сегодня буду учиться.", "options": ["見た", "見て", "見る", "見ない"], "correct": 0, "level": "N4" }, { "question": "この本は読み___です。", "question_ru": "Эту книгу легко/трудно читать.", "options": ["やすい", "にくい", "たい", "そう"], "correct": 0, "level": "N3" }, { "question": "彼の話を聞く___、涙が出てきた。", "question_ru": "Слушая его рассказ, у меня потекли слёзы.", "options": ["につれて", "にしたがって", "とともに", "うちに"], "correct": 0, "level": "N2" }, { "question": "その計画は実現不可能と___。", "question_ru": "Этот план считается невыполнимым.", "options": ["言わざるを得ない", "言うまでもない", "言いかねない", "言うに及ばない"], "correct": 0, "level": "N2" }, { "question": "彼の行動は___に堪えない。", "question_ru": "Его поведение невозможно понять/вынести.", "options": ["理解", "批判", "説明", "弁解"], "correct": 0, "level": "N1" } ] # Глобальный экземпляр сервиса ai_service = AIService()