[Rust Frontend] Bump `xgrammar-structural-tag` and enable local extension (#49161)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>

Bugen Zhao committed Jul 20, 2026 at 23:22 UTC e2d7adeb642bd6e6bdb4f3305005e8608765d7cf
20 files changed +84 -85
rust/Cargo.lock
+2 -2
@@ -6321,9 +6321,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
6321
6322 [[package]]
6323 name = "xgrammar-structural-tag"
6324 -version = "0.1.0+xgrammar.0.2.2.4d145cc"
6324 +version = "0.2.0+xgrammar.0.2.4.dd729e7"
6325 source = "registry+https://github.com/rust-lang/crates.io-index"
6326 -checksum = "2436dea2393d55a3b188588aa300c5a8afe8f45a77da52c611fb4498a6c876e6"
6326 +checksum = "d4d24c842efc3c24e9756aa426d530cbdac0980e49af223cb384e276e981ca0a"
6327 dependencies = [
6328 "auto_impl",
6329 "serde",
rust/Cargo.toml
+1 -1
@@ -143,7 +143,7 @@ vllm-server = { path = "src/server" }
143 vllm-text = { path = "src/text" }
144 vllm-tokenizer = { path = "src/tokenizer" }
145 winnow = { version = "1.0.2", features = ["simd"] }
146 -xgrammar-structural-tag = "0.1.0"
146 +xgrammar-structural-tag = "0.2.0"
147 zeromq = { version = "0.6.0", default-features = false, features = [
148 "tokio-runtime",
149 "all-transport",
rust/src/chat/src/output/default/mod.rs
+1 -1
@@ -74,7 +74,7 @@ impl DefaultChatOutputProcessor {
74 Box::new(CombinedParser::new(reasoning_parser, tool_parser)) as Box<dyn UnifiedParser>
75 };
76
77 - apply_structural_tag_constraint(request, parser.structural_tag_model())?;
77 + apply_structural_tag_constraint(request, parser.structural_tag_builder())?;
78
79 if parser.preserve_special_tokens() {
80 request.decode_options.skip_special_tokens = false;
rust/src/chat/src/output/default/structural_tag.rs
+22 -16
@@ -7,7 +7,8 @@ use thiserror_ext::AsReport;
7 use vllm_engine_core_client::protocol::structured_outputs::{
8 StructuredOutputBackend, StructuredOutputsParams,
9 };
10 -use vllm_parser::tool::StructuralTagModel;
10 +use vllm_parser::tool::StructuralTagBuilder;
11 +use xgrammar_structural_tag::builders::StructuralTagOptions;
12 use xgrammar_structural_tag::{
13 FunctionDefinition, FunctionToolParam, ToolChoice as StructuralTagToolChoice, ToolParam,
14 build_structural_tag,
@@ -20,9 +21,9 @@ use crate::{Error, Result as ChatResult};
21 /// support and the request's tool choice.
22 pub(super) fn apply_structural_tag_constraint(
23 request: &mut ChatRequest,
23 - model: Option<StructuralTagModel>,
24 + builder: Option<&dyn StructuralTagBuilder>,
25 ) -> ChatResult<()> {
25 - let Some(model) = model else {
26 + let Some(builder) = builder else {
27 return Ok(());
28 };
29 let Some(tool_choice) = structural_tag_tool_choice(request) else {
@@ -42,11 +43,16 @@ pub(super) fn apply_structural_tag_constraint(
43 })
44 .collect::<Vec<_>>();
45
45 - let structural_tag = build_structural_tag(model, &tools, tool_choice, false)
46 - .and_then(|tag| tag.to_json_string())
47 - .map_err(|error| Error::StructuralTag {
48 - message: error.to_report_string(),
49 - })?;
46 + let structural_tag = build_structural_tag(
47 + builder,
48 + &tools,
49 + tool_choice,
50 + StructuralTagOptions::default().with_reasoning(false),
51 + )
52 + .and_then(|tag| tag.to_json_string())
53 + .map_err(|error| Error::StructuralTag {
54 + message: error.to_report_string(),
55 + })?;
56
57 // Overwrite any existing structured output settings with the structural tag constraint.
58 request.sampling_params.structured_outputs = Some(StructuredOutputsParams {
@@ -141,7 +147,7 @@ mod tests {
147 let mut request = request(ChatToolChoice::Auto, vec![chat_tool("search", Some(true))]);
148 let parser = qwen3_coder_parser(&request.tools);
149
144 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
150 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
151 .expect("structural tag should build");
152
153 let tag = structural_tag_value(&request);
@@ -154,7 +160,7 @@ mod tests {
160 let mut request = request(ChatToolChoice::Auto, vec![chat_tool("search", None)]);
161 let parser = qwen3_coder_parser(&request.tools);
162
157 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
163 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
164 .expect("structural tag decision should succeed");
165
166 assert!(request.sampling_params.structured_outputs.is_none());
@@ -169,7 +175,7 @@ mod tests {
175 });
176 let parser = qwen3_coder_parser(&request.tools);
177
172 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
178 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
179 .expect("structural tag should build");
180
181 let params = structured_outputs(&request);
@@ -184,7 +190,7 @@ mod tests {
190 let mut request = request(ChatToolChoice::Required, vec![chat_tool("search", None)]);
191 let parser = qwen3_coder_parser(&request.tools);
192
187 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
193 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
194 .expect("structural tag should build");
195
196 let tag = structural_tag_value(&request);
@@ -201,7 +207,7 @@ mod tests {
207 });
208 let parser = qwen3_coder_parser(&request.tools);
209
204 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
210 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
211 .expect("structural tag should build");
212
213 let params = structured_outputs(&request);
@@ -221,7 +227,7 @@ mod tests {
227 );
228 let parser = qwen3_coder_parser(&request.tools);
229
224 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
230 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
231 .expect("structural tag should build");
232
233 let tag = structural_tag_value(&request).to_string();
@@ -234,7 +240,7 @@ mod tests {
240 let mut request = request(ChatToolChoice::None, vec![chat_tool("search", Some(true))]);
241 let parser = qwen3_coder_parser(&request.tools);
242
237 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
243 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
244 .expect("structural tag decision should succeed");
245
246 assert!(request.sampling_params.structured_outputs.is_none());
@@ -249,7 +255,7 @@ mod tests {
255 });
256 let parser = qwen3_coder_parser(&request.tools);
257
252 - apply_structural_tag_constraint(&mut request, parser.structural_tag_model())
258 + apply_structural_tag_constraint(&mut request, parser.structural_tag_builder())
259 .expect("structural tag decision should succeed");
260
261 let params = structured_outputs(&request);
rust/src/parser/benches/utils/adapter.rs
+3 -3
@@ -4,7 +4,7 @@
4 use std::sync::Arc;
5
6 use vllm_parser::tool::{
7 - Result, StructuralTagModel, Tool, ToolParser, ToolParserError, ToolParserOutput,
7 + Result, StructuralTagBuilder, Tool, ToolParser, ToolParserError, ToolParserOutput,
8 };
9 use vllm_parser::unified::{
10 UnifiedParser, UnifiedParserError, UnifiedParserEvent, UnifiedParserOutput,
@@ -85,8 +85,8 @@ impl<T: UnifiedParser> ToolParser for UnifiedToolParserAdapter<T> {
85 self.inner.preserve_special_tokens()
86 }
87
88 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
89 - self.inner.structural_tag_model()
88 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
89 + self.inner.structural_tag_builder()
90 }
91
92 fn tool_call_id(&self, tool_index: usize) -> Option<&str> {
rust/src/parser/src/tool/deepseek_dsml/deepseek_v32.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{DeepSeekDsmlToolParser, DsmlTokens};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 /// Tool parser for DeepSeek V3.2 models.
8 ///
@@ -47,8 +47,8 @@ impl ToolParser for DeepSeekV32ToolParser {
47 true
48 }
49
50 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
51 - Some(StructuralTagModel::DeepSeekV32)
50 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
51 + Some(xgrammar_structural_tag::Model::DeepSeekV32.builder())
52 }
53
54 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/deepseek_dsml/deepseek_v4.rs
+6 -9
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{DeepSeekDsmlToolParser, DsmlTokens};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 /// Tool parser for DeepSeek V4 models.
8 ///
@@ -50,8 +50,8 @@ impl ToolParser for DeepSeekV4ToolParser {
50 true
51 }
52
53 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
54 - Some(StructuralTagModel::DeepSeekV4)
53 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
54 + Some(xgrammar_structural_tag::Model::DeepSeekV4.builder())
55 }
56
57 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
@@ -73,7 +73,7 @@ mod tests {
73
74 use super::DeepSeekV4ToolParser;
75 use crate::tool::test_utils::{collect_stream, test_tools};
76 - use crate::tool::{StructuralTagModel, ToolParser, ToolParserTestExt as _};
76 + use crate::tool::{ToolParser, ToolParserTestExt as _};
77
78 fn build_tool_call(function_name: &str, params: &[(&str, &str)]) -> String {
79 let params = params
@@ -91,13 +91,10 @@ mod tests {
91 }
92
93 #[test]
94 - fn deepseek_v4_exposes_structural_tag_model() {
94 + fn deepseek_v4_exposes_structural_tag_builder() {
95 let parser = DeepSeekV4ToolParser::new(&test_tools());
96
97 - assert_eq!(
98 - parser.structural_tag_model(),
99 - Some(StructuralTagModel::DeepSeekV4)
100 - );
97 + assert!(parser.structural_tag_builder().is_some());
98 }
99
100 #[test]
rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{DeepSeekJsonFormat, DeepSeekJsonToolParser};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 /// Tool parser for DeepSeek V3 JSON-fenced tool calls.
8 ///
@@ -35,8 +35,8 @@ impl ToolParser for DeepSeekV3ToolParser {
35 Ok(Box::new(Self::new(tools)))
36 }
37
38 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
39 - Some(StructuralTagModel::DeepSeekR1)
38 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
39 + Some(xgrammar_structural_tag::Model::DeepSeekR1.builder())
40 }
41
42 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{DeepSeekJsonFormat, DeepSeekJsonToolParser};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 /// Tool parser for DeepSeek V3.1 raw JSON tool calls.
8 ///
@@ -31,8 +31,8 @@ impl ToolParser for DeepSeekV31ToolParser {
31 Ok(Box::new(Self::new(tools)))
32 }
33
34 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
35 - Some(StructuralTagModel::DeepSeekV31)
34 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
35 + Some(xgrammar_structural_tag::Model::DeepSeekV31.builder())
36 }
37
38 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/glm_xml/glm47_moe.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{GlmXmlToolParser, Separator};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 /// Tool parser for GLM-4.7 MoE XML-style tool calls.
8 ///
@@ -25,8 +25,8 @@ impl ToolParser for Glm47MoeToolParser {
25 Ok(Box::new(Self::new(tools)))
26 }
27
28 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
29 - Some(StructuralTagModel::Glm47)
28 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
29 + Some(xgrammar_structural_tag::Model::Glm47.builder())
30 }
31
32 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/hy_v3.rs
+3 -3
@@ -10,7 +10,7 @@ use winnow::token::{literal, rest, take_until};
10 use super::parameters::ToolSchemas;
11 use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker};
12 use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput};
13 -use crate::tool::{StructuralTagModel, Tool};
13 +use crate::tool::{StructuralTagBuilder, Tool};
14
15 const TOOL_CALLS_START: &str = "<tool_calls>";
16 const TOOL_CALLS_END: &str = "</tool_calls>";
@@ -116,8 +116,8 @@ impl ToolParser for HyV3ToolParser {
116 Ok(Box::new(Self::new(tools)))
117 }
118
119 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
120 - Some(StructuralTagModel::HyV3)
119 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
120 + Some(xgrammar_structural_tag::Model::HyV3.builder())
121 }
122
123 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/json/hermes.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{JsonToolCallConfig, JsonToolCallParser, JsonToolCallWhitespace};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 const HERMES_CONFIG: JsonToolCallConfig = JsonToolCallConfig {
8 parser_name: "Hermes",
@@ -48,8 +48,8 @@ impl ToolParser for HermesToolParser {
48 Ok(Box::new(Self::new(tools)))
49 }
50
51 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
52 - Some(StructuralTagModel::Hermes)
51 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
52 + Some(xgrammar_structural_tag::Model::Hermes.builder())
53 }
54
55 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/json/llama.rs
+5 -3
@@ -12,7 +12,9 @@ use super::{
12 argument_delta_event, tool_call_header_event,
13 };
14 use crate::tool::utils::{JsonObjectScanState, parse_buffered_event};
15 -use crate::tool::{Result, StructuralTagModel, Tool, ToolCallDelta, ToolParser, ToolParserOutput};
15 +use crate::tool::{
16 + Result, StructuralTagBuilder, Tool, ToolCallDelta, ToolParser, ToolParserOutput,
17 +};
18
19 #[derive(Debug, Clone, PartialEq, Eq)]
20 enum LlamaJsonMode {
@@ -136,8 +138,8 @@ impl ToolParser for Llama3JsonToolParser {
138 Ok(Box::new(Self::new(tools)))
139 }
140
139 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
140 - Some(StructuralTagModel::Llama)
141 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
142 + Some(xgrammar_structural_tag::Model::Llama.builder())
143 }
144
145 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/json/qwen.rs
+3 -3
@@ -2,7 +2,7 @@
2 // SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 use super::{JsonToolCallConfig, JsonToolCallParser, JsonToolCallWhitespace};
5 -use crate::tool::{Result, StructuralTagModel, Tool, ToolParser, ToolParserOutput};
5 +use crate::tool::{Result, StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
6
7 const QWEN_XML_CONFIG: JsonToolCallConfig = JsonToolCallConfig {
8 parser_name: "Qwen XML",
@@ -50,8 +50,8 @@ impl ToolParser for Qwen3XmlToolParser {
50 Ok(Box::new(Self::new(tools)))
51 }
52
53 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
54 - Some(StructuralTagModel::Qwen3)
53 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
54 + Some(xgrammar_structural_tag::Model::Qwen3.builder())
55 }
56
57 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/kimi_k2.rs
+3 -3
@@ -11,7 +11,7 @@ use winnow::token::{literal, rest, take_until, take_while};
11
12 use super::utils::{JsonObjectScanState, parse_buffered_event, safe_text_len, take_json_object};
13 use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput};
14 -use crate::tool::{StructuralTagModel, Tool};
14 +use crate::tool::{StructuralTagBuilder, Tool};
15
16 const TOOL_CALLS_START: &str = "<|tool_calls_section_begin|>";
17 const TOOL_CALLS_END: &str = "<|tool_calls_section_end|>";
@@ -150,8 +150,8 @@ impl ToolParser for KimiK2ToolParser {
150 true
151 }
152
153 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
154 - Some(StructuralTagModel::Kimi)
153 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
154 + Some(xgrammar_structural_tag::Model::Kimi.builder())
155 }
156
157 fn tool_call_id(&self, tool_index: usize) -> Option<&str> {
rust/src/parser/src/tool/minimax_m2.rs
+3 -3
@@ -10,7 +10,7 @@ use winnow::token::{literal, rest, take_until};
10 use super::parameters::ToolSchemas;
11 use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker};
12 use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput};
13 -use crate::tool::{StructuralTagModel, Tool};
13 +use crate::tool::{StructuralTagBuilder, Tool};
14
15 const TOOL_CALL_START: &str = "<minimax:tool_call>";
16 const TOOL_CALL_END: &str = "</minimax:tool_call>";
@@ -115,8 +115,8 @@ impl ToolParser for MinimaxM2ToolParser {
115 Ok(Box::new(Self::new(tools)))
116 }
117
118 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
119 - Some(StructuralTagModel::Minimax)
118 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
119 + Some(xgrammar_structural_tag::Model::Minimax.builder())
120 }
121
122 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
rust/src/parser/src/tool/mod.rs
+3 -3
@@ -36,7 +36,7 @@ pub use qwen_coder::Qwen3CoderToolParser;
36 pub use seed_oss::SeedOssToolParser;
37 use serde::{Deserialize, Serialize};
38 use serde_json::Value;
39 -pub use xgrammar_structural_tag::Model as StructuralTagModel;
39 +pub use xgrammar_structural_tag::builders::StructuralTagBuilder;
40
41 use crate::utils;
42
@@ -187,8 +187,8 @@ pub trait ToolParser: Send {
187 false
188 }
189
190 - /// Return the xgrammar structural-tag model used for strict tool calling.
191 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
190 + /// Return the xgrammar structural-tag builder used for strict tool calling.
191 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
192 None
193 }
194
rust/src/parser/src/tool/qwen_coder.rs
+7 -10
@@ -9,8 +9,8 @@ use winnow::token::{literal, take_until};
9
10 use super::parameters::ToolSchemas;
11 use super::utils::{MarkerScanState, parse_buffered_event, safe_text_len, take_until_marker};
12 -use super::{Result, StructuralTagModel, ToolCallDelta, ToolParser, ToolParserOutput};
13 -use crate::tool::Tool;
12 +use super::{Result, ToolCallDelta, ToolParser, ToolParserOutput};
13 +use crate::tool::{StructuralTagBuilder, Tool};
14
15 const TOOL_CALL_START: &str = "<tool_call>";
16 const TOOL_CALL_END: &str = "</tool_call>";
@@ -146,8 +146,8 @@ impl ToolParser for Qwen3CoderToolParser {
146 Ok(Box::new(Self::new(tools)))
147 }
148
149 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
150 - Some(StructuralTagModel::Qwen3Coder)
149 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
150 + Some(xgrammar_structural_tag::Model::Qwen3Coder.builder())
151 }
152
153 fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
@@ -294,7 +294,7 @@ mod tests {
294 use serde_json::{Value, json};
295 use thiserror_ext::AsReport;
296
297 - use super::{Qwen3CoderToolParser, StructuralTagModel, ToolParser};
297 + use super::{Qwen3CoderToolParser, ToolParser};
298 use crate::tool::test_utils::{collect_stream, split_by_chars, test_tools};
299 use crate::tool::{ToolParserOutput, ToolParserTestExt as _};
300
@@ -308,13 +308,10 @@ mod tests {
308 }
309
310 #[test]
311 - fn qwen_coder_exposes_structural_tag_model() {
311 + fn qwen_coder_exposes_structural_tag_builder() {
312 let parser = Qwen3CoderToolParser::new(&test_tools());
313
314 - assert_eq!(
315 - parser.structural_tag_model(),
316 - Some(StructuralTagModel::Qwen3Coder)
317 - );
314 + assert!(parser.structural_tag_builder().is_some());
315 }
316
317 #[test]
rust/src/parser/src/unified/combined.rs
+4 -7
@@ -7,7 +7,7 @@ use vllm_tokenizer::DynTokenizer;
7
8 use super::{Result, UnifiedParser, UnifiedParserError, UnifiedParserOutput};
9 use crate::reasoning::ReasoningParser;
10 -use crate::tool::{StructuralTagModel, Tool, ToolParser, ToolParserOutput};
10 +use crate::tool::{StructuralTagBuilder, Tool, ToolParser, ToolParserOutput};
11
12 /// Unified parser that composes existing reasoning and tool parsers.
13 pub struct CombinedParser {
@@ -79,8 +79,8 @@ impl UnifiedParser for CombinedParser {
79 || self.tool.as_ref().is_some_and(|parser| parser.preserve_special_tokens())
80 }
81
82 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
83 - self.tool.as_ref().and_then(|parser| parser.structural_tag_model())
82 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
83 + self.tool.as_ref().and_then(|parser| parser.structural_tag_builder())
84 }
85
86 fn tool_call_id(&self, tool_index: usize) -> Option<&str> {
@@ -269,10 +269,7 @@ mod tests {
269 fn combined_parser_emits_tool_calls_from_visible_content() {
270 let tool = Qwen3XmlToolParser::create(&test_tools()).unwrap();
271 let mut parser = CombinedParser::new(None, Some(tool));
272 - assert!(matches!(
273 - parser.structural_tag_model(),
274 - Some(crate::tool::StructuralTagModel::Qwen3)
275 - ));
272 + assert!(parser.structural_tag_builder().is_some());
273
274 let output = collect(
275 &mut parser,
rust/src/parser/src/unified/mod.rs
+3 -3
@@ -16,7 +16,7 @@ use vllm_tokenizer::DynTokenizer;
16
17 use crate::reasoning::ReasoningError;
18 use crate::tool::{
19 - StructuralTagModel, Tool, ToolCallDelta, ToolParserError, ToolParserEvent, ToolParserOutput,
19 + StructuralTagBuilder, Tool, ToolCallDelta, ToolParserError, ToolParserEvent, ToolParserOutput,
20 };
21
22 /// Result alias for unified parser operations.
@@ -171,8 +171,8 @@ pub trait UnifiedParser: Send {
171 false
172 }
173
174 - /// Return the xgrammar structural-tag model used for strict tool calling.
175 - fn structural_tag_model(&self) -> Option<StructuralTagModel> {
174 + /// Return the xgrammar structural-tag builder used for strict tool calling.
175 + fn structural_tag_builder(&self) -> Option<&dyn StructuralTagBuilder> {
176 None
177 }
178