| --- |
| 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. |
|
|