llama_cpp_bindings/tool_call_template_overrides/
gemma4_call_block.rs1use llama_cpp_bindings_types::PairedQuoteShape;
2use llama_cpp_bindings_types::ToolCallArgsShape;
3use llama_cpp_bindings_types::ToolCallMarkers;
4use llama_cpp_bindings_types::ToolCallValueQuote;
5
6pub struct Gemma4CallBlockOverride;
7
8impl Gemma4CallBlockOverride {
9 const TEMPLATE_FINGERPRINT: &'static str = "'<|tool_call>call:'";
10
11 #[must_use]
12 pub fn markers() -> ToolCallMarkers {
13 ToolCallMarkers {
14 open: "<|tool_call>call:".to_owned(),
15 close: "}".to_owned(),
16 args_shape: ToolCallArgsShape::PairedQuote(PairedQuoteShape {
17 name_args_separator: "{".to_owned(),
18 value_quote: ToolCallValueQuote {
19 open: "<|\"|>".to_owned(),
20 close: "<|\"|>".to_owned(),
21 },
22 }),
23 }
24 }
25
26 #[must_use]
27 pub fn detect(template: &str) -> Option<ToolCallMarkers> {
28 if !template.contains(Self::TEMPLATE_FINGERPRINT) {
29 return None;
30 }
31 Some(Self::markers())
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use llama_cpp_bindings_types::ToolCallArgsShape;
38
39 use super::Gemma4CallBlockOverride;
40
41 #[test]
42 fn detects_gemma4_template_with_tool_call_call_literal() {
43 use llama_cpp_bindings_types::PairedQuoteShape;
44 use llama_cpp_bindings_types::ToolCallValueQuote;
45
46 let template = "...{{- '<|tool_call>call:' + function['name'] + '{' -}}...";
47 let markers =
48 Gemma4CallBlockOverride::detect(template).expect("Gemma 4 template must be detected");
49
50 assert_eq!(markers.open, "<|tool_call>call:");
51 assert_eq!(markers.close, "}");
52 assert_eq!(
53 markers.args_shape,
54 ToolCallArgsShape::PairedQuote(PairedQuoteShape {
55 name_args_separator: "{".to_owned(),
56 value_quote: ToolCallValueQuote {
57 open: "<|\"|>".to_owned(),
58 close: "<|\"|>".to_owned(),
59 },
60 })
61 );
62 }
63
64 #[test]
65 fn returns_none_for_template_without_fingerprint() {
66 assert!(Gemma4CallBlockOverride::detect("just some plain template body").is_none());
67 }
68
69 #[test]
70 fn returns_none_for_empty_template() {
71 assert!(Gemma4CallBlockOverride::detect("").is_none());
72 }
73
74 #[test]
75 fn returns_none_when_fingerprint_substring_appears_without_jinja_apostrophes() {
76 let template = "doc explaining the <|tool_call>call: format in prose, not as a literal";
77 assert!(Gemma4CallBlockOverride::detect(template).is_none());
78 }
79}