force-upgrade huggingface_hub during runtime install (datasets needs BucketNotFoundError)
Browse files- handler.py +30 -32
handler.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
| 1 |
-
"""HF Inference Endpoint handler for
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
`requires_python>=3.12`
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 44 |
-
|
| 45 |
try:
|
| 46 |
import lerobot.policies.pi0.modeling_pi0 # noqa: F401
|
| 47 |
-
|
|
|
|
| 48 |
return
|
| 49 |
except ImportError:
|
| 50 |
pass
|
| 51 |
-
log.info("installing lerobot
|
| 52 |
subprocess.check_call(
|
| 53 |
[
|
| 54 |
-
sys.executable,
|
| 55 |
-
"-m",
|
| 56 |
-
"pip",
|
| 57 |
-
"install",
|
| 58 |
"--no-cache-dir",
|
| 59 |
-
"--
|
|
|
|
| 60 |
"lerobot[pi0]==0.5.1",
|
|
|
|
| 61 |
],
|
| 62 |
env={**os.environ, "PIP_IGNORE_REQUIRES_PYTHON": "1"},
|
| 63 |
)
|
| 64 |
-
log.info("
|
| 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)
|
| 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
|
| 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)
|
| 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
|
| 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()
|