RishiSaraf/pupil-backend
Pupil Backend
FastAPI service for Pupil ("Readwise for Video"). Ingests YouTube URLs and locally-uploaded videos (via browser-extracted frames — the full video never reaches the server), runs the vision pipeline, and returns structured notes.
Deploying as a Hugging Face Docker Space
This folder is the Space repository root. The Dockerfile builds a long-running uvicorn container that serves the API and runs the background job queue (lifespan hook in main.py). Default port 7860 matches app_port above.
Why HF Spaces: the pipeline loads torch + DINOv2 (~1–2 GB resident). The free cpu-basic flavor (2 vCPU / 16 GB RAM) comfortably holds it; 512 MB hosts (Render/Railway free) cannot.
Required secrets (Space → Settings → Variables and secrets)
Set as Secrets (injected as runtime env vars; read via os.getenv):
Optional / tunable (see config.py for defaults): GEMINI_API_KEY, OPENROUTER_API_KEY, OPENAI_MODEL, FREE_TIER_VIDEO_LIMIT, HEAVY_JOB_CONCURRENCY, LIGHT_JOB_CONCURRENCY, VISION_MAX_CONCURRENCY.
See .env.example for the full list. After the Space boots, its URL (https://<user>-<space>.hf.space) is what the frontend's NEXT_PUBLIC_API_URL must point to.
Notes on free-tier behavior
- The Space sleeps after 48h of inactivity; the next request cold-starts (~1–2 min to re-import torch / reload DINOv2 on the first YouTube job).
- Disk is ephemeral (HF persistent storage is no longer offered). Uploaded frames in
storage/client_frames/are transient by design; a restart mid-job fails that job cleanly with a "please re-upload" message.
Local dev
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 7860 --reload