import json from openai import AsyncOpenAI from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.setting import Setting DEEPSEEK_BASE_URL = "https://api.deepseek.com" DEEPSEEK_MODEL = "deepseek-chat" async def _get_api_key(db: AsyncSession) -> str: result = await db.execute(select(Setting).where(Setting.key == "deepseek_api_key")) setting = result.scalar_one_or_none() if not setting or not setting.value: raise ValueError("API ключ DeepSeek не настроен. Перейдите в Настройки.") return setting.value def _client(api_key: str) -> AsyncOpenAI: return AsyncOpenAI(api_key=api_key, base_url=DEEPSEEK_BASE_URL) async def check_connection(db: AsyncSession) -> str: api_key = await _get_api_key(db) client = _client(api_key) response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": "Ответь одним словом: работает"}], max_tokens=10, ) return response.choices[0].message.content.strip() async def generate_questions(db: AsyncSession, topic: str, count: int = 7) -> list[dict]: api_key = await _get_api_key(db) client = _client(api_key) prompt = f"""Сгенерируй {count} вопросов для теста по теме: "{topic}". Верни ТОЛЬКО JSON без пояснений в следующем формате: {{ "questions": [ {{ "text": "Текст вопроса", "answers": [ {{"text": "Вариант 1", "is_correct": true}}, {{"text": "Вариант 2", "is_correct": false}}, {{"text": "Вариант 3", "is_correct": false}} ] }} ] }} Требования: - Минимум 3 варианта ответа на каждый вопрос - Ровно один правильный ответ на каждый вопрос - Вопросы должны проверять практические знания по теме - Варианты ответов должны быть правдоподобными""" response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, max_tokens=3000, ) data = json.loads(response.choices[0].message.content) return data["questions"] async def improve_question( db: AsyncSession, question: str, answers: list[str] ) -> dict: api_key = await _get_api_key(db) client = _client(api_key) answers_str = "\n".join(f"{i + 1}. {a}" for i, a in enumerate(answers)) prompt = f"""Улучши формулировки вопроса и вариантов ответов для теста. Сделай их более чёткими, однозначными и профессиональными. Вопрос: {question} Варианты ответов (верни в том же порядке и том же количестве): {answers_str} Верни ТОЛЬКО JSON без пояснений: {{ "question": "улучшенный текст вопроса", "answers": ["улучшенный вариант 1", "улучшенный вариант 2", ...] }}""" response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, max_tokens=600, ) return json.loads(response.choices[0].message.content) async def generate_distractors( db: AsyncSession, question: str, existing_answers: list[str] ) -> list[str]: api_key = await _get_api_key(db) client = _client(api_key) existing_str = "\n".join(f"- {a}" for a in existing_answers) prompt = f"""Для вопроса теста сгенерируй 3 правдоподобных неправильных варианта ответа (дистракторы). Вопрос: {question} Уже существующие варианты ответов: {existing_str} Верни ТОЛЬКО JSON без пояснений: {{"distractors": ["Вариант 1", "Вариант 2", "Вариант 3"]}} Требования: - Дистракторы должны быть правдоподобными, но неправильными - Не повторяй уже существующие варианты - Дистракторы должны быть сопоставимы по длине с существующими вариантами""" response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, max_tokens=400, ) data = json.loads(response.choices[0].message.content) return data["distractors"] async def review_test(db: AsyncSession, title: str, questions: list[dict]) -> str: api_key = await _get_api_key(db) client = _client(api_key) questions_str = "" for i, q in enumerate(questions, 1): questions_str += f"\nВопрос {i}: {q.get('text', '')}\n" for a in q.get("answers", []): marker = "✓" if a.get("is_correct") else "✗" questions_str += f" {marker} {a.get('text', '')}\n" prompt = f"""Проанализируй тест и дай рекомендации по улучшению его качества. Название теста: {title} Вопросы: {questions_str} Оцени по следующим критериям: 1. Качество и чёткость формулировок вопросов 2. Качество вариантов ответов (правдоподобность дистракторов) 3. Охват темы и разнообразие вопросов 4. Конкретные рекомендации по улучшению Отвечай на русском языке, структурированно.""" response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": prompt}], max_tokens=1500, ) return response.choices[0].message.content.strip() async def improve_all( db: AsyncSession, title: str, questions: list[dict] ) -> list[dict]: api_key = await _get_api_key(db) client = _client(api_key) questions_str = "" for i, q in enumerate(questions, 1): questions_str += f"\nВопрос {i}: {q.get('text', '')}\n" for j, a in enumerate(q.get("answers", []), 1): questions_str += f" {j}. {a.get('text', '') if isinstance(a, dict) else a}\n" prompt = f"""Улучши формулировки всех вопросов и вариантов ответов в тесте. Сделай их более чёткими, однозначными и профессиональными. Название теста: {title} Вопросы: {questions_str} Верни ТОЛЬКО JSON. Для каждого вопроса — улучшенную формулировку и все варианты ответов в том же порядке и том же количестве: {{ "questions": [ {{ "question": "улучшенный текст вопроса 1", "answers": ["улучшенный вариант 1", "улучшенный вариант 2", "..."] }} ] }}""" response = await client.chat.completions.create( model=DEEPSEEK_MODEL, messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, max_tokens=4000, ) data = json.loads(response.choices[0].message.content) return data["questions"]