File size: 14,964 Bytes
7c6ffa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6515ef9
7c6ffa6
 
6515ef9
7c6ffa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6515ef9
 
7c6ffa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
---
title: DocDoe Backend
emoji: πŸ“š
colorFrom: indigo
colorTo: gray
sdk: docker
app_port: 7860
pinned: false
short_description: DocDoe FastAPI backend β€” tuition brain + study API
---

<!--
  The YAML block above is Hugging Face Space metadata (must stay at the very top
  of this file). When this backend/ folder is pushed to a Hugging Face Space, HF
  reads it to build the FastAPI app as a Docker Space on port 7860. It is inert
  in the main repo. Full runbook: backend/DEPLOY-huggingface.md.
-->

# AI Exam Success Backend

FastAPI foundation for the AI Exam Success Platform. This phase uses local file storage with an S3-compatible storage abstraction, SQLAlchemy models, Pydantic schemas, document chunk retrieval, pluggable AI providers, provider-based TTS/audio sync, and local Remotion MP4 export. AI mock mode is free by default; video voice generation now prefers Edge TTS for local real-audio testing when installed.

## Setup

```powershell
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
```

Optional:

```powershell
Copy-Item .env.example .env
```

Set `DATABASE_URL` to PostgreSQL when you have Postgres running. Without a `.env`, the app uses a local SQLite development database so the mock API can run immediately.

## Auth Modes

Local development keeps auth friction low by default:

```powershell
AUTH_ENABLED="false"
AUTH_PROVIDER="dev"
ALLOW_INSECURE_DEV_AUTH="true"
```

This is deliberately limited to local development. Set `ALLOW_INSECURE_DEV_AUTH=true` only with a localhost frontend; any other deployment must enable real authentication.

JWT mode enables real email/password signup and login through the FastAPI backend:

```powershell
AUTH_ENABLED="true"
AUTH_PROVIDER="jwt"
JWT_SECRET_KEY="replace-with-a-long-random-secret"
JWT_ALGORITHM="HS256"
ACCESS_TOKEN_EXPIRE_MINUTES="10080"
```

Use `POST /auth/signup` or `POST /auth/login` to get an access token, then send it as:

```text
Authorization: Bearer <access_token>
```

Supabase mode is provider-ready for later hosted auth:

```powershell
AUTH_ENABLED="true"
AUTH_PROVIDER="supabase"
SUPABASE_URL="https://your-project.supabase.co"
SUPABASE_JWT_SECRET="your-supabase-jwt-secret"
SUPABASE_ANON_KEY="your-supabase-anon-key"
```

The backend validates the Supabase JWT and creates a matching local user record when needed. Keep secrets server-side only; the frontend should use the anon key, never the JWT secret.

## AI Provider Modes

Mock mode is the default and needs no API key:

```powershell
AI_PROVIDER="mock"
```

Gemini mode uses the official `google-genai` SDK:

```powershell
AI_PROVIDER="gemini"
GEMINI_API_KEY="your-api-key"
GEMINI_MODEL_FLASH="gemini-2.5-flash"
GEMINI_MODEL_PRO="gemini-2.5-pro"
AI_FALLBACK_TO_MOCK="true"
```

When `AI_FALLBACK_TO_MOCK` is true, the backend logs Gemini failures safely and returns mock output instead of breaking the student flow.

## TTS Provider Modes

AI4Bharat Indic Parler-TTS is the preferred DocDoe teacher voice. It supports
Indian English and Malayalam, runs locally, and has no per-minute API cost.
The model repository is gated, so accept its access terms once and use a free
Hugging Face token for the initial download:

```powershell
py -3.11 -m venv .tts-venv
.\.tts-venv\Scripts\Activate.ps1
pip install -r requirements-tts-ai4bharat.txt
$env:HF_HUB_DISABLE_XET="1"
TTS_PROVIDER="ai4bharat"
TTS_DEFAULT_PROVIDER="ai4bharat"
VIDEO_TTS_PROVIDER="ai4bharat"
HUGGINGFACE_API_KEY="your-free-hugging-face-token"
AI4BHARAT_TTS_MODEL="ai4bharat/indic-parler-tts"
AI4BHARAT_TTS_SPEAKER="Anjali"
AI4BHARAT_TTS_DEVICE="auto"
AI4BHARAT_TTS_PRECISION="float32"
TTS_DEFAULT_VOICE="nila"
TTS_OUTPUT_DIR="public/generated/audio"
AUDIO_NORMALIZE="true"
```

Narration should keep scientific words in English and write Malayalam support
phrases in Malayalam script. The model automatically detects both languages.
Romanized Manglish can remain in subtitles, but native script is used internally
for dependable pronunciation.

On Windows, keep the pinned `sentencepiece==0.2.0` from the requirements file.
The newer 0.2.1 wheel can terminate the process while loading this model's
Malayalam tokenizer. The provider also retries the rare sampled output that
ends before a complete teaching sentence is spoken.

`float32` is the quality-first default and has been validated on the local RTX
3060 12 GB for clear Malayalam. Set `AI4BHARAT_TTS_PRECISION="float16"` only
when capacity matters more than Malayalam voice quality.

Edge TTS remains available as a lightweight development fallback when
`requirements-tts.txt` is installed.

Mock TTS is still available for silent timing tests only. Outputs are labeled `Silent mock audio`, and final rendering blocks silent mock audio unless explicitly allowed by the request.

Optional provider presets:

```powershell
TTS_PROVIDER="kokoro"
KOKORO_VOICE="af_heart"
KOKORO_SPEED="0.95"
```

`indic`, `hybrid`, `edge`, and `elevenlabs` provider classes are available behind the same interface. If a real provider is requested and fails, the backend returns a clear audio generation error instead of silently creating mock audio.

Generate sample audio props for Remotion:

```powershell
python scripts/generate_sample_tts.py --provider edge --voice-mode english_soft
python scripts/generate_sample_tts.py --provider mock --voice-mode ml_en_mix
```

## Storage Provider Modes

Local storage is the default and keeps the existing static URLs working:

```powershell
STORAGE_PROVIDER="local"
STORAGE_UPLOAD_PREFIX="uploads"
STORAGE_AUDIO_PREFIX="generated-audio"
STORAGE_VIDEO_PREFIX="generated-videos"
```

Cloud storage uses an S3-compatible client, so the same interface can target Cloudflare R2, AWS S3, Wasabi, or MinIO later:

```powershell
STORAGE_PROVIDER="r2"
STORAGE_BUCKET="your-bucket"
STORAGE_REGION="auto"
STORAGE_ENDPOINT_URL="https://<account-id>.r2.cloudflarestorage.com"
STORAGE_ACCESS_KEY_ID="your-access-key"
STORAGE_SECRET_ACCESS_KEY="your-secret-key"
STORAGE_PUBLIC_BASE_URL="https://media.example.com"
STORAGE_AUDIO_PREFIX="generated-audio"
STORAGE_VIDEO_PREFIX="generated-videos"
```

For AWS S3, set `STORAGE_PROVIDER="s3"` and use the bucket region. `STORAGE_PUBLIC_BASE_URL` should point to a public bucket URL, CDN, or custom domain. If cloud credentials or bucket settings are missing, the backend logs a safe warning and falls back to local storage.

Cloud buckets need public read access or a public CDN/domain for generated MP4/audio playback. Configure bucket CORS to allow `GET`/`HEAD` from the frontend origin during development and production.

## Local Video Rendering

Final MP4 export uses the local Remotion CLI through a fixed Node script. The backend writes render inputs to `generated-video-jobs/{video_id}/`, renders to `generated-videos/`, and saves a metadata JSON next to each MP4.

```powershell
GENERATED_VIDEO_JOBS_DIR="generated-video-jobs"
GENERATED_VIDEO_OUTPUT_DIR="generated-videos"
VIDEO_RENDER_TIMEOUT_SECONDS="420"
```

Render an existing audio-enhanced props file:

```powershell
npm run render:from-json -- --props public/generated/audio/sample-rest-api-en/scene-plan-with-audio.json --output generated-videos/manual-render.mp4
```

Render the built-in 9:16 anime visual explainer sample:

```powershell
npm run render:sample-anime-video
```

Generate and render the golden 60-second Photosynthesis anime explainer with real Edge TTS audio:

```powershell
npm run video:photosynthesis-real-audio
```

The final render flow validates audio before Remotion starts. Each scene must have a non-empty, duration-verified `audio_src`, and the finished MP4 is checked for an audio stream with `ffprobe` when available.

The user-facing flow should use render jobs:

```text
queued 0% -> running 10% -> preparing inputs 20% -> rendering 40% -> metadata 90% -> ready 100%
```

## Run

```powershell
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
```

Open:

- `http://127.0.0.1:8000/health`
- `http://127.0.0.1:8000/docs`

## Key Endpoints

- `GET /health`
- `POST /auth/signup`
- `POST /auth/login`
- `GET /auth/session`
- `GET /users/me`
- `PATCH /users/me`
- `POST /documents/upload`
- `GET /documents`
- `GET /documents/{document_id}`
- `GET /documents/{document_id}/preview`
- `GET /documents/{document_id}/chunks`
- `POST /documents/{document_id}/retrieve`
- `POST /documents/{document_id}/study-map`
- `POST /generations/notes`
- `POST /generations/simple-explanation`
- `POST /generations/exam-mode`
- `POST /quizzes/generate`
- `POST /flashcards/generate`
- `POST /previous-papers/upload`
- `POST /previous-papers/analyze`
- `POST /previous-papers/{paper_id}/extract-questions`
- `GET /previous-papers/{paper_id}/questions`
- `POST /previous-papers/analyze-structured`
- `POST /video/scene-plan`
- `POST /video/generate-audio`
- `POST /video/render-final`
- `POST /video/render-jobs`
- `GET /video/render-jobs`
- `GET /video/render-jobs/{job_id}`
- `DELETE /video/render-jobs/{job_id}`
- `GET /generated/audio/{user_id}/{video_id}/scene-001.wav` (owner-authenticated)
- `GET /generated/videos/{user_id}/{file_name}.mp4` (owner-authenticated)

## Local demo seed

Seed a realistic closed-beta demo dataset into your local database with a single command.
No provider calls, no secrets, no downloads β€” safe for air-gapped dev machines.

```powershell
cd backend
python scripts/seed_beta_demo.py
python scripts/seed_beta_demo.py --verbose   # show every row created/skipped
```

What gets created:

| Entity | Detail |
|---|---|
| User | `demo@docdoe.in`, role: student. Local password defaults to `demo1234`; production requires `DOCDOE_DEMO_PASSWORD`. |
| Study profile | Kerala HSE +1 Computer Science |
| User plan | free trial, 100 generation limit, 30-day window |
| Documents (Γ—4) | notes, question paper, synthetic non-personal classifier fixture, and chapter, each with 3 text chunks |
| Video jobs (Γ—2) | `script_ready` (awaiting render) + `completed` (mock, no real file) |

The script is **idempotent** β€” running it twice creates no duplicates. Seeded content contains no real name, school, parent, phone number, address, or other personal information.
Stub `.txt` files are written to `uploads/demo_*.txt` so file paths are non-null.

Production seeding refuses to run unless a strong password is supplied:

```powershell
$env:DOCDOE_DEMO_PASSWORD = "use-a-generated-secret-with-16-plus-characters"
python scripts/seed_beta_demo.py
```

**Remove demo data:**

```powershell
python scripts/clear_beta_demo.py             # delete demo records
python scripts/clear_beta_demo.py --dry-run   # preview what would be deleted
```

`clear_beta_demo.py` deletes only the demo user and their associated rows.
It also removes the `uploads/demo_*.txt` stub files created by the seed script.
All other user data is untouched.

## Notes

- All user library endpoints are protected by `app/core/auth.py`. With auth disabled they use the demo student; with auth enabled they require a valid bearer token.
- Documents, generations, quizzes, flashcards, previous papers, structured previous questions, generated audio, and video render jobs are scoped to the current user. Request bodies are not trusted for `user_id`.
- Uploaded files are stored in `backend/uploads`.
- Database tables are created automatically on app startup for the MVP.
- The AI provider supports `MockAIProvider` and `GeminiAIProvider`.
- Gemini responses are requested as structured JSON and use retrieved chunks as context instead of sending full documents.
- The exam-tutor output style is shaped for simple meaning, keywords, memory tricks, exam answers, quick checks, and mistakes to avoid.
- Video scene plans support `visual_style`: `clean_explainer` or `anime_visual_explainer`, and `video_format`: `16:9` or `9:16`. The anime visual style uses original mascot panels, floating cards, icons, arrows, bottom subtitles, and short-form progressive reveal. It must not use copyrighted anime clips, copied characters, avatars, or real-person likenesses.
- `/video/generate-audio` accepts either the existing single `language` flow or a `languages` array and returns `audio_variants` for separate language-specific MP4 rendering later.
- Previous papers can be uploaded, converted into structured `PreviousQuestion` rows, and analyzed for repeated topics, chapter weightage, marks distribution, question-type distribution, high-probability topics, expected questions, and study priorities.
- Text extraction supports `.txt`, `.md`, and selectable-text `.pdf` files out of the box.
- Image OCR supports `.jpg`, `.jpeg`, `.png`, and `.webp` through `pytesseract`, but the Tesseract executable must be installed separately and available on `PATH`.
- Ready documents are split into overlapping chunks automatically after extraction.
- Retrieval currently uses simple keyword scoring with subject/chapter boosts. This is the RAG interface for later vector embeddings.
- Phase 13 adds `app/services/context_builder.py`, `app/services/study_intelligence.py`, and `app/services/ai_quality.py`. The context builder combines document metadata, retrieved chunks, latest study map, previous-paper signals, selected language/mode, and strict tutor rules before calling the AI provider.
- AI outputs now use richer exam-focused shapes for study maps, smart notes, simple explanation, exam mode, quizzes, flashcards, previous-paper analysis, and video scene plans. Mock mode mirrors those shapes so frontend development stays free.
- Rule-based AI quality scoring is attached where useful as `quality_score`, with clarity, exam focus, conciseness, usefulness, completeness, and missing-section checks.
- Video audio generation saves browser-accessible files under `public/generated/audio/{video_id}` by default and updates scene timing using detected audio duration plus a small padding.
- Generated audio and final MP4 files are uploaded through `app/services/storage_provider.py` when `STORAGE_PROVIDER` is `r2` or `s3`; local files remain as fallback.
- Final video rendering saves MP4 files under `generated-videos/` and writes `{video_id}.metadata.json` with duration, scene count, provider, storage object details, and warnings.
- Video render jobs are stored in the database so the frontend can poll progress and show render history at `/videos`.
- Local generated videos can consume disk. Periodically review `generated-videos/`, `generated-video-jobs/`, and `public/generated/audio/`; `app/services/video_cleanup.py` has dry-run helpers for old files.
- The default product voices are Nila, Nila Malayalam, and Nila Mix. They are voice directions and provider presets, not cloned or copyrighted character voices.

## OCR Setup On Windows

Install Tesseract OCR, then restart the terminal so `tesseract.exe` is on `PATH`.

```powershell
tesseract --version
```

If that command works, image uploads can extract text. If it does not, image uploads are saved but marked `failed` with a clear extraction error.