[Rust Frontend] Upgrade MiniJinja to 2.22 & remove method lookup workaround (#51235)

Signed-off-by: Bugen Zhao <i@bugenzhao.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

Bugen Zhao committed Aug 11, 2026 at 10:23 UTC d8c70f22434afcbd6644aa43d3f23aecb6e5a09f
5 files changed +12 -98
rust/Cargo.lock
+4 -5
@@ -2402,9 +2402,9 @@ dependencies = [
2402
2403 [[package]]
2404 name = "minijinja"
2405 -version = "2.18.0"
2405 +version = "2.22.0"
2406 source = "registry+https://github.com/rust-lang/crates.io-index"
2407 -checksum = "328251e58ad8e415be6198888fc207502727dc77945806421ab34f35bf012e7d"
2407 +checksum = "ef84a52be188a1d4124bd717903fdde96ca4705f2b56adfe2d91fcc57fcb6987"
2408 dependencies = [
2409 "indexmap 2.13.0",
2410 "memo-map",
@@ -2414,9 +2414,9 @@ dependencies = [
2414
2415 [[package]]
2416 name = "minijinja-contrib"
2417 -version = "2.18.0"
2417 +version = "2.22.0"
2418 source = "registry+https://github.com/rust-lang/crates.io-index"
2419 -checksum = "8c6302e47d2b51f9fc978268ff7f5a014de5caa2ad48440309fd10ee711480d7"
2419 +checksum = "fd6e279dc925840c2d9ebc1e9f85410611d01292561297463ba3e516c3ad94dd"
2420 dependencies = [
2421 "minijinja",
2422 "serde",
@@ -5520,7 +5520,6 @@ dependencies = [
5520 "expect-test",
5521 "futures",
5522 "half",
5523 - "indexmap 2.13.0",
5523 "itertools 0.14.0",
5524 "llm-multimodal",
5525 "minijinja",
rust/Cargo.toml
+2 -2
@@ -61,8 +61,8 @@ itertools = "0.14.0"
61 libc = "0.2.177"
62 llm-multimodal = { git = "https://github.com/smg-project/llm-multimodal", rev = "15adba5e025d8636ba4a334fb379b1371f6196a1", default-features = false, features = ["native-tls"] }
63 mimalloc = "0.1.52"
64 -minijinja = { version = "2.0", features = ["unstable_machinery", "json", "builtins", "loader", "loop_controls", "preserve_order"] }
65 -minijinja-contrib = { version = "2.0", features = ["pycompat"] }
64 +minijinja = { version = "2.22", features = ["unstable_machinery", "json", "builtins", "loader", "loop_controls", "preserve_order"] }
65 +minijinja-contrib = { version = "2.22", features = ["pycompat"] }
66 native-tls-vendored = { package = "native-tls", version = "0.2.18", features = ["vendored"] }
67 ndarray = { version = "0.17", features = ["serde"] }
68 openai-harmony = { package = "oss-harmony", git = "https://github.com/oss-harmony/harmony", tag = "v0.0.11", default-features = false }
rust/src/chat/Cargo.toml
-1
@@ -10,7 +10,6 @@ asynk-strim-attr.workspace = true
10 easy-ext.workspace = true
11 futures.workspace = true
12 half.workspace = true
13 -indexmap.workspace = true
13 itertools.workspace = true
14 llm-multimodal.workspace = true
15 minijinja.workspace = true
rust/src/chat/src/renderer/hf/mod.rs
+6 -10
@@ -16,7 +16,6 @@ use self::format::{
16 ChatTemplateContentFormat, ChatTemplateContentFormatOption as ContentFormatOption,
17 };
18 use self::template::{CompiledChatTemplate, TemplateContext};
19 -use self::value::{TemplateValue, to_template_value};
19 use super::{ChatRenderer, RenderedPrompt, effective_template_kwargs};
20 use crate::error::Result;
21 use crate::request::{ChatContent, ChatContentPart, ChatMessage, ChatRequest};
@@ -28,7 +27,6 @@ mod error;
27 mod format;
28 mod template;
29 mod tojson;
31 -mod value;
30
31 pub use template::{load_chat_template, resolve_chat_template};
32
@@ -277,7 +275,7 @@ struct TemplateToolCall {
275 #[derive(Debug, Serialize)]
276 struct TemplateToolFunction {
277 name: String,
280 - arguments: TemplateValue,
278 + arguments: JsonValue,
279 }
280
281 #[derive(Debug, Serialize)]
@@ -291,7 +289,7 @@ pub(super) struct TemplateTool {
289 struct TemplateToolDefinition {
290 name: String,
291 description: Option<String>,
294 - parameters: TemplateValue,
292 + parameters: JsonValue,
293 strict: Option<bool>,
294 }
295
@@ -384,8 +382,6 @@ fn to_template_tool_calls(
382 error.as_report()
383 ))
384 })?;
387 - let arguments = to_template_value(arguments);
388 -
385 tool_calls.push(TemplateToolCall {
386 id: tool_call.id.clone(),
387 r#type: "function",
@@ -564,7 +560,7 @@ fn to_template_tools(tools: &[ChatTool]) -> Vec<TemplateTool> {
560 function: TemplateToolDefinition {
561 name: tool.name.clone(),
562 description: tool.description.clone(),
567 - parameters: to_template_value(tool.parameters.clone()),
563 + parameters: tool.parameters.clone(),
564 strict: tool.strict,
565 },
566 })
@@ -1008,7 +1004,7 @@ mod tests {
1004 .apply_chat_template(&request)
1005 .unwrap();
1006
1011 - assert_eq!(rendered.prompt, Prompt::Text("<bos>|true".to_string()));
1007 + assert_eq!(rendered.prompt, Prompt::Text("<bos>|True".to_string()));
1008 }
1009
1010 #[test]
@@ -1093,7 +1089,7 @@ mod tests {
1089
1090 let rendered = renderer.render(&request).unwrap().prompt;
1091
1096 - assert_eq!(rendered, Prompt::Text("true|x".to_string()));
1092 + assert_eq!(rendered, Prompt::Text("True|x".to_string()));
1093 }
1094
1095 #[test]
@@ -1146,7 +1142,7 @@ mod tests {
1142
1143 let rendered = renderer.render(&request).unwrap();
1144
1149 - assert_eq!(rendered.prompt, Prompt::Text("none|true".to_string()));
1145 + assert_eq!(rendered.prompt, Prompt::Text("none|True".to_string()));
1146 assert_eq!(
1147 rendered.effective_template_kwargs.get("reasoning_effort"),
1148 Some(&Value::String("none".to_string()))
rust/src/chat/src/renderer/hf/value.rs deleted
-80
@@ -1,80 +0,0 @@
1 -// SPDX-License-Identifier: Apache-2.0
2 -// SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 -
4 -use std::sync::Arc;
5 -
6 -use indexmap::IndexMap;
7 -use minijinja::value::{Enumerator, Object, ObjectExt, ObjectRepr};
8 -use minijinja::{Error as TemplateError, ErrorKind as TemplateErrorKind, State};
9 -use serde::Serialize;
10 -use serde_json::Value as JsonValue;
11 -
12 -/// A wrapper around `minijinja::Value` that can be constructed with `to_template_value` and used
13 -/// as a value in the chat template.
14 -#[derive(Debug, Serialize)]
15 -#[serde(transparent)]
16 -pub(super) struct TemplateValue(minijinja::Value);
17 -
18 -pub(super) fn to_template_value(value: JsonValue) -> TemplateValue {
19 - TemplateValue(match value {
20 - JsonValue::Array(values) => values
21 - .into_iter()
22 - .map(to_template_value)
23 - .map(|value| value.0)
24 - .collect::<minijinja::Value>(),
25 - JsonValue::Object(values) => minijinja::Value::from_object(TemplateMap(
26 - values
27 - .into_iter()
28 - .map(|(key, value)| (key, to_template_value(value).0))
29 - .collect(),
30 - )),
31 - // For primitive values, directly convert them to `minijinja::Value` using `from_serialize`.
32 - value => minijinja::Value::from_serialize(value),
33 - })
34 -}
35 -
36 -/// A custom map type that always returns `UnknownMethod` for method calls, so that pycompat can
37 -/// always handle dict methods through the unknown-method callback.
38 -///
39 -/// Use `IndexMap` to preserve the original key order when iterating.
40 -///
41 -/// MiniJinja's default map can resolve a same-named field before Python dict methods. HF templates
42 -/// commonly call `dict.items()`, which would fail if the map had an `items` field.
43 -/// See issue: https://github.com/mitsuhiko/minijinja/issues/903
44 -#[derive(Debug)]
45 -struct TemplateMap(IndexMap<String, minijinja::Value>);
46 -
47 -impl Object for TemplateMap {
48 - fn repr(self: &Arc<Self>) -> ObjectRepr {
49 - ObjectRepr::Map
50 - }
51 -
52 - fn get_value(self: &Arc<Self>, key: &minijinja::Value) -> Option<minijinja::Value> {
53 - self.0.get(key.as_str()?).cloned()
54 - }
55 -
56 - fn get_value_by_str(self: &Arc<Self>, key: &str) -> Option<minijinja::Value> {
57 - self.0.get(key).cloned()
58 - }
59 -
60 - fn enumerate(self: &Arc<Self>) -> Enumerator {
61 - self.mapped_rev_enumerator(|this| {
62 - Box::new(this.0.keys().map(|key| minijinja::Value::from(key.as_str())))
63 - })
64 - }
65 -
66 - fn enumerator_len(self: &Arc<Self>) -> Option<usize> {
67 - Some(self.0.len())
68 - }
69 -
70 - fn call_method(
71 - self: &Arc<Self>,
72 - _state: &State<'_, '_>,
73 - _method: &str,
74 - _args: &[minijinja::Value],
75 - ) -> std::result::Result<minijinja::Value, TemplateError> {
76 - // Always return `UnknownMethod` for method calls,
77 - // so that pycompat can handle dict methods through the unknown-method callback.
78 - Err(TemplateError::from(TemplateErrorKind::UnknownMethod))
79 - }
80 -}