22# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33
44import dataclasses
5+ import importlib
56import pickle
67from collections .abc import Sequence
78from inspect import isclass
89from types import FunctionType
910from typing import Any , Optional , Union
1011
1112import cloudpickle
13+ import msgspec
1214import numpy as np
1315import torch
1416import zmq
2224 MultiModalFlatField , MultiModalKwargs ,
2325 MultiModalKwargsItem ,
2426 MultiModalSharedField , NestedTensors )
27+ from vllm .v1 .engine import UtilityResult
2528
2629logger = init_logger (__name__ )
2730
@@ -46,6 +49,10 @@ def _log_insecure_serialization_warning():
4649 "VLLM_ALLOW_INSECURE_SERIALIZATION=1" )
4750
4851
52+ def _typestr (t : type ):
53+ return t .__module__ , t .__qualname__
54+
55+
4956class MsgpackEncoder :
5057 """Encoder with custom torch tensor and numpy array serialization.
5158
@@ -122,6 +129,18 @@ def enc_hook(self, obj: Any) -> Any:
122129 for itemlist in mm ._items_by_modality .values ()
123130 for item in itemlist ]
124131
132+ if isinstance (obj , UtilityResult ):
133+ result = obj .result
134+ if not envs .VLLM_ALLOW_INSECURE_SERIALIZATION or result is None :
135+ return None , result
136+ # Since utility results are not strongly typed, we also encode
137+ # the type (or a list of types in the case it's a list) to
138+ # help with correct msgspec deserialization.
139+ cls = result .__class__
140+ return _typestr (cls ) if cls is not list else [
141+ _typestr (type (v )) for v in result
142+ ], result
143+
125144 if not envs .VLLM_ALLOW_INSECURE_SERIALIZATION :
126145 raise TypeError (f"Object of type { type (obj )} is not serializable"
127146 "Set VLLM_ALLOW_INSECURE_SERIALIZATION=1 to allow "
@@ -237,8 +256,33 @@ def dec_hook(self, t: type, obj: Any) -> Any:
237256 k : self ._decode_nested_tensors (v )
238257 for k , v in obj .items ()
239258 })
259+ if t is UtilityResult :
260+ return self ._decode_utility_result (obj )
240261 return obj
241262
263+ def _decode_utility_result (self , obj : Any ) -> UtilityResult :
264+ result_type , result = obj
265+ if result_type is not None :
266+ if not envs .VLLM_ALLOW_INSECURE_SERIALIZATION :
267+ raise TypeError ("VLLM_ALLOW_INSECURE_SERIALIZATION must "
268+ "be set to use custom utility result types" )
269+ assert isinstance (result_type , list )
270+ if len (result_type ) == 2 and isinstance (result_type [0 ], str ):
271+ result = self ._convert_result (result_type , result )
272+ else :
273+ assert isinstance (result , list )
274+ result = [
275+ self ._convert_result (rt , r )
276+ for rt , r in zip (result_type , result )
277+ ]
278+ return UtilityResult (result )
279+
280+ def _convert_result (self , result_type : Sequence [str ], result : Any ):
281+ mod_name , name = result_type
282+ mod = importlib .import_module (mod_name )
283+ result_type = getattr (mod , name )
284+ return msgspec .convert (result , result_type , dec_hook = self .dec_hook )
285+
242286 def _decode_ndarray (self , arr : Any ) -> np .ndarray :
243287 dtype , shape , data = arr
244288 # zero-copy decode. We assume the ndarray will not be kept around,
0 commit comments