from __future__ import annotations from typing import List from quiz.models import Question, Quest from services.model_router import ModelRouter from services.json_parser import extract_json from config.prompts import QUIZ_AGENT_BATCH_SYSTEM class QuizAgent: def __init__(self, router: ModelRouter): self._router = router def generate_for_quest(self, quest: Quest, source_text: str = "", questions_per_quest: int = 3, language: str = "en") -> List[Question]: """ Single Nemotron call generates all questions for a quest (regular + boss). source_text grounds questions in actual material, reducing hallucination. """ source_snippet = source_text[:800] if source_text else "No source provided." prompt = ( f"Quest: {quest.name}\n" f"Topics: {', '.join(quest.topics)}\n" f"Boss topic: {quest.boss_topic}\n" f"Language: {language}\n" f"Source material:\n{source_snippet}\n\n" f"Generate {questions_per_quest} regular questions (is_boss:false) " f"and 1 boss question (is_boss:true) as the last item." ) raw = self._router.reason(prompt, QUIZ_AGENT_BATCH_SYSTEM) try: data = extract_json(raw) except ValueError as exc: raise ValueError(f"QuizAgent: {exc}") from exc questions = [] for q_data in data.get("questions", []): questions.append(Question( text=q_data["question"], topic=q_data.get("topic", quest.boss_topic), options=q_data["options"], correct_idx=q_data["correct_idx"], explanation=q_data.get("explanation", ""), difficulty=q_data.get("difficulty", "medium"), q_type=q_data.get("type", "mcq"), is_boss=q_data.get("is_boss", False), source_excerpt=source_snippet[:200], language=language, )) return questions