@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / ebf53ba373
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

[Bugfix][Rust Frontend] Set a structured-output backend so requests do not 500 (#44729)

Signed-off-by: Ting Sun <suntcrick@gmail.com> Signed-off-by: Bugen Zhao <i@bugenzhao.com> Co-authored-by: Bugen Zhao <i@bugenzhao.com>

Ting SUN committed Jun 9, 2026 at 13:30 UTC ebf53ba373bf296d6c0fb8e88c02011397636ef0
1 file changed +40
rust/src/engine-core-client/src/protocol/mod.rs
+40
@@ -162,6 +162,21 @@ pub enum RequestOutputKind {
162 FinalOnly = 2,
163 }
164
165 +/// Structured-output backend selected for EngineCore grammar compilation.
166 +///
167 +/// Python vLLM stores this in `StructuredOutputsParams._backend` after request
168 +/// validation. The Rust frontend currently always lowers structured-output
169 +/// requests to guidance, while ignoring any user-supplied `_backend` value.
170 +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
171 +#[serde(rename_all = "kebab-case")]
172 +pub enum StructuredOutputBackend {
173 + Xgrammar,
174 + #[default]
175 + Guidance,
176 + Outlines,
177 + LmFormatEnforcer,
178 +}
179 +
180 /// The stop reason associated with a finished output.
181 ///
182 /// Python models this as the union-typed `stop_reason: int | str | None`
@@ -209,6 +224,17 @@ pub struct StructuredOutputsParams {
224 pub whitespace_pattern: Option<String>,
225 /// Structural tag configuration (JSON-encoded string).
226 pub structural_tag: Option<String>,
227 + /// Structured-output backend, mirroring Python's internal `_backend`.
228 + ///
229 + /// User-supplied values are ignored during deserialization. This matches
230 + /// Python's request boundary, where `_backend` is set by validation rather
231 + /// than accepted as a request-level backend selector.
232 + #[serde(
233 + default,
234 + rename = "_backend",
235 + deserialize_with = "serde_with::rust::deserialize_ignore_any"
236 + )]
237 + pub backend: StructuredOutputBackend,
238 }
239
240 /// Engine-core-facing sampling parameters for text generation.
@@ -600,4 +626,18 @@ mod tests {
626
627 expect_test::expect![[r#"messagepack decode failed for u64: wrong msgpack marker FixMap(1); value fallback: {"status": "READY"}"#]].assert_eq(&error.to_report_string());
628 }
629 +
630 + #[test]
631 + fn structured_outputs_backend_ignores_deserialized_value() {
632 + let params: StructuredOutputsParams = serde_json::from_value(serde_json::json!({
633 + "json_object": true,
634 + "_backend": "xgrammar",
635 + }))
636 + .unwrap();
637 +
638 + assert_eq!(params.backend, StructuredOutputBackend::Guidance);
639 +
640 + let value = serde_json::to_value(params).unwrap();
641 + assert_eq!(value["_backend"], "guidance");
642 + }
643 }