FastAPI + LangChain + Pinecone + Supabase RAG-powered quiz generation API.
quizrag-backend/
├── app/
│ ├── api/routes/
│ │ ├── auth.py # Sign up, sign in, /me
│ │ ├── resources.py # PDF upload, URL ingest, list, delete
│ │ └── quiz.py # Generate quiz, save results, history
│ ├── core/
│ │ ├── config.py # Pydantic settings from .env
│ │ └── security.py # JWT auth, password hashing
│ ├── models/
│ │ └── schemas.py # Request/response Pydantic models
│ ├── services/
│ │ ├── ingestion_service.py # PDF (PyMuPDF) + URL (trafilatura) extraction
│ │ ├── quiz_service.py # Gemini quiz generation from retrieved chunks
│ │ ├── rag_service.py # Pinecone + LangChain embeddings/retrieval
│ │ └── supabase_client.py # Supabase singleton
│ └── main.py # FastAPI app + CORS + routers
├── scripts/
│ └── supabase_schema.sql # Run this in Supabase SQL Editor
├── requirements.txt
└── .env.example
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtcp .env.example .env
# Fill in all values in .envSee scripts/supabase_schema.sql — paste into Supabase SQL Editor.
uvicorn app.main:app --reload --port 8000API docs: http://localhost:8000/docs
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/signup | Create account |
| POST | /api/auth/signin | Login |
| GET | /api/auth/me | Current user |
| POST | /api/resources/upload-pdf | Upload PDF |
| POST | /api/resources/ingest-url | Ingest URL |
| GET | /api/resources/ | List resources |
| DELETE | /api/resources/{id} | Delete resource |
| POST | /api/quiz/generate | Generate quiz |
| POST | /api/quiz/results | Save result |
| GET | /api/quiz/history | Quiz history |
| GET | /api/quiz/{id} | Get quiz |
Generic quiz generators produce questions from whatever the language model was trained on. That is fine for broad trivia, but useless when you need to be tested on specific material — a proprietary codebase, a niche academic paper, an internal onboarding document, or a textbook chapter the model has never seen.
ZappQuiz uses Retrieval-Augmented Generation to close that gap:
-
Ingest — when you upload a PDF or URL, the text is split into overlapping chunks (~800 tokens each, 150-token overlap) and each chunk is embedded using Gemini's
embedding-001model into a 768-dimensional vector. -
Store — vectors are stored in Pinecone under a per-user namespace, so retrieval is always scoped to the requesting user's own content.
-
Retrieve — at quiz generation time, the topic or focus area you specify is embedded using the same model, and the top-k most semantically similar chunks are pulled from Pinecone. For multi-resource quizzes, chunks are retrieved independently from each selected resource and then merged before generation.
-
Generate — the retrieved chunks are injected into the quiz generation prompt as context. The prompt explicitly instructs the model: "Based ONLY on the provided context..." — so questions are anchored to your material, not to the model's general knowledge.
This pipeline means the quiz is only as knowledgeable as the documents you provide. The quality of the questions is a direct reflection of the quality of the source material. A detailed, well-structured 50-page PDF will produce sharp, nuanced questions. A two-paragraph summary will produce shallow ones. This is a fundamental property of RAG, not a limitation of the model.
To prevent quizzes from becoming pure regurgitation exercises, approximately 20% of
questions (rounded down, minimum 1 for quizzes of 5+ questions) are generated without
retrieval — the model is instead given the topic name and a short concept summary extracted
from the top chunks, and asked to generate questions from its wider training knowledge.
These questions test whether the learner can connect the specific material to the broader
field. They are tagged "source": "external" in the response JSON so the frontend can
badge them distinctly.