mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-18 18:20:08 +00:00
965d42825f
**#1 Plan-update phase reports correct skip count.** ``_run_plan_update_phase`` only ran ``run_plan_updates`` for episodes with at least one interjection but hardcoded ``episodes_skipped=0``. The summary undercounted skipped episodes. Now returns ``len(records) - processed`` so processed + skipped == total. **#2 ``run_hf_job.py`` installs ``openai``.** The ``CMD`` block does ``pip install --no-deps lerobot[branch]`` then explicitly lists transitive deps. ``openai`` was missing — and since ``VlmConfig.backend`` defaults to ``"openai"``, the job would have ``ImportError``'d when ``vlm_client._make_openai_client`` ran. **#3 Dedupe subtask-span reconstruction.** Module 1's ``_reconstruct_subtasks_from_rows`` (no ``and spans`` guard) and Module 2's ``_read_subtask_spans`` (with the guard) had near- identical logic. Promoted to ``reconstruct_subtask_spans`` in ``reader.py`` using the safer guarded form. Both modules now import the single helper. **#5 Atomic staging.py JSONL writes.** Mirroring the parquet-writer fix from an earlier review round: ``EpisodeStaging.write`` now writes to a sibling ``.tmp`` and ``Path.replace`` atomically. A crash mid-write can no longer leave a half-written JSONL that ``read()`` would then fail to parse. **#6 Atomic ``info.json`` write.** Same pattern in ``executor._ensure_annotation_metadata_in_info`` — ``info.json`` is load-bearing for dataset metadata, so partial writes brick the dataset. **#7 Writer's role-key guard.** ``_normalize_persistent_row`` and ``_normalize_event_row`` accessed ``row["role"]`` directly while every other field used ``.get()``. Pre-validate ``"role" in row`` and raise a friendly ``ValueError`` naming the row, so a future module that accidentally drops ``role`` fails with a triagable message instead of a bare KeyError deep in the writer. **#8 Last subtask span's ``end`` extends to episode end.** ``reconstruct_subtask_spans`` (the new shared helper) takes an optional ``episode_end_t``. When provided, the final span's ``end`` is closed to that timestamp instead of equalling its own ``start`` (zero duration). Both Module 1's plan-update pass and Module 2's interjection anchoring pass ``record.frame_timestamps[-1]``, so downstream "current subtask at refresh_t" lookups no longer miss refreshes that land inside the final span. Sweep: 66 passed, 0 failed. Pre-commit clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
#!/usr/bin/env python
|
|
"""Launch ``lerobot-annotate`` on a Hugging Face job (vllm + Qwen3.6 MoE).
|
|
|
|
Spawns one ``h200x2`` job that:
|
|
|
|
1. installs this branch of ``lerobot`` plus the annotation extras,
|
|
2. boots two vllm servers (one per GPU) with Qwen3.6-35B-A3B-FP8,
|
|
3. runs Module 1/2/3 across the dataset (per-camera VQA via PR 3471),
|
|
4. uploads the annotated dataset to ``--push_to_hub``.
|
|
|
|
Usage:
|
|
|
|
HF_TOKEN=hf_... uv run python examples/annotation/run_hf_job.py
|
|
|
|
Adjust ``CMD`` below to point at your own dataset / target hub repo.
|
|
"""
|
|
|
|
import os
|
|
|
|
from huggingface_hub import get_token, run_job
|
|
|
|
token = os.environ.get("HF_TOKEN") or get_token()
|
|
if not token:
|
|
raise RuntimeError("No HF token. Run `huggingface-cli login` or `export HF_TOKEN=hf_...`")
|
|
|
|
CMD = (
|
|
"apt-get update -qq && apt-get install -y -qq git ffmpeg && "
|
|
"pip install --no-deps "
|
|
"'lerobot @ git+https://github.com/huggingface/lerobot.git@feat/language-annotation-pipeline' && "
|
|
"pip install --upgrade-strategy only-if-needed "
|
|
# Mirror lerobot's [annotations] runtime deps. ``openai`` is required
|
|
# because ``VlmConfig.backend`` defaults to ``"openai"`` (which talks
|
|
# to a vllm/transformers/ktransformers OpenAI-compatible server).
|
|
"datasets pyarrow av jsonlines draccus gymnasium torchcodec mergedeep pyyaml-include "
|
|
"toml typing-inspect openai && "
|
|
"export VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0 && "
|
|
"export VLLM_VIDEO_BACKEND=pyav && "
|
|
"lerobot-annotate "
|
|
"--repo_id=imstevenpmwork/super_poulain_draft "
|
|
"--vlm.backend=openai "
|
|
"--vlm.model_id=Qwen/Qwen3.6-35B-A3B-FP8 "
|
|
"--vlm.parallel_servers=2 "
|
|
"--vlm.num_gpus=2 "
|
|
'--vlm.serve_command="vllm serve Qwen/Qwen3.6-35B-A3B-FP8 '
|
|
"--tensor-parallel-size 1 --max-model-len 32768 "
|
|
'--gpu-memory-utilization 0.8 --uvicorn-log-level warning --port {port}" '
|
|
"--vlm.serve_ready_timeout_s=1800 "
|
|
"--vlm.client_concurrency=256 "
|
|
"--vlm.max_new_tokens=512 "
|
|
"--executor.episode_parallelism=32 "
|
|
"--vlm.chat_template_kwargs='{enable_thinking: false}' "
|
|
"--vlm.camera_key=observation.images.wrist "
|
|
"--module_1.frames_per_second=1.0 "
|
|
"--module_1.use_video_url=true "
|
|
"--module_1.use_video_url_fps=1.0 "
|
|
"--module_3.K=1 --module_3.vqa_emission_hz=0.2 "
|
|
"--push_to_hub=pepijn223/super_poulain_qwen36moe-3"
|
|
)
|
|
|
|
job = run_job(
|
|
image="vllm/vllm-openai:latest",
|
|
command=["bash", "-c", CMD],
|
|
flavor="h200x2",
|
|
secrets={"HF_TOKEN": token},
|
|
timeout="2h",
|
|
)
|
|
print(f"Job URL: {job.url}")
|
|
print(f"Job ID: {job.id}")
|