tool_chat_template_gemma3_pythonic.jinja 5.53 KB
Newer Older
raojy's avatar
raojy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{#- Begin-of-sequence token to start the model prompt -#}
{{ bos_token }}
{#- Extracts the system message. Gemma does not support system messages so it will be prepended to first user message. -#}
{%- if messages[0]['role'] == 'system' -%}
    {%- if messages[0]['content'] is string -%}
        {%- set first_user_prefix = messages[0]['content'] + '\n\n' -%}
    {%- else -%}
        {%- set first_user_prefix = messages[0]['content'][0]['text'] + '\n\n' -%}
    {%- endif -%}
    {%- set loop_messages = messages[1:] -%}
{%- else -%}
    {%- set first_user_prefix = "" -%}
    {%- set loop_messages = messages -%}
{%- endif -%}
{#- Set tools to none if not defined for this ChatCompletion request (helps avoid errors later) -#}
{%- if not tools is defined %}
    {%- set tools = none %}
{%- endif %}
{#- Validate alternating user/assistant messages (excluding 'tool' messages and ones with tool_calls) -#}
{%- for message in loop_messages | rejectattr("role", "equalto", "tool") | selectattr("tool_calls", "undefined") -%}
    {%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
        {{ raise_exception("Conversation roles must alternate user/assistant/user/assistant/...") }}
    {%- endif -%}
{%- endfor -%}

{#- Main loop over all messages in the conversation history -#}
{%- for message in loop_messages -%}
    {#- Normalize roles for model prompt formatting -#}
    {%- if (message['role'] == 'assistant') -%}
        {%- set role = "model" -%}
    {%- elif (message['role'] == 'tool') -%}
        {%- set role = "user" -%}
    {%- else -%}
        {%- set role = message['role'] -%}
    {%- endif -%}
    {#- Mark the start of a message block with the appropriate role -#}
    {{ '<start_of_turn>' + role + '\n' -}}

    {#- Insert system message content (if present) at the beginning of the first message. -#}
    {%- if loop.first -%}
        {{ first_user_prefix }}
        {#- Append system message with tool information if using tools in message request. -#}
        {%- if tools is not none -%}
            {{- "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" -}}
            {{- "Example Format: [func_name1(params_name1=params_value1, params_name2=params_value2...), func_name2(params)] \n" -}}
            {{- "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" -}}
            {{- "Here is a list of functions in JSON format that you can invoke.\n" -}}
            {{- tools | tojson(indent=4) -}}
            {{- "\n\n" -}}
        {%- endif -%}
    {%- endif -%}

    {#- Format model tool calls (turns where model indicates they want to call a tool) -#}
    {%- if 'tool_calls' in message -%}
        {#- Opening bracket for tool call list. -#}
        {{- '[' -}}
        {#- For each tool call -#}
        {%- for tool_call in message.tool_calls -%}
            {#- Get tool call function. -#}
            {%- if tool_call.function is defined -%}
                {%- set tool_call = tool_call.function -%}
            {%- endif -%}
            {#- Function name & opening parenthesis. -#}
            {{- tool_call.name + '(' -}}

            {#-- Handle arguments as list (positional) or dict (named) --#}
            {#-- Named arguments (dict) --#}
            {%- if tool_call.arguments is iterable and tool_call.arguments is mapping -%}
                {%- set first = true -%}
                {%- for key, val in tool_call.arguments.items() -%}
                    {%- if not first %}, {% endif -%}
                    {{ key }}={{ val | tojson }}
                    {%- set first = false -%}
                {%- endfor -%}
            {#-- Positional arguments (list) --#}
            {%- elif tool_call.arguments is iterable -%}
                {{- tool_call.arguments | map('tojson') | join(', ') -}}
            {#-- Fallback: single positional value --#}
            {%- else -%}
                {{- tool_call.arguments | tojson -}}
            {#-- Closing parenthesis. --#}
            {%- endif -%}
                {{- ')' -}}
            {#-- If more than one tool call, place comma and move to formatting next tool call --#}
            {%- if not loop.last -%}, {% endif -%}
        {%- endfor -%}
        {#- Closing bracket for tool call list. -#}
        {{- ']' -}}
    {%- endif -%}
    
    {#- Tool response start tag (for messages from a tool) -#}
    {%- if (message['role'] == 'tool') -%}
        {{ '<tool_response>\n' -}}
    {%- endif -%}

    {#- Render the message content: handle plain string or multimodal content like image/text -#}
    {%- if message['content'] is string -%}
        {{ message['content'] | trim }}
    {%- elif message['content'] is iterable -%}
        {%- for item in message['content'] -%}
            {%- if item['type'] == 'image' -%}
                {{ '<start_of_image>' }}
            {%- elif item['type'] == 'text' -%}
                {{ item['text'] | trim }}
            {%- endif -%}
        {%- endfor -%}
    {%- else -%}
        {{ raise_exception("Invalid content type") }}
    {%- endif -%}

    {#- Tool response end tag -#}
    {%- if (message['role'] == 'tool') -%}
        {{ '</tool_response>' -}}
    {%- endif -%}

    {#- Mark end of a single turn -#}
    {{ '<end_of_turn>\n' }}
{%- endfor -%}

{#- If generation is to be triggered, add model prompt prefix -#}
{%- if add_generation_prompt -%}
    {{'<start_of_turn>model\n'}}
{%- endif -%}