From e2d7adeb642bd6e6bdb4f3305005e8608765d7cf Mon Sep 17 00:00:00 2001 From: Bugen Zhao Date: Mon, 20 Jul 2026 23:22:24 +0800 Subject: [PATCH] [Rust Frontend] Bump `xgrammar-structural-tag` and enable local extension (#49161) Signed-off-by: Bugen Zhao --- rust/Cargo.lock | 4 +- rust/Cargo.toml | 2 +- rust/src/chat/src/output/default/mod.rs | 2 +- .../chat/src/output/default/structural_tag.rs | 38 +++++++++++-------- rust/src/parser/benches/utils/adapter.rs | 6 +-- .../src/tool/deepseek_dsml/deepseek_v32.rs | 6 +-- .../src/tool/deepseek_dsml/deepseek_v4.rs | 15 +++----- .../src/tool/deepseek_json/deepseek_v3.rs | 6 +-- .../src/tool/deepseek_json/deepseek_v31.rs | 6 +-- rust/src/parser/src/tool/glm_xml/glm47_moe.rs | 6 +-- rust/src/parser/src/tool/hy_v3.rs | 6 +-- rust/src/parser/src/tool/json/hermes.rs | 6 +-- rust/src/parser/src/tool/json/llama.rs | 8 ++-- rust/src/parser/src/tool/json/qwen.rs | 6 +-- rust/src/parser/src/tool/kimi_k2.rs | 6 +-- rust/src/parser/src/tool/minimax_m2.rs | 6 +-- rust/src/parser/src/tool/mod.rs | 6 +-- rust/src/parser/src/tool/qwen_coder.rs | 17 ++++----- rust/src/parser/src/unified/combined.rs | 11 ++---- rust/src/parser/src/unified/mod.rs | 6 +-- 20 files changed, 84 insertions(+), 85 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index e7a157923f8..e61b16e6d9c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -6321,9 +6321,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "xgrammar-structural-tag" -version = "0.1.0+xgrammar.0.2.2.4d145cc" +version = "0.2.0+xgrammar.0.2.4.dd729e7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2436dea2393d55a3b188588aa300c5a8afe8f45a77da52c611fb4498a6c876e6" +checksum = "d4d24c842efc3c24e9756aa426d530cbdac0980e49af223cb384e276e981ca0a" dependencies = [ "auto_impl", "serde", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 5fc9ea6edb7..d42e51c649a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -143,7 +143,7 @@ vllm-server = { path = "src/server" } vllm-text = { path = "src/text" } vllm-tokenizer = { path = "src/tokenizer" } winnow = { version = "1.0.2", features = ["simd"] } -xgrammar-structural-tag = "0.1.0" +xgrammar-structural-tag = "0.2.0" zeromq = { version = "0.6.0", default-features = false, features = [ "tokio-runtime", "all-transport", diff --git a/rust/src/chat/src/output/default/mod.rs b/rust/src/chat/src/output/default/mod.rs index ab288336f4c..4317e48fce2 100644 --- a/rust/src/chat/src/output/default/mod.rs +++ b/rust/src/chat/src/output/default/mod.rs @@ -74,7 +74,7 @@ impl DefaultChatOutputProcessor { Box::new(CombinedParser::new(reasoning_parser, tool_parser)) as Box }; - apply_structural_tag_constraint(request, parser.structural_tag_model())?; + apply_structural_tag_constraint(request, parser.structural_tag_builder())?; if parser.preserve_special_tokens() { request.decode_options.skip_special_tokens = false; diff --git a/rust/src/chat/src/output/default/structural_tag.rs b/rust/src/chat/src/output/default/structural_tag.rs index 79aa1a1f1b5..c0b31bd7456 100644 --- a/rust/src/chat/src/output/default/structural_tag.rs +++ b/rust/src/chat/src/output/default/structural_tag.rs @@ -7,7 +7,8 @@ use thiserror_ext::AsReport; use vllm_engine_core_client::protocol::structured_outputs::{ StructuredOutputBackend, StructuredOutputsParams, }; -use vllm_parser::tool::StructuralTagModel; +use vllm_parser::tool::StructuralTagBuilder; +use xgrammar_structural_tag::builders::StructuralTagOptions; use xgrammar_structural_tag::{ FunctionDefinition, FunctionToolParam, ToolChoice as StructuralTagToolChoice, ToolParam, build_structural_tag, @@ -20,9 +21,9 @@ use crate::{Error, Result as ChatResult}; /// support and the request's tool choice. pub(super) fn apply_structural_tag_constraint( request: &mut ChatRequest, - model: Option, + builder: Option<&dyn StructuralTagBuilder>, ) -> ChatResult<()> { - let Some(model) = model else { + let Some(builder) = builder else { return Ok(()); }; let Some(tool_choice) = structural_tag_tool_choice(request) else { @@ -42,11 +43,16 @@ pub(super) fn apply_structural_tag_constraint( }) .collect::>(); - let structural_tag = build_structural_tag(model, &tools, tool_choice, false) - .and_then(|tag| tag.to_json_string()) - .map_err(|error| Error::StructuralTag { - message: error.to_report_string(), - })?; + let structural_tag = build_structural_tag( + builder, + &tools, + tool_choice, + StructuralTagOptions::default().with_reasoning(false), + ) + .and_then(|tag| tag.to_json_string()) + .map_err(|error| Error::StructuralTag { + message: error.to_report_string(), + })?; // Overwrite any existing structured output settings with the structural tag constraint. request.sampling_params.structured_outputs = Some(StructuredOutputsParams { @@ -141,7 +147,7 @@ mod tests { let mut request = request(ChatToolChoice::Auto, vec![chat_tool("search", Some(true))]); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag should build"); let tag = structural_tag_value(&request); @@ -154,7 +160,7 @@ mod tests { let mut request = request(ChatToolChoice::Auto, vec![chat_tool("search", None)]); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag decision should succeed"); assert!(request.sampling_params.structured_outputs.is_none()); @@ -169,7 +175,7 @@ mod tests { }); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag should build"); let params = structured_outputs(&request); @@ -184,7 +190,7 @@ mod tests { let mut request = request(ChatToolChoice::Required, vec![chat_tool("search", None)]); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag should build"); let tag = structural_tag_value(&request); @@ -201,7 +207,7 @@ mod tests { }); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag should build"); let params = structured_outputs(&request); @@ -221,7 +227,7 @@ mod tests { ); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag should build"); let tag = structural_tag_value(&request).to_string(); @@ -234,7 +240,7 @@ mod tests { let mut request = request(ChatToolChoice::None, vec![chat_tool("search", Some(true))]); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag decision should succeed"); assert!(request.sampling_params.structured_outputs.is_none()); @@ -249,7 +255,7 @@ mod tests { }); let parser = qwen3_coder_parser(&request.tools); - apply_structural_tag_constraint(&mut request, parser.structural_tag_model()) + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder()) .expect("structural tag decision should succeed"); let params = structured_outputs(&request); diff --git a/rust/src/parser/benches/utils/adapter.rs b/rust/src/parser/benches/utils/adapter.rs index ab017b5136c..243f19e3ce8 100644 --- a/rust/src/parser/benches/utils/adapter.rs +++ b/rust/src/parser/benches/utils/adapter.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use vllm_parser::tool::{ - Result, StructuralTagModel, Tool, ToolParser, ToolParserError, ToolParserOutput, + Result, StructuralTagBuilder, Tool, ToolParser, ToolParserError, ToolParserOutput, }; use vllm_parser::unified::{ UnifiedParser, UnifiedParserError, UnifiedParserEvent, UnifiedParserOutput, @@ -85,8 +85,8 @@ impl ToolParser for UnifiedToolParserAdapter { self.inner.preserve_special_tokens() } - fn structural_tag_model(&self) -> Option { - self.inner.structural_tag_model() + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + self.inner.structural_tag_builder() } fn tool_call_id(&self, tool_index: usize) -> Option<&str> { diff --git a/rust/src/parser/src/tool/deepseek_dsml/deepseek_v32.rs b/rust/src/parser/src/tool/deepseek_dsml/deepseek_v32.rs index aebcde8a036..09bb0f189bf 100644 --- a/rust/src/parser/src/tool/deepseek_dsml/deepseek_v32.rs +++ b/rust/src/parser/src/tool/deepseek_dsml/deepseek_v32.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{DeepSeekDsmlToolParser, DsmlTokens}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Tool parser for DeepSeek V3.2 models. /// @@ -47,8 +47,8 @@ impl ToolParser for DeepSeekV32ToolParser { true } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::DeepSeekV32) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::DeepSeekV32.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/deepseek_dsml/deepseek_v4.rs b/rust/src/parser/src/tool/deepseek_dsml/deepseek_v4.rs index 31470abcdfc..f4540ea47ca 100644 --- a/rust/src/parser/src/tool/deepseek_dsml/deepseek_v4.rs +++ b/rust/src/parser/src/tool/deepseek_dsml/deepseek_v4.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{DeepSeekDsmlToolParser, DsmlTokens}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Tool parser for DeepSeek V4 models. /// @@ -50,8 +50,8 @@ impl ToolParser for DeepSeekV4ToolParser { true } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::DeepSeekV4) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::DeepSeekV4.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { @@ -73,7 +73,7 @@ mod tests { use super::DeepSeekV4ToolParser; use crate::tool::test_utils::{collect_stream, test_tools}; - use crate::tool::{StructuralTagModel, ToolParser, ToolParserTestExt as _}; + use crate::tool::{ToolParser, ToolParserTestExt as _}; fn build_tool_call(function_name: &str, params: &[(&str, &str)]) -> String { let params = params @@ -91,13 +91,10 @@ mod tests { } #[test] - fn deepseek_v4_exposes_structural_tag_model() { + fn deepseek_v4_exposes_structural_tag_builder() { let parser = DeepSeekV4ToolParser::new(&test_tools()); - assert_eq!( - parser.structural_tag_model(), - Some(StructuralTagModel::DeepSeekV4) - ); + assert!(parser.structural_tag_builder().is_some()); } #[test] diff --git a/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs b/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs index 3e3c16624d0..f932cb60196 100644 --- a/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs +++ b/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{DeepSeekJsonFormat, DeepSeekJsonToolParser}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Tool parser for DeepSeek V3 JSON-fenced tool calls. /// @@ -35,8 +35,8 @@ impl ToolParser for DeepSeekV3ToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::DeepSeekR1) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::DeepSeekR1.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs b/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs index 1a1ec3634af..34d5eed7848 100644 --- a/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs +++ b/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{DeepSeekJsonFormat, DeepSeekJsonToolParser}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Tool parser for DeepSeek V3.1 raw JSON tool calls. /// @@ -31,8 +31,8 @@ impl ToolParser for DeepSeekV31ToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::DeepSeekV31) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::DeepSeekV31.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/glm_xml/glm47_moe.rs b/rust/src/parser/src/tool/glm_xml/glm47_moe.rs index f13cd7983e5..06477b40491 100644 --- a/rust/src/parser/src/tool/glm_xml/glm47_moe.rs +++ b/rust/src/parser/src/tool/glm_xml/glm47_moe.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{GlmXmlToolParser, Separator}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Tool parser for GLM-4.7 MoE XML-style tool calls. /// @@ -25,8 +25,8 @@ impl ToolParser for Glm47MoeToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Glm47) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Glm47.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/hy_v3.rs b/rust/src/parser/src/tool/hy_v3.rs index 227f4aa2874..a1426d6a3bd 100644 --- a/rust/src/parser/src/tool/hy_v3.rs +++ b/rust/src/parser/src/tool/hy_v3.rs @@ -10,7 +10,7 @@ use winnow::token::{literal, rest, take_until}; use super::parameters::ToolSchemas; use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker}; use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput}; -use crate::tool::{StructuralTagModel, Tool}; +use crate::tool::{StructuralTagBuilder, Tool}; const TOOL_CALLS_START: &str = ""; const TOOL_CALLS_END: &str = ""; @@ -116,8 +116,8 @@ impl ToolParser for HyV3ToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::HyV3) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::HyV3.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/json/hermes.rs b/rust/src/parser/src/tool/json/hermes.rs index 64c594ee0ff..aed27a8cd4d 100644 --- a/rust/src/parser/src/tool/json/hermes.rs +++ b/rust/src/parser/src/tool/json/hermes.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{JsonToolCallConfig, JsonToolCallParser, JsonToolCallWhitespace}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; const HERMES_CONFIG: JsonToolCallConfig = JsonToolCallConfig { parser_name: "Hermes", @@ -48,8 +48,8 @@ impl ToolParser for HermesToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Hermes) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Hermes.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/json/llama.rs b/rust/src/parser/src/tool/json/llama.rs index 23e49724f07..262340a9e80 100644 --- a/rust/src/parser/src/tool/json/llama.rs +++ b/rust/src/parser/src/tool/json/llama.rs @@ -12,7 +12,9 @@ use super::{ argument_delta_event, tool_call_header_event, }; use crate::tool::utils::{JsonObjectScanState, parse_buffered_event}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolCallDelta, ToolParser, ToolParserOutput}; +use crate::tool::{ + Result, StructuralTagBuilder, Tool, ToolCallDelta, ToolParser, ToolParserOutput, +}; #[derive(Debug, Clone, PartialEq, Eq)] enum LlamaJsonMode { @@ -136,8 +138,8 @@ impl ToolParser for Llama3JsonToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Llama) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Llama.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/json/qwen.rs b/rust/src/parser/src/tool/json/qwen.rs index cf7c1fbe237..7d5b71078aa 100644 --- a/rust/src/parser/src/tool/json/qwen.rs +++ b/rust/src/parser/src/tool/json/qwen.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright contributors to the vLLM project use super::{JsonToolCallConfig, JsonToolCallParser, JsonToolCallWhitespace}; -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; const QWEN_XML_CONFIG: JsonToolCallConfig = JsonToolCallConfig { parser_name: "Qwen XML", @@ -50,8 +50,8 @@ impl ToolParser for Qwen3XmlToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Qwen3) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Qwen3.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/kimi_k2.rs b/rust/src/parser/src/tool/kimi_k2.rs index aa4dc0e6ad6..540b5a3d37b 100644 --- a/rust/src/parser/src/tool/kimi_k2.rs +++ b/rust/src/parser/src/tool/kimi_k2.rs @@ -11,7 +11,7 @@ use winnow::token::{literal, rest, take_until, take_while}; use super::utils::{JsonObjectScanState, parse_buffered_event, safe_text_len, take_json_object}; use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput}; -use crate::tool::{StructuralTagModel, Tool}; +use crate::tool::{StructuralTagBuilder, Tool}; const TOOL_CALLS_START: &str = "<|tool_calls_section_begin|>"; const TOOL_CALLS_END: &str = "<|tool_calls_section_end|>"; @@ -150,8 +150,8 @@ impl ToolParser for KimiK2ToolParser { true } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Kimi) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Kimi.builder()) } fn tool_call_id(&self, tool_index: usize) -> Option<&str> { diff --git a/rust/src/parser/src/tool/minimax_m2.rs b/rust/src/parser/src/tool/minimax_m2.rs index 903b14eb6a5..d90e6676f0b 100644 --- a/rust/src/parser/src/tool/minimax_m2.rs +++ b/rust/src/parser/src/tool/minimax_m2.rs @@ -10,7 +10,7 @@ use winnow::token::{literal, rest, take_until}; use super::parameters::ToolSchemas; use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker}; use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput}; -use crate::tool::{StructuralTagModel, Tool}; +use crate::tool::{StructuralTagBuilder, Tool}; const TOOL_CALL_START: &str = ""; const TOOL_CALL_END: &str = ""; @@ -115,8 +115,8 @@ impl ToolParser for MinimaxM2ToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Minimax) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Minimax.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { diff --git a/rust/src/parser/src/tool/mod.rs b/rust/src/parser/src/tool/mod.rs index 216d3dff4cf..e25eae882f9 100644 --- a/rust/src/parser/src/tool/mod.rs +++ b/rust/src/parser/src/tool/mod.rs @@ -36,7 +36,7 @@ pub use qwen_coder::Qwen3CoderToolParser; pub use seed_oss::SeedOssToolParser; use serde::{Deserialize, Serialize}; use serde_json::Value; -pub use xgrammar_structural_tag::Model as StructuralTagModel; +pub use xgrammar_structural_tag::builders::StructuralTagBuilder; use crate::utils; @@ -187,8 +187,8 @@ pub trait ToolParser: Send { false } - /// Return the xgrammar structural-tag model used for strict tool calling. - fn structural_tag_model(&self) -> Option { + /// Return the xgrammar structural-tag builder used for strict tool calling. + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { None } diff --git a/rust/src/parser/src/tool/qwen_coder.rs b/rust/src/parser/src/tool/qwen_coder.rs index 361de524e8b..0a9385a85ce 100644 --- a/rust/src/parser/src/tool/qwen_coder.rs +++ b/rust/src/parser/src/tool/qwen_coder.rs @@ -9,8 +9,8 @@ use winnow::token::{literal, take_until}; use super::parameters::ToolSchemas; use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker}; -use super::{Result, StructuralTagModel, ToolCallDelta, ToolParser, ToolParserOutput}; -use crate::tool::Tool; +use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput}; +use crate::tool::{StructuralTagBuilder, Tool}; const TOOL_CALL_START: &str = ""; const TOOL_CALL_END: &str = ""; @@ -146,8 +146,8 @@ impl ToolParser for Qwen3CoderToolParser { Ok(Box::new(Self::new(tools))) } - fn structural_tag_model(&self) -> Option { - Some(StructuralTagModel::Qwen3Coder) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + Some(xgrammar_structural_tag::Model::Qwen3Coder.builder()) } fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> { @@ -294,7 +294,7 @@ mod tests { use serde_json::{Value, json}; use thiserror_ext::AsReport; - use super::{Qwen3CoderToolParser, StructuralTagModel, ToolParser}; + use super::{Qwen3CoderToolParser, ToolParser}; use crate::tool::test_utils::{collect_stream, split_by_chars, test_tools}; use crate::tool::{ToolParserOutput, ToolParserTestExt as _}; @@ -308,13 +308,10 @@ mod tests { } #[test] - fn qwen_coder_exposes_structural_tag_model() { + fn qwen_coder_exposes_structural_tag_builder() { let parser = Qwen3CoderToolParser::new(&test_tools()); - assert_eq!( - parser.structural_tag_model(), - Some(StructuralTagModel::Qwen3Coder) - ); + assert!(parser.structural_tag_builder().is_some()); } #[test] diff --git a/rust/src/parser/src/unified/combined.rs b/rust/src/parser/src/unified/combined.rs index 13183192c6c..afecc31665b 100644 --- a/rust/src/parser/src/unified/combined.rs +++ b/rust/src/parser/src/unified/combined.rs @@ -7,7 +7,7 @@ use vllm_tokenizer::DynTokenizer; use super::{Result, UnifiedParser, UnifiedParserError, UnifiedParserOutput}; use crate::reasoning::ReasoningParser; -use crate::tool::{StructuralTagModel, Tool, ToolParser, ToolParserOutput}; +use crate::tool::{StructuralTagBuilder, Tool, ToolParser, ToolParserOutput}; /// Unified parser that composes existing reasoning and tool parsers. pub struct CombinedParser { @@ -79,8 +79,8 @@ impl UnifiedParser for CombinedParser { || self.tool.as_ref().is_some_and(|parser| parser.preserve_special_tokens()) } - fn structural_tag_model(&self) -> Option { - self.tool.as_ref().and_then(|parser| parser.structural_tag_model()) + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { + self.tool.as_ref().and_then(|parser| parser.structural_tag_builder()) } fn tool_call_id(&self, tool_index: usize) -> Option<&str> { @@ -269,10 +269,7 @@ mod tests { fn combined_parser_emits_tool_calls_from_visible_content() { let tool = Qwen3XmlToolParser::create(&test_tools()).unwrap(); let mut parser = CombinedParser::new(None, Some(tool)); - assert!(matches!( - parser.structural_tag_model(), - Some(crate::tool::StructuralTagModel::Qwen3) - )); + assert!(parser.structural_tag_builder().is_some()); let output = collect( &mut parser, diff --git a/rust/src/parser/src/unified/mod.rs b/rust/src/parser/src/unified/mod.rs index d5ecb649290..4700211ae93 100644 --- a/rust/src/parser/src/unified/mod.rs +++ b/rust/src/parser/src/unified/mod.rs @@ -16,7 +16,7 @@ use vllm_tokenizer::DynTokenizer; use crate::reasoning::ReasoningError; use crate::tool::{ - StructuralTagModel, Tool, ToolCallDelta, ToolParserError, ToolParserEvent, ToolParserOutput, + StructuralTagBuilder, Tool, ToolCallDelta, ToolParserError, ToolParserEvent, ToolParserOutput, }; /// Result alias for unified parser operations. @@ -171,8 +171,8 @@ pub trait UnifiedParser: Send { false } - /// Return the xgrammar structural-tag model used for strict tool calling. - fn structural_tag_model(&self) -> Option { + /// Return the xgrammar structural-tag builder used for strict tool calling. + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> { None }