title: DocDoe Backend
emoji: π
colorFrom: indigo
colorTo: gray
sdk: docker
app_port: 7860
pinned: false
short_description: DocDoe FastAPI backend β tuition brain + study API
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
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
Optional:
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:
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:
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:
Authorization: Bearer <access_token>
Supabase mode is provider-ready for later hosted auth:
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:
AI_PROVIDER="mock"
Gemini mode uses the official google-genai SDK:
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:
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:
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:
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:
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:
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.
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:
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:
npm run render:sample-anime-video
Generate and render the golden 60-second Photosynthesis anime explainer with real Edge TTS audio:
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:
queued 0% -> running 10% -> preparing inputs 20% -> rendering 40% -> metadata 90% -> ready 100%
Run
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
Open:
http://127.0.0.1:8000/healthhttp://127.0.0.1:8000/docs
Key Endpoints
GET /healthPOST /auth/signupPOST /auth/loginGET /auth/sessionGET /users/mePATCH /users/mePOST /documents/uploadGET /documentsGET /documents/{document_id}GET /documents/{document_id}/previewGET /documents/{document_id}/chunksPOST /documents/{document_id}/retrievePOST /documents/{document_id}/study-mapPOST /generations/notesPOST /generations/simple-explanationPOST /generations/exam-modePOST /quizzes/generatePOST /flashcards/generatePOST /previous-papers/uploadPOST /previous-papers/analyzePOST /previous-papers/{paper_id}/extract-questionsGET /previous-papers/{paper_id}/questionsPOST /previous-papers/analyze-structuredPOST /video/scene-planPOST /video/generate-audioPOST /video/render-finalPOST /video/render-jobsGET /video/render-jobsGET /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.
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:
$env:DOCDOE_DEMO_PASSWORD = "use-a-generated-secret-with-16-plus-characters"
python scripts/seed_beta_demo.py
Remove demo data:
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
MockAIProviderandGeminiAIProvider. - 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_explaineroranime_visual_explainer, andvideo_format:16:9or9: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-audioaccepts either the existing singlelanguageflow or alanguagesarray and returnsaudio_variantsfor separate language-specific MP4 rendering later.- Previous papers can be uploaded, converted into structured
PreviousQuestionrows, 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.pdffiles out of the box. - Image OCR supports
.jpg,.jpeg,.png, and.webpthroughpytesseract, but the Tesseract executable must be installed separately and available onPATH. - 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, andapp/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.pywhenSTORAGE_PROVIDERisr2ors3; local files remain as fallback. - Final video rendering saves MP4 files under
generated-videos/and writes{video_id}.metadata.jsonwith 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/, andpublic/generated/audio/;app/services/video_cleanup.pyhas 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.
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.