diff --git a/src/lerobot/annotations/steerable_pipeline/executor.py b/src/lerobot/annotations/steerable_pipeline/executor.py index b910b9e19..903fa0831 100644 --- a/src/lerobot/annotations/steerable_pipeline/executor.py +++ b/src/lerobot/annotations/steerable_pipeline/executor.py @@ -186,7 +186,7 @@ class Executor: staging_dir: Path, module: Any, ) -> PhaseResult: - if not module.enabled: + if module is None or not module.enabled: print(f"[annotate] phase={name} skipped (module disabled)", flush=True) return PhaseResult(name=name, episodes_processed=0, episodes_skipped=len(records)) n = len(records) @@ -238,7 +238,7 @@ class Executor: ``plan`` module with the interjection timestamps so its existing prompt path is reused. """ - if not self.plan.enabled or not self.interjections.enabled: + if not self.plan or not self.plan.enabled or not self.interjections or not self.interjections.enabled: return PhaseResult(name="plan_update", episodes_processed=0, episodes_skipped=len(records)) processed = 0 for record in records: diff --git a/src/lerobot/scripts/lerobot_annotate.py b/src/lerobot/scripts/lerobot_annotate.py index 7444daaa1..6bda51978 100644 --- a/src/lerobot/scripts/lerobot_annotate.py +++ b/src/lerobot/scripts/lerobot_annotate.py @@ -64,17 +64,23 @@ def annotate(cfg: AnnotationPipelineConfig) -> None: root = _resolve_root(cfg) logger.info("annotate: root=%s", root) - vlm = make_vlm_client(cfg.vlm) - frame_provider = make_frame_provider(root, camera_key=cfg.vlm.camera_key, video_backend=cfg.video_backend) + needs_vlm = cfg.plan.enabled or cfg.interjections.enabled or cfg.vqa.enabled + vlm = make_vlm_client(cfg.vlm) if needs_vlm else None + frame_provider = ( + make_frame_provider(root, camera_key=cfg.vlm.camera_key, video_backend=cfg.video_backend) + if needs_vlm + else None + ) # Surface the resolved cameras up front so a silent vqa-module no-op # is obvious in job output rather than discovered post-hoc by counting # parquet rows. - cam_keys = list(getattr(frame_provider, "camera_keys", []) or []) - logger.info( - "annotate: frame_provider default camera=%r, all cameras=%s", - getattr(frame_provider, "camera_key", None), - cam_keys, - ) + cam_keys = list(getattr(frame_provider, "camera_keys", []) or []) if frame_provider else [] + if frame_provider: + logger.info( + "annotate: frame_provider default camera=%r, all cameras=%s", + getattr(frame_provider, "camera_key", None), + cam_keys, + ) if cfg.vqa.enabled and not cam_keys: logger.warning( "annotate: the vqa module is enabled but no cameras were " @@ -82,15 +88,27 @@ def annotate(cfg: AnnotationPipelineConfig) -> None: "meta/info.json for observation.images.* features, or pass " "--vlm.camera_key= to seed the cameras list." ) - plan = PlanSubtasksMemoryModule(vlm=vlm, config=cfg.plan, frame_provider=frame_provider) - interjections = InterjectionsAndSpeechModule( - vlm=vlm, config=cfg.interjections, seed=cfg.seed, frame_provider=frame_provider + plan = ( + PlanSubtasksMemoryModule(vlm=vlm, config=cfg.plan, frame_provider=frame_provider) + if needs_vlm + else None + ) + interjections = ( + InterjectionsAndSpeechModule( + vlm=vlm, config=cfg.interjections, seed=cfg.seed, frame_provider=frame_provider + ) + if needs_vlm + else None + ) + vqa = ( + GeneralVqaModule(vlm=vlm, config=cfg.vqa, seed=cfg.seed, frame_provider=frame_provider) + if needs_vlm + else None ) - vqa = GeneralVqaModule(vlm=vlm, config=cfg.vqa, seed=cfg.seed, frame_provider=frame_provider) advantage = AdvantageModule(config=cfg.advantage) writer = LanguageColumnsWriter() validator = StagingValidator( - dataset_camera_keys=tuple(getattr(frame_provider, "camera_keys", []) or []) or None, + dataset_camera_keys=tuple(cam_keys) or None, ) executor = Executor(