[Rust Frontend] Keep `--max-model-len` engine-owned (#49944)

Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Bugen Zhao <i@bugenzhao.com>

Bugen Zhao committed Jul 27, 2026 at 14:50 UTC fd9d2ede6fa05313e4a7482c5edb88bf8c565230
3 files changed +63 -17
rust/src/cmd/src/cli.rs
-6
@@ -148,11 +148,6 @@ pub struct SharedRuntimeArgs {
148 #[arg(long)]
149 #[serde(default)]
150 pub language_model_only: bool,
151 - /// Override the maximum model context length. When set, the frontend uses
152 - /// this value instead of the model's `max_position_embeddings` from
153 - /// `config.json`.
154 - #[arg(long)]
155 - pub max_model_len: Option<u32>,
151 /// Maximum number of log probabilities to return when `logprobs` is
152 /// specified in sampling parameters. `-1` means no cap.
153 #[arg(long, value_parser = clap::value_parser!(i32).range(-1..), allow_negative_numbers = true)]
@@ -664,7 +659,6 @@ impl ServeArgs {
659
660 self.managed_engine.clone().into_config(
661 self.runtime.model.clone(),
667 - self.runtime.max_model_len,
662 self.runtime.max_logprobs,
663 profiler_config,
664 reasoning_parser.as_deref(),
rust/src/cmd/src/cli/tests.rs
+55 -8
@@ -58,9 +58,6 @@ fn serve_args_forward_python_flags_with_separator() {
58 reasoning_parser: Auto,
59 renderer: Auto,
60 language_model_only: false,
61 - max_model_len: Some(
62 - 512,
63 - ),
61 max_logprobs: None,
62 grpc_port: None,
63 shutdown_timeout: 0,
@@ -102,6 +99,9 @@ fn serve_args_forward_python_flags_with_separator() {
99 handshake_port: None,
100 data_parallel_size: 1,
101 data_parallel_size_local: None,
102 + max_model_len: Some(
103 + "512",
104 + ),
105 python_args: [
106 "--dtype",
107 "float16",
@@ -756,7 +756,6 @@ fn frontend_args_accept_json() {
756 reasoning_parser: None,
757 renderer: Auto,
758 language_model_only: false,
759 - max_model_len: None,
759 max_logprobs: None,
760 grpc_port: None,
761 shutdown_timeout: 0,
@@ -823,11 +822,32 @@ fn frontend_args_json_applies_defaults() {
822 assert_eq!(args.runtime.tool_call_parser, ParserSelection::None);
823 assert_eq!(args.runtime.reasoning_parser, ParserSelection::None);
824 assert_eq!(args.runtime.renderer, RendererSelection::Auto);
826 - assert_eq!(args.runtime.max_model_len, None);
825 assert_eq!(args.runtime.max_logprobs, None);
826 assert_eq!(args.runtime.shutdown_timeout, 0);
827 }
828
829 +#[test]
830 +fn frontend_args_json_ignores_engine_owned_max_model_len() {
831 + let cli = Cli::try_parse_from([
832 + "vllm-rs",
833 + "frontend",
834 + "--listen-fd",
835 + "3",
836 + "--input-address",
837 + "ipc:///tmp/input.sock",
838 + "--output-address",
839 + "ipc:///tmp/output.sock",
840 + "--args-json",
841 + r#"{"model_tag":"Qwen/Qwen3-0.6B","max_model_len":-1}"#,
842 + ])
843 + .unwrap();
844 +
845 + let Command::Frontend(args) = cli.command else {
846 + panic!("expected frontend args");
847 + };
848 + assert_eq!(args.runtime.model, "Qwen/Qwen3-0.6B");
849 +}
850 +
851 #[test]
852 fn frontend_args_json_accepts_supported_non_default_fields() {
853 let cli = Cli::try_parse_from([
@@ -840,7 +860,7 @@ fn frontend_args_json_accepts_supported_non_default_fields() {
860 "--output-address",
861 "ipc:///tmp/output.sock",
862 "--args-json",
843 - r#"{"model_tag":"Qwen/Qwen3-0.6B","engine_ready_timeout_secs":42,"tool_call_parser":"hermes","reasoning_parser":"qwen3_thinking","tokenizer_mode":"deepseek_v32","language_model_only":true,"max_model_len":8192,"max_logprobs":-1,"shutdown_timeout":3}"#,
863 + r#"{"model_tag":"Qwen/Qwen3-0.6B","engine_ready_timeout_secs":42,"tool_call_parser":"hermes","reasoning_parser":"qwen3_thinking","tokenizer_mode":"deepseek_v32","language_model_only":true,"max_logprobs":-1,"shutdown_timeout":3}"#,
864 ])
865 .unwrap();
866
@@ -858,11 +878,38 @@ fn frontend_args_json_accepts_supported_non_default_fields() {
878 );
879 assert_eq!(args.runtime.renderer, RendererSelection::DeepSeekV32);
880 assert!(args.runtime.language_model_only);
861 - assert_eq!(args.runtime.max_model_len, Some(8192));
881 assert_eq!(args.runtime.max_logprobs, Some(-1));
882 assert_eq!(args.runtime.shutdown_timeout, 3);
883 }
884
885 +#[test]
886 +fn serve_args_forward_auto_max_model_len_to_managed_engine() {
887 + let cli = Cli::try_parse_from([
888 + "vllm-rs",
889 + "serve",
890 + "Qwen/Qwen3-0.6B",
891 + "--max-model-len",
892 + "auto",
893 + ])
894 + .unwrap();
895 +
896 + let Command::Serve(args) = cli.command else {
897 + panic!("expected serve args");
898 + };
899 + assert_eq!(args.managed_engine.max_model_len.as_deref(), Some("auto"));
900 +
901 + let config = args.to_managed_engine_config(5555);
902 + expect![[r#"
903 + [
904 + "--max-model-len",
905 + "auto",
906 + "--reasoning-parser",
907 + "qwen3",
908 + ]
909 + "#]]
910 + .assert_debug_eq(&config.python_args);
911 +}
912 +
913 #[test]
914 fn serve_args_accept_none_reasoning_parser() {
915 let cli = Cli::try_parse_from([
@@ -1278,7 +1325,6 @@ fn serve_args_accept_handshake_aliases() {
1325 reasoning_parser: Auto,
1326 renderer: Auto,
1327 language_model_only: false,
1281 - max_model_len: None,
1328 max_logprobs: None,
1329 grpc_port: None,
1330 shutdown_timeout: 0,
@@ -1322,6 +1368,7 @@ fn serve_args_accept_handshake_aliases() {
1368 ),
1369 data_parallel_size: 4,
1370 data_parallel_size_local: None,
1371 + max_model_len: None,
1372 python_args: [],
1373 },
1374 },
rust/src/managed-engine/src/cli.rs
+8 -3
@@ -39,6 +39,12 @@ pub struct ManagedEngineArgs {
39 /// Number of data parallel replicas to run on this node.
40 #[arg(long)]
41 pub data_parallel_size_local: Option<usize>,
42 + /// Maximum model context length forwarded to the managed Python engine.
43 + ///
44 + /// Rust leaves validation to Python so values such as `auto` and
45 + /// human-readable integers retain their engine-owned semantics.
46 + #[arg(long)]
47 + pub max_model_len: Option<String>,
48
49 /// Additional arguments forwarded to `python -m vllm.entrypoints.cli.main
50 /// serve ...`.
@@ -78,7 +84,6 @@ impl ManagedEngineArgs {
84 pub fn into_config(
85 self,
86 model: String,
81 - max_model_len: Option<u32>,
87 max_logprobs: Option<i32>,
88 profiler_config: Option<String>,
89 reasoning_parser: Option<&str>,
@@ -89,9 +94,9 @@ impl ManagedEngineArgs {
94 ) -> ManagedEngineConfig {
95 let mut python_args = self.python_args;
96 // Manually forward some args to the Python engine.
92 - if let Some(max_model_len) = max_model_len {
97 + if let Some(max_model_len) = self.max_model_len {
98 python_args.push("--max-model-len".to_string());
94 - python_args.push(max_model_len.to_string());
99 + python_args.push(max_model_len);
100 }
101 if let Some(max_logprobs) = max_logprobs {
102 python_args.push("--max-logprobs".to_string());