From 81c1c460b3eb12083a02abe0b8d85247964d4526 Mon Sep 17 00:00:00 2001 From: zhengjia Date: Tue, 8 Sep 2026 22:15:48 -0400 Subject: [PATCH] [Bugfix][Frontend] Accept the msgpack-numpy package's numpy markers on the OpenPI endpoint (#6051) Signed-off-by: zjli2013 Co-authored-by: Cursor --- .../openai_api/test_openpi_connection.py | 54 ++++++++++++++++++- vllm_omni/entrypoints/openpi/connection.py | 16 +++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/tests/entrypoints/openai_api/test_openpi_connection.py b/tests/entrypoints/openai_api/test_openpi_connection.py index 398ae6494..19ec23b6d 100644 --- a/tests/entrypoints/openai_api/test_openpi_connection.py +++ b/tests/entrypoints/openai_api/test_openpi_connection.py @@ -68,7 +68,7 @@ def test_pack_and_unpack_round_trip_numpy_values(): assert decoded["nested"][0]["done"] == np.bool_(True) -def test_unpack_accepts_msgpack_numpy_marker_dicts(): +def test_unpack_accepts_vllm_native_marker_dicts(): action = np.asarray([[1.0, 2.0]], dtype=np.float32) payload = { b"actions": { @@ -88,6 +88,58 @@ def test_unpack_accepts_msgpack_numpy_marker_dicts(): np.testing.assert_allclose(decoded[b"actions"], np.asarray([[1.0, 0.0]], dtype=np.float32)) +def test_unpack_accepts_msgpack_numpy_marker_dicts(): + action = np.asarray([[1.0, 2.0]], dtype=np.float32) + payload = { + b"actions": { + b"nd": True, + b"type": action.dtype.str, + b"kind": b"", + b"shape": action.shape, + b"data": action.tobytes(), + } + } + + decoded = openpi_connection._unpack_numpy(payload) + + np.testing.assert_allclose(decoded[b"actions"], action) + assert decoded[b"actions"].dtype == np.float32 + + +def test_unpack_rejects_msgpack_numpy_structured_array_markers(): + values = np.zeros(2, dtype=[("a", " {nd: true, type, kind, shape, data} scalar -> {nd: false, type, kind, data} + +`kind` is required on the `nd` markers because it is what separates them from a +plain user mapping, but its value is dialect-specific: the vLLM-native markers +carry the dtype kind character while the `msgpack-numpy` package leaves it empty +for plain dtypes and sets "V" for structured ones. Both spellings are accepted. +Scalars packed by `msgpack-numpy` omit `kind` entirely and are therefore left as +mappings. """ from __future__ import annotations @@ -83,9 +90,14 @@ def _decode_vllm_numpy_marker(obj: dict[Any, Any]) -> Any: if nd is _MISSING or dtype is _MISSING or kind is _MISSING or data is _MISSING: return _MISSING - dtype_obj = np.dtype(_decode_marker_text(dtype)) kind_text = _decode_marker_text(kind) - if dtype_obj.kind != kind_text: + # Structured markers carry a dtype descriptor list in `type`, so reject them + # before `np.dtype()` sees something it cannot parse. + if kind_text == "V": + raise ValueError("Unsupported dtype: structured arrays") + + dtype_obj = np.dtype(_decode_marker_text(dtype)) + if kind_text not in ("", dtype_obj.kind): raise ValueError(f"NumPy dtype marker kind mismatch: {dtype_obj.kind!r} != {kind_text!r}") if dtype_obj.kind in ("V", "O", "c"): raise ValueError(f"Unsupported dtype: {dtype_obj}")