[Rust Frontend] Make --max-model-len optional for the render server (#55328)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Signed-off-by: Wen Zhou <wenzhou@redhat.com>
This commit is contained in:
Wen Zhou
2026-09-09 08:54:54 +00:00
committed by GitHub
co-authored by Claude Opus 5 mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent 5acd95906b
commit 68dcc4fd86
5 changed files with 59 additions and 8 deletions
+3 -2
View File
@@ -139,9 +139,10 @@ pub struct RenderArgs {
/// How message content is exposed to the chat template.
#[arg(long, default_value_t)]
chat_template_content_format: ChatTemplateContentFormatOption,
/// Maximum model context length used for request validation.
/// Maximum model context length used for request validation. When not set,
/// prompt-length validation is skipped and the engine enforces its own limit in later stage.
#[arg(long)]
max_model_len: u32,
max_model_len: Option<u32>,
/// Maximum accepted logprobs count; -1 disables the cap.
#[arg(long, value_parser = clap::value_parser!(i32).range(-1..), allow_negative_numbers = true)]
max_logprobs: Option<i32>,
+13 -1
View File
@@ -57,7 +57,7 @@ fn render_args_build_config_without_tls() {
assert_eq!(config.model, "Qwen/Qwen2.5-0.5B-Instruct");
assert_eq!(config.host, "127.0.0.1");
assert_eq!(config.port, 8080);
assert_eq!(config.max_model_len, 32768);
assert_eq!(config.max_model_len, Some(32768));
assert_eq!(config.served_model_name, ["qwen"]);
assert_eq!(config.tool_call_parser, ParserSelection::Auto);
assert_eq!(config.reasoning_parser, ParserSelection::Auto);
@@ -66,6 +66,18 @@ fn render_args_build_config_without_tls() {
assert!(config.tls.is_none());
}
#[test]
fn render_args_allow_omitted_max_model_len() {
let cli = Cli::try_parse_from(["vllm-rs", "render", "Qwen/Qwen2.5-0.5B-Instruct"]).unwrap();
let Command::Render(args) = cli.command else {
panic!("expected render args");
};
let config = args.into_config();
assert_eq!(config.max_model_len, None);
}
#[test]
fn render_args_build_config_with_tls() {
let cli = Cli::try_parse_from([
+8 -3
View File
@@ -33,7 +33,7 @@ pub struct RenderConfig {
pub chat_template: Option<String>,
pub default_chat_template_kwargs: HashMap<String, Value>,
pub chat_template_content_format: ChatTemplateContentFormatOption,
pub max_model_len: u32,
pub max_model_len: Option<u32>,
pub max_logprobs: Option<i32>,
pub tls: Option<TlsConfig>,
}
@@ -56,6 +56,9 @@ impl RenderConfig {
pub(crate) struct RenderState {
pub(crate) model: String,
pub(crate) served_model_names: Vec<String>,
/// Unlike `text.max_model_len()` this stays `None` when unset,
/// so model cards advertise `null` instead of the `u32::MAX`.
pub(crate) max_model_len: Option<u32>,
pub(crate) text: TextRequestProcessor,
pub(crate) chat: ChatRequestProcessor,
}
@@ -77,7 +80,8 @@ async fn build_state(config: &RenderConfig) -> Result<Arc<RenderState>> {
.context("failed to load renderer/tokenizer backends")?;
let served_model_names =
crate::effective_served_model_names(&config.model, &config.served_model_name);
let text = TextRequestProcessor::new(loaded.text_backend, config.max_model_len)
let max_model_len = config.max_model_len.unwrap_or(u32::MAX);
let text = TextRequestProcessor::new(loaded.text_backend, max_model_len)
.with_max_logprobs(config.max_logprobs);
let chat = ChatRequestProcessor::render_only(loaded.chat_backend).with_parser_selections(
config.tool_call_parser.clone(),
@@ -86,6 +90,7 @@ async fn build_state(config: &RenderConfig) -> Result<Arc<RenderState>> {
Ok(Arc::new(RenderState {
model: config.model.clone(),
served_model_names,
max_model_len: config.max_model_len,
text,
chat,
}))
@@ -154,7 +159,7 @@ mod tests {
chat_template: None,
default_chat_template_kwargs: HashMap::new(),
chat_template_content_format: ChatTemplateContentFormatOption::Auto,
max_model_len: 128,
max_model_len: Some(128),
max_logprobs: None,
tls: None,
},
+1 -1
View File
@@ -55,7 +55,7 @@ async fn list_models(State(state): State<Arc<RenderState>>) -> Json<ListModelsRe
owned_by: "vllm".to_string(),
root: Some(state.model.clone()),
parent: None,
max_model_len: Some(state.text.max_model_len()),
max_model_len: state.max_model_len,
})
.collect(),
})
+34 -1
View File
@@ -691,12 +691,25 @@ fn test_render_app() -> axum::Router {
fn test_render_app_with_parser_selections(
tool_call_parser: ParserSelection,
reasoning_parser: ParserSelection,
) -> axum::Router {
test_render_app_with(Some(128), tool_call_parser, reasoning_parser)
}
fn test_render_app_with_max_model_len(max_model_len: Option<u32>) -> axum::Router {
test_render_app_with(max_model_len, ParserSelection::Auto, ParserSelection::Auto)
}
fn test_render_app_with(
max_model_len: Option<u32>,
tool_call_parser: ParserSelection,
reasoning_parser: ParserSelection,
) -> axum::Router {
let backend = Arc::new(FakeChatBackend::new());
build_render_router(Arc::new(RenderState {
model: "backend-model".to_string(),
served_model_names: vec!["render-model".to_string()],
text: TextRequestProcessor::new(backend.clone(), 128),
max_model_len,
text: TextRequestProcessor::new(backend.clone(), max_model_len.unwrap_or(u32::MAX)),
chat: ChatRequestProcessor::render_only(backend)
.with_parser_selections(tool_call_parser, reasoning_parser),
}))
@@ -1414,6 +1427,26 @@ async fn render_completion_returns_generate_request_with_body_request_id() {
assert!(json[0].get("mm_features").is_none());
}
#[tokio::test]
async fn render_list_models_reports_configured_max_model_len() {
for (configured, expected) in [(Some(128), json!(128)), (None, serde_json::Value::Null)] {
let mut app = test_render_app_with_max_model_len(configured);
let response = app
.call(Request::builder().uri("/v1/models").body(Body::empty()).expect("build request"))
.await
.expect("call app");
assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), usize::MAX).await.expect("read body");
let json: serde_json::Value = serde_json::from_slice(&body).expect("decode json");
let card = json["data"][0].as_object().expect("card object");
assert!(card.contains_key("max_model_len"));
assert_eq!(
card["max_model_len"], expected,
"configured: {configured:?}"
);
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn render_chat_response_can_be_submitted_to_generate_unchanged() {