shalev396/video-quiz-generator
๐ AI Quiz Generator
Upload an audio or video recording (a lecture, talk or meeting) or paste a transcript. The Space transcribes it with Whisper, asks Qwen2.5-Instruct for a multiple-choice quiz in strict JSON, checks the reply against the schema and lets you take the quiz and get graded. The reply is parsed leniently (code fences, smart quotes, trailing commas and stray quotes are fixed; whole question objects are salvaged from a broken reply) but every question is validated strictly. When the quiz is not valid the model gets up to 2 repair turns, then one fresh generation with light seeded sampling (plus 1 repair turn).
API
/predict, inputs, in order:
Outputs: [quiz, seconds, device]. seconds is a float, device is "gpu" or "cpu", and quiz is:
{
"questions": [
{"question": "...", "options": ["...", "...", "...", "..."], "answer_index": 0, "explanation": "..."}
],
"valid": true, // every schema check passed (incl. exactly n_questions questions)
"valid_first_try": true, // the first reply was valid: no repair turn or retry was needed
"strict_json_first_try": true, // the first reply was valid JSON as written (no syntax fix)
"attempts": 1, // model calls: 1 generation + repair turns (+ 1 fresh retry), at most 5
"repairs": [], // repair turns run, in order: "fix" (corrected reply) | "more" (missing questions)
"regenerated": false, // true when the fresh sampled generation ran
"parse": ["strict"], // per attempt: "strict" | "lenient" | "salvaged" | "unreadable"
"chosen_attempt": 1, // which attempt was returned (the first valid one, else the one with most questions)
"problems": [], // what failed validation in the returned attempt
"n_questions": 4, // requested (clamped to 2-10)
"n_delivered": 4, // questions actually returned; < n_questions only when valid is false
"difficulty": "mixed",
"transcript": "...", // the text the quiz was written from
"source": "media", // "media" | "text"
"audio_seconds": 11.01, // null for text input
"audio_truncated": false,
"asr_model": "openai/whisper-small",
"llm_model": "Qwen/Qwen2.5-1.5B-Instruct"
}When no attempt returns a single usable question, the call fails with an error message instead.
S=https://shalev396-video-quiz-generator.hf.space
# pasted transcript
curl -X POST $S/gradio_api/call/predict -H "Content-Type: application/json" \
-d '{"data": [null, "Honey bees live in colonies. A single queen lays the eggs, while workers collect nectar.", 3, "easy"]}'
# a media file by URL
WAV=https://huggingface.co/spaces/shalev396/video-quiz-generator/resolve/main/examples/jfk.wav
curl -X POST $S/gradio_api/call/predict -H "Content-Type: application/json" \
-d "{\"data\": [{\"path\": \"$WAV\", \"meta\": {\"_type\": \"gradio.FileData\"}}, \"\", 2, \"mixed\"]}"
curl -N $S/gradio_api/call/predict/<event_id>import { Client, handle_file } from "@gradio/client";
const client = await Client.connect("shalev396/video-quiz-generator");
const { data } = await client.predict("/predict", [
handle_file("https://huggingface.co/spaces/shalev396/video-quiz-generator/resolve/main/examples/jfk.wav"),
"", 2, "mixed",
]);
// data = [{questions: [...], valid: true, ...}, 4.2, "gpu"]How it works
pipeline.py (in this Space) does everything: PyAV decodes the audio track to 16 kHz mono, Whisper transcribes it in 30 s windows, Qwen2.5-Instruct writes the quiz (greedy decoding, one worked example in the prompt), and the reply is validated, with the repair / retry turns above when needed. On ZeroGPU no new repair or retry call starts after half of the GPU time budget. There is no model repo: all three models are pretrained Hub checkpoints, preloaded at build time.
Hardware: ZeroGPU runs whisper-small + Qwen2.5-1.5B-Instruct. On CPU hardware the same code runs whisper-tiny + Qwen2.5-1.5B-Instruct (~2.5 min per quiz). Qwen2.5-0.5B was evaluated and rejected: 0/6 valid quizzes. There is no CPU fallback on ZeroGPU: a visitor without GPU quota gets the ZeroGPU error message.
Evaluation (schema-valid rate, LLM-judge scores) is in the training notebook.
