Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

96 lines
3.6 KiB
Python
Raw Permalink 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.
"""Quiz block delegates to the existing question generation coordinator."""
from __future__ import annotations
import logging
from typing import Any
from ..models import BlockType, SourceAnchor
from .base import BlockContext, BlockGenerator, GenerationFailure
logger = logging.getLogger(__name__)
class QuizGenerator(BlockGenerator):
block_type = BlockType.QUIZ
async def _generate(
self, ctx: BlockContext
) -> tuple[dict[str, Any], list[SourceAnchor], dict[str, Any]]:
params = ctx.block.params
chapter_title = params.get("chapter_title", ctx.chapter.title)
chapter_summary = params.get("chapter_summary", ctx.chapter.summary)
objectives = params.get("objectives") or ctx.chapter.learning_objectives
num_questions = max(1, min(8, int(params.get("num_questions") or 3)))
difficulty = str(params.get("difficulty") or "medium")
question_type = str(params.get("question_type") or "")
topic = chapter_title.strip() or ctx.book_id
# Fold chapter context directly into the topic so the planner sees
# it without needing a separate "preference" channel.
extra_context = "; ".join(filter(None, [chapter_summary, *objectives]))
if extra_context:
topic = f"{topic}\n\n[Chapter context: {extra_context}]"
question_types = [question_type] if question_type else []
try:
from deeptutor.agents.question.coordinator import AgentCoordinator
coordinator = AgentCoordinator(
kb_name=ctx.primary_kb,
language=ctx.language,
enable_idea_rag=ctx.rag_enabled and bool(ctx.primary_kb),
)
summary = await coordinator.generate_from_topic(
user_topic=topic,
num_questions=num_questions,
difficulty=difficulty,
question_types=question_types,
)
except Exception as exc:
logger.warning(f"QuizGenerator failed: {exc}", exc_info=True)
raise GenerationFailure(f"quiz generation failed: {exc}") from exc
questions = self._extract_questions(summary)
if not questions:
raise GenerationFailure("no questions generated")
return (
{"questions": questions, "topic": topic},
[],
{
"completed": summary.get("completed", 0),
"failed": summary.get("failed", 0),
"kb": ctx.primary_kb,
},
)
@staticmethod
def _extract_questions(summary: dict[str, Any]) -> list[dict[str, Any]]:
results = summary.get("results") or []
if not isinstance(results, list):
return []
out: list[dict[str, Any]] = []
for item in results:
if not isinstance(item, dict) or not item.get("success"):
continue
qa = item.get("qa_pair") or {}
if not isinstance(qa, dict):
continue
out.append(
{
"question_id": qa.get("question_id", ""),
"question": qa.get("question", ""),
"question_type": qa.get("question_type", "written"),
"options": qa.get("options") or {},
"correct_answer": qa.get("correct_answer", ""),
"explanation": qa.get("explanation", ""),
"difficulty": qa.get("difficulty", ""),
"concentration": qa.get("concentration", ""),
}
)
return out
__all__ = ["QuizGenerator"]