From d48e3ff95252f73e6272e1e72473b3899b49d65e Mon Sep 17 00:00:00 2001 From: "mamonov.ep" Date: Thu, 4 Dec 2025 16:45:03 +0300 Subject: [PATCH] 123 --- services/ai_service.py | 213 +++++++++++++++++++++++++---------------- 1 file changed, 128 insertions(+), 85 deletions(-) diff --git a/services/ai_service.py b/services/ai_service.py index 078e67f..d78bfb6 100644 --- a/services/ai_service.py +++ b/services/ai_service.py @@ -1,28 +1,60 @@ +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 - base_url = ( + # Используем 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"compat" - ) - self.client = AsyncOpenAI( - api_key=settings.openai_api_key, - base_url=base_url + f"openai" ) + self.use_cloudflare = True + logger.info(f"AI Service initialized with Cloudflare Gateway: {self.base_url}") else: # Прямое подключение к OpenAI - self.client = AsyncOpenAI(api_key=settings.openai_api_key) + 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, target_lang: str = "ru") -> Dict: """ @@ -53,21 +85,22 @@ class AIService: Важно: верни только JSON, без дополнительного текста.""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - помощник для изучения английского языка. Отвечай только в формате JSON."}, - {"role": "user", "content": prompt} - ], - temperature=0.3, - response_format={"type": "json_object"} - ) + logger.info(f"[GPT Request] translate_word: word='{word}', target_lang='{target_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.choices[0].message.content) + 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, @@ -106,21 +139,22 @@ class AIService: Учитывай возможные вариации ответа. Если смысл передан правильно, даже с небольшими грамматическими неточностями, засчитывай ответ.""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - преподаватель английского языка. Проверяй ответы справедливо, учитывая контекст."}, - {"role": "user", "content": prompt} - ], - temperature=0.3, - response_format={"type": "json_object"} - ) + 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.choices[0].message.content) + 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": "Ошибка проверки ответа", @@ -150,21 +184,22 @@ class AIService: Предложение должно быть простым и естественным. Контекст должен четко подсказывать правильное слово.""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - преподаватель английского языка. Создавай простые и понятные упражнения."}, - {"role": "user", "content": prompt} - ], - temperature=0.7, - response_format={"type": "json_object"} - ) + logger.info(f"[GPT Request] generate_fill_in_sentence: word='{word}'") + + 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.choices[0].message.content) + 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, @@ -205,21 +240,23 @@ class AIService: - Разнообразными (существительные, глаголы, прилагательные)""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - преподаватель английского языка. Подбирай полезные и актуальные слова."}, - {"role": "user", "content": prompt} - ], - temperature=0.7, - response_format={"type": "json_object"} - ) + logger.info(f"[GPT Request] generate_thematic_words: theme='{theme}', level='{level}', count={count}") + + 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.choices[0].message.content) + result = json.loads(response_data['choices'][0]['message']['content']) + words_count = len(result.get('words', [])) + logger.info(f"[GPT Response] generate_thematic_words: success, generated {words_count} words") return result.get('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) -> List[Dict]: @@ -259,21 +296,24 @@ class AIService: - Разнообразие: существительные, глаголы, прилагательные, устойчивые выражения""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - преподаватель английского языка. Помогаешь извлекать полезные слова для изучения из текстов."}, - {"role": "user", "content": prompt} - ], - temperature=0.5, - response_format={"type": "json_object"} - ) + 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}") + + 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.choices[0].message.content) + 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") -> Dict: @@ -316,21 +356,22 @@ class AIService: - Подсказки должны помочь пользователю ответить""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - дружелюбный собеседник для практики английского. Веди естественный диалог."}, - {"role": "user", "content": prompt} - ], - temperature=0.8, - response_format={"type": "json_object"} - ) + logger.info(f"[GPT Request] start_conversation: scenario='{scenario}', level='{level}'") + + 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.choices[0].message.content) + 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": "Привет! Как дела сегодня?", @@ -388,6 +429,8 @@ User: {user_message} - Используй лексику уровня {level}""" try: + logger.info(f"[GPT Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}") + # Формируем сообщения для API messages = [ {"role": "system", "content": f"Ты - дружелюбный собеседник для практики английского языка уровня {level}. Веди естественный диалог и помогай исправлять ошибки."} @@ -403,18 +446,16 @@ User: {user_message} # Добавляем инструкцию для форматирования ответа messages.append({"role": "user", "content": prompt}) - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=messages, - temperature=0.8, - response_format={"type": "json_object"} - ) + response_data = await self._make_openai_request(messages, temperature=0.8) import json - result = json.loads(response.choices[0].message.content) + 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": "Понятно. Расскажи мне больше об этом.", @@ -458,21 +499,23 @@ User: {user_message} - Вопросы на грамматику, лексику и понимание""" try: - response = await self.client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "system", "content": "Ты - эксперт по тестированию уровня английского языка. Создавай объективные тесты."}, - {"role": "user", "content": prompt} - ], - temperature=0.7, - response_format={"type": "json_object"} - ) + logger.info(f"[GPT Request] generate_level_test: generating 7 questions") + + 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.choices[0].message.content) + 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 с базовыми вопросами return [ {