From a5003c0c6a0d557b0e3716813fc7bb2702ff6546 Mon Sep 17 00:00:00 2001 From: Roman Sirokov Date: Mon, 24 Aug 2026 10:42:52 +0000 Subject: [PATCH] fix(cli): accept negative float flag values in space form The space form of negative-number float flags (e.g. `serve --temperature -1`) was rejected by clap with a confusing "unexpected argument '-1'" error, because clap parsed the leading-dash token as a flag rather than a value. Only the equals form (`--temperature=-1`) reached the range validator that reports the valid range. Enable `allow_negative_numbers` on the affected float flags (`--temperature` and `--top-p` on both `chat` and `serve`) so the space form reaches the value parser and both forms validate identically, surfacing a clear range-validation message instead of an unexpected argument error. Add regression tests covering both the space and equals forms for `--temperature` and `--top-p` on `chat` and `serve`. Signed-off-by: Roman Sirokov --- apps/rocm/src/main.rs | 56 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/apps/rocm/src/main.rs b/apps/rocm/src/main.rs index 5b4ac58a..79b07211 100644 --- a/apps/rocm/src/main.rs +++ b/apps/rocm/src/main.rs @@ -257,10 +257,13 @@ echo \"Summarize this\" | rocm chat --provider anthropic")] #[arg(long)] prompt: Option, /// Sampling temperature to send with the request (>= 0.0). Higher is more random. - #[arg(long, value_parser = parse_non_negative_temperature)] + // `allow_negative_numbers` lets the space form (`--temperature -1`) reach + // `parse_non_negative_temperature` for a clear range error, instead of clap + // rejecting `-1` as an unexpected argument. + #[arg(long, allow_negative_numbers = true, value_parser = parse_non_negative_temperature)] temperature: Option, /// Nucleus sampling probability to send with the request (0.0-1.0). - #[arg(long = "top-p", value_parser = parse_unit_interval)] + #[arg(long = "top-p", allow_negative_numbers = true, value_parser = parse_unit_interval)] top_p: Option, /// Maximum number of tokens to generate in the response. #[arg(long = "max-tokens", value_parser = parse_positive_u32)] @@ -398,10 +401,13 @@ rocm serve qwen2.5-7b-instruct --verbose --device gpu_required")] #[arg(long, value_name = "FRACTION", allow_hyphen_values = true)] gpu_memory_utilization: Option, /// Default sampling temperature for generation (>= 0.0). - #[arg(long, value_parser = parse_non_negative_temperature)] + // `allow_negative_numbers` lets the space form (`--temperature -1`) reach + // `parse_non_negative_temperature` for a clear range error, instead of clap + // rejecting `-1` as an unexpected argument. + #[arg(long, allow_negative_numbers = true, value_parser = parse_non_negative_temperature)] temperature: Option, /// Default nucleus sampling probability for generation (0.0-1.0). - #[arg(long = "top-p", value_parser = parse_unit_interval)] + #[arg(long = "top-p", allow_negative_numbers = true, value_parser = parse_unit_interval)] top_p: Option, /// Default maximum number of tokens to generate per response. #[arg(long = "max-tokens", value_parser = parse_positive_u32)] @@ -18253,6 +18259,48 @@ mod tests { ); } + #[test] + fn serve_negative_sampling_space_form_reaches_range_validator() { + // The space form (`--temperature -1`) must reach the range validator and + // report a range error, not be rejected by clap as an unexpected argument. + // This is the classic negative-number-as-flag gotcha; `allow_negative_numbers` + // makes both forms validate identically. + for args in [ + &["--temperature", "-1"][..], + &["--temperature=-1"][..], + &["--top-p", "-0.5"][..], + &["--top-p=-0.5"][..], + ] { + assert_eq!( + parse_serve(args) + .expect_err("negative sampling value is rejected") + .kind(), + clap::error::ErrorKind::ValueValidation, + "expected range validation for {args:?}" + ); + } + } + + #[test] + fn chat_negative_sampling_space_form_reaches_range_validator() { + for args in [ + &["--temperature", "-1"][..], + &["--temperature=-1"][..], + &["--top-p", "-0.5"][..], + &["--top-p=-0.5"][..], + ] { + let mut argv = vec!["rocm", "chat", "--prompt", "hi"]; + argv.extend_from_slice(args); + assert_eq!( + Cli::try_parse_from(argv) + .expect_err("negative sampling value is rejected") + .kind(), + clap::error::ErrorKind::ValueValidation, + "expected range validation for {args:?}" + ); + } + } + #[test] fn chat_parses_generation_flags() { let cli = Cli::try_parse_from([