pub const QWEN3_CHATML: &str = include_str!("chat_templates/qwen3-chatml.jinja");
pub const DEEPSEEK_V4_FLASH_0731: &str =
include_str!("chat_templates/deepseek-v4-flash-0731.jinja");
pub const QWEN3_CHATML_LEN: usize = 7764;
pub const DEEPSEEK_V4_FLASH_0731_LEN: usize = 7646;
pub fn arch_default_chat_template(arch: &str) -> Option<&'static str> {
match arch {
"qwen35" | "qwen35moe" => Some(QWEN3_CHATML),
"deepseek4" => Some(DEEPSEEK_V4_FLASH_0731),
_ => None,
}
}
pub fn validate_tool_chat_template(arch: &str, template: &str) -> Result<(), String> {
let required: &[&str] = match arch {
"gemma4" => &[
"<|turn>model",
"<|tool_call>",
"call:",
"<tool_call|>",
"<|tool_response>",
"<tool_response|>",
],
"qwen35" | "qwen35moe" => &[
"<|im_start|>",
"<|im_end|>",
"<tool_call>",
"<function=",
"</function>",
"</tool_call>",
"<tool_response>",
"</tool_response>",
],
_ => return Ok(()),
};
let missing: Vec<&str> = required
.iter()
.copied()
.filter(|marker| !template.contains(marker))
.collect();
if missing.is_empty() {
Ok(())
} else {
Err(format!(
"{arch} tokenizer.chat_template is incompatible with its native tool parser; missing markers: {}",
missing.join(", ")
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vendor_chat_template_lengths_match_fixtures() {
assert_eq!(
QWEN3_CHATML.len(),
QWEN3_CHATML_LEN,
"Qwen3 ChatML fixture drifted from vendor: expected {} bytes, fixture has {}",
QWEN3_CHATML_LEN,
QWEN3_CHATML.len()
);
assert_eq!(
DEEPSEEK_V4_FLASH_0731.len(),
DEEPSEEK_V4_FLASH_0731_LEN,
"DeepSeek-V4 template drifted from pinned llama.cpp reference"
);
}
#[test]
fn arch_default_qwen35_resolves_to_qwen3_chatml() {
assert_eq!(arch_default_chat_template("qwen35"), Some(QWEN3_CHATML));
assert_eq!(arch_default_chat_template("qwen35moe"), Some(QWEN3_CHATML));
}
#[test]
fn arch_default_deepseek4_resolves_to_flash_0731() {
assert_eq!(
arch_default_chat_template("deepseek4"),
Some(DEEPSEEK_V4_FLASH_0731)
);
}
#[test]
fn arch_default_unknown_arch_returns_none() {
assert_eq!(arch_default_chat_template("unknown"), None);
assert_eq!(arch_default_chat_template("qwen2"), None);
assert_eq!(arch_default_chat_template("gemma4"), None);
assert_eq!(arch_default_chat_template("llama"), None);
}
#[test]
fn native_tool_templates_match_their_registered_family_contracts() {
validate_tool_chat_template("qwen35moe", QWEN3_CHATML).expect("pinned Qwen 3.6 template");
let gemma =
include_str!("../serve/api/test_fixtures/gemma4-apex-embedded-chat-template.jinja");
validate_tool_chat_template("gemma4", gemma).expect("pinned Gemma 4 template");
}
#[test]
fn cross_family_or_incomplete_tool_templates_fail_closed() {
let gemma =
include_str!("../serve/api/test_fixtures/gemma4-apex-embedded-chat-template.jinja");
assert!(validate_tool_chat_template("qwen35moe", gemma).is_err());
assert!(validate_tool_chat_template("gemma4", QWEN3_CHATML).is_err());
assert!(validate_tool_chat_template("gemma4", "{{ messages }}").is_err());
}
}