CoolFace
Apppublic

RishiSaraf/pupil-backend

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
App README

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):

NamePurpose
OPENAI_API_KEYVision extraction + synthesis (gpt-4.1-nano/mini).
SUPABASE_URLSupabase project URL.
SUPABASE_SERVICE_KEYSupabase service-role key (server-side writes).
ALLOWED_ORIGINSComma-separated frontend origins for CORS, e.g. https://your-app.vercel.app.

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

bash
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 7860 --reload