main
rs 119 lines 3.67 KB
Raw
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 //! Flag parity with the Python `vllm bench serve` parser.
5 //!
6 //! `VLLM_USE_RUST_BENCH=1` makes `vllm bench serve` exec this crate's binary
7 //! with the raw argv, so every documented Python flag must either parse here
8 //! or be a deliberate, allowlisted gap. The snapshot is kept current by
9 //! `tests/benchmarks/test_rust_bench_cli_parity.py` in the repo root.
10
11 use std::collections::HashSet;
12
13 use clap::{CommandFactory, Parser};
14 use vllm_bench::BenchServeArgs;
15
16 #[derive(Parser)]
17 struct TestCli {
18 #[command(flatten)]
19 args: BenchServeArgs,
20 }
21
22 const PYTHON_FLAGS: &str = include_str!("python_serve_flags.txt");
23
24 /// Python-only flags: `vllm bench serve` features vllm-bench deliberately does
25 /// not implement. Adding an entry is a decision to let the flag fail under
26 /// Rust delegation; remove the entry once the flag gains Rust support.
27 const PYTHON_ONLY: &[&str] = &[
28 // asr dataset
29 "--asr-max-audio-len-sec",
30 "--asr-min-audio-len-sec",
31 // bfcl dataset
32 "--bfcl-categories",
33 // blazedit dataset
34 "--blazedit-max-distance",
35 "--blazedit-min-distance",
36 // spec_bench dataset
37 "--spec-bench-category",
38 "--spec-bench-output-len",
39 // timed-trace dataset
40 "--timed-trace-chunk-hash-size",
41 "--timed-trace-label-hash-ids",
42 "--timed-trace-label-input-length",
43 "--timed-trace-label-output-length",
44 "--timed-trace-label-timestamp",
45 "--timed-trace-sec-multiplier",
46 // client-side chat templating / request shaping
47 "--chat-template-kwargs",
48 "--custom-ensure-client-side-data",
49 "--use-beam-search",
50 // result post-processing and plotting
51 "--plot-dataset-stats",
52 "--plot-timeline",
53 "--timeline-itl-thresholds",
54 // misc python-side controls
55 "--hf-name",
56 "--no-self-timed",
57 "--self-timed",
58 "--no-stream",
59 "--probe-request-rate",
60 ];
61
62 fn rust_flags() -> HashSet<String> {
63 let cmd = TestCli::command();
64 let mut flags = HashSet::new();
65 for arg in cmd.get_arguments() {
66 if let Some(long) = arg.get_long() {
67 flags.insert(format!("--{long}"));
68 }
69 if let Some(aliases) = arg.get_all_aliases() {
70 for alias in aliases {
71 flags.insert(format!("--{alias}"));
72 }
73 }
74 }
75 flags
76 }
77
78 fn python_flags() -> Vec<&'static str> {
79 PYTHON_FLAGS
80 .lines()
81 .map(str::trim)
82 .filter(|line| !line.is_empty() && !line.starts_with('#'))
83 .collect()
84 }
85
86 #[test]
87 fn python_serve_flags_parse_or_are_allowlisted() {
88 let known = rust_flags();
89 let missing: Vec<&str> = python_flags()
90 .into_iter()
91 .filter(|flag| !known.contains(*flag) && !PYTHON_ONLY.contains(flag))
92 .collect();
93 assert!(
94 missing.is_empty(),
95 "Python `vllm bench serve` flags unknown to vllm-bench: {missing:?}. \
96 Support them in src/cli.rs (a clap alias is enough for renames) or \
97 add them to PYTHON_ONLY above as a deliberate gap."
98 );
99 }
100
101 #[test]
102 fn python_only_allowlist_is_not_stale() {
103 let known = rust_flags();
104 let stale: Vec<&&str> = PYTHON_ONLY.iter().filter(|f| known.contains(**f)).collect();
105 assert!(
106 stale.is_empty(),
107 "PYTHON_ONLY entries now supported by vllm-bench, remove them: {stale:?}"
108 );
109 }
110
111 #[test]
112 fn python_only_entries_exist_in_snapshot() {
113 let snapshot: HashSet<&str> = python_flags().into_iter().collect();
114 let gone: Vec<&&str> = PYTHON_ONLY.iter().filter(|f| !snapshot.contains(**f)).collect();
115 assert!(
116 gone.is_empty(),
117 "PYTHON_ONLY entries no longer exist in the Python parser, remove them: {gone:?}"
118 );
119 }