Hyper-personalized AI exam prep. Upload your lecture materials → AI learns your professor's exam style → generates unlimited style-matched mock exams.
- Upload syllabus, lecture slides, past exams (PDF, PPT, DOCX, images)
- AI analyzes professor's style using Claude Opus 4.1 with extended thinking
- Generates unlimited mock exams matching your professor's exact style
- Real-time scoring + professor-perspective explanations
- Weakness heatmap tracking your weakest concepts
- Cram mode for pre-exam power studying
- Real-time token usage counter
- Docker & Docker Compose
- Anthropic API key
git clone <repo>
cd exam-prep-ai
cp .env.example .env
# Edit .env and set ANTHROPIC_API_KEY=your-key-heredocker compose up --buildApp available at:
- Frontend: http://localhost:3003
- Backend API: http://localhost:8001
- API Docs: http://localhost:8001/docs
- Readiness: http://localhost:8001/ready
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --buildThe dev override bind-mounts backend/ and frontend/ into the containers:
- Backend reloads through
uvicorn --reload - Frontend runs
next dev - Backend runs
alembic upgrade headbefore starting unlessRUN_MIGRATIONS=false - Uploaded files still persist in the shared
uploads_datavolume
The backend adds a request ID to every response (X-Request-ID), logs request outcomes, caps request setup time with REQUEST_TIMEOUT_SECONDS, and rate-limits repeated failed logins with AUTH_RATE_LIMIT_MAX_FAILURES over AUTH_RATE_LIMIT_WINDOW_SECONDS. AI streams send heartbeat events and fail with a retryable timeout after AI_STREAM_EVENT_TIMEOUT_SECONDS without upstream progress. Uploads also validate obvious extension/content-type mismatches before saving files.
exam-prep-ai/
├── backend/ # FastAPI Python backend
│ ├── app/
│ │ ├── core/ # Config, security, database
│ │ ├── models/ # SQLAlchemy ORM models
│ │ ├── schemas/ # Pydantic schemas
│ │ ├── routers/ # API route handlers
│ │ └── services/ # Business logic (Claude API, file parsing)
│ ├── migrations/ # Alembic database migrations
│ └── tests/ # pytest test suite
├── frontend/ # Next.js React frontend
│ └── src/
│ ├── app/ # Next.js App Router pages
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks (useSSE, useAuth)
│ ├── lib/ # API client, utilities
│ └── types/ # TypeScript type definitions
├── docker-compose.yml
├── init.sql # PostgreSQL initialization
└── .env.example # Environment variables template
- Upload: Add your syllabus + lecture slides + past exams
- Analyze: AI extracts professor's exam patterns using extended thinking (takes 30-60s for deep analysis)
- Generate: Get unlimited mock exams in your professor's exact style
- Study: Take exams, get AI-graded feedback from professor's perspective
- Track: See your weakness heatmap and improve weak concepts
Uses Claude Opus 4.1 with extended thinking:
- Pattern Analysis: 30,000 token thinking budget for deep professor style extraction
- Exam Generation: 10,000 token thinking budget per question for style verification
- Grading: Instant grading with professor-perspective explanations
- All responses stream in real-time via SSE
Note: The thinking: {type: "adaptive"} specification uses Claude's extended thinking feature implemented as {"type": "enabled", "budget_tokens": N} per the current Anthropic API.
GET /health- Liveness probeGET /ready- Readiness probe for DB and upload storage
POST /auth/register- Create accountPOST /auth/login- Login (returns JWT)GET /auth/me- Get current userPATCH /auth/me- Update profilePATCH /auth/me/password- Change passwordDELETE /auth/me- Delete account
GET /courses- List your coursesPOST /courses- Create courseGET /courses/{id}- Get course
POST /courses/{id}/materials- Upload filesGET /courses/{id}/materials- List filesPOST /courses/{id}/materials/{material_id}/retry- Retry failed parsingDELETE /courses/{id}/materials/{material_id}- Delete uploaded file
POST /courses/{id}/analysis- Start professor analysis (streaming)GET /courses/{id}/analysis- Get saved analysis
POST /courses/{id}/exams- Generate exam (streaming)GET /courses/{id}/exams- List course examsGET /exams- List recent exams across coursesGET /exams/{id}- Get exam with questionsDELETE /exams/{id}- Delete an exam and its answersPOST /exams/{id}/submit- Submit answers, get resultsGET /exams/{id}/result- Re-open saved grading resultsGET /courses/{id}/heatmap- Get weakness heatmap
cd backend
pip install -r requirements.txt
# Set .env variables
uvicorn app.main:app --reloadcd backend
pytest --cov=app --cov-report=term-missingStart the backend first, then run:
cd backend
E2E_API_URL=http://127.0.0.1:8000 python scripts/e2e_smoke.pyThe smoke test covers registration, login, course creation, material upload and retry, analysis streaming, exam generation, submission, and saved result retrieval.
To verify real Claude credentials and model configuration:
cd backend
USE_MOCK_CLAUDE=false ANTHROPIC_API_KEY=your-key python scripts/claude_smoke.pyTo run the browser smoke flow, start the backend and frontend first, then run:
cd frontend
E2E_BASE_URL=http://127.0.0.1:3000 npm run e2ecd backend
alembic upgrade head
alembic revision --autogenerate -m "describe change"DATABASE_URL is read from the same environment settings as the app. Docker startup runs alembic upgrade head automatically by default. Set RUN_MIGRATIONS=false only when another release step already handles migrations.
For legacy local Docker volumes that were originally created with SQLAlchemy create_all, the initial migration is intentionally tolerant: it stamps the existing schema path and adds the materials.processing_error column if it is missing.
cd frontend
npm install
npm run dev| Variable | Description | Required |
|---|---|---|
| ENVIRONMENT | Runtime mode (development or production) |
No (default: development) |
| ANTHROPIC_API_KEY | Your Anthropic API key | Yes |
| DATABASE_URL | PostgreSQL connection string | Yes |
| SECRET_KEY | JWT secret (32+ chars) | Yes |
| USE_MOCK_CLAUDE | Use deterministic mock AI responses | No (default in Docker: true) |
| RUN_MIGRATIONS | Run Alembic before backend startup | No (default: true) |
| AUTO_CREATE_TABLES | Fallback SQLAlchemy table creation | No (default in Docker: false) |
| CORS_ORIGINS | Comma-separated frontend origins allowed by the API | No (local defaults) |
| MATERIAL_PROCESSING_STALE_MINUTES | Mark abandoned material parsing jobs failed after this many minutes | No (default: 30) |
| MAX_UPLOAD_FILES | Maximum number of files per upload request | No (default: 10) |
| CLAUDE_MODEL | Claude model ID | No (default: claude-opus-4-1-20250805) |
| THINKING_BUDGET_ANALYSIS | Thinking tokens for analysis | No (default: 30000) |
| THINKING_BUDGET_GENERATION | Thinking tokens for generation | No (default: 10000) |