mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-08 18:41:54 +00:00
fix(jobs): address claude review findings on remote training
Resolve the claude[bot] review on #3856: - Reject reward-model training under --job.target with a clear error instead of crashing on a None policy inside build_remote_config_file. - Support --policy.path remote runs: validate() no longer requires repo_id for remote runs (it is auto-generated in submit_to_hf), and repo_id/push_to_hub are now set after validate() resolves the policy. - Narrow the bare `except Exception` in _tail_logs/_poll_until_done to (OSError, httpx.HTTPError) so programming errors surface instead of being silently retried or counted as job failures. - Install the SIGINT detach handler only on the main thread. - Generate model repo timestamps in UTC.
This commit is contained in:
+50
-3
@@ -18,6 +18,7 @@ import threading
|
||||
from types import SimpleNamespace
|
||||
|
||||
import draccus
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from lerobot.configs.train import TrainPipelineConfig
|
||||
@@ -58,9 +59,9 @@ def test_poll_until_done_exits_when_done_already_set(monkeypatch):
|
||||
assert _poll_until_done("j", done, poll_interval=0.01) is None
|
||||
|
||||
|
||||
def test_poll_until_done_gives_up_after_repeated_failures(monkeypatch):
|
||||
def test_poll_until_done_gives_up_after_repeated_network_failures(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"lerobot.jobs.hf.inspect_job", lambda job_id: (_ for _ in ()).throw(RuntimeError("boom"))
|
||||
"lerobot.jobs.hf.inspect_job", lambda job_id: (_ for _ in ()).throw(httpx.ConnectError("boom"))
|
||||
)
|
||||
done = threading.Event()
|
||||
result = _poll_until_done("j", done, poll_interval=0.001, max_failures=3)
|
||||
@@ -68,6 +69,14 @@ def test_poll_until_done_gives_up_after_repeated_failures(monkeypatch):
|
||||
assert done.is_set()
|
||||
|
||||
|
||||
def test_poll_until_done_propagates_programming_errors(monkeypatch):
|
||||
"""A bug (e.g. TypeError) must surface, not be silently retried as a transient failure."""
|
||||
monkeypatch.setattr("lerobot.jobs.hf.inspect_job", lambda job_id: (_ for _ in ()).throw(TypeError("bug")))
|
||||
done = threading.Event()
|
||||
with pytest.raises(TypeError):
|
||||
_poll_until_done("j", done, poll_interval=0.001, max_failures=3)
|
||||
|
||||
|
||||
def test_resolve_wandb_key_from_env(monkeypatch):
|
||||
monkeypatch.setenv("WANDB_API_KEY", "abc123")
|
||||
assert resolve_wandb_api_key() == "abc123"
|
||||
@@ -121,6 +130,23 @@ def _minimal_cfg():
|
||||
)
|
||||
|
||||
|
||||
def test_validate_skips_repo_id_check_for_remote():
|
||||
"""Remote runs auto-assign repo_id in submit_to_hf, so validate() must not demand it up front."""
|
||||
cfg = _minimal_cfg() # remote target, push_to_hub default True, no explicit repo_id
|
||||
assert cfg.policy.repo_id is None
|
||||
cfg.validate() # must not raise
|
||||
|
||||
|
||||
def test_validate_requires_repo_id_for_local_push():
|
||||
"""Local runs that push to the Hub still need an explicit repo_id."""
|
||||
cfg = draccus.parse(
|
||||
TrainPipelineConfig,
|
||||
args=["--dataset.repo_id", "u/d", "--policy.type", "act"],
|
||||
)
|
||||
with pytest.raises(ValueError, match="repo_id"):
|
||||
cfg.validate()
|
||||
|
||||
|
||||
def test_build_remote_config_applies_overrides(tmp_path):
|
||||
cfg = _minimal_cfg()
|
||||
dest = tmp_path / "train_config.json"
|
||||
@@ -192,7 +218,7 @@ def test_submit_requires_login(monkeypatch):
|
||||
|
||||
|
||||
def test_submit_passes_validation_and_submits(monkeypatch):
|
||||
"""Regression: repo_id must be set BEFORE cfg.validate() or validation raises."""
|
||||
"""A type-based policy with no explicit repo_id is auto-assigned one and submitted."""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
# Patch get_token
|
||||
@@ -261,6 +287,27 @@ def test_submit_passes_validation_and_submits(monkeypatch):
|
||||
assert call["labels"].get("lerobot") == "true"
|
||||
|
||||
|
||||
def test_submit_rejects_reward_model_training(monkeypatch):
|
||||
"""Remote training only supports policies; reward-model runs fail fast with a clear error."""
|
||||
monkeypatch.setattr("lerobot.jobs.hf.get_token", lambda: "tok")
|
||||
|
||||
class FakeHfApi:
|
||||
def __init__(self, token=None):
|
||||
pass
|
||||
|
||||
def whoami(self, token=None):
|
||||
return {"name": "alice"}
|
||||
|
||||
monkeypatch.setattr("lerobot.jobs.hf.HfApi", FakeHfApi)
|
||||
|
||||
cfg = _minimal_cfg()
|
||||
cfg.reward_model = SimpleNamespace(type="reward") # marks this as reward-model training
|
||||
monkeypatch.setattr(cfg, "validate", lambda: None) # skip pretrained-path resolution
|
||||
|
||||
with pytest.raises(ValueError, match="reward model"):
|
||||
submit_to_hf(cfg)
|
||||
|
||||
|
||||
@pytest.mark.timeout(15)
|
||||
def test_submit_returns_when_job_completes(monkeypatch):
|
||||
"""Non-detach path must RETURN (not hang) once the job reaches a terminal stage."""
|
||||
|
||||
Reference in New Issue
Block a user