@@ -81,15 +81,11 @@ def _validate_config(cfg: FinetuneConfig) -> list[str]:
8181
8282
8383def _infer_policy_type (base : str ) -> str :
84- """Derive lerobot 's policy registry name from the base model id.
84+ """Derive LeRobot 's policy registry name from a model id.
8585
86- lerobot 0.5.1 registers policies by short name (smolvla, pi0, pi05,
87- act, diffusion, vqbet, ...) and requires `--policy.type=<name>` at
88- CLI time. The full HF model id (e.g. 'lerobot/smolvla_base') goes
89- to `--policy.pretrained_model_path=...` separately.
90-
91- Falls back to raising a clear error for unrecognized bases rather
92- than guessing. Customers can override via extra_lerobot_args={"policy.type": "..."}.
86+ This metadata helper is retained for callers that need a short policy
87+ name. It is deliberately not used to select a pretrained policy on the
88+ LeRobot 0.5.1 CLI: ``--policy.path`` loads the type from the checkpoint.
9389 """
9490 base_lower = base .lower ()
9591 if "smolvla" in base_lower :
@@ -102,9 +98,8 @@ def _infer_policy_type(base: str) -> str:
10298 # lerobot 0.5.1 can't load N1.6 per prior Step-3 finding; v0.6 work.
10399 return "gr00t_n1_5"
104100 raise ValueError (
105- f"Could not infer --policy.type from base={ base !r} . "
106- f"Supported in v0.3: lerobot/smolvla_base. For other bases, "
107- f"pass policy.type explicitly via extra_lerobot_args."
101+ f"Could not infer policy type from base={ base !r} . "
102+ "Supported identifiers include smolvla, pi0, pi05, and gr00t."
108103 )
109104
110105
@@ -121,25 +116,42 @@ def _build_lerobot_command(cfg: FinetuneConfig) -> list[str]:
121116 customers can reproduce manually.
122117
123118 Key arg names per lerobot 0.5.1:
124- --policy.pretrained_model_path (NOT --policy.path)
119+ --policy.path (pretrained checkpoint)
120+ --policy.type (from-scratch policy only)
125121 --optimizer.lr (NOT --policy.optimizer_lr)
126122 --peft.method_type=lora (enables PEFT)
127123 --peft.r (LoRA rank)
128124
125+ LeRobot's parser rejects using ``--policy.path`` and ``--policy.type``
126+ together. A pretrained command therefore selects the policy only by
127+ checkpoint path; LeRobot loads the policy type from that checkpoint's
128+ config. From-scratch commands select the policy only by type.
129+
129130 cfg.precision is intentionally NOT passed through — lerobot 0.5.1
130131 doesn't expose a top-level precision flag; it's baked into the
131132 policy config. v0.5 will add per-policy precision overrides.
132133 """
133- # draccus requires `policy.type` to select which PreTrainedConfig
134- # subclass to decode into. Use explicit cfg.policy when set, else
135- # infer from the base-model id.
136134 explicit_policy = getattr (cfg , "policy" , "auto" )
137- if explicit_policy != "auto" :
138- policy_type = explicit_policy
139- else :
140- policy_type = _infer_policy_type (cfg .base )
141135 is_from_scratch = (explicit_policy != "auto" and cfg .mode == "full" )
142136
137+ # These select the policy and must be emitted exactly once by this
138+ # builder. In particular, LeRobot 0.5.1 rejects policy.path + policy.type,
139+ # while the older pretrained_* spellings are not valid selection flags.
140+ reserved_policy_args = {
141+ "policy.path" ,
142+ "policy.type" ,
143+ "policy.pretrained_path" ,
144+ "policy.pretrained_model_path" ,
145+ }
146+ conflicting_args = reserved_policy_args .intersection (cfg .extra_lerobot_args )
147+ if conflicting_args :
148+ names = ", " .join (sorted (conflicting_args ))
149+ raise ValueError (
150+ f"extra_lerobot_args cannot override policy selection ({ names } ); "
151+ "use FinetuneConfig.base for pretrained policies or "
152+ "FinetuneConfig.policy for from-scratch policies"
153+ )
154+
143155 # lerobot-train wants to OWN its output_dir (errors if pre-existing
144156 # and resume=False). We keep cfg.output as the tether orchestration
145157 # root, and give lerobot a subdirectory it creates fresh on each run.
@@ -153,7 +165,6 @@ def _build_lerobot_command(cfg: FinetuneConfig) -> list[str]:
153165
154166 cmd = [
155167 "lerobot-train" ,
156- f"--policy.type={ policy_type } " ,
157168 f"--policy.repo_id={ repo_id } " ,
158169 f"--policy.push_to_hub=false" ,
159170 f"--dataset.repo_id={ cfg .dataset } " ,
@@ -163,9 +174,11 @@ def _build_lerobot_command(cfg: FinetuneConfig) -> list[str]:
163174 f"--optimizer.lr={ cfg .learning_rate } " ,
164175 f"--seed={ cfg .seed } " ,
165176 ]
166- if not is_from_scratch :
167- # Pretrained-base path: pass the HF id / local checkpoint to load weights from.
168- cmd .append (f"--policy.pretrained_path={ cfg .base } " )
177+ if is_from_scratch :
178+ cmd .append (f"--policy.type={ explicit_policy } " )
179+ else :
180+ # LeRobot 0.5.1 loads both config and weights through policy.path.
181+ cmd .append (f"--policy.path={ cfg .base } " )
169182 if is_from_scratch and cfg .chunk_size :
170183 # ACT (and similar chunked policies) need chunk_size; pretrained bases
171184 # bake this in. auto_soarm convention (per its train.py): set
0 commit comments