Skip to content

oldhero5/waldo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

83 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Waldo

Where's Waldo? Right here, finding objects in your video.

Self-hosted ML platform for video object detection at scale. Auto-label any object in video using text prompts or click-based exemplars (powered by SAM 3), train YOLO26 detectors on the labeled data, monitor training live, and deploy the model to a serving endpoint or edge device.

📘 Full documentation: docs-site/ — quickstart, UI tour with screenshots and short walkthrough videos, API reference, deployment guides. Build it locally with cd docs-site && npm install && npm run start.

Architecture

                    ┌──────────────────────────────────────────┐
                    │              React UI (SPA)              │
                    └──────────────┬───────────────────────────┘
                                   │
                    ┌──────────────▼───────────────────────────┐
                    │          FastAPI  (waldo-app)            │
                    │   REST API  ·  WebSocket  ·  Static UI   │
                    └──┬───────────────┬───────────────┬───────┘
                       │               │               │
              ┌────────▼──┐    ┌───────▼───┐    ┌──────▼──────┐
              │  Celery   │    │  Celery   │    │             │
              │  Labeler  │    │  Trainer  │    │   Infra     │
              │  (SAM 3)  │    │  (YOLO)   │    │             │
              └───────────┘    └───────────┘    │ PostgreSQL  │
                                                │ Redis       │
                                                │ MinIO       │
                                                └─────────────┘

Pipeline: Upload video → Label (text or click) → Review → Train YOLO → Deploy API

Quickstart

Step 1 — Get a Hugging Face token. Waldo uses SAM 3 to do the labeling. Create a read-only token at huggingface.co/settings/tokens and accept the license on the facebook/sam3 model page. You'll paste the token into the installer.

Step 2 — Install. One command. Picks the right Docker profile for your platform (NVIDIA / Apple MPS / CPU), installs missing prereqs, pulls the prebuilt image from Docker Hub, and brings the stack up — no local build, no Node, no Vite.

# macOS / Linux / WSL
curl -fsSL https://raw.githubusercontent.com/oldhero5/waldo/main/install.sh | bash
# Windows (PowerShell)
irm https://raw.githubusercontent.com/oldhero5/waldo/main/install.ps1 | iex
:: Windows (cmd.exe)
curl -fsSL https://raw.githubusercontent.com/oldhero5/waldo/main/install.cmd -o install.cmd && install.cmd && del install.cmd

The installer prompts for the HF token early so you can walk away. To skip the prompt, pass it on the command line or via env:

curl -fsSL https://raw.githubusercontent.com/oldhero5/waldo/main/install.sh \
  | bash -s -- --hf-token hf_xxxxxxxxxxxxx
# or:  HF_TOKEN=hf_xxx curl -fsSL ... | bash

The Windows wrappers install/verify WSL2 + Docker Desktop, then hand off to install.sh inside Ubuntu — that's where Waldo actually runs.

Step 3 — Sign in. Open http://localhost:8000. Default dev credentials are printed at first boot:

  • email: admin@waldo.ai
  • password: waldopass (dev default — override with ADMIN_BOOTSTRAP_PASSWORD for production)

Step 4 — Label your first video. From the UI: Upload → drag in a clip → Auto-label → type a prompt (e.g. car) → Preview. The first preview takes ~2 minutes (it downloads the SAM 3 weights to a Docker volume); subsequent calls are sub-second. Then walk through Review → Train → Deploy.

Already cloned the repo?

git clone https://github.com/oldhero5/waldo.git && cd waldo
./install.sh                                 # full install (will prompt for HF_TOKEN)
./install.sh --skip-up --skip-models --yes   # config only, no model download
./install.sh --build-from-source             # build the image locally (contributors)

Installer flags

Flag Default What it does
--hf-token TOKEN (prompt) Hugging Face read token — required for SAM 3 weights
--dir PATH ~/waldo Where to clone if Waldo isn't already on disk
--branch NAME main Branch to clone
--cpu off Force CPU even if a GPU is detected
--gpu nvidia|apple|none auto Override GPU detection
--build-from-source off Build the image locally instead of pulling (slow; requires Node)
--skip-prereqs off Don't install Docker/uv — assume present
--skip-models off Don't download SAM 3 weights
--skip-up off Don't run docker compose up — config only
--yes off Non-interactive (HF_TOKEN can be set later in .env)

PowerShell uses the same flags PascalCased (-HfToken, -Dir, -SkipUp, -Yes, ...).

Manual setup (for the curious)

The installer is the recommended path, but Waldo is just docker-compose.yml

  • a published image underneath. If you want to drive it by hand:
git clone https://github.com/oldhero5/waldo.git && cd waldo
cp .env.example .env                     # add HF_TOKEN
make up                                  # pulls oldhero5/waldo:latest, auto-routes by OS

make up auto-detects your platform and picks the right compose profile: Darwin → apple, Linux/WSL with nvidia-sminvidia, otherwise → cpu. You can still override explicitly with PROFILE=....

Platform Command Auto-detected profile Image Workers in Docker? GPU
macOS (Apple Silicon) make up apple oldhero5/waldo:latest (app only) ❌ native Apple MPS
Linux + NVIDIA make up nvidia oldhero5/waldo:cuda CUDA
Linux (CPU only) make up cpu oldhero5/waldo:latest none
Windows (WSL 2) + NVIDIA make up nvidia oldhero5/waldo:cuda CUDA
Force a specific profile make up PROFILE=nvidia (or cpu/apple) per profile per profile per profile

The apple profile is also kept as an alias for cpu for one release, so old make up PROFILE=apple invocations on Linux keep working.

To build from source instead of pulling — useful for contributing or running unreleased changes:

make build PROFILE=nvidia    # builds Dockerfile.cuda locally and tags :dev-cuda
make build                   # builds Dockerfile (CPU) locally and tags :dev

macOS (Apple Silicon)

Apple's MPS cannot be passed through to Linux containers. Two options:

make up          # Everything in Docker, CPU workers. Slowest but zero setup.
make up-mac      # Infra + app in Docker, native MPS workers. Recommended.

make up-mac starts the labeler and trainer natively so they can reach the M-series GPU. Logs land in /tmp/waldo-labeler.log and /tmp/waldo-trainer.log. (make up-gpu is kept as a back-compat alias.)

Linux with NVIDIA CUDA

The installer handles the NVIDIA Container Toolkit on apt/dnf systems. If you'd rather drive it manually:

# 1. Driver + toolkit (Ubuntu example — see NVIDIA docs for others)
sudo apt install -y nvidia-driver-550
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# 2. Verify the GPU is visible to Docker
make gpu-check

# 3. Start Waldo with the nvidia profile
make up PROFILE=nvidia

# 4. Confirm the workers see the GPU inside the container
make gpu-logs

The nvidia profile pulls oldhero5/waldo:cuda, which is built from Dockerfile.cuda on top of nvidia/cuda:12.4.0-devel-ubuntu22.04 and installs CUDA-enabled PyTorch from download.pytorch.org/whl/cu124 (PyPI ships CPU-only torch by default — a common silent-failure trap). The Apple path uses MLX, which is macOS-only. Training uses CUDA bf16 for speed.

Each nvidia worker runs scripts/entrypoint-worker.sh on boot, which prints nvidia-smi + torch.cuda.is_available() so make gpu-logs immediately shows whether passthrough is working. Compose also sets shm_size: 4gb so PyTorch dataloader IPC doesn't OOM on the 64 MB Docker default.

Windows with NVIDIA CUDA (via WSL 2)

Windows GPU support goes through WSL 2 + Docker Desktop. The NVIDIA driver lives on the Windows host; CUDA inside WSL is provided by the driver automatically — do not install a Linux CUDA driver inside WSL.

The PowerShell installer (install.ps1) handles WSL2 + Docker Desktop and then hands off to install.sh inside Ubuntu. If you'd prefer to drive it manually, the chain is the same: install the Windows NVIDIA driver, install WSL2 + Ubuntu, install Docker Desktop with WSL integration, then run the Linux installer (or make up PROFILE=nvidia) inside Ubuntu.

Windows gotchas:

  • Keep the repo in the WSL filesystem (~/waldo, not /mnt/c/...). Cross-filesystem I/O is 10–20× slower, and ffmpeg frame extraction becomes the bottleneck.
  • Line endings: configure git to keep LF (git config --global core.autocrlf input) so shell scripts don't break.
  • File watcher limits: if make dev-ui misses changes, raise the inotify limit: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
  • Memory: Docker Desktop defaults to half of host RAM. Training YOLO on 720p video wants ≥ 16 GB allocated — adjust in %UserProfile%\.wslconfig:
    [wsl2]
    memory=24GB
    processors=8
    
    then wsl --shutdown and restart.

Waldo has not been validated against native Windows (no WSL). Don't try it — Ultralytics, Celery's solo pool, and ffmpeg all behave differently there.

Linux without a GPU (CPU only)

make up          # Auto-selects the cpu profile (CPU-only image)

Works for small datasets and smoke tests. Don't expect to train on real video.

Verify it's running

http://localhost:8000    # Waldo UI
http://localhost:9001    # MinIO console  (minioadmin / minioadmin)

The app runs database migrations automatically on startup.

Common commands

make up              # Pull image, start everything
make pull            # Just pull (refresh after a release)
make build           # Build the image locally (contributors only)
make logs            # Tail all containers
make down            # Stop everything
make dev-ui          # Vite dev server with hot reload (proxies API)
make build-ui        # Production build → app/static/ (only useful for build-from-source)
make migrate         # Run Alembic migrations
make test            # Python test suite
make download-models # Download SAM 3 weights

Web UI

Page Path Description
Upload /upload Drag-and-drop video upload
Label /label/:videoId Text search + click mode, 5 task types
Review /review/:jobId Annotation reviewer with hotkeys
Train /train/:jobId Variant picker, hyperparameters, live metrics
Deploy /deploy Endpoints, test console, model registry, monitoring
Agent /agent Local LangGraph agent (Ollama + gemma4:e4b) — ask in plain English, it calls real Waldo tools to inspect data and run jobs on your behalf

AI Agent

/agent ships a LangGraph ReAct agent backed by a local Ollama (no third-party API). It can list your projects/datasets/models, recommend training settings, and — with the action toggle on — start labeling jobs, launch training runs, and activate models.

The compose stack runs Ollama as a sidecar service and an ollama-init one-shot pulls gemma4:e4b (~9.6 GB) on first boot. After that, chats stream in real time. See the Agent docs for the tool list, SSE event shapes, and troubleshooting.

API

All endpoints live under /api/v1 and are documented at /docs (OpenAPI). Highlights:

Method Path Description
POST /upload Upload video
POST /label Start text-prompt labeling
POST /label/exemplar Start click-based labeling
POST /label/preview Async prompt preview (202 + poll /job/{id})
GET /job/{job_id} Generic Celery task result polling
GET /status/{job_id} Per-frame labeling progress
POST /train Start training run
GET /train/{run_id} Status + metrics + loss history
WS /ws/training/{run_id} Live metrics stream
POST /predict/image?model_id=ID Image inference
POST /predict/video?model_id=ID Video inference with tracking
GET /models List trained models

Example pipeline

Long-running endpoints (label, label/preview, training, comparisons, exports) return 202 Accepted with a {job_id, status, result_url} envelope. Poll the result_url until the job is complete instead of blocking the connection. See API overview for full details.

# 1. Upload video
curl -X POST http://localhost:8000/api/v1/upload -F "file=@clip.mp4"

# 2. Try a prompt on a few frames before committing to the full job.
#    Returns 202 immediately with a job_id; poll /api/v1/job/{job_id}
#    until status == "completed" and read result.frames[].
curl -X POST http://localhost:8000/api/v1/label/preview \
  -H "Content-Type: application/json" \
  -d '{"video_id": "VIDEO_ID", "prompts": ["person"]}'
curl http://localhost:8000/api/v1/job/PREVIEW_JOB_ID

# 3. Kick off the full labeling job
curl -X POST http://localhost:8000/api/v1/label \
  -H "Content-Type: application/json" \
  -d '{"video_id": "VIDEO_ID", "text_prompt": "person", "task_type": "segment"}'

# 4. Poll for per-frame labeling progress (LabelingJob row)
curl http://localhost:8000/api/v1/status/JOB_ID

# 5. Train a YOLO model on the dataset
curl -X POST http://localhost:8000/api/v1/train \
  -H "Content-Type: application/json" \
  -d '{"job_id": "JOB_ID", "model_variant": "yolo26n-seg", "hyperparameters": {"epochs": 50}}'

# 6. Watch training (or open the Train page in the UI)
curl http://localhost:8000/api/v1/train/RUN_ID

# 7. Run inference against the trained model
curl -X POST "http://localhost:8000/api/v1/predict/image?model_id=MODEL_ID" \
  -H "Authorization: Bearer wld_YOUR_KEY" \
  -F "file=@test.jpg"

YOLO task types

SAM 3 always outputs segmentation masks. Waldo's converters reshape them into whatever format the selected YOLO task needs:

Task Output YOLO variants
Segmentation Polygon vertices yolo26n-segyolo26x-seg
Detection Bounding boxes yolo26nyolo26x
Classification Cropped images in class dirs yolo26n-clsyolo26x-cls
OBB 4 rotated corner points yolo26n-obbyolo26m-obb
Pose Bbox + centroid keypoint yolo26n-poseyolo26m-pose

Configuration

Everything comes from environment variables. See .env.example.

Variable Default Description
DEVICE mps mps (Apple), cuda (NVIDIA), or cpu
DTYPE float32 float32, bfloat16, float16
SAM3_MODEL_ID facebook/sam3 HuggingFace transformers model (Linux/CUDA — needs model.safetensors)
SAM3_MLX_MODEL_ID mlx-community/sam3.1-bf16 MLX variant (macOS Apple Silicon)
HF_TOKEN HuggingFace read token (required) — accept the license on the facebook/sam3 model page first
ADMIN_BOOTSTRAP_EMAIL admin@waldo.ai First admin's email
ADMIN_BOOTSTRAP_PASSWORD waldopass (dev) First admin's password — override in production
SLACK_WEBHOOK_URL Training alerts
NTFY_TOPIC Push notifications

Testing

# Unit tests (no infra)
uv run pytest tests/test_converters.py tests/test_frame_extractor.py -v

# Trainer tests (Redis + MinIO)
uv run pytest tests/test_trainer.py -v

# API tests (full stack running)
uv run pytest tests/test_api.py tests/test_api_extended.py -v

# Everything
make test && cd ui && npx playwright test

Project structure

waldo/
├── app/                    # FastAPI application
│   ├── main.py             # Entrypoint, routers, SPA fallback
│   ├── api/                # Route handlers
│   └── static/             # Built React UI
├── labeler/                # SAM 3 labeling pipeline
│   ├── sam3_engine.py      # PyTorch path (Linux/CUDA)
│   ├── video_labeler.py    # MLX path (macOS)
│   ├── text_labeler.py     # Text-prompt flow
│   ├── frame_extractor.py  # ffmpeg extraction + phash dedup
│   └── converters/         # Mask → YOLO format converters
├── trainer/                # YOLO training pipeline
│   ├── train_manager.py    # Ultralytics orchestrator
│   ├── dataset_builder.py  # Dataset prep from DB
│   ├── metrics_streamer.py # Redis pub/sub for live metrics
│   └── exporter.py         # ONNX, TFLite, CoreML export
├── lib/                    # Shared library
│   ├── config.py           # Pydantic settings
│   ├── db.py               # SQLAlchemy models
│   ├── storage.py          # MinIO client
│   └── tasks.py            # Celery tasks
├── ui/                     # React 19 + Vite + TypeScript + Tailwind
├── alembic/                # Database migrations
├── scripts/                # Setup + maintenance scripts
├── tests/                  # Python test suite
├── Dockerfile              # Unified image (app | labeler | trainer via $WALDO_ROLE)
├── Dockerfile.cuda         # Same image on a CUDA base + GPU torch wheels
├── docker-compose.yml      # All services; apple/cpu/nvidia profiles, pulls from Docker Hub
└── docker-compose.build.yml # Override that builds from source instead of pulling

Documentation

The full docs live in docs-site/ (Docusaurus). Highlights:

  • Quickstart — upload a clip, auto-label, train, deploy in ~15 minutes
  • UI Tour — every page screenshotted, with short walkthrough videos
  • Architecture — services, data model, security
  • API Reference — every REST endpoint grouped by resource
  • Workflow Blocks — composable blocks for the visual editor
  • Deployment — Docker, Linux, Windows, and edge devices

Run the site locally:

cd docs-site
npm install
npm run start          # http://localhost:3000

Or build static HTML with npm run build. Screenshots and videos can be regenerated against your local Waldo with npm run screenshots and npx playwright test scripts/recordings.spec.ts.

License

See LICENSE.

About

Self-hosted ML platform for video object detection. SAM 3 auto-labeling + YOLO26 training + serving + edge deploy. Docs: https://oldhero5.github.io/waldo/

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors