import json from openai import AsyncOpenAI from app.core.config import settings from app.schemas import ChallengeGenerated class GPTService: """Service for generating challenges using OpenAI GPT""" def __init__(self): self.client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY) async def generate_challenges( self, game_title: str, game_genre: str | None = None ) -> list[ChallengeGenerated]: """ Generate challenges for a game using GPT. Args: game_title: Name of the game game_genre: Optional genre of the game Returns: List of generated challenges """ genre_text = f" (жанр: {game_genre})" if game_genre else "" prompt = f"""Ты — эксперт по видеоиграм. Сгенерируй 6 КОНКРЕТНЫХ челленджей для игры "{game_title}"{genre_text}. ВАЖНО: Челленджи должны быть СПЕЦИФИЧНЫМИ для этой игры! - Используй РЕАЛЬНЫЕ названия локаций, боссов, персонажей, миссий, уровней из игры - Основывайся на том, какие челленджи РЕАЛЬНО делают игроки в этой игре (спидраны, no-hit боссов, сбор коллекционных предметов и т.д.) - НЕ генерируй абстрактные челленджи типа "пройди уровень" или "убей 10 врагов" Примеры ХОРОШИХ челленджей: - Dark Souls: "Победи Орнштейна и Смоуга без призыва" / "Пройди Чумной город без отравления" - GTA V: "Получи золото в миссии «Ювелирное дело»" / "Выиграй уличную гонку на Vinewood" - Hollow Knight: "Победи Хорнет без получения урона" / "Найди все грибные споры в Грибных пустошах" - Minecraft: "Убей Дракона Края за один визит в Энд" / "Построй работающую ферму железа" Требования по сложности: - 2 лёгких (15-30 мин): простые задачи, знакомство с игрой - 2 средних (1-2 часа): требуют навыка или исследования - 2 сложных (3+ часа): серьёзный челлендж, достижения, полное прохождение Формат ответа — JSON: - title: название на русском (до 50 символов), конкретное и понятное - description: что именно сделать (1-2 предложения), с деталями из игры - type: completion | no_death | speedrun | collection | achievement | challenge_run - difficulty: easy | medium | hard - points: easy=20-40, medium=45-75, hard=90-150 - estimated_time: время в минутах - proof_type: screenshot | video | steam - proof_hint: ЧТО КОНКРЕТНО должно быть видно на скриншоте/видео (экран победы, достижение, локация и т.д.) Ответь ТОЛЬКО JSON: {{"challenges": [{{"title": "...", "description": "...", "type": "...", "difficulty": "...", "points": 50, "estimated_time": 30, "proof_type": "...", "proof_hint": "..."}}]}}""" response = await self.client.chat.completions.create( model="gpt-5-mini", messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, temperature=0.8, max_tokens=2500, ) content = response.choices[0].message.content data = json.loads(content) challenges = [] for ch in data.get("challenges", []): # Validate and normalize type ch_type = ch.get("type", "completion") if ch_type not in ["completion", "no_death", "speedrun", "collection", "achievement", "challenge_run"]: ch_type = "completion" # Validate difficulty difficulty = ch.get("difficulty", "medium") if difficulty not in ["easy", "medium", "hard"]: difficulty = "medium" # Validate proof_type proof_type = ch.get("proof_type", "screenshot") if proof_type not in ["screenshot", "video", "steam"]: proof_type = "screenshot" # Validate points based on difficulty points = ch.get("points", 30) if not isinstance(points, int) or points < 1: points = 30 # Clamp points to expected ranges if difficulty == "easy": points = max(20, min(40, points)) elif difficulty == "medium": points = max(45, min(75, points)) elif difficulty == "hard": points = max(90, min(150, points)) challenges.append(ChallengeGenerated( title=ch.get("title", "Unnamed Challenge")[:100], description=ch.get("description", "Complete the challenge"), type=ch_type, difficulty=difficulty, points=points, estimated_time=ch.get("estimated_time"), proof_type=proof_type, proof_hint=ch.get("proof_hint"), )) return challenges