Add a framework-free HTML/CSS/JS labeling UI: set/session/status
filters, a frame browser, drag-to-draw/move/resize bounding boxes with
a per-set class picker, per-frame status control, and Left/Right frame
navigation. No login - a locally cached username is sent for
attribution only, matching the backend's get-or-create user model.
Backend additions the frontend needed: serve frame images from a
configurable FRAMES_ROOT via a /images static mount, permissive CORS
(internal tool, not publicly exposed), and a GET /sets/{id}/frames
endpoint returning frames joined with their per-set label status
(defaulting missing rows to unlabeled) for the frame browser.
Verified the full call chain end-to-end against a running backend +
static frontend server (set/class creation, frame ingest, image
serving, label CRUD, status updates, CORS preflight) - every field
name the JS reads matches the API responses. 18/18 backend tests
pass. Not yet verified: actual interactive browser use (no browser
tooling available here) - try drag-to-draw/resize locally.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Labeling Tool — Backend
API + data model for the bounding-box labeling tool. See CLAUDE.md §3.2 for the full requirements this is built against.
Status: core data model + CRUD API implemented (sets, hierarchical classes,
frames, labels, per-frame/per-set status). Not yet implemented: dataset
versioning/promotion (enemy-v1, enemy-v2, ...) — deferred since it needs its
own design pass (snapshot semantics: labels are editable at any time, but a
promoted dataset version must stay reproducible). Also not yet implemented: any
frontend, auth/login (see below), or the active-learning auto-label workflow.
Stack
- FastAPI + SQLAlchemy (2.0), SQLite by default (
./labeling.db), swappable via theDATABASE_URLenv var (e.g. to Postgres later without code changes — one Postgres-compatible ORM). - Multi-user, no auth yet: labels/status changes take a plain
created_by/updated_byusername string, resolved via get-or-create (users.py). There's no login flow — attribution only, since there's no UI yet that would need real auth.
Data model
LabelSet— a label set (Enemy,Items,Traps, ...), one per detector model.MainClass/SubClass— the per-set Main → Sub class hierarchy (e.g.Enemy→Bat,Snake), created ad hoc via the API, no migration needed to add classes.Frame— one labelable image, identified by(session_name, frame_index)— matches the recording tool's frame-extraction output 1:1.Label— one bounding box (x, y, width, heightin pixel space), scoped to a frame + set + sub-class.FrameSetStatus— per-frame, per-set label state (unlabeled/auto_labeled/reviewed).
Setup
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Run
spelunkai-labeling-backend
# or: uvicorn spelunkai_labeling_backend.main:create_app --factory --reload
Interactive API docs at http://127.0.0.1:8000/docs once running.
Testing
pytest
Each test gets a fully isolated app + SQLite file via create_app(database_url=...)
(see tests/conftest.py) — no shared state between tests, no real server needed.