[Frontend] Consolidate startup utils. (#62390)

Signed-off-by: wang.yuqi <yuqi.wang@daocloud.io>
This commit is contained in:
wang.yuqi authored and GitHub committed 2026-10-19 16:00:10 +00:00
1 parent a3cee21d0b
commit 5caabdbb65
38 files changed
+312 -302

No files matched your search

+2
View File
@@ -1004,11 +1004,13 @@ steps:
- tests/entrypoints/unit_tests
- tests/entrypoints/rl/weight_transfer
- tests/entrypoints/launchers
- tests/entrypoints/common
- vllm/platforms/rocm.py
commands:
- pytest -v -s entrypoints/unit_tests
- pytest -v -s entrypoints/rl/weight_transfer
- pytest -v -s entrypoints/launchers
- pytest -v -s entrypoints/common
- label: ":amd: (MI300) Entrypoints Integration (Responses API)" # TBD
timeout_in_minutes: 50
+2
View File
@@ -34,9 +34,11 @@ steps:
- vllm/utils/flashinfer.py
- tests/entrypoints/unit_tests
- tests/entrypoints/launchers
- tests/entrypoints/common
commands:
- pytest -v -s entrypoints/unit_tests
- pytest -v -s entrypoints/launchers
- pytest -v -s entrypoints/common
mirror:
amd:
label: ":amd: (MI355 DPX) Entrypoints Unit"
+1 -1
View File
@@ -183,7 +183,7 @@ DownloadKernelsSubcommand = auto_mock(
"vllm.entrypoints.cli.download_kernels", "DownloadKernelsSubcommand"
)
sweep_subcommands = auto_mock("vllm.benchmarks.sweep.cli", "SUBCOMMANDS")
openai_cli_args = auto_mock("vllm.entrypoints.launchers", "cli_args")
openai_cli_args = auto_mock("vllm.entrypoints.common", "cli_args")
run_batch = auto_mock("vllm.entrypoints.launchers", "run_batch")
if TYPE_CHECKING:
+3 -3
View File
@@ -137,7 +137,7 @@ Remove these arguments to continue."
/// recognizes but does not support yet.
///
/// Source of truth in Python vLLM:
/// - `vllm.entrypoints.openai.cli_args.make_arg_parser(...)`
/// - `vllm.entrypoints.common.cli_args.make_arg_parser(...)`
/// - `vllm.entrypoints.cli.serve.ServeSubcommand.subparser_init(...)`
///
/// These are not part of `EngineArgs`, `AsyncEngineArgs`, `BaseFrontendArgs`,
@@ -384,8 +384,8 @@ pub struct EngineUnsupportedArgs {
/// does not support yet.
///
/// Source of truth in Python vLLM:
/// - `vllm.entrypoints.openai.cli_args.BaseFrontendArgs`
/// - `vllm.entrypoints.openai.cli_args.FrontendArgs`
/// - `vllm.entrypoints.common.cli_args.BaseFrontendArgs`
/// - `vllm.entrypoints.common.cli_args.FrontendArgs`
///
/// These are not engine args. They belong to the Python OpenAI-compatible
/// frontend / API-server layer itself, for example chat-template configuration,
+1 -1
View File
@@ -481,7 +481,7 @@ def test_compilation_config():
def test_trust_request_mm_kwargs_cli():
from vllm.entrypoints.launchers.cli_args import FrontendArgs
from vllm.entrypoints.common.cli_args import FrontendArgs
parser = FrontendArgs.add_cli_args(FlexibleArgumentParser())
@@ -6,7 +6,7 @@ by the Cohere v2 chat API:
* ``vllm/renderers/registry.py``: new ``"cohere"`` renderer entry.
* ``vllm/tokenizers/registry.py``: new ``"cohere"`` tokenizer entry
(aliased to the cached HF tokenizer).
* ``vllm/entrypoints/openai/cli_args.py``: new
* ``vllm/entrypoints/common/cli_args.py``: new
``cohere_is_reasoning_model`` field.
* ``vllm/config/model.py``: ``"cohere"`` added to ``TokenizerMode``
Literal.
@@ -18,7 +18,7 @@ import typing
import pytest
from vllm.config.model import TokenizerMode
from vllm.entrypoints.launchers.cli_args import BaseFrontendArgs, make_arg_parser
from vllm.entrypoints.common.cli_args import BaseFrontendArgs, make_arg_parser
from vllm.renderers.registry import RENDERER_REGISTRY
from vllm.tokenizers.registry import TokenizerRegistry
from vllm.utils.argparse_utils import FlexibleArgumentParser
Whitespace-only changes.
@@ -6,10 +6,10 @@ from types import SimpleNamespace
import pytest
import vllm.entrypoints.launchers.cli_args as cli_args_module
import vllm.entrypoints.common.cli_args as cli_args_module
from tests.utils import VLLM_PATH
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.entrypoints.launchers.cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
validate_parsed_serve_args,
)
+69
View File
@@ -0,0 +1,69 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from argparse import Namespace
from vllm.entrypoints.common import startup
from vllm.entrypoints.common.startup import redact_sensitive_args
class TestRedactSensitiveArgs:
API_KEY = "sk-test-secret-12345"
def test_redact_replaces_sensitive_values_only(self):
args = {
"api_key": self.API_KEY,
"hf_token": "hf_secret",
"watermark_config": {"algorithm": "gumbel", "key": 42},
"other": "visible",
}
redacted = redact_sensitive_args(args)
assert redacted == {
"api_key": "***",
"hf_token": "***",
"watermark_config": "***",
"other": "visible",
}
# original dict must not be mutated
assert args == {
"api_key": self.API_KEY,
"hf_token": "hf_secret",
"watermark_config": {"algorithm": "gumbel", "key": 42},
"other": "visible",
}
def test_no_sensitive_fields_returns_original(self):
args = {"model_tag": "org/model", "other": "visible"}
assert redact_sensitive_args(args) is args
def test_api_key_not_in_log(self, monkeypatch, caplog):
non_default = {
"model_tag": "org/model",
"default_chat_template_kwargs": {"enable_thinking": False},
"api_key": self.API_KEY,
"enable_auto_tool_choice": True,
"tool_call_parser": "qwen3_coder",
}
monkeypatch.setattr(startup, "get_non_default_args", lambda args: non_default)
with caplog.at_level("INFO", logger="vllm.entrypoints.common.startup"):
startup.log_non_default_args(args=Namespace())
message = caplog.text
assert self.API_KEY not in message
assert "'api_key': '***'" in message
# non-sensitive args are still logged
assert "org/model" in message
assert "qwen3_coder" in message
def test_hf_token_not_in_log(self, monkeypatch, caplog):
hf_token = "hf_TESTTOKENVALUE123"
non_default = {
"model_tag": "org/model",
"hf_token": hf_token,
"tool_call_parser": "qwen3_coder",
}
monkeypatch.setattr(startup, "get_non_default_args", lambda args: non_default)
with caplog.at_level("INFO", logger="vllm.entrypoints.common.startup"):
startup.log_non_default_args(args=Namespace())
message = caplog.text
assert hf_token not in message
assert "'hf_token': '***'" in message
assert "org/model" in message
@@ -417,7 +417,7 @@ def test_rust_frontend_launch_log_redacts_credentials(monkeypatch):
Credentials must not reach the log line."""
import subprocess as subprocess_mod
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm.v1.utils import RustFrontendProcessManager
@@ -469,7 +469,7 @@ def test_rust_frontend_uses_config_model_as_model_tag(monkeypatch, caplog, tmp_p
"""Config-only model selection must be forwarded to the Rust frontend."""
import subprocess as subprocess_mod
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm.v1.utils import RustFrontendProcessManager
@@ -517,7 +517,7 @@ def test_rust_frontend_inherits_grpc_listener(monkeypatch):
"""The Python-bound gRPC socket is passed to Rust like the HTTP one."""
import subprocess as subprocess_mod
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm.v1.utils import RustFrontendProcessManager
@@ -577,7 +577,7 @@ def test_rust_frontend_inherits_grpc_listener(monkeypatch):
def test_rust_frontend_spawn_failure_cleans_up_listeners(tmp_path):
"""No child owns the listeners when Popen fails, so the parent removes them."""
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm.v1.utils import RustFrontendProcessManager
@@ -7,7 +7,7 @@ from starlette.datastructures import State
import vllm.entrypoints.launchers.render.app_state as app_state_mod
from vllm.config import ModelConfig, VllmConfig
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
@@ -24,6 +24,7 @@ from vllm.entrypoints.cli.launch import (
RenderSubcommand,
cmd_init,
)
from vllm.entrypoints.common import startup
from vllm.snapshot.controller import (
LocalSnapshotTools,
SnapshotCreateError,
@@ -159,9 +160,7 @@ def test_snapshot_environment_contract(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
):
from vllm.entrypoints.serve.utils import api_utils
monkeypatch.setattr(api_utils, "cli_env_setup", pytest.fail)
monkeypatch.setattr(startup, "cli_env_setup", pytest.fail)
secret = "snapshot-secret"
monkeypatch.setenv("VLLM_API_KEY", secret)
monkeypatch.setenv("VLLM_USER_SETTING", secret)
@@ -4,7 +4,7 @@
from fastapi import FastAPI
from starlette.testclient import TestClient
from vllm.entrypoints.launchers.cli_args import FrontendArgs
from vllm.entrypoints.common.cli_args import FrontendArgs
from vllm.entrypoints.serve.middleware.register import init_entrypoints_middleware
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import asyncio
from argparse import Namespace
import pytest
from fastapi import FastAPI, Request
@@ -11,7 +9,6 @@ from vllm.entrypoints.generate.base.protocol import StreamOptions
from vllm.entrypoints.serve.utils import api_utils
from vllm.entrypoints.serve.utils.api_utils import (
get_max_tokens,
redact_sensitive_args,
should_include_usage,
)
@@ -165,66 +162,3 @@ class TestGetMaxTokens:
input_length=150,
default_sampling_params={"max_tokens": 2048},
)
class TestRedactSensitiveArgs:
API_KEY = "sk-test-secret-12345"
def test_redact_replaces_sensitive_values_only(self):
args = {
"api_key": self.API_KEY,
"hf_token": "hf_secret",
"watermark_config": {"algorithm": "gumbel", "key": 42},
"other": "visible",
}
redacted = redact_sensitive_args(args)
assert redacted == {
"api_key": "***",
"hf_token": "***",
"watermark_config": "***",
"other": "visible",
}
# original dict must not be mutated
assert args == {
"api_key": self.API_KEY,
"hf_token": "hf_secret",
"watermark_config": {"algorithm": "gumbel", "key": 42},
"other": "visible",
}
def test_no_sensitive_fields_returns_original(self):
args = {"model_tag": "org/model", "other": "visible"}
assert redact_sensitive_args(args) is args
def test_api_key_not_in_log(self, monkeypatch, caplog):
non_default = {
"model_tag": "org/model",
"default_chat_template_kwargs": {"enable_thinking": False},
"api_key": self.API_KEY,
"enable_auto_tool_choice": True,
"tool_call_parser": "qwen3_coder",
}
monkeypatch.setattr(api_utils, "get_non_default_args", lambda args: non_default)
with caplog.at_level("INFO", logger="vllm.entrypoints.serve.utils.api_utils"):
api_utils.log_non_default_args(args=Namespace())
message = caplog.text
assert self.API_KEY not in message
assert "'api_key': '***'" in message
# non-sensitive args are still logged
assert "org/model" in message
assert "qwen3_coder" in message
def test_hf_token_not_in_log(self, monkeypatch, caplog):
hf_token = "hf_TESTTOKENVALUE123"
non_default = {
"model_tag": "org/model",
"hf_token": hf_token,
"tool_call_parser": "qwen3_coder",
}
monkeypatch.setattr(api_utils, "get_non_default_args", lambda args: non_default)
with caplog.at_level("INFO", logger="vllm.entrypoints.serve.utils.api_utils"):
api_utils.log_non_default_args(args=Namespace())
message = caplog.text
assert hf_token not in message
assert "'hf_token': '***'" in message
assert "org/model" in message
+1 -1
View File
@@ -17,8 +17,8 @@ import pytest
from fastapi import APIRouter, FastAPI
from vllm_add_dummy_endpoint_plugin import DummyAdminEndpointPlugin
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.entrypoints.launchers.app import build_app
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.plugins import load_endpoint_plugins
from vllm.plugins.endpoint_plugins.interface import (
EndpointPlugin,
+1
View File
@@ -42,6 +42,7 @@ SILENT_GROUPS = [
"tests/entrypoints/serve",
"tests/entrypoints/tool_parsers",
"tests/entrypoints/unit_tests",
"tests/entrypoints/common",
"tests/kernels/attention",
"tests/kernels/core",
"tests/kernels/helion",
+1 -1
View File
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import argparse
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.utils.argparse_utils import FlexibleArgumentParser
from .plot import SweepPlotArgs
+1 -1
View File
@@ -9,7 +9,7 @@ import typing
from vllm import envs
from vllm.entrypoints.cli.benchmark.base import BenchmarkSubcommandBase
from vllm.entrypoints.cli.types import CLISubcommand
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.logger import init_logger
if typing.TYPE_CHECKING:
+2 -2
View File
@@ -7,12 +7,12 @@ import inspect
import uvloop
from vllm.entrypoints.cli.types import CLISubcommand
from vllm.entrypoints.launchers.cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
validate_parsed_serve_args,
)
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.launchers.render.entry import run_launch_fastapi
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.logger import init_logger
from vllm.utils.argparse_utils import FlexibleArgumentParser
+1 -4
View File
@@ -49,10 +49,7 @@ def main():
import vllm.entrypoints.cli.run_batch
import vllm.entrypoints.cli.serve
import vllm.entrypoints.cli.snapshot
from vllm.entrypoints.serve.utils.api_utils import (
VLLM_SUBCMD_PARSER_EPILOG,
cli_env_setup,
)
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG, cli_env_setup
from vllm.utils.argparse_utils import FlexibleArgumentParser
CMD_MODULES = [
+1 -1
View File
@@ -34,7 +34,7 @@ from fastapi.responses import Response
from vllm.engine.arg_utils import EngineArgs
from vllm.entrypoints.cli.types import CLISubcommand
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.logger import init_logger
from vllm.model_executor.model_loader.weight_cache.protocol import (
check_ipc_platform_support,
+1 -1
View File
@@ -7,7 +7,7 @@ import importlib.metadata
import typing
from vllm.entrypoints.cli.types import CLISubcommand
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.logger import init_logger
if typing.TYPE_CHECKING:
+3 -3
View File
@@ -10,15 +10,15 @@ import uvloop
import vllm
import vllm.envs as envs
from vllm.entrypoints.cli.types import CLISubcommand
from vllm.entrypoints.launchers.api_server.entry import run_server, setup_server
from vllm.entrypoints.launchers.cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
propagate_flash_late_interaction,
validate_parsed_serve_args,
)
from vllm.entrypoints.common.startup import VLLM_SUBCMD_PARSER_EPILOG
from vllm.entrypoints.launchers.api_server.entry import run_server, setup_server
from vllm.entrypoints.launchers.dp_supervisor import run_dp_supervisor
from vllm.entrypoints.launchers.launcher import create_server_socket
from vllm.entrypoints.serve.utils.api_utils import VLLM_SUBCMD_PARSER_EPILOG
from vllm.logger import init_logger
from vllm.reasoning import ReasoningParserManager
from vllm.usage.usage_lib import UsageContext
+1 -1
View File
@@ -114,7 +114,7 @@ class SnapshotSubcommand(CLISubcommand):
"create", help="Create a snapshot from an initialized TP1 engine."
)
if self.create_requested:
from vllm.entrypoints.launchers.cli_args import make_arg_parser
from vllm.entrypoints.common.cli_args import make_arg_parser
create_parser = make_arg_parser(create_parser)
else:
@@ -17,6 +17,10 @@ from vllm.engine.arg_utils import AsyncEngineArgs, optional_type
from vllm.entrypoints.anthropic.protocol import (
AnthropicDisabledThinkingEffortOption,
)
from vllm.entrypoints.launchers.utils.constants import (
H11_MAX_HEADER_COUNT_DEFAULT,
H11_MAX_INCOMPLETE_EVENT_SIZE_DEFAULT,
)
from vllm.entrypoints.openai.models.protocol import LoRAModulePath
from vllm.logger import init_logger
from vllm.renderers.chat_utils import (
@@ -27,11 +31,6 @@ from vllm.tool_parsers import ToolParserManager
from vllm.tool_parsers.tool_strict_level import ToolStrictLevelName
from vllm.utils.argparse_utils import FlexibleArgumentParser
from .utils.constants import (
H11_MAX_HEADER_COUNT_DEFAULT,
H11_MAX_INCOMPLETE_EVENT_SIZE_DEFAULT,
)
logger = init_logger(__name__)
@@ -571,3 +570,33 @@ def propagate_flash_late_interaction(args, engine_args) -> None:
engine_args.pooler_config = PoolerConfig(enable_flash_late_interaction=False)
else:
engine_args.pooler_config.enable_flash_late_interaction = False
def init_parser_plugin(args: argparse.Namespace):
from vllm.reasoning import ReasoningParserManager
if args.tool_parser_plugin and len(args.tool_parser_plugin) > 3:
ToolParserManager.import_tool_parser(args.tool_parser_plugin)
if args.reasoning_parser_plugin and len(args.reasoning_parser_plugin) > 3:
ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
def validate_api_server_args(args: argparse.Namespace):
from vllm.reasoning import ReasoningParserManager
valid_tool_parses = ToolParserManager.list_registered()
if args.enable_auto_tool_choice and args.tool_call_parser not in valid_tool_parses:
raise KeyError(
f"invalid tool call parser: {args.tool_call_parser} "
f"(chose from {{ {','.join(valid_tool_parses)} }})"
)
valid_reasoning_parsers = ReasoningParserManager.list_registered()
if (
reasoning_parser := args.structured_outputs_config.reasoning_parser
) and reasoning_parser not in valid_reasoning_parsers:
raise KeyError(
f"invalid reasoning parser: {reasoning_parser} "
f"(chose from {{ {','.join(valid_reasoning_parsers)} }})"
)
+149
View File
@@ -0,0 +1,149 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import dataclasses
import os
from argparse import Namespace
from logging import Logger
from string import Template
from typing import Any
from vllm import EngineArgs, envs
from vllm.logger import current_formatter_type, init_logger
logger = init_logger(__name__)
def log_version_and_model(lgr: Logger, version: str, model_name: str) -> None:
if envs.VLLM_DISABLE_LOG_LOGO or (formatter := current_formatter_type(lgr)) is None:
message = "vLLM server version %s, serving model %s"
else:
logo_template = Template(
"\n ${w}█ █ █▄ ▄█${r}\n"
" ${o}▄▄${r} ${b}▄█${r} ${w}█ █ █ ▀▄▀ █${r} version ${w}%s${r}\n"
" ${o}█${r}${b}▄█▀${r} ${w}█ █ █ █${r} model ${w}%s${r}\n"
" ${b}▀▀${r} ${w}▀▀▀▀▀ ▀▀▀▀▀ ▀ ▀${r}\n"
)
colors = {
"w": "\033[1m", # bold, default foreground
"o": "\033[93m", # orange
"b": "\033[94m", # blue
"r": "\033[0m", # reset
}
if formatter != "color":
# monochrome logo (no ansi escape codes)
colors = dict.fromkeys(colors, "")
message = logo_template.substitute(colors)
lgr.info(message, version, model_name)
VLLM_SUBCMD_PARSER_EPILOG = (
"For full list: vllm {subcmd} --help=all\n"
"For a section: vllm {subcmd} --help=ModelConfig (case-insensitive)\n" # noqa: E501
"For a flag: vllm {subcmd} --help=max-model-len (_ or - accepted)\n" # noqa: E501
"Documentation: https://docs.vllm.ai\n"
)
# Fields whose values must never be logged verbatim.
_SENSITIVE_ARG_FIELDS = frozenset({"api_key", "hf_token", "watermark_config"})
def redact_sensitive_args(args: dict[str, Any]) -> dict[str, Any]:
"""Return a copy of `args` with sensitive values redacted for logging."""
if not any(key in _SENSITIVE_ARG_FIELDS for key in args):
return args
return {
key: ("***" if key in _SENSITIVE_ARG_FIELDS else value)
for key, value in args.items()
}
def get_non_default_args(args: Namespace | EngineArgs) -> dict[str, Any]:
from vllm.entrypoints.common.cli_args import make_arg_parser
from vllm.utils.argparse_utils import FlexibleArgumentParser
non_default_args = {}
# Handle Namespace
if isinstance(args, Namespace):
parser = make_arg_parser(FlexibleArgumentParser())
for arg, default in vars(parser.parse_args([])).items():
if default != getattr(args, arg):
non_default_args[arg] = getattr(args, arg)
# Handle EngineArgs instance
elif isinstance(args, EngineArgs):
default_args = EngineArgs(model=args.model) # Create default instance
for field in dataclasses.fields(args):
current_val = getattr(args, field.name)
default_val = getattr(default_args, field.name)
if current_val != default_val:
non_default_args[field.name] = current_val
if default_args.model != EngineArgs.model:
non_default_args["model"] = default_args.model
else:
raise TypeError(
"Unsupported argument type. Must be Namespace or EngineArgs instance."
)
return non_default_args
def log_non_default_args(args: Namespace | EngineArgs):
non_default_args = get_non_default_args(args)
logger.info("non-default args: %s", redact_sensitive_args(non_default_args))
def _jsonify_arg_value(value: Any) -> Any:
if value is None or isinstance(value, bool | int | float | str):
return value
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return {
key: _jsonify_arg_value(val)
for key, val in dataclasses.asdict(value).items()
}
if isinstance(value, dict):
return {str(key): _jsonify_arg_value(val) for key, val in value.items()}
if isinstance(value, tuple | list):
return [_jsonify_arg_value(item) for item in value]
if (model_dump := getattr(value, "model_dump", None)) is not None:
return _jsonify_arg_value(model_dump(mode="json"))
if (to_dict := getattr(value, "dict", None)) is not None:
return _jsonify_arg_value(to_dict())
return repr(value)
def jsonify_non_default_args(
args: Namespace | EngineArgs,
*,
exclude: set[str] | None = None,
) -> dict[str, Any]:
non_default_args = get_non_default_args(args)
if exclude is not None:
for key in exclude:
non_default_args.pop(key, None)
return {key: _jsonify_arg_value(value) for key, value in non_default_args.items()}
def cli_env_setup():
# The safest multiprocessing method is `spawn`, as the default `fork` method
# is not compatible with some accelerators. The default method will be
# changing in future versions of Python, so we should use it explicitly when
# possible.
#
# We only set it here in the CLI entrypoint, because changing to `spawn`
# could break some existing code using vLLM as a library. `spawn` will cause
# unexpected behavior if the code is not protected by
# `if __name__ == "__main__":`.
#
# References:
# - https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods
# - https://pytorch.org/docs/stable/notes/multiprocessing.html#cuda-in-multiprocessing
# - https://pytorch.org/docs/stable/multiprocessing.html#sharing-cuda-tensors
# - https://docs.habana.ai/en/latest/PyTorch/Getting_Started_with_PyTorch_and_Gaudi/Getting_Started_with_PyTorch.html?highlight=multiprocessing#torch-multiprocessing-for-dataloaders
if "VLLM_WORKER_MULTIPROC_METHOD" not in os.environ:
logger.debug("Setting VLLM_WORKER_MULTIPROC_METHOD to 'spawn'")
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
@@ -7,7 +7,7 @@ from typing import cast
from starlette.datastructures import State
from vllm.engine.protocol import EngineClient
from vllm.entrypoints.launchers.cli_args import resolve_default_chat_template_kwargs
from vllm.entrypoints.common.cli_args import resolve_default_chat_template_kwargs
from vllm.entrypoints.mcp.tool_server import init_tool_server
from vllm.entrypoints.openai.models.protocol import BaseModelPath
from vllm.entrypoints.openai.models.serving import OpenAIServingModels
+6 -12
View File
@@ -15,9 +15,8 @@ from typing import Any
import vllm.envs as envs
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.protocol import EngineClient
from vllm.entrypoints.common.cli_args import init_parser_plugin
from vllm.logger import configure_logging_from_args, init_logger
from vllm.reasoning import ReasoningParserManager
from vllm.tool_parsers import ToolParserManager
from vllm.usage.usage_lib import UsageContext
from vllm.utils.system_utils import decorate_logs
@@ -51,7 +50,7 @@ async def build_async_engine_client(
# Context manager to handle engine_client lifecycle
# Ensures everything is shutdown and cleaned up on error/exit
engine_args = AsyncEngineArgs.from_cli_args(args)
from ..cli_args import propagate_flash_late_interaction
from vllm.entrypoints.common.cli_args import propagate_flash_late_interaction
propagate_flash_late_interaction(args, engine_args)
if client_config:
@@ -181,11 +180,7 @@ async def run_server_worker(
listen_address, sock, args, client_config=None, **uvicorn_kwargs
) -> None:
"""Run a single API server worker."""
if args.tool_parser_plugin and len(args.tool_parser_plugin) > 3:
ToolParserManager.import_tool_parser(args.tool_parser_plugin)
if args.reasoning_parser_plugin and len(args.reasoning_parser_plugin) > 3:
ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
init_parser_plugin(args)
peer_loads = None
if client_config and "api_server_loads" in client_config:
@@ -209,13 +204,12 @@ async def run_server_worker(
def main():
import uvloop
from vllm.entrypoints.serve.utils.api_utils import cli_env_setup
from vllm.utils.argparse_utils import FlexibleArgumentParser
from ..cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
validate_parsed_serve_args,
)
from vllm.entrypoints.common.startup import cli_env_setup
from vllm.utils.argparse_utils import FlexibleArgumentParser
# NOTE(simon):
# This section should be in sync with vllm/entrypoints/cli/main.py for CLI
+1 -1
View File
@@ -43,7 +43,7 @@ import uvloop
from vllm import envs
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.entrypoints.serve.utils.api_utils import log_version_and_model
from vllm.entrypoints.common.startup import log_version_and_model
from vllm.logger import configure_logging_from_args, init_logger
from vllm.usage.usage_lib import UsageContext
from vllm.utils.argparse_utils import FlexibleArgumentParser
+7 -29
View File
@@ -14,14 +14,16 @@ from fastapi import FastAPI
from vllm import envs
from vllm.engine.protocol import EngineClient
from vllm.entrypoints.launchers.utils.ssl import SSLCertRefresher
from vllm.entrypoints.serve.utils.api_utils import (
from vllm.entrypoints.common.cli_args import (
init_parser_plugin,
validate_api_server_args,
)
from vllm.entrypoints.common.startup import (
log_non_default_args,
log_version_and_model,
)
from vllm.entrypoints.launchers.utils.ssl import SSLCertRefresher
from vllm.logger import init_logger
from vllm.reasoning import ReasoningParserManager
from vllm.tool_parsers import ToolParserManager
from vllm.tracing import instrument
from vllm.utils.network_utils import find_process_using_port, is_valid_ipv6_address
from vllm.utils.system_utils import set_ulimit
@@ -359,36 +361,12 @@ def create_server_unix_socket(path: str) -> socket.socket:
return sock
def validate_api_server_args(args):
valid_tool_parses = ToolParserManager.list_registered()
if args.enable_auto_tool_choice and args.tool_call_parser not in valid_tool_parses:
raise KeyError(
f"invalid tool call parser: {args.tool_call_parser} "
f"(chose from {{ {','.join(valid_tool_parses)} }})"
)
valid_reasoning_parsers = ReasoningParserManager.list_registered()
if (
reasoning_parser := args.structured_outputs_config.reasoning_parser
) and reasoning_parser not in valid_reasoning_parsers:
raise KeyError(
f"invalid reasoning parser: {reasoning_parser} "
f"(chose from {{ {','.join(valid_reasoning_parsers)} }})"
)
@instrument(span_name="API server setup")
def setup_server(args, *, reuse_port: bool):
"""Validate API server args and create the server socket."""
log_version_and_model(logger, VLLM_VERSION, args.model)
log_non_default_args(args)
if args.tool_parser_plugin and len(args.tool_parser_plugin) > 3:
ToolParserManager.import_tool_parser(args.tool_parser_plugin)
if args.reasoning_parser_plugin and len(args.reasoning_parser_plugin) > 3:
ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
init_parser_plugin(args)
validate_api_server_args(args)
# workaround to make sure that we bind the port before the engine is set up.
@@ -5,7 +5,7 @@ from argparse import Namespace
from starlette.datastructures import State
from vllm.config import VllmConfig
from vllm.entrypoints.launchers.cli_args import resolve_default_chat_template_kwargs
from vllm.entrypoints.common.cli_args import resolve_default_chat_template_kwargs
from vllm.entrypoints.mcp.tool_server import init_tool_server
from vllm.entrypoints.openai.models.protocol import BaseModelPath
from vllm.entrypoints.openai.models.serving import OpenAIModelRegistry
+3 -4
View File
@@ -108,13 +108,12 @@ async def run_launch_fastapi(args: argparse.Namespace) -> None:
if __name__ == "__main__":
import uvloop
from vllm.entrypoints.serve.utils.api_utils import cli_env_setup
from vllm.utils.argparse_utils import FlexibleArgumentParser
from ..cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
validate_parsed_serve_args,
)
from vllm.entrypoints.common.startup import cli_env_setup
from vllm.utils.argparse_utils import FlexibleArgumentParser
cli_env_setup()
parser = FlexibleArgumentParser(
+1 -1
View File
@@ -31,6 +31,7 @@ from vllm.config import config
from vllm.connections import HTTPResponseSizeExceededError, global_http_connection
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.protocol import EngineClient
from vllm.entrypoints.common.cli_args import BaseFrontendArgs
from vllm.entrypoints.openai.chat_completion.protocol import (
ChatCompletionRequest,
ChatCompletionResponse,
@@ -69,7 +70,6 @@ from vllm.utils.mem_constants import MiB_bytes
from vllm.version import __version__ as VLLM_VERSION
from .api_server.entry import init_app_state
from .cli_args import BaseFrontendArgs
logger = init_logger(__name__)
+2 -3
View File
@@ -29,16 +29,17 @@ from vllm.config.model import (
from vllm.config.quantization import QuantizationConfigArgs
from vllm.engine.arg_utils import EngineArgs
from vllm.entrypoints.common.offline import _O, _R, OfflineInferenceMixin
from vllm.entrypoints.common.startup import log_non_default_args
from vllm.entrypoints.generate.beam_search.offline import BeamSearchOfflineMixin
from vllm.entrypoints.pooling.offline import PoolingOfflineMixin
from vllm.entrypoints.rl.offline import RLOfflineMixin
from vllm.entrypoints.serve.utils.api_utils import log_non_default_args
from vllm.inputs import PromptType
from vllm.logger import configure_logging_if_needed, init_logger
from vllm.lora.request import LoRARequest
from vllm.model_executor.layers.quantization import QuantizationMethods
from vllm.outputs import PoolingRequestOutput, RequestOutput
from vllm.platforms import current_platform
from vllm.renderers import ChatParams
from vllm.renderers.chat_utils import (
ChatCompletionMessageParam,
ChatTemplateContentFormatOption,
@@ -52,8 +53,6 @@ from vllm.v1.engine import PauseMode
from vllm.v1.engine.llm_engine import LLMEngine
from vllm.v1.sample.logits_processor import LogitsProcessor
from ..renderers import ChatParams
if TYPE_CHECKING:
from vllm.v1.metrics.reader import Metric
+1 -143
View File
@@ -2,13 +2,8 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import asyncio
import dataclasses
import functools
import os
from argparse import Namespace
from http import HTTPStatus
from logging import Logger
from string import Template
from typing import Any
from fastapi import Request
@@ -17,23 +12,13 @@ from fastapi.responses import JSONResponse, Response, StreamingResponse
from pydantic import BaseModel
from starlette.background import BackgroundTask, BackgroundTasks
from vllm import envs
from vllm.engine.arg_utils import EngineArgs
from vllm.entrypoints.generate.base.protocol import StreamOptions
from vllm.entrypoints.openai.models.protocol import LoRAModulePath
from vllm.logger import current_formatter_type, init_logger
from vllm.logger import init_logger
from vllm.platforms import current_platform
from vllm.utils.argparse_utils import FlexibleArgumentParser
logger = init_logger(__name__)
VLLM_SUBCMD_PARSER_EPILOG = (
"For full list: vllm {subcmd} --help=all\n"
"For a section: vllm {subcmd} --help=ModelConfig (case-insensitive)\n" # noqa: E501
"For a flag: vllm {subcmd} --help=max-model-len (_ or - accepted)\n" # noqa: E501
"Documentation: https://docs.vllm.ai\n"
)
async def listen_for_disconnect(request: Request) -> None:
"""Returns if a disconnect message is received."""
@@ -216,27 +201,6 @@ def load_aware_call(func):
return wrapper
def cli_env_setup():
# The safest multiprocessing method is `spawn`, as the default `fork` method
# is not compatible with some accelerators. The default method will be
# changing in future versions of Python, so we should use it explicitly when
# possible.
#
# We only set it here in the CLI entrypoint, because changing to `spawn`
# could break some existing code using vLLM as a library. `spawn` will cause
# unexpected behavior if the code is not protected by
# `if __name__ == "__main__":`.
#
# References:
# - https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods
# - https://pytorch.org/docs/stable/notes/multiprocessing.html#cuda-in-multiprocessing
# - https://pytorch.org/docs/stable/multiprocessing.html#sharing-cuda-tensors
# - https://docs.habana.ai/en/latest/PyTorch/Getting_Started_with_PyTorch_and_Gaudi/Getting_Started_with_PyTorch.html?highlight=multiprocessing#torch-multiprocessing-for-dataloaders
if "VLLM_WORKER_MULTIPROC_METHOD" not in os.environ:
logger.debug("Setting VLLM_WORKER_MULTIPROC_METHOD to 'spawn'")
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
def get_max_tokens(
max_model_len: int,
max_tokens: int | None,
@@ -276,87 +240,6 @@ def get_max_tokens(
)
def get_non_default_args(args: Namespace | EngineArgs) -> dict[str, Any]:
from vllm.entrypoints.launchers.cli_args import make_arg_parser
non_default_args = {}
# Handle Namespace
if isinstance(args, Namespace):
parser = make_arg_parser(FlexibleArgumentParser())
for arg, default in vars(parser.parse_args([])).items():
if default != getattr(args, arg):
non_default_args[arg] = getattr(args, arg)
# Handle EngineArgs instance
elif isinstance(args, EngineArgs):
default_args = EngineArgs(model=args.model) # Create default instance
for field in dataclasses.fields(args):
current_val = getattr(args, field.name)
default_val = getattr(default_args, field.name)
if current_val != default_val:
non_default_args[field.name] = current_val
if default_args.model != EngineArgs.model:
non_default_args["model"] = default_args.model
else:
raise TypeError(
"Unsupported argument type. Must be Namespace or EngineArgs instance."
)
return non_default_args
def _jsonify_arg_value(value: Any) -> Any:
if value is None or isinstance(value, bool | int | float | str):
return value
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return {
key: _jsonify_arg_value(val)
for key, val in dataclasses.asdict(value).items()
}
if isinstance(value, dict):
return {str(key): _jsonify_arg_value(val) for key, val in value.items()}
if isinstance(value, tuple | list):
return [_jsonify_arg_value(item) for item in value]
if (model_dump := getattr(value, "model_dump", None)) is not None:
return _jsonify_arg_value(model_dump(mode="json"))
if (to_dict := getattr(value, "dict", None)) is not None:
return _jsonify_arg_value(to_dict())
return repr(value)
def jsonify_non_default_args(
args: Namespace | EngineArgs,
*,
exclude: set[str] | None = None,
) -> dict[str, Any]:
non_default_args = get_non_default_args(args)
if exclude is not None:
for key in exclude:
non_default_args.pop(key, None)
return {key: _jsonify_arg_value(value) for key, value in non_default_args.items()}
# Fields whose values must never be logged verbatim.
_SENSITIVE_ARG_FIELDS = frozenset({"api_key", "hf_token", "watermark_config"})
def redact_sensitive_args(args: dict[str, Any]) -> dict[str, Any]:
"""Return a copy of `args` with sensitive values redacted for logging."""
if not any(key in _SENSITIVE_ARG_FIELDS for key in args):
return args
return {
key: ("***" if key in _SENSITIVE_ARG_FIELDS else value)
for key, value in args.items()
}
def log_non_default_args(args: Namespace | EngineArgs):
non_default_args = get_non_default_args(args)
logger.info("non-default args: %s", redact_sensitive_args(non_default_args))
def should_include_usage(
stream_options: StreamOptions | None, enable_force_include_usage: bool
) -> tuple[bool, bool]:
@@ -393,31 +276,6 @@ def process_lora_modules(
return lora_modules
def log_version_and_model(lgr: Logger, version: str, model_name: str) -> None:
if envs.VLLM_DISABLE_LOG_LOGO or (formatter := current_formatter_type(lgr)) is None:
message = "vLLM server version %s, serving model %s"
else:
logo_template = Template(
"\n ${w}█ █ █▄ ▄█${r}\n"
" ${o}▄▄${r} ${b}▄█${r} ${w}█ █ █ ▀▄▀ █${r} version ${w}%s${r}\n"
" ${o}█${r}${b}▄█▀${r} ${w}█ █ █ █${r} model ${w}%s${r}\n"
" ${b}▀▀${r} ${w}▀▀▀▀▀ ▀▀▀▀▀ ▀ ▀${r}\n"
)
colors = {
"w": "\033[1m", # bold, default foreground
"o": "\033[93m", # orange
"b": "\033[94m", # blue
"r": "\033[0m", # reset
}
if formatter != "color":
# monochrome logo (no ansi escape codes)
colors = dict.fromkeys(colors, "")
message = logo_template.substitute(colors)
lgr.info(message, version, model_name)
async def validate_json_request(raw_request: Request):
content_type = raw_request.headers.get("content-type", "").lower()
media_type = content_type.split(";", maxsplit=1)[0]
+1 -1
View File
@@ -176,7 +176,7 @@ async def wait_for_release_marker(
def parse_vllm_args(argv: list[str]) -> Any:
from vllm.entrypoints.launchers.cli_args import (
from vllm.entrypoints.common.cli_args import (
make_arg_parser,
validate_parsed_serve_args,
)
+2 -2
View File
@@ -331,7 +331,7 @@ class RustFrontendProcessManager:
inherited_fds.append(grpc_fd)
if stats_update_address is not None:
cmd.extend(["--coordinator-address", stats_update_address])
from vllm.entrypoints.serve.utils.api_utils import jsonify_non_default_args
from vllm.entrypoints.common.startup import jsonify_non_default_args
args_dict = jsonify_non_default_args(
args,
@@ -366,7 +366,7 @@ class RustFrontendProcessManager:
# The subprocess needs the real values, but the log must not carry
# credentials such as api_key or hf_token.
from vllm.entrypoints.serve.utils.api_utils import redact_sensitive_args
from vllm.entrypoints.common.startup import redact_sensitive_args
redacted_json = json.dumps(redact_sensitive_args(args_dict), sort_keys=True)
logger.info("Launching Rust frontend: %s", " ".join(cmd[:-1] + [redacted_json]))