feat: мульти-провайдер AI, выбор типов заданий, настройка количества
- Добавлена поддержка нескольких AI провайдеров (OpenAI, Google Gemini) - Добавлена админ-панель (/admin) для переключения AI моделей - Добавлен AIModelService для управления моделями в БД - Добавлен выбор типа заданий (микс, перевод слов, подстановка, перевод предложений) - Добавлена настройка количества заданий (5-15) - ai_service динамически выбирает провайдера на основе активной модели - Обработка ограничений моделей (temperature, response_format) - Очистка markdown обёртки из ответов Gemini 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,56 +2,160 @@ import logging
|
||||
import httpx
|
||||
from openai import AsyncOpenAI
|
||||
from config.settings import settings
|
||||
from typing import Dict, List
|
||||
from database.db import async_session_maker
|
||||
from database.models import AIProvider
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AIService:
|
||||
"""Сервис для работы с OpenAI API через Cloudflare Gateway"""
|
||||
"""Сервис для работы с AI API (OpenAI и Google)"""
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = settings.openai_api_key
|
||||
self.openai_api_key = settings.openai_api_key
|
||||
self.google_api_key = settings.google_api_key
|
||||
|
||||
# Проверяем, настроен ли Cloudflare AI Gateway
|
||||
if settings.cloudflare_account_id:
|
||||
# Используем Cloudflare AI Gateway с прямыми HTTP запросами
|
||||
self.base_url = (
|
||||
self.openai_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}")
|
||||
logger.info(f"AI Service initialized with Cloudflare Gateway: {self.openai_base_url}")
|
||||
else:
|
||||
# Прямое подключение к OpenAI
|
||||
self.base_url = "https://api.openai.com/v1"
|
||||
self.openai_base_url = "https://api.openai.com/v1"
|
||||
self.use_cloudflare = False
|
||||
logger.info("AI Service initialized with direct OpenAI connection")
|
||||
|
||||
# Google Gemini API URL (через Cloudflare Gateway или напрямую)
|
||||
if settings.cloudflare_account_id:
|
||||
self.google_base_url = (
|
||||
f"https://gateway.ai.cloudflare.com/v1/"
|
||||
f"{settings.cloudflare_account_id}/"
|
||||
f"{settings.cloudflare_gateway_id}/"
|
||||
f"google-ai-studio/v1"
|
||||
)
|
||||
else:
|
||||
self.google_base_url = "https://generativelanguage.googleapis.com/v1beta"
|
||||
|
||||
# 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"
|
||||
# Кеш активной модели (обновляется при запросах)
|
||||
self._cached_model: Optional[str] = None
|
||||
self._cached_provider: Optional[AIProvider] = None
|
||||
|
||||
async def _get_active_model(self) -> tuple[str, AIProvider]:
|
||||
"""Получить активную модель и провайдера из БД"""
|
||||
from services.ai_model_service import AIModelService, DEFAULT_MODEL, DEFAULT_PROVIDER
|
||||
|
||||
async with async_session_maker() as session:
|
||||
model = await AIModelService.get_active_model(session)
|
||||
if model:
|
||||
self._cached_model = model.model_name
|
||||
self._cached_provider = model.provider
|
||||
return model.model_name, model.provider
|
||||
|
||||
return DEFAULT_MODEL, DEFAULT_PROVIDER
|
||||
|
||||
async def _make_request(self, messages: list, temperature: float = 0.3) -> dict:
|
||||
"""Выполнить запрос к активному AI провайдеру"""
|
||||
model_name, provider = await self._get_active_model()
|
||||
|
||||
if provider == AIProvider.google:
|
||||
return await self._make_google_request(messages, temperature, model_name)
|
||||
else:
|
||||
return await self._make_openai_request(messages, temperature, model_name)
|
||||
|
||||
async def _make_google_request(self, messages: list, temperature: float = 0.3, model: str = "gemini-2.5-flash-lite") -> dict:
|
||||
"""Выполнить запрос к Google Gemini API (через Cloudflare Gateway или напрямую)"""
|
||||
url = f"{self.google_base_url}/models/{model}:generateContent"
|
||||
|
||||
# Конвертируем формат сообщений OpenAI в формат Google
|
||||
# System message добавляем как первое user сообщение
|
||||
contents = []
|
||||
|
||||
for msg in messages:
|
||||
role = msg["role"]
|
||||
content = msg["content"]
|
||||
|
||||
if role == "system":
|
||||
# Добавляем system как user сообщение в начало
|
||||
contents.insert(0, {"role": "user", "parts": [{"text": f"[System instruction]: {content}"}]})
|
||||
elif role == "user":
|
||||
contents.append({"role": "user", "parts": [{"text": content}]})
|
||||
elif role == "assistant":
|
||||
contents.append({"role": "model", "parts": [{"text": content}]})
|
||||
|
||||
payload = {
|
||||
"contents": contents,
|
||||
"generationConfig": {
|
||||
"temperature": temperature
|
||||
}
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
"x-goog-api-key": self.google_api_key
|
||||
}
|
||||
|
||||
response = await self.http_client.post(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# Конвертируем ответ Google в формат OpenAI для совместимости
|
||||
text = data["candidates"][0]["content"]["parts"][0]["text"]
|
||||
|
||||
# Убираем markdown обёртку если есть (```json ... ```)
|
||||
if text.startswith('```'):
|
||||
lines = text.split('\n')
|
||||
# Убираем первую строку (```json) и последнюю (```)
|
||||
if lines[-1].strip() == '```':
|
||||
lines = lines[1:-1]
|
||||
else:
|
||||
lines = lines[1:]
|
||||
text = '\n'.join(lines)
|
||||
|
||||
return {
|
||||
"choices": [{
|
||||
"message": {
|
||||
"content": text
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
async def _make_openai_request(self, messages: list, temperature: float = 0.3, model: str = "gpt-4o-mini") -> dict:
|
||||
"""Выполнить запрос к OpenAI API (через Cloudflare или напрямую)"""
|
||||
url = f"{self.openai_base_url}/chat/completions"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.openai_api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
"temperature": temperature,
|
||||
"response_format": {"type": "json_object"}
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
# Модели с ограничениями (не поддерживают temperature и json mode)
|
||||
limited_models = {"gpt-5-nano", "o1", "o1-mini", "o1-preview", "o3-mini"}
|
||||
|
||||
if model not in limited_models:
|
||||
payload["temperature"] = temperature
|
||||
|
||||
# JSON mode
|
||||
payload["response_format"] = {"type": "json_object"}
|
||||
|
||||
response = await self.http_client.post(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
@@ -85,22 +189,22 @@ class AIService:
|
||||
Важно: верни только JSON, без дополнительного текста."""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] translate_word: word='{word}', source='{source_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_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')}'")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI Error] translate_word: {type(e).__name__}: {str(e)}")
|
||||
# Fallback в случае ошибки
|
||||
return {
|
||||
"word": word,
|
||||
@@ -164,14 +268,14 @@ class AIService:
|
||||
- Верни только JSON, без дополнительного текста"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] translate_word_with_contexts: word='{word}', source='{source_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_request(messages, temperature=0.3)
|
||||
|
||||
import json
|
||||
content = response_data['choices'][0]['message']['content']
|
||||
@@ -184,11 +288,11 @@ class AIService:
|
||||
|
||||
result = json.loads(content)
|
||||
translations_count = len(result.get('translations', []))
|
||||
logger.info(f"[GPT Response] translate_word_with_contexts: success, {translations_count} translations")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI Error] translate_word_with_contexts: {type(e).__name__}: {str(e)}")
|
||||
# Fallback в случае ошибки
|
||||
return {
|
||||
"word": word,
|
||||
@@ -250,14 +354,14 @@ class AIService:
|
||||
- Для каждого слова укажи точный перевод и транскрипцию"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] translate_words_batch: {len(words)} words, {source_lang} -> {translation_lang}")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_request(messages, temperature=0.3)
|
||||
|
||||
import json
|
||||
content = response_data['choices'][0]['message']['content']
|
||||
@@ -278,14 +382,14 @@ class AIService:
|
||||
break
|
||||
|
||||
if not isinstance(result, list):
|
||||
logger.warning(f"[GPT Warning] translate_words_batch: unexpected format, got {type(result)}")
|
||||
logger.warning(f"[AI 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")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI Error] translate_words_batch: {type(e).__name__}: {str(e)}")
|
||||
# Возвращаем слова без перевода в случае ошибки
|
||||
return [{"word": w, "translation": "", "transcription": ""} for w in words]
|
||||
|
||||
@@ -317,22 +421,22 @@ class AIService:
|
||||
Учитывай возможные вариации ответа. Если смысл передан правильно, даже с небольшими грамматическими неточностями, засчитывай ответ."""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] check_answer: user_answer='{user_answer[:30]}...'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_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)}")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI Error] check_answer: {type(e).__name__}: {str(e)}")
|
||||
return {
|
||||
"is_correct": False,
|
||||
"feedback": "Ошибка проверки ответа",
|
||||
@@ -364,28 +468,73 @@ class AIService:
|
||||
Предложение должно быть простым и естественным. Контекст должен четко подсказывать правильное слово."""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] generate_fill_in_sentence: word='{word}', lang='{learning_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_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")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI 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_sentence_for_translation(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": "предложение на {learning_lang} со словом {word}",
|
||||
"translation": "перевод предложения на {translation_lang}"
|
||||
}}
|
||||
|
||||
Предложение должно быть простым (5-10 слов), естественным и подходящим для изучения языка."""
|
||||
|
||||
try:
|
||||
logger.info(f"[AI Request] generate_sentence_for_translation: word='{word}', lang='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - преподаватель иностранных языков. Создавай простые и понятные примеры для практики перевода."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
response_data = await self._make_request(messages, temperature=0.7)
|
||||
|
||||
import json
|
||||
result = json.loads(response_data['choices'][0]['message']['content'])
|
||||
logger.info(f"[AI Response] generate_sentence_for_translation: success")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[AI Error] generate_sentence_for_translation: {type(e).__name__}: {str(e)}")
|
||||
# Fallback - простое предложение
|
||||
return {
|
||||
"sentence": f"I use {word} every day.",
|
||||
"translation": f"Я использую {word} каждый день."
|
||||
}
|
||||
|
||||
async def generate_thematic_words(
|
||||
self,
|
||||
theme: str,
|
||||
@@ -445,14 +594,14 @@ class AIService:
|
||||
- Разнообразными (существительные, глаголы, прилагательные)"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] generate_thematic_words: theme='{theme}', level='{level}', count={count}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_request(messages, temperature=0.7)
|
||||
|
||||
import json
|
||||
result = json.loads(response_data['choices'][0]['message']['content'])
|
||||
@@ -466,14 +615,14 @@ class AIService:
|
||||
]
|
||||
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")
|
||||
logger.info(f"[AI 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")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI 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]:
|
||||
@@ -514,23 +663,23 @@ class AIService:
|
||||
|
||||
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}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_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")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI 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:
|
||||
@@ -583,22 +732,22 @@ class AIService:
|
||||
- Подсказки должны помочь пользователю ответить"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] start_conversation: scenario='{scenario}', level='{level}', learn='{learning_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI 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)
|
||||
response_data = await self._make_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}'")
|
||||
logger.info(f"[AI Response] start_conversation: success, scenario='{scenario}'")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[GPT Error] start_conversation: {type(e).__name__}: {str(e)}")
|
||||
logger.error(f"[AI Error] start_conversation: {type(e).__name__}: {str(e)}")
|
||||
return {
|
||||
"message": "Hello! How are you today?",
|
||||
"translation": "Привет! Как дела сегодня?",
|
||||
@@ -667,7 +816,7 @@ User: {user_message}
|
||||
- Используй лексику уровня {level}"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
logger.info(f"[AI Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
# Формируем сообщения для API
|
||||
messages = [
|
||||
@@ -684,16 +833,16 @@ User: {user_message}
|
||||
# Добавляем инструкцию для форматирования ответа
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
response_data = await self._make_openai_request(messages, temperature=0.8)
|
||||
response_data = await self._make_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}")
|
||||
logger.info(f"[AI 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)}")
|
||||
logger.error(f"[AI Error] continue_conversation: {type(e).__name__}: {str(e)}")
|
||||
return {
|
||||
"response": "I see. Tell me more about that.",
|
||||
"translation": "Понятно. Расскажи мне больше об этом.",
|
||||
@@ -756,7 +905,7 @@ User: {user_message}
|
||||
- Вопросы на грамматику, лексику и понимание"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] generate_level_test: generating 7 questions for {learning_language}")
|
||||
logger.info(f"[AI Request] generate_level_test: generating 7 questions for {learning_language}")
|
||||
|
||||
system_msg = f"Ты - эксперт по тестированию уровня {language_name} языка. Создавай объективные тесты."
|
||||
messages = [
|
||||
@@ -764,16 +913,16 @@ User: {user_message}
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
response_data = await self._make_openai_request(messages, temperature=0.7)
|
||||
response_data = await self._make_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")
|
||||
logger.info(f"[AI 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")
|
||||
logger.error(f"[AI Error] generate_level_test: {type(e).__name__}: {str(e)}, using fallback questions")
|
||||
# Fallback с базовыми вопросами
|
||||
if learning_language == "ja":
|
||||
return self._get_jlpt_fallback_questions()
|
||||
|
||||
Reference in New Issue
Block a user