tailor
Refine distributed robot datasets into training-ready data.
English | 简体中文
tailor is a data-refinement pipeline for robot learning datasets (LeRobot v2.0 / v2.1 / v3.0):
- Accept — verify incoming distributed datasets: format-version compliance, metadata-vs-data consistency ("does the package match the label"), transfer integrity, dims and stats vs your training config
- Cut — episode-level filtering (frame-level issues escalate to episode removal to preserve action-chunk coherence)
- Sew — deterministic cleaning only: timestamp rebuild, stats recompute. No imputation, no video re-encode (planned — see roadmap)
- Re-check — cleaned output must pass the acceptance suite again before release
- Register — provenance ledger: source version, dropped episodes + why, recipe hash
Declarative recipes (YAML) in git; every dataset is reproducible and auditable.
tailor-made datasets for your training.
Quickstart
pip install lerobot-tailor
# or track main: pip install git+https://github.com/XBR-1111/tailor
tailor check /path/to/lerobot/dataset
tailor check dataset.zip # zip archive (wrapper dirs peeled)
tailor check lerobot/unitreeh1_fold_clothes # HF repo id: meta pre-check, no full download
tailor check lerobot/... --full # download everything, all 22 checks
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ check ┃ status ┃ detail ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━┩
│ info_json │ PASS │ │
│ codebase_version │ PASS │ v3.0 │
│ fps │ PASS │ 50 │
│ episodes_metadata │ PASS │ 38 episodes │
│ episode_files │ PASS │ │
│ total_frames │ PASS │ 19000 │
│ episode_lengths │ PASS │ │
│ task_indices │ PASS │ 1 task │
│ state_action_dims │ PASS │ │
│ nan_inf │ PASS │ │
│ timestamps │ PASS │ │
└───────────────────┴────────┴──────────────┘
ACCEPTED · 11 passed, 0 warn, 0 failed · 11 skipped (meta-only run)
Remote mode pulls only meta/* by default (seconds, a few MB) and runs every
metadata-level check — file-presence checks SKIP honestly instead of faking a
FAIL. Downloads cache under ~/.cache/tailor-hf/.
Exit codes make it a CI gate: 0 accepted · 1 rejected · 2 unreadable
(--fail-on-warn makes WARN exit 1 too). --json emits machine-readable output,
--markdown report.md writes a PR/dataset-card friendly report, --max-episodes N
samples N episodes of huge datasets — a convenience sample, so pair it with
--sample last: tail-end episodes are a common corruption site that a
first-N sample never sees.
Filter — the Cut stage
tailor filter rewrites a dataset without the episodes you don't want, using
lerobot's official delete_episodes engine (so the output is format-compatible
by construction — parquet, videos and stats are rebuilt consistently). The
result is re-checked automatically; its verdict is printed.
# drop explicit episodes (say, the ones a report flagged)
tailor filter /data/ds --drop-episodes 5,17,23 -o /data/ds_clean
# drop everything FAIL-level checks can pin to an episode (--drop-warn adds WARN)
tailor filter /data/ds --from-check --drop-warn
# rule-based
tailor filter /data/ds --min-length 100 # too short for your policy
tailor filter /data/ds --dedupe # byte-identical trajectories, keep first
Requires pip install 'lerobot-tailor[lerobot]' (the acceptance suite itself stays
dependency-free). Currently v3.0 datasets. Every drop is printed with its
reason — no silent removals.
Clean — deterministic repairs
tailor clean fixes what deterministic math can fix: meta/stats.json
recomputed from the parquet data (video entries preserved), timestamps
rebuilt as frame_index / fps. Dry-run by default; --apply writes, backing
up every replaced file to <dataset>_pre_clean/ outside the dataset root, then
re-checks the dataset and prints the verdict. No lerobot dependency.
tailor clean /data/ds # dry-run: what would change, nothing written
tailor clean /data/ds --apply # repair + backup + automatic re-check
Register — the provenance ledger
Every filter and clean --apply appends one event to
meta/tailor_ledger.jsonl inside the dataset, so the processing history
travels with the data: filter writes a birth record into the output (source
fingerprint, dropped episodes and why), clean appends what it rewrote.
tailor provenance /data/ds_filtered
# 2026-10-31T02:37:45+00:00 filter from raw (b6b8be2337b9aae4, 38 eps) -> dropped 1: ep37 (explicit --drop-episodes)
# 2026-10-31T02:37:47+00:00 clean rewrote ['meta/stats.json']
check never writes — acceptance is read-only. Fingerprints are structural
(episode metadata + data file sizes), for identity not cryptographic proof.
Use as a CI gate
tailor check is designed to sit in someone else's CI: exit codes are the
contract (0 accepted / 1 rejected / 2 unreadable), --json for
machines, --markdown for humans. A ready-made reusable workflow ships in
this repo — one line gates a dataset in your pipeline:
# your-repo/.github/workflows/dataset-quality.yml
jobs:
check-dataset:
uses: XBR-1111/tailor/.github/workflows/tailor-check.yml@v0.9.0
with:
dataset: lerobot/unitreeh1_fold_clothes # HF repo id, local path, or .zip
fail-on-warn: false
A rejected dataset fails the job; the markdown report lands in the run's
artifacts. Pin to a tag; check is read-only and never writes to the dataset.
Configurable acceptance profiles
What counts as acceptable differs per robot, task and training config. A profile (YAML in git — your dataset SLA, versioned and auditable) overrides per-check thresholds and severities; tailor is the engine that executes it:
# my_requirements.yaml
checks:
codebase_version:
allowed: [v2.1] # this org only accepts v2.1
fps:
allowed: [30, 50] # hardware reality
episode_lengths:
min: 100 # policy window needs headroom
severity: fail # short episodes are a hard reject here
timestamps:
spacing_tolerance: 0.2
tailor check /data/dataset --profile my_requirements.yaml
Any check can be remapped between WARN and FAIL, and any check can be
switched off entirely with enabled: false (it reports SKIP naming the
profile, is not executed, and contributes nothing to --from-check).
Unknown check names or keys in a profile fail fast with a clear error — a
typo in your requirements must not silently keep a default you meant to
change.
The 23 acceptance checks
| check | catches |
|---|---|
| info_json | missing / invalid meta/info.json |
| codebase_version | format version outside v2.0/v2.1/v3.0 |
| fps | non-positive or unusual fps |
| episodes_metadata | missing/invalid episode metadata |
| episode_files | data shard/parquet missing on disk (v2 files, v3 shard pointers) |
| total_frames | declared total ≠ sum of episode lengths |
| episode_lengths | zero/negative lengths; very short episodes that break chunked sampling |
| task_indices | task references out of range; declared task count mismatch |
| state_action_dims | state/action dims ≠ info.json features |
| nan_inf | non-finite values in state/action |
| timestamps | non-monotonic timestamps; frame spacing deviating >10% from 1/fps |
| stats_recompute | declared stats.json mean/std drifting from recomputed values |
| action_quality | frozen (stuck) action runs; one action dim pegged at its declared range edge; sudden jumps beyond k·sigma |
| video_files | declared video streams missing on disk (v2 per-episode files, v3 shard pointers) |
| video_streams | undecodable video; frame count vs episode lengths; resolution vs declared shape |
| video_sync | cross-camera from/to timestamp drift in one episode (v3.0 metadata; stereo rigs without hardware sync) |
| zero_variance | dims that never move (std < eps): normalization would divide by zero |
| schema_consistency | column sets/dtypes drifting across episodes |
| frame_continuity | dropped frames (row count vs declared length, frame_index holes) |
| training_readiness | episodes shorter than your policy's chunk_size/horizon (configure to enable) |
| portability | symlinks (break on transfer), stray files in data/, unexpected top-level entries |
| duplicate_episodes | byte-identical state/action trajectories across episodes |
| splits | overlapping episode ranges across declared splits (train/eval leakage) |
Validated on real distributed datasets across all three supported formats —
v3.0: lerobot/unitreeh1_fold_clothes (921 MB); v2.1 AND v2.0 of
lerobot/pusht (206 eps), lerobot/xarm_lift_medium_image (800 eps) and
lerobot/utaustin_mutex (1500 eps / 362k frames / 1.4 GB — the full check
runs end-to-end in ~3 s), all checked via the @ref syntax since main
always points at the latest format. Real findings on the way: frozen action
runs in 6 utaustin episodes (worst: 12 identical actions in ep 180), and
range-edge pegging in 3 pusht v2.0 episodes. The v2.x round surfaced and
fixed a layout bug where every v2.x dataset misreported all video files
missing (the synthetic fixture carried the same wrong layout — real data
caught what fixtures could not).
Also validated on a real 921 MB distributed dataset (lerobot/unitreeh1_fold_clothes,
38 episodes / 19k frames / stereo video) plus deterministic synthetic fixtures
with injected corruptions — every corruption is caught by a test, on both
format versions. The real dataset itself shipped two findings tailor caught
on first contact: an action dim pegged at its range edge for 29 frames
(gripper holding a folded shirt — WARN), and a genuine metadata defect where
cam_left's episode-to-shard pointers all target file-000.mp4 while 500 of
its frames actually live in an orphan file-001.mp4 — verified with the
official reader: LeRobotDataset loads fine but every episode-37 frame
raises IndexError (video index capped at 18500), so training crashes the
moment a sampler touches the last episode.
Performance
The suite converts each episode column to a numpy array exactly once and
shares it across checks (DatasetSnapshot.col_array), probes videos through
container metadata (nb_frames) in a thread pool, and vectorizes the
run-length scans. On a synthetic 150k-frame dataset (300 episodes x 500
frames): checks stage 0.79s -> 0.22s vs v0.5, ~11x on the pure check
computation — the end-to-end ratio is lower because loading PyAV (~0.16s,
one-time per process) dominates what's left. Reproduce with
python scripts/bench.py.
Status
Acceptance suite, filter/clean and the provenance ledger shipped (v0.6). Roadmap, in priority order:
Accept depth (v0.2) — complete.
Pipeline closure (v0.3+) — Cut/Sew/Re-check/Register:
shipped (see above): explicit / rule-based / check-driven drops, dedupe, on officialfilterdelete_episodes, auto re-checkshipped (see above): stats recompute + timestamp rebuild, dry-run firstcleanprovenance ledgershipped (see above): append-only, travels with the dataset- YAML recipes (tracking upstream
recipe.pydirection rather than inventing a schema)
Reference points: lerobot-doctor (diagnostics, now in the official dataset visualizer), Calibra (episode disposition decisions). tailor's lane is the acceptance gate plus the executed pipeline — verdict + exit code as a CI door, then deterministic refinement with provenance, not another report.
Metadata
Release files for lerobot-tailor 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lerobot_tailor-0.9.0.tar.gz | 244.4 kB | With provenance | Details for lerobot_tailor-0.9.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lerobot_tailor-0.9.0-py3-none-any.whl | Python 3 | none | any | Details for lerobot_tailor-0.9.0-py3-none-any.whl |
Total release size: 285.4 kB
Release files / lerobot_tailor-0.9.0.tar.gz
| Download URL | lerobot_tailor-0.9.0.tar.gz |
|---|---|
| Size | 244.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ecea8b77a51195f195228171ab15bd053c5bbdd531c465562bd6bd0624149f49
|
|
BLAKE2b-256 checksum How to use checksums |
417517d630a812ecf4c8e0518e9fc56111f234e8faa3f51e2eb40cd53cf22995
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / lerobot_tailor-0.9.0-py3-none-any.whl
| Download URL | lerobot_tailor-0.9.0-py3-none-any.whl |
|---|---|
| Size | 41.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8ef2be432e478676820d2c302ac326b974acbefb54b0b83952dde2a32508639c
|
|
BLAKE2b-256 checksum How to use checksums |
43ee20b617bf0ec23589191e377d2bda7d1a9f120579d460cdf57e353122bc24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for lerobot-tailor 0.9.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for lerobot_tailor-0.9.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.