main
py 157 lines 3.66 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Helper functions to work with nested JSON structures."""
4
5 from collections.abc import Callable, Iterable
6 from functools import reduce
7 from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar, overload
8
9 if TYPE_CHECKING:
10 import torch
11
12 from vllm.multimodal.inputs import BatchedTensorInputs
13
14 _T = TypeVar("_T")
15 _U = TypeVar("_U")
16
17 JSONTree: TypeAlias = (
18 dict[str, "JSONTree[_T]"] | list["JSONTree[_T]"] | tuple["JSONTree[_T]", ...] | _T
19 )
20 """A nested JSON structure where the leaves need not be JSON-serializable."""
21
22 _JSONTree: TypeAlias = (
23 dict[str, "JSONTree[_T]"]
24 | list["JSONTree[_T]"]
25 | tuple["JSONTree[_T]", ...]
26 | dict[str, _T]
27 | list[_T]
28 | tuple[_T, ...]
29 | _T
30 )
31 """
32 Same as `JSONTree` but with additional `Union` members to satisfy overloads.
33 """
34
35
36 def json_iter_leaves(value: JSONTree[_T]) -> Iterable[_T]:
37 """Iterate through each leaf in a nested JSON structure."""
38 if isinstance(value, dict):
39 for v in value.values():
40 yield from json_iter_leaves(v)
41 elif isinstance(value, (list, tuple)):
42 for v in value:
43 yield from json_iter_leaves(v)
44 else:
45 yield value
46
47
48 @overload
49 def json_map_leaves(
50 func: Callable[["torch.Tensor"], "torch.Tensor"],
51 value: "BatchedTensorInputs",
52 ) -> "BatchedTensorInputs": ...
53
54
55 @overload
56 def json_map_leaves(
57 func: Callable[[_T], _U],
58 value: _T | dict[str, _T],
59 ) -> _U | dict[str, _U]: ...
60
61
62 @overload
63 def json_map_leaves(
64 func: Callable[[_T], _U],
65 value: _T | list[_T],
66 ) -> _U | list[_U]: ...
67
68
69 @overload
70 def json_map_leaves(
71 func: Callable[[_T], _U],
72 value: _T | tuple[_T, ...],
73 ) -> _U | tuple[_U, ...]: ...
74
75
76 @overload
77 def json_map_leaves(
78 func: Callable[[_T], _U],
79 value: JSONTree[_T],
80 ) -> JSONTree[_U]: ...
81
82
83 def json_map_leaves(
84 func: Callable[[_T], _U],
85 value: Any,
86 ) -> "BatchedTensorInputs" | _JSONTree[_U]:
87 """Apply a function to each leaf in a nested JSON structure."""
88 if isinstance(value, dict):
89 return {k: json_map_leaves(func, v) for k, v in value.items()} # type: ignore
90 elif isinstance(value, list):
91 return [json_map_leaves(func, v) for v in value] # type: ignore
92 elif isinstance(value, tuple):
93 return tuple(json_map_leaves(func, v) for v in value)
94 else:
95 return func(value)
96
97
98 @overload
99 def json_reduce_leaves(
100 func: Callable[[_T, _T], _T],
101 value: _T | dict[str, _T],
102 /,
103 ) -> _T: ...
104
105
106 @overload
107 def json_reduce_leaves(
108 func: Callable[[_T, _T], _T],
109 value: _T | list[_T],
110 /,
111 ) -> _T: ...
112
113
114 @overload
115 def json_reduce_leaves(
116 func: Callable[[_T, _T], _T],
117 value: _T | tuple[_T, ...],
118 /,
119 ) -> _T: ...
120
121
122 @overload
123 def json_reduce_leaves(
124 func: Callable[[_T, _T], _T],
125 value: JSONTree[_T],
126 /,
127 ) -> _T: ...
128
129
130 @overload
131 def json_reduce_leaves(
132 func: Callable[[_U, _T], _U],
133 value: JSONTree[_T],
134 initial: _U,
135 /,
136 ) -> _U: ...
137
138
139 def json_reduce_leaves(
140 func: Callable[[_T, _T], _T] | Callable[[_U, _T], _U],
141 value: _JSONTree[_T],
142 initial: _U = ..., # type: ignore[assignment]
143 /,
144 ) -> _T | _U:
145 """Apply a function of two arguments cumulatively to each leaf in a
146 nested JSON structure, from left to right, so as to reduce the
147 sequence to a single value.
148 """
149 if initial is ...:
150 return reduce(func, json_iter_leaves(value)) # type: ignore
151
152 return reduce(func, json_iter_leaves(value), initial) # type: ignore
153
154
155 def json_count_leaves(value: JSONTree[_T]) -> int:
156 """Count the number of leaves in a nested JSON structure."""
157 return sum(1 for _ in json_iter_leaves(value))