From 94b71ae10626d6e05677062f653051e01eb225ab Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:31:10 +0100 Subject: [PATCH] Use `metavar` to list the choices for a CLI arg when custom values are also accepted (#21760) Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- docs/mkdocs/hooks/generate_argparse.py | 5 +++++ tests/engine/test_arg_utils.py | 4 ++++ vllm/engine/arg_utils.py | 18 +++++++++++------- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/mkdocs/hooks/generate_argparse.py b/docs/mkdocs/hooks/generate_argparse.py index 22cf41e6041d2..b003b5fd6ccef 100644 --- a/docs/mkdocs/hooks/generate_argparse.py +++ b/docs/mkdocs/hooks/generate_argparse.py @@ -62,6 +62,11 @@ class MarkdownFormatter(HelpFormatter): choices = f'`{"`, `".join(str(c) for c in choices)}`' self._markdown_output.append( f"Possible choices: {choices}\n\n") + elif ((metavar := action.metavar) + and isinstance(metavar, (list, tuple))): + metavar = f'`{"`, `".join(str(m) for m in metavar)}`' + self._markdown_output.append( + f"Possible choices: {metavar}\n\n") self._markdown_output.append(f"{action.help}\n\n") diff --git a/tests/engine/test_arg_utils.py b/tests/engine/test_arg_utils.py index 5a91758414a5a..1d1926068d28c 100644 --- a/tests/engine/test_arg_utils.py +++ b/tests/engine/test_arg_utils.py @@ -72,6 +72,10 @@ def test_get_type(type_hints, type, expected): "type": int, "choices": [1, 2] }), + ({str, Literal["x", "y"]}, { + "type": str, + "metavar": ["x", "y"] + }), ({Literal[1, "a"]}, Exception), ]) def test_literal_to_kwargs(type_hints, expected): diff --git a/vllm/engine/arg_utils.py b/vllm/engine/arg_utils.py index 10353a95b864f..d4d6001a428d2 100644 --- a/vllm/engine/arg_utils.py +++ b/vllm/engine/arg_utils.py @@ -108,15 +108,19 @@ def get_type(type_hints: set[TypeHint], type: TypeHintT) -> TypeHintT: def literal_to_kwargs(type_hints: set[TypeHint]) -> dict[str, Any]: - """Convert Literal type hints to argparse kwargs.""" + """Get the `type` and `choices` from a `Literal` type hint in `type_hints`. + + If `type_hints` also contains `str`, we use `metavar` instead of `choices`. + """ type_hint = get_type(type_hints, Literal) - choices = get_args(type_hint) - choice_type = type(choices[0]) - if not all(isinstance(choice, choice_type) for choice in choices): + options = get_args(type_hint) + option_type = type(options[0]) + if not all(isinstance(option, option_type) for option in options): raise ValueError( - "All choices must be of the same type. " - f"Got {choices} with types {[type(c) for c in choices]}") - return {"type": choice_type, "choices": sorted(choices)} + "All options must be of the same type. " + f"Got {options} with types {[type(c) for c in options]}") + kwarg = "metavar" if contains_type(type_hints, str) else "choices" + return {"type": option_type, kwarg: sorted(options)} def is_not_builtin(type_hint: TypeHint) -> bool: