riklo commited on
Commit
7ffdca2
Β·
verified Β·
1 Parent(s): 6ffe733

force-upgrade huggingface_hub during runtime install (datasets needs BucketNotFoundError)

Browse files
Files changed (1) hide show
  1. handler.py +30 -32
handler.py CHANGED
@@ -1,14 +1,18 @@
1
- """HF Inference Endpoint handler for the lerobot folding_final policy.
2
-
3
- Strategy: lerobot 0.5.1 is the version that matches the model's config
4
- schema and doesn't need a patched transformers, but its pyproject pins
5
- `requires_python>=3.12`. HFIE's default container is Python 3.11. The
6
- lerobot/pi0 source itself doesn't use any 3.12-only syntax β€” the version
7
- pin is metadata-only. So we install lerobot at runtime inside __init__
8
- with `--ignore-requires-python`, then import.
9
-
10
- This adds ~90–180 s to the first cold-start (the install + ~600 MB of
11
- deps). Subsequent warm requests are normal.
 
 
 
 
12
  """
13
  from __future__ import annotations
14
 
@@ -40,35 +44,34 @@ ACTION_FEATURE_NAMES = [
40
 
41
 
42
  def _ensure_lerobot() -> None:
43
- """Install lerobot 0.5.1 ignoring its >=3.12 pip metadata check.
44
- The pi0 module itself is 3.11-compatible (no PEP 695 syntax)."""
45
  try:
46
  import lerobot.policies.pi0.modeling_pi0 # noqa: F401
47
- log.info("lerobot already installed")
 
48
  return
49
  except ImportError:
50
  pass
51
- log.info("installing lerobot[pi0]==0.5.1 (ignoring requires-python) ...")
52
  subprocess.check_call(
53
  [
54
- sys.executable,
55
- "-m",
56
- "pip",
57
- "install",
58
  "--no-cache-dir",
59
- "--ignore-requires-python",
 
60
  "lerobot[pi0]==0.5.1",
 
61
  ],
62
  env={**os.environ, "PIP_IGNORE_REQUIRES_PYTHON": "1"},
63
  )
64
- log.info("lerobot install done")
65
 
66
 
67
  class EndpointHandler:
68
  def __init__(self, path: str = "") -> None:
69
  _ensure_lerobot()
70
 
71
- # Imports must be after _ensure_lerobot.
72
  from huggingface_hub import snapshot_download
73
  from lerobot.policies.pi0.modeling_pi0 import PI0Policy
74
 
@@ -98,7 +101,7 @@ class EndpointHandler:
98
  images_in = inputs.get("images") or {}
99
 
100
  batch = self._build_batch(images_in, state, prompt)
101
- actions = self.policy.predict_action_chunk(batch) # (1, T, A_padded)
102
  arr = actions.squeeze(0).detach().to("cpu").float().numpy()
103
  if arr.shape[1] > 16:
104
  arr = arr[:, :16]
@@ -111,12 +114,7 @@ class EndpointHandler:
111
 
112
  # ── helpers ──────────────────────────────────────────────────────
113
 
114
- def _build_batch(
115
- self,
116
- images_in: dict[str, str],
117
- state: np.ndarray,
118
- prompt: str,
119
- ) -> dict[str, Any]:
120
  try:
121
  from lerobot.constants import OBS_STATE
122
  except ImportError:
@@ -135,7 +133,7 @@ class EndpointHandler:
135
  return batch
136
 
137
  @staticmethod
138
- def _decode_image(b64: str | None) -> np.ndarray | None:
139
  if not b64:
140
  return None
141
  try:
@@ -146,7 +144,7 @@ class EndpointHandler:
146
  log.warning("image decode failed: %s", e)
147
  return None
148
 
149
- def _warmup(self) -> None:
150
  dummy = base64.b64encode(_dummy_jpeg(224, 224)).decode("ascii")
151
  self({"inputs": {
152
  "images": {"left_wrist": dummy, "right_wrist": dummy, "base": dummy},
@@ -155,7 +153,7 @@ class EndpointHandler:
155
  }})
156
 
157
 
158
- def _dummy_jpeg(w: int, h: int) -> bytes:
159
  buf = io.BytesIO()
160
  Image.fromarray(np.zeros((h, w, 3), dtype=np.uint8)).save(buf, format="JPEG")
161
  return buf.getvalue()
 
1
+ """HF Inference Endpoint handler for lerobot folding_final (pi0.5).
2
+
3
+ Why this is fiddly:
4
+ - The model was saved by lerobot >=0.5.1 (config has fields added there).
5
+ - lerobot 0.5.x's pyproject pins `requires_python>=3.12`, but HFIE's
6
+ default container is Python 3.11.
7
+ - lerobot 0.5.x's pi0 source itself uses no Python-3.12-only syntax β€”
8
+ the version pin is metadata-only. So we install it at runtime with
9
+ `--ignore-requires-python`.
10
+ - The container pre-installs an OLDER `huggingface_hub`; lerobot 0.5.1's
11
+ transitive `datasets` dep needs a newer one (uses `BucketNotFoundError`).
12
+ Forcing `--upgrade` so pip overwrites the pre-installed copy.
13
+
14
+ Cold-start: ~120–180 s extra for the pip install (lerobot + ~600 MB deps).
15
+ Warm requests are normal.
16
  """
17
  from __future__ import annotations
18
 
 
44
 
45
 
46
  def _ensure_lerobot() -> None:
47
+ """Install lerobot 0.5.1 + its modern deps, overriding the pre-installed
48
+ huggingface_hub (too old for `datasets` to import)."""
49
  try:
50
  import lerobot.policies.pi0.modeling_pi0 # noqa: F401
51
+ from huggingface_hub.errors import BucketNotFoundError # noqa: F401
52
+ log.info("lerobot + modern hub already present")
53
  return
54
  except ImportError:
55
  pass
56
+ log.info("installing lerobot 0.5.1 + upgraded deps ...")
57
  subprocess.check_call(
58
  [
59
+ sys.executable, "-m", "pip", "install",
 
 
 
60
  "--no-cache-dir",
61
+ "--upgrade", # overwrite pre-installed older copies
62
+ "--ignore-requires-python", # pip metadata gate; pi0 source is 3.11-compat
63
  "lerobot[pi0]==0.5.1",
64
+ "huggingface_hub>=0.30", # for BucketNotFoundError
65
  ],
66
  env={**os.environ, "PIP_IGNORE_REQUIRES_PYTHON": "1"},
67
  )
68
+ log.info("install done")
69
 
70
 
71
  class EndpointHandler:
72
  def __init__(self, path: str = "") -> None:
73
  _ensure_lerobot()
74
 
 
75
  from huggingface_hub import snapshot_download
76
  from lerobot.policies.pi0.modeling_pi0 import PI0Policy
77
 
 
101
  images_in = inputs.get("images") or {}
102
 
103
  batch = self._build_batch(images_in, state, prompt)
104
+ actions = self.policy.predict_action_chunk(batch)
105
  arr = actions.squeeze(0).detach().to("cpu").float().numpy()
106
  if arr.shape[1] > 16:
107
  arr = arr[:, :16]
 
114
 
115
  # ── helpers ──────────────────────────────────────────────────────
116
 
117
+ def _build_batch(self, images_in, state, prompt):
 
 
 
 
 
118
  try:
119
  from lerobot.constants import OBS_STATE
120
  except ImportError:
 
133
  return batch
134
 
135
  @staticmethod
136
+ def _decode_image(b64):
137
  if not b64:
138
  return None
139
  try:
 
144
  log.warning("image decode failed: %s", e)
145
  return None
146
 
147
+ def _warmup(self):
148
  dummy = base64.b64encode(_dummy_jpeg(224, 224)).decode("ascii")
149
  self({"inputs": {
150
  "images": {"left_wrist": dummy, "right_wrist": dummy, "base": dummy},
 
153
  }})
154
 
155
 
156
+ def _dummy_jpeg(w, h):
157
  buf = io.BytesIO()
158
  Image.fromarray(np.zeros((h, w, 3), dtype=np.uint8)).save(buf, format="JPEG")
159
  return buf.getvalue()