目录

0 引言

1 先大体全局搜一下

2 怎么增加添加一个命令行参数:以--all2all-backend为例

2.1 第 1 步:在 ParallelConfig 里定义字段      

2.2 第 2 步:在 EngineArgs 里镜像字段

2.3 第 3 步:注册命令行参数

2.4 第 4 步:把 CLI 值写进 ParallelConfig

3 怎么从main.py一层层下来的

3.1 vllm018/vllm/entrypoints/cli/main.py

3.2 vllm018/vllm/entrypoints/cli/serve.py

3.3 vllm018/vllm/entrypoints/openai/api_server.py

4 函数调用汇总

5 命令行可以用空格也可以用等号,可以用下划线_也可以用-

​编辑

6 参考文献


abstract
CLI Command Line Interface
@dataclass 自动生成初始化和 __repr__函数,
若定义了__post_init__,自动调用__post_init__函数,
repr(representation)就是用字符串表示对象的类名和成员
literal 字面量

CLI 值先进 EngineArgs,再写进 ParallelConfig,再装进 VllmConfig
EngineArgs, ParallelConfig, VllmConfig

all2all_backend: All2AllBackend = ParallelConfig.all2all_backend     变量名 类型 默认值

星号解析列表或元组,星星解析词典key=value

0 引言

工作中需要将之前用环境变量设置的变量,改为用命令行传输,借此机会,梳理下怎么添加一个参数,以及这个参数的从上到下传递链路。

1 先大体全局搜一下

然后先把搜索结果过一遍,基本上 就能有个大概了,

2 怎么增加添加一个命令行参数:以--all2all-backend为例

2.1 第 1 步:在 ParallelConfig 里定义字段      

vllm021/vllm/config/parallel.py

先加类型别名(可选但推荐):

All2AllBackend = Literal[
    "naive",
    "pplx",
    "deepep_high_throughput",
    "deepep_low_latency",
    "mori",
    "nixl_ep",
    "allgather_reducescatter",
    "flashinfer_all2allv",  # temporary alias for flashinfer_nvlink_two_sided
    "flashinfer_nvlink_two_sided",
    "flashinfer_nvlink_one_sided",
]

然后还是当前文件,class ParallelConfig:类中添加字段和字段说明

    all2all_backend: All2AllBackend = "allgather_reducescatter"
    """All2All backend for MoE expert parallel communication. Available options:

    - "allgather_reducescatter": All2all based on allgather and reducescatter
    - "deepep_high_throughput": Use deepep high-throughput kernels
    - "deepep_low_latency": Use deepep low-latency kernels
    - "mori": Use mori kernels
    - "nixl_ep": Use nixl-ep kernels
    - "flashinfer_nvlink_two_sided": Use flashinfer two-sided kernels for mnnvl
    - "flashinfer_nvlink_one_sided": Use flashinfer high-throughput a2a kernels"""

2.2 第 2 步:在 EngineArgs 里镜像字段

文件:vllm021/vllm/engine/arg_utils.py
位置:class EngineArgs 成员声明处(现约 480 行)

前面已经在parallelconfig类中添加这个参数了,这里为什么在EngineArgs类中又添加了一个同名字段,

为什么要“镜像”一份,而不是 CLI 直接改 ParallelConfig?因为链路是分层的:

命令行 args
    → 先填进扁平的 EngineArgs(第 2、3 步 + from_cli_args)
    → 再在 create_engine_config 里拆开,分别构造
         ModelConfig / ParallelConfig / CacheConfig / ...
    → 最后装进 VllmConfig

engineargs其实就是暂存 解析出来的命令行参数的,解析出来之后再给到parallelconfig

2.3 第 3 步:注册命令行参数

文件:还是 arg_utils.py
位置:EngineArgs.add_cli_args 里(现约 1047–1049 行)

前面已有:

parallel_kwargs = get_kwargs(ParallelConfig)

再加:

parallel_group.add_argument(
    "--all2all-backend", **parallel_kwargs["all2all_backend"]
)

这里 parallel_kwargs = get_kwargs(ParallelConfig)这里相当于把parallel里面的所有的变量全都解析了一遍,然后得到一个词典,里面的key就是变量名,value就是defular, choices, type, help信息

大致是

{
  "all2all_backend": {
      "default": "allgather_reducescatter",
      "choices": [...],
      "type": str,
      "help": "...",   # 只有 --help 时通常才有内容
  },
  "tensor_parallel_size": { ... },
  "enable_expert_parallel": { ... },
  ...
}

然后下面

parallel_group.add_argument(
    "--all2all-backend", **parallel_kwargs["all2all_backend"]
)

就是类似于

# 等价于手写一长串,只是帮你省事
parallel_group.add_argument(
    "--all2all-backend",
    default="allgather_reducescatter",
    choices=[...],
    type=str,
    help="...",
)

2.4 第 4 步:把 CLI 值写进 ParallelConfig

文件:还是 arg_utils.py
位置:class EngineArgs:的create_engine_config函数里面,构造 ParallelConfig(...) 时(现约 1934 行)

parallel_config = ParallelConfig(
    ...
    all2all_backend=self.all2all_backend,
    ...
)

3 怎么从main.py一层层下来的

前面总结的是如果开发代码,增加一个 命令行参数,需要修改哪些代码,接下来看一下,增加完之后,真正使用的时候,一个命令行参数是怎么一步步传下来的。

3.1 vllm018/vllm/entrypoints/cli/main.py


    parser = FlexibleArgumentParser(
        description="vLLM CLI",
        epilog=VLLM_SUBCMD_PARSER_EPILOG.format(subcmd="[subcommand]"),
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=importlib.metadata.version("vllm"),
    )
    subparsers = parser.add_subparsers(required=False, dest="subparser")
    cmds = {}
    for cmd_module in CMD_MODULES:
        new_cmds = cmd_module.cmd_init()
        for cmd in new_cmds:
            cmd.subparser_init(subparsers).set_defaults(dispatch_function=cmd.cmd)
            cmds[cmd.name] = cmd
    args = parser.parse_args()
    if args.subparser in cmds:
        cmds[args.subparser].validate(args)

    if hasattr(args, "dispatch_function"):
        args.dispatch_function(args)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

这里有个args = parser.parse_args(),就相当于是解析了命令行参数。
然后有个 args.dispatch_function(args),这就是去了相应的function,这里
dispatch_function 是在注册子命令时绑定的。main.py 68 行:
cmd.subparser_init(subparsers).set_defaults(dispatch_function=cmd.cmd)
执行的是 vllm serve ... 时,args.subparser == "serve",
所以:args.dispatch_function  ==  ServeSubcommand.cmd
也就是 serve.py 里的 cmd 函数。
那么就去了serve.py

3.2 vllm018/vllm/entrypoints/cli/serve.py

在这个文件

class ServeSubcommand(CLISubcommand):
    """The `serve` subcommand for the vLLM CLI."""

    name = "serve"

    @staticmethod
    def cmd(args: argparse.Namespace) -> None:

这里往下

       if args.api_server_count < 1:
            run_headless(args)
        elif args.api_server_count > 1:
            run_multi_api_server(args)
        else:
            # Single API server (this process).
            args.api_server_count = None
            uvloop.run(run_server(args))

然后就去了vllm018/vllm/entrypoints/openai/api_server.py里面的run_server

3.3 vllm018/vllm/entrypoints/openai/api_server.py

这里去了670行的这个函数,那么

然后就到了前面分析的vllm018/vllm/engine/arg_utils.py里面的**from_cli_args**函数和**create_engine_config**函数了。也即是获取参数,以及构造了vllmconfig,

4 函数调用汇总

vllm serve(命令行)
│
├─ vllm018/vllm/entrypoints/cli/main.py:53
│    parser = FlexibleArgumentParser(...)
│
├─ vllm018/vllm/entrypoints/cli/main.py:68
│    cmd.subparser_init(subparsers).set_defaults(dispatch_function=cmd.cmd)
│    (vllm serve 时 → dispatch_function = ServeSubcommand.cmd)
│
├─ vllm018/vllm/entrypoints/cli/main.py:70
│    args = parser.parse_args()
│    └─ 得到 args(含 args.all2all_backend 等)
│       (内部:utils/argparse_utils.py:179 FlexibleArgumentParser.parse_args
│         → :360 super().parse_args())
│
├─ vllm018/vllm/entrypoints/cli/main.py:72
│    cmds[args.subparser].validate(args)
│    └─ serve 时 → entrypoints/cli/serve.py:120 validate_parsed_serve_args
│
└─ vllm018/vllm/entrypoints/cli/main.py:75
     args.dispatch_function(args)
     │
     └─ vllm018/vllm/entrypoints/cli/serve.py:48
          ServeSubcommand.cmd(args)
          │
          └─ vllm018/vllm/entrypoints/cli/serve.py:118   【单 API 常见分支】
               uvloop.run(run_server(args))
               │
               └─ vllm018/vllm/entrypoints/openai/api_server.py:649
                    async def run_server(args, **uvicorn_kwargs)
                    │
                    └─ vllm018/vllm/entrypoints/openai/api_server.py:656
                         await run_server_worker(listen_address, sock, args, ...)
                         │
                         └─ vllm018/vllm/entrypoints/openai/api_server.py:659
                              async def run_server_worker(...)
                              │
                              └─ vllm018/vllm/entrypoints/openai/api_server.py:670
                                   async with build_async_engine_client(args, ...)
                                   │
                                   └─ vllm018/vllm/entrypoints/openai/api_server.py:77
                                        async def build_async_engine_client(args, ...)
                                        │
                                        ├─ vllm018/vllm/entrypoints/openai/api_server.py:95
                                        │    engine_args = AsyncEngineArgs.from_cli_args(args)
                                        │    │
                                        │    └─ vllm018/vllm/engine/arg_utils.py:1337
                                        │         def from_cli_args(cls, args)   # 1337–1344
                                        │         └─ 得到 EngineArgs(含 all2all_backend)
                                        │
                                        └─ vllm018/vllm/entrypoints/openai/api_server.py:103
                                             async with build_async_engine_client_from_engine_args(engine_args, ...)
                                             │
                                             └─ vllm018/vllm/entrypoints/openai/api_server.py:113
                                                  async def build_async_engine_client_from_engine_args(engine_args, ...)
                                                  │
                                                  └─ vllm018/vllm/entrypoints/openai/api_server.py:129
                                                       vllm_config = engine_args.create_engine_config(...)
                                                       │
                                                       └─ vllm018/vllm/engine/arg_utils.py:1476
                                                            def create_engine_config(self, ...) -> VllmConfig
                                                            │
                                                            ├─ vllm018/vllm/engine/arg_utils.py:1716
                                                            │    parallel_config = ParallelConfig(
                                                            │        ...,
                                                            │        all2all_backend=self.all2all_backend,  # 约 1736
                                                            │        ...
                                                            │    )
                                                            │    └─ vllm018/vllm/config/parallel.py:161 字段定义
                                                            │         └─ __post_init__ 校验(HCU patch 可能 env 覆盖)
                                                            │
                                                            └─ vllm018/vllm/engine/arg_utils.py:1923
                                                                 config = VllmConfig(
                                                                     ...,
                                                                     parallel_config=parallel_config,
                                                                     ...
                                                                 )
                                                                 └─ 总配置 VllmConfig 就绪

5 命令行可以用空格也可以用等号,可以用下划线_也可以用-

从下面的这段代码可以看出来,这里我们可以用下面的四种写法都是可以的

  • --all2all_backend deepep_high_throughput
  • --all2all_backend=deepep_high_throughput
  • --all2all-backend deepep_high_throughput
  • --all2all-backend=deepep_high_throughput

其中,假如使我们写了_,那么会被替换成-,就是下图中的repl函数,

然后下面的if else分支可以看出来,他既支持空格,也支持等号,但是注意这里的if lese其实只是把_转成-,并没有处理参数,这里如果是有等号,那是把_改成-,如果是空格是把key的_改成-,修改完之后,本来空格的还是空格,本来等号的还是等号,至于根据等号和空格进行解析,不是这个函数的工作,是在其他地方解析的,

6 参考文献

VLLM开源代码:https://github.com/vllm-project/vllm

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐