diff --git a/docs/cli/README.md b/docs/cli/README.md index 3541437659ca..1d951747a7ac 100644 --- a/docs/cli/README.md +++ b/docs/cli/README.md @@ -37,6 +37,9 @@ Start the vLLM OpenAI Compatible API server. # To search by keyword vllm serve --help=max + + # To view full help with pager (less/more) + vllm serve --help=page ``` ## chat diff --git a/vllm/entrypoints/utils.py b/vllm/entrypoints/utils.py index 6c37ce818e6d..87334f458fee 100644 --- a/vllm/entrypoints/utils.py +++ b/vllm/entrypoints/utils.py @@ -5,6 +5,7 @@ import argparse import asyncio import functools import os +import subprocess import sys from typing import Any, Optional, Union @@ -25,7 +26,8 @@ VLLM_SUBCMD_PARSER_EPILOG = ( " - To view a argument group: --help=ModelConfig\n" " - To view a single argument: --help=max-num-seqs\n" " - To search by keyword: --help=max\n" - " - To list all groups: --help=listgroup") + " - To list all groups: --help=listgroup\n" + " - To view help with pager: --help=page") async def listen_for_disconnect(request: Request) -> None: @@ -190,6 +192,24 @@ def _validate_truncation_size( return truncate_prompt_tokens +def _output_with_pager(text: str): + """Output text using scrolling view if available and appropriate.""" + + pagers = ['less -R', 'more'] + for pager_cmd in pagers: + try: + proc = subprocess.Popen(pager_cmd.split(), + stdin=subprocess.PIPE, + text=True) + proc.communicate(input=text) + return + except (subprocess.SubprocessError, OSError, FileNotFoundError): + continue + + # No pager worked, fall back to normal print + print(text) + + def show_filtered_argument_or_group_from_help(parser: argparse.ArgumentParser, subcommand_name: list[str]): @@ -208,16 +228,24 @@ def show_filtered_argument_or_group_from_help(parser: argparse.ArgumentParser, if arg.startswith('--help='): search_keyword = arg.split('=', 1)[1] + # Enable paged view for full help + if search_keyword == 'page': + help_text = parser.format_help() + _output_with_pager(help_text) + sys.exit(0) + # List available groups if search_keyword == 'listgroup': - print("\nAvailable argument groups:") + output_lines = ["\nAvailable argument groups:"] for group in parser._action_groups: if group.title and not group.title.startswith( "positional arguments"): - print(f" - {group.title}") + output_lines.append(f" - {group.title}") if group.description: - print(" " + group.description.strip()) - print() + output_lines.append(" " + + group.description.strip()) + output_lines.append("") + _output_with_pager("\n".join(output_lines)) sys.exit(0) # For group search @@ -229,7 +257,7 @@ def show_filtered_argument_or_group_from_help(parser: argparse.ArgumentParser, formatter.add_text(group.description) formatter.add_arguments(group._group_actions) formatter.end_section() - print(formatter.format_help()) + _output_with_pager(formatter.format_help()) sys.exit(0) # For single arg @@ -243,10 +271,10 @@ def show_filtered_argument_or_group_from_help(parser: argparse.ArgumentParser, matched_actions.append(action) if matched_actions: - print(f"\nParameters matching '{search_keyword}':\n") + header = f"\nParameters matching '{search_keyword}':\n" formatter = parser._get_formatter() formatter.add_arguments(matched_actions) - print(formatter.format_help()) + _output_with_pager(header + formatter.format_help()) sys.exit(0) print(f"\nNo group or parameter matching '{search_keyword}'")