A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[Misc] Add gemma3 chat template with pythonic-style function calling (#17149)
Signed-off-by: Philip Chung <philip.f.chung@gmail.com>
Philip Chung committed
Aug 21, 2025 at 21:06 UTC
de9c085e17f049c3cea970aa5c5f784231706795
1 file changed
+123
examples/tool_chat_template_gemma3_pythonic.jinja
new
+123
@@ -0,0 +1,123 @@
1
+{#- Begin-of-sequence token to start the model prompt -#}
2
+{{ bos_token }}
3
+{#- Extracts the system message. Gemma does not support system messages so it will be prepended to first user message. -#}
4
+{%- if messages[0]['role'] == 'system' -%}
5
+ {%- if messages[0]['content'] is string -%}
6
+ {%- set first_user_prefix = messages[0]['content'] + '\n\n' -%}
7
+ {%- else -%}
8
+ {%- set first_user_prefix = messages[0]['content'][0]['text'] + '\n\n' -%}
9
+ {%- endif -%}
10
+ {%- set loop_messages = messages[1:] -%}
11
+{%- else -%}
12
+ {%- set first_user_prefix = "" -%}
13
+ {%- set loop_messages = messages -%}
14
+{%- endif -%}
15
+{#- Set tools to none if not defined for this ChatCompletion request (helps avoid errors later) -#}
16
+{%- if not tools is defined %}
17
+ {%- set tools = none %}
18
+{%- endif %}
19
+{#- Validate alternating user/assistant messages (excluding 'tool' messages and ones with tool_calls) -#}
20
+{%- for message in loop_messages | rejectattr("role", "equalto", "tool") | selectattr("tool_calls", "undefined") -%}
21
+ {%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
22
+ {{ raise_exception("Conversation roles must alternate user/assistant/user/assistant/...") }}
23
+ {%- endif -%}
24
+{%- endfor -%}
25
+
26
+{#- Main loop over all messages in the conversation history -#}
27
+{%- for message in loop_messages -%}
28
+ {#- Normalize roles for model prompt formatting -#}
29
+ {%- if (message['role'] == 'assistant') -%}
30
+ {%- set role = "model" -%}
31
+ {%- elif (message['role'] == 'tool') -%}
32
+ {%- set role = "user" -%}
33
+ {%- else -%}
34
+ {%- set role = message['role'] -%}
35
+ {%- endif -%}
36
+ {#- Mark the start of a message block with the appropriate role -#}
37
+ {{ '<start_of_turn>' + role + '\n' -}}
38
+
39
+ {#- Insert system message content (if present) at the beginning of the first message. -#}
40
+ {%- if loop.first -%}
41
+ {{ first_user_prefix }}
42
+ {#- Append system message with tool information if using tools in message request. -#}
43
+ {%- if tools is not none -%}
44
+ {{- "Tools (functions) are available. If you decide to invoke one or more of the tools, you must respond with a python list of the function calls.\n" -}}
45
+ {{- "Example Format: [func_name1(params_name1=params_value1, params_name2=params_value2...), func_name2(params)] \n" -}}
46
+ {{- "Do not use variables. DO NOT USE MARKDOWN SYNTAX. You SHOULD NOT include any other text in the response if you call a function. If none of the functions can be used, point it out. If you lack the parameters required by the function, also point it out.\n" -}}
47
+ {{- "Here is a list of functions in JSON format that you can invoke.\n" -}}
48
+ {{- tools | tojson(indent=4) -}}
49
+ {{- "\n\n" -}}
50
+ {%- endif -%}
51
+ {%- endif -%}
52
+
53
+ {#- Format model tool calls (turns where model indicates they want to call a tool) -#}
54
+ {%- if 'tool_calls' in message -%}
55
+ {#- Opening bracket for tool call list. -#}
56
+ {{- '[' -}}
57
+ {#- For each tool call -#}
58
+ {%- for tool_call in message.tool_calls -%}
59
+ {#- Get tool call function. -#}
60
+ {%- if tool_call.function is defined -%}
61
+ {%- set tool_call = tool_call.function -%}
62
+ {%- endif -%}
63
+ {#- Function name & opening parenthesis. -#}
64
+ {{- tool_call.name + '(' -}}
65
+
66
+ {#-- Handle arguments as list (positional) or dict (named) --#}
67
+ {#-- Named arguments (dict) --#}
68
+ {%- if tool_call.arguments is iterable and tool_call.arguments is mapping -%}
69
+ {%- set first = true -%}
70
+ {%- for key, val in tool_call.arguments.items() -%}
71
+ {%- if not first %}, {% endif -%}
72
+ {{ key }}={{ val | tojson }}
73
+ {%- set first = false -%}
74
+ {%- endfor -%}
75
+ {#-- Positional arguments (list) --#}
76
+ {%- elif tool_call.arguments is iterable -%}
77
+ {{- tool_call.arguments | map('tojson') | join(', ') -}}
78
+ {#-- Fallback: single positional value --#}
79
+ {%- else -%}
80
+ {{- tool_call.arguments | tojson -}}
81
+ {#-- Closing parenthesis. --#}
82
+ {%- endif -%}
83
+ {{- ')' -}}
84
+ {#-- If more than one tool call, place comma and move to formatting next tool call --#}
85
+ {%- if not loop.last -%}, {% endif -%}
86
+ {%- endfor -%}
87
+ {#- Closing bracket for tool call list. -#}
88
+ {{- ']' -}}
89
+ {%- endif -%}
90
+
91
+ {#- Tool response start tag (for messages from a tool) -#}
92
+ {%- if (message['role'] == 'tool') -%}
93
+ {{ '<tool_response>\n' -}}
94
+ {%- endif -%}
95
+
96
+ {#- Render the message content: handle plain string or multimodal content like image/text -#}
97
+ {%- if message['content'] is string -%}
98
+ {{ message['content'] | trim }}
99
+ {%- elif message['content'] is iterable -%}
100
+ {%- for item in message['content'] -%}
101
+ {%- if item['type'] == 'image' -%}
102
+ {{ '<start_of_image>' }}
103
+ {%- elif item['type'] == 'text' -%}
104
+ {{ item['text'] | trim }}
105
+ {%- endif -%}
106
+ {%- endfor -%}
107
+ {%- else -%}
108
+ {{ raise_exception("Invalid content type") }}
109
+ {%- endif -%}
110
+
111
+ {#- Tool response end tag -#}
112
+ {%- if (message['role'] == 'tool') -%}
113
+ {{ '</tool_response>' -}}
114
+ {%- endif -%}
115
+
116
+ {#- Mark end of a single turn -#}
117
+ {{ '<end_of_turn>\n' }}
118
+{%- endfor -%}
119
+
120
+{#- If generation is to be triggered, add model prompt prefix -#}
121
+{%- if add_generation_prompt -%}
122
+ {{'<start_of_turn>model\n'}}
123
+{%- endif -%}
\ No newline at end of file