Skip to content

Commit c30c27c

Browse files
committed
add gemma3 chat template with pythonic-style function calling (--tool-call-parser=pythonic)
1 parent 9420a1f commit c30c27c

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -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 -%}

0 commit comments

Comments
 (0)