Files
game-marathon/backend/app/services/gpt.py
2025-12-16 03:53:53 +07:00

114 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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