| name | label-studio-on-spaces |
| description | Deploy and use Label Studio on Hugging Face Spaces. Use when the user wants to stand up an LS instance on Spaces, push tasks from a Hub dataset, add ML predictions as prelabels, export reviewed annotations back to a Hub dataset, or needs a human-in-the-loop labelling workflow for ML training or evaluation data (image bounding boxes, text NER, classification, etc.). |
Label Studio on Hugging Face Spaces
Label Studio is an open-source data labelling platform. Hosted on Hugging Face Spaces with an attached Storage Bucket, it gives you free annotation infrastructure that survives Space restarts and integrates cleanly with the rest of the Hub (Datasets for source data and exports, Models for prelabelling, Jobs for batch inference).
This skill helps an agent work against a running LS-on-Spaces instance and stand a new one up when needed.
Decision tree
Pick the path that matches what the user wants:
For API schemas (object detection + text NER), pagination patterns, and idempotency rules: references/ls_api.md. For label config XML beyond the worked examples, see the Label Studio template gallery.
The three runtime operations
Deployment is covered in references/deployment.md — three hf CLI commands. The sections below cover talking to a running LS instance.
1. Authenticate
LS supports two token models: Personal Access Tokens (PAT) — a long-lived JWT refresh token paired with a ~5-minute access token (recommended) — and legacy tokens (single never-expiring token, less secure). This skill uses PATs. Generate one from LS via Account & Settings → Personal Access Tokens, then store it for the scripts:
Recommended: .env file (auto-loaded via python-dotenv in scripts/ls_helpers.py). Copy .env.example to .env in your working directory and fill in:
LS_URL=https://<namespace>-<space>.hf.space
LS_REFRESH_TOKEN=<your-PAT>
The scripts will pick these up automatically. (.env is in .gitignore so you won't accidentally commit credentials.)
Alternative: pass inline (LS_URL=... LS_REFRESH_TOKEN=... uv run ...) or export from your shell.
Once set, the client exchanges the refresh token for a fresh access token at the start of every operation:
import requests
def access_token(ls_url: str, refresh: str) -> str:
r = requests.post(f"{ls_url}/api/token/refresh/", json={"refresh": refresh}, timeout=30)
r.raise_for_status()
return r.json()["access"]
Critical: the access token expires after 5 minutes. For any operation that might run longer (long inference loops, large task imports), implement 401-auto-refresh:
if r.status_code == 401:
H["Authorization"] = f"Bearer {access_token(ls_url, refresh)}"
r = requests.post(url, headers=H, json=body, timeout=30)
Full reusable helper: scripts/ls_helpers.py.
2. Push tasks, predictions, pull annotations
All three follow the same shape: load source data → for each row, POST to LS API.
- Tasks:
POST /api/projects/{id}/import with [{"data": {"image": <url>, "id": <stable_id>}}, ...]. Use IIIF/HTTPS URLs for images, not file paths.
- Predictions:
POST /api/predictions/ with {"task": <id>, "model_version": <label>, "score": <float>, "result": [...]}. Box coords are percentages of original_width/original_height, not pixels.
- Annotations (pull):
GET /api/tasks?project=<id>&page=<n>&fields=all. Pagination ends with a 404, not an empty list — handle in the loop.
Schemas, idempotency rules, and per-modality examples in references/ls_api.md. Working scripts in scripts/.
3. Persist across restarts
Handled at deploy time — bucket mounted at /data plus LABEL_STUDIO_BASE_DATA_DIR=/data, STORAGE_PERSISTENCE=1, and a fixed SECRET_KEY. All set via hf repos duplicate flags. See references/deployment.md.
Sharp edges (the load-bearing few)
These cost the most time in practice:
- 5-minute JWT access token — refresh on 401, or split long jobs.
- LS box coords are percentages — convert with
original_width/original_height.
- Task pagination returns 404 past the last page — break on 404, don't raise.
USER root required in Dockerfile for bucket writes (upstream image runs as UID 1001). The official LS Space already has the patch; custom forks need it.
- Mixed-content blocks images — LS-on-Spaces is HTTPS; image URLs must be HTTPS too.
cpu-basic ignores --sleep-time — 48h fixed timeout. Use a paid tier if you need auto-pause.
Hub primitives exercised
This skill threads four Hub primitives in one workflow:
- Spaces (hosting LS)
- Storage Buckets (persisting LS state)
- Datasets (source data + exports)
- Jobs (batch prelabel inference)
Combined, the LS-on-Spaces pattern means: free annotation infrastructure on the same platform as your data and your models, with no SaaS in the loop.