Skip to main content

tailor

Refine distributed robot datasets into training-ready data.

CI Python 3.12+ Ruff mypy License

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:

  • filter shipped (see above): explicit / rule-based / check-driven drops, dedupe, on official delete_episodes, auto re-check
  • clean shipped (see above): stats recompute + timestamp rebuild, dry-run first
  • provenance ledger shipped (see above): append-only, travels with the dataset
  • YAML recipes (tracking upstream recipe.py direction 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)

Source distribution for lerobot-tailor 0.9.0
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)

Table of built distributions (wheels) for lerobot-tailor 0.9.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page