A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 06:00 UTC
Support non-string values in JSON keys from CLI (#19471)
Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>
Cyrus Leung committed
Jun 11, 2025 at 17:34 UTC
a2142f01969a7c327da69be1e82a6c01a530116c
3 files changed
+48
-24
tests/test_config.py
+17
-17
@@ -13,32 +13,32 @@ from vllm.model_executor.layers.pooler import PoolingType
13
from vllm.platforms import current_platform
14
15
16
-class TestConfig1:
16
+class _TestConfig1:
17
pass
18
19
20
@dataclass
21
-class TestConfig2:
21
+class _TestConfig2:
22
a: int
23
"""docstring"""
24
25
26
@dataclass
27
-class TestConfig3:
27
+class _TestConfig3:
28
a: int = 1
29
30
31
@dataclass
32
-class TestConfig4:
32
+class _TestConfig4:
33
a: Union[Literal[1], Literal[2]] = 1
34
"""docstring"""
35
36
37
@pytest.mark.parametrize(("test_config", "expected_error"), [
38
- (TestConfig1, "must be a dataclass"),
39
- (TestConfig2, "must have a default"),
40
- (TestConfig3, "must have a docstring"),
41
- (TestConfig4, "must use a single Literal"),
38
+ (_TestConfig1, "must be a dataclass"),
39
+ (_TestConfig2, "must have a default"),
40
+ (_TestConfig3, "must have a docstring"),
41
+ (_TestConfig4, "must use a single Literal"),
42
])
43
def test_config(test_config, expected_error):
44
with pytest.raises(Exception, match=expected_error):
@@ -57,23 +57,23 @@ def test_compile_config_repr_succeeds():
57
assert 'inductor_passes' in val
58
59
60
-def test_get_field():
60
+@dataclass
61
+class _TestConfigFields:
62
+ a: int
63
+ b: dict = field(default_factory=dict)
64
+ c: str = "default"
65
62
- @dataclass
63
- class TestConfig:
64
- a: int
65
- b: dict = field(default_factory=dict)
66
- c: str = "default"
66
67
+def test_get_field():
68
with pytest.raises(ValueError):
69
- get_field(TestConfig, "a")
69
+ get_field(_TestConfigFields, "a")
70
71
- b = get_field(TestConfig, "b")
71
+ b = get_field(_TestConfigFields, "b")
72
assert isinstance(b, Field)
73
assert b.default is MISSING
74
assert b.default_factory is dict
75
76
- c = get_field(TestConfig, "c")
76
+ c = get_field(_TestConfigFields, "c")
77
assert isinstance(c, Field)
78
assert c.default == "default"
79
assert c.default_factory is MISSING
tests/test_utils.py
+15
@@ -272,6 +272,15 @@ def test_dict_args(parser):
272
"val5",
273
"--hf_overrides.key-7.key_8",
274
"val6",
275
+ # Test data type detection
276
+ "--hf_overrides.key9",
277
+ "100",
278
+ "--hf_overrides.key10",
279
+ "100.0",
280
+ "--hf_overrides.key11",
281
+ "true",
282
+ "--hf_overrides.key12.key13",
283
+ "null",
284
]
285
parsed_args = parser.parse_args(args)
286
assert parsed_args.model_name == "something.something"
@@ -286,6 +295,12 @@ def test_dict_args(parser):
295
"key-7": {
296
"key_8": "val6",
297
},
298
+ "key9": 100,
299
+ "key10": 100.0,
300
+ "key11": True,
301
+ "key12": {
302
+ "key13": None,
303
+ },
304
}
305
306
vllm/utils.py
+16
-7
@@ -1466,7 +1466,7 @@ class FlexibleArgumentParser(ArgumentParser):
1466
pattern = re.compile(r"(?<=--)[^\.]*")
1467
1468
# Convert underscores to dashes and vice versa in argument names
1469
- processed_args = []
1469
+ processed_args = list[str]()
1470
for arg in args:
1471
if arg.startswith('--'):
1472
if '=' in arg:
@@ -1483,7 +1483,7 @@ class FlexibleArgumentParser(ArgumentParser):
1483
else:
1484
processed_args.append(arg)
1485
1486
- def create_nested_dict(keys: list[str], value: str):
1486
+ def create_nested_dict(keys: list[str], value: str) -> dict[str, Any]:
1487
"""Creates a nested dictionary from a list of keys and a value.
1488
1489
For example, `keys = ["a", "b", "c"]` and `value = 1` will create:
@@ -1494,7 +1494,10 @@ class FlexibleArgumentParser(ArgumentParser):
1494
nested_dict = {key: nested_dict}
1495
return nested_dict
1496
1497
- def recursive_dict_update(original: dict, update: dict):
1497
+ def recursive_dict_update(
1498
+ original: dict[str, Any],
1499
+ update: dict[str, Any],
1500
+ ):
1501
"""Recursively updates a dictionary with another dictionary."""
1502
for k, v in update.items():
1503
if isinstance(v, dict) and isinstance(original.get(k), dict):
@@ -1502,19 +1505,25 @@ class FlexibleArgumentParser(ArgumentParser):
1505
else:
1506
original[k] = v
1507
1505
- delete = set()
1506
- dict_args: dict[str, dict] = defaultdict(dict)
1508
+ delete = set[int]()
1509
+ dict_args = defaultdict[str, dict[str, Any]](dict)
1510
for i, processed_arg in enumerate(processed_args):
1511
if processed_arg.startswith("--") and "." in processed_arg:
1512
if "=" in processed_arg:
1510
- processed_arg, value = processed_arg.split("=", 1)
1513
+ processed_arg, value_str = processed_arg.split("=", 1)
1514
if "." not in processed_arg:
1515
# False positive, . was only in the value
1516
continue
1517
else:
1515
- value = processed_args[i + 1]
1518
+ value_str = processed_args[i + 1]
1519
delete.add(i + 1)
1520
+
1521
key, *keys = processed_arg.split(".")
1522
+ try:
1523
+ value = json.loads(value_str)
1524
+ except json.decoder.JSONDecodeError:
1525
+ value = value_str
1526
+
1527
# Merge all values with the same key into a single dict
1528
arg_dict = create_nested_dict(keys, value)
1529
recursive_dict_update(dict_args[key], arg_dict)