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

104 lines
4.2 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"""Для видеоигры "{game_title}"{genre_text} сгенерируй 6 челленджей для игрового марафона.
Требования:
- 2 лёгких челленджа (15-30 минут игры)
- 2 средних челленджа (1-2 часа игры)
- 2 сложных челленджа (3+ часов или высокая сложность)
Для каждого челленджа укажи:
- 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" содержащим массив челленджей.
Пример формата:
{{"challenges": [{{"title": "...", "description": "...", "type": "...", "difficulty": "...", "points": 50, "estimated_time": 30, "proof_type": "...", "proof_hint": "..."}}]}}"""
response = await self.client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
temperature=0.7,
max_tokens=2000,
)
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