mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2025-11-21 12:16:57 +00:00
common : Generalized XML-style tool-call parsing with streaming support (GLM 4.5/4.6 + MiniMax M2 + SeedOSS + Kimi-K2 + Qwen3-Coder + Apriel-1.5 + Xiaomi-MiMo) (#16932)
* Add files via upload
* fix unit test
* fix crashes for --reasoning-format=none
* Patch buggy official MiniMax-M2 chat template
* add upstream minja fix: https://github.com/ochafik/minja/pull/7
* Fix <think> token not generated
* add test copied from https://github.com/ggml-org/llama.cpp/pull/16946
* cleanup
* Hopes to fix the compilation error on CI
* Delete chat template patching since it’s fixed by upstream Minja
* Remove undeeded Minimax-M2 template patch
https://github.com/ochafik/minja/pull/7#issuecomment-3480356100
* Add proper handling of optional parameters with test
merged tests from: 23d4bb75c4
* Fix making all tool parameters optional
* Move xml tool parser to separate file
* cleanup & add tests for GLM4.5
* add streaming tests & enhancement & cleanups
Add streaming test for both GLM 4.5 and minimax-m2.
Cleanup for preserved_tokens.
Cleanup for grammar rule name.
Enhance the parser's stability.
* cleanup & add support for Kimi-K2 Qwen3-Coder Apriel-1.5 Xiaomi-MiMo
* apply suggestions from reviewers
* fix a misuse for data.grammar_lazy
* fix grammar when tool have no argument
* Fix `no triggers set for lazy grammar!` for GLM4.5/4.6. Insert additional stops for Kimi-K2
* update chat.cpp
* fix grammar for GLM 4.5/4.6
* Try fix Jinja template for GLM
* Try fix GLM-4.6.jinja
* Update common/chat-parser-xml-toolcall.cpp
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
* Update tests/test-chat.cpp
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
* improve chat template for GLM, rename Kimi-K2 template to Kimi-K2-Thinking
* Improve Kimi-K2 chat template
* Fix unit test
* Fix "Invalid tool call arguments passed" in a rare case.
In a rare case, the model may emit a raw string that begins with a valid JSON string. This commit adds unit tests to cover that scenario and fixes the regression introduced during the Kimi-K2 adaptation.
---------
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
This commit is contained in:
106
models/templates/GLM-4.6.jinja
Normal file
106
models/templates/GLM-4.6.jinja
Normal file
@@ -0,0 +1,106 @@
|
||||
[gMASK]<sop>
|
||||
{%- if tools -%}
|
||||
<|system|>
|
||||
# Tools
|
||||
|
||||
You may call one or more functions to assist with the user query.
|
||||
|
||||
You are provided with function signatures within <tools></tools> XML tags:
|
||||
<tools>
|
||||
{% for tool in tools %}
|
||||
{{ tool | tojson(ensure_ascii=False) }}
|
||||
{% endfor %}
|
||||
</tools>
|
||||
|
||||
For each function call, output the function name and arguments within the following XML format:
|
||||
<tool_call>{function-name}
|
||||
<arg_key>{arg-key-1}</arg_key>
|
||||
<arg_value>{arg-value-1}</arg_value>
|
||||
<arg_key>{arg-key-2}</arg_key>
|
||||
<arg_value>{arg-value-2}</arg_value>
|
||||
...
|
||||
</tool_call>{%- endif -%}
|
||||
{%- macro visible_text(content) -%}
|
||||
{%- if content is string -%}
|
||||
{{- content }}
|
||||
{%- elif content is iterable and content is not mapping -%}
|
||||
{%- for item in content -%}
|
||||
{%- if item is mapping and item.type == 'text' -%}
|
||||
{{- item.text }}
|
||||
{%- elif item is string -%}
|
||||
{{- item }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- else -%}
|
||||
{{- content }}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
{%- set ns = namespace(last_user_index=-1) %}
|
||||
{%- for m in messages %}
|
||||
{%- if m.role == 'user' %}
|
||||
{% set ns.last_user_index = loop.index0 -%}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{% for m in messages %}
|
||||
{%- if m.role == 'user' -%}<|user|>
|
||||
{{ visible_text(m.content) }}
|
||||
{{- '/nothink' if (enable_thinking is defined and not enable_thinking and not visible_text(m.content).endswith("/nothink")) else '' -}}
|
||||
{%- elif m.role == 'assistant' -%}
|
||||
<|assistant|>
|
||||
{%- set reasoning_content = '' %}
|
||||
{%- set content = visible_text(m.content) %}
|
||||
{%- if m.reasoning_content is string %}
|
||||
{%- set reasoning_content = m.reasoning_content %}
|
||||
{%- else %}
|
||||
{%- if '</think>' in content %}
|
||||
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
||||
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if loop.index0 > ns.last_user_index and reasoning_content -%}
|
||||
{{ '\n<think>' + reasoning_content.strip() + '</think>'}}
|
||||
{%- else -%}
|
||||
{{ '\n<think></think>' }}
|
||||
{%- endif -%}
|
||||
{%- if content.strip() -%}
|
||||
{{ '\n' + content.strip() }}
|
||||
{%- endif -%}
|
||||
{% if m.tool_calls %}
|
||||
{% for tc in m.tool_calls %}
|
||||
{%- if tc.function %}
|
||||
{%- set tc = tc.function %}
|
||||
{%- endif %}
|
||||
{{ '\n<tool_call>' + tc.name }}
|
||||
{% set _args = tc.arguments or {} %}
|
||||
{% if _args is not mapping %}
|
||||
{{ raise_exception("Invalid tool call arguments passed: " + _args | string) }}
|
||||
{% endif %}
|
||||
{% for k, v in _args.items() %}
|
||||
<arg_key>{{ k }}</arg_key>
|
||||
<arg_value>{{ v | tojson(ensure_ascii=False) if v is not string else v }}</arg_value>
|
||||
{% endfor %}
|
||||
</tool_call>{% endfor %}
|
||||
{% endif %}
|
||||
{%- elif m.role == 'tool' -%}
|
||||
{%- if m.content is string -%}
|
||||
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
||||
{{- '<|observation|>' }}
|
||||
{%- endif %}
|
||||
{{- '\n<tool_response>\n' }}
|
||||
{{- m.content }}
|
||||
{{- '\n</tool_response>' }}
|
||||
{%- else -%}
|
||||
<|observation|>{% for tr in m.content %}
|
||||
|
||||
<tool_response>
|
||||
{{ tr.output if tr.output is defined else tr }}
|
||||
</tool_response>{% endfor -%}
|
||||
{% endif -%}
|
||||
{%- elif m.role == 'system' -%}
|
||||
<|system|>
|
||||
{{ visible_text(m.content) }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if add_generation_prompt -%}
|
||||
<|assistant|>{{- '\n<think></think>' if (enable_thinking is defined and not enable_thinking) else '' -}}
|
||||
{%- endif -%}
|
||||
64
models/templates/Kimi-K2-Instruct.jinja
Normal file
64
models/templates/Kimi-K2-Instruct.jinja
Normal file
@@ -0,0 +1,64 @@
|
||||
{% macro render_content(msg) -%}
|
||||
{%- set c = msg.get('content') -%}
|
||||
{%- if c is string -%}
|
||||
{{ c }}
|
||||
{%- elif c is not none -%}
|
||||
{% for content in c -%}
|
||||
{% if content['type'] == 'image' or 'image' in content or 'image_url' in content -%}
|
||||
<|media_start|>image<|media_content|><|media_pad|><|media_end|>
|
||||
{% else -%}
|
||||
{{ content['text'] }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- set tool_response_queue = namespace(ids=[]) -%}
|
||||
{%- set tool_call_counter = namespace(value=1) -%}
|
||||
|
||||
{%- if tools -%}
|
||||
<|im_system|>tool_declare<|im_middle|>{{ tools | tojson }}<|im_end|>
|
||||
{%- endif -%}
|
||||
{% for message in messages %}
|
||||
{%- if loop.first and messages[0]['role'] != 'system' -%}
|
||||
<|im_system|>system<|im_middle|>You are Kimi, an AI assistant created by Moonshot AI.<|im_end|>
|
||||
{% endif %}
|
||||
|
||||
{%- set role_name = message.get('name') or message['role'] -%}
|
||||
{%- if message['role'] == 'user' -%}
|
||||
<|im_user|>{{role_name}}<|im_middle|>
|
||||
{%- elif message['role'] == 'assistant' -%}
|
||||
<|im_assistant|>{{role_name}}<|im_middle|>
|
||||
{%- else -%}
|
||||
<|im_system|>{{role_name}}<|im_middle|>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if message['role'] == 'assistant' and message.get('tool_calls') -%}
|
||||
{{render_content(message)}}<|tool_calls_section_begin|>
|
||||
{%- for tool_call in message['tool_calls'] -%}
|
||||
{%- if tool_call['id'] is defined -%}
|
||||
{%- set formatted_id = tool_call['id'] -%}
|
||||
{%- else -%}
|
||||
{%- set formatted_id = 'functions.' + tool_call['function']['name'] + ':' + (tool_call_counter.value | string) -%}
|
||||
{%- set tool_call_counter.value = tool_call_counter.value + 1 -%}
|
||||
{%- endif -%}
|
||||
{%- set _ = tool_response_queue.ids.append(formatted_id) -%}
|
||||
<|tool_call_begin|>{{ formatted_id }}<|tool_call_argument_begin|>{% if tool_call['function']['arguments'] is string %}{{ tool_call['function']['arguments'] }}{% else %}{{ tool_call['function']['arguments'] | tojson }}{% endif %}<|tool_call_end|>
|
||||
{%- endfor -%}
|
||||
<|tool_calls_section_end|>
|
||||
{%- elif message['role'] == 'tool' -%}
|
||||
{%- if tool_response_queue.ids -%}
|
||||
{%- set tool_call_id = tool_response_queue.ids.pop(0) -%}
|
||||
{%- else -%}
|
||||
{%- set tool_call_id = 'functions.' + message.get('name', 'unknown') + ':' + (tool_call_counter.value | string) -%}
|
||||
{%- endif -%}
|
||||
## Return of {{ tool_call_id }}
|
||||
{{render_content(message)}}
|
||||
{%- elif message['content'] is not none -%}
|
||||
{{render_content(message)}}
|
||||
{%- endif -%}
|
||||
<|im_end|>
|
||||
{%- endfor -%}
|
||||
{%- if add_generation_prompt -%}
|
||||
<|im_assistant|>assistant<|im_middle|>
|
||||
{%- endif -%}
|
||||
112
models/templates/Kimi-K2-Thinking.jinja
Normal file
112
models/templates/Kimi-K2-Thinking.jinja
Normal file
@@ -0,0 +1,112 @@
|
||||
{%- macro render_content(msg) -%}
|
||||
{%- set c = msg.get('content') -%}
|
||||
{%- if c is string -%}
|
||||
{{ c }}
|
||||
{%- elif c is not none -%}
|
||||
{% for content in c -%}
|
||||
{% if content['type'] == 'image' or 'image' in content or 'image_url' in content -%}
|
||||
<|media_start|>image<|media_content|><|media_pad|><|media_end|>
|
||||
{% else -%}
|
||||
{{ content['text'] }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% macro set_roles(message) -%}
|
||||
{%- set role_name = message.get('name') or message['role'] -%}
|
||||
{%- if message['role'] == 'user' -%}
|
||||
<|im_user|>{{role_name}}<|im_middle|>
|
||||
{%- elif message['role'] == 'assistant' -%}
|
||||
<|im_assistant|>{{role_name}}<|im_middle|>
|
||||
{%- else -%}
|
||||
<|im_system|>{{role_name}}<|im_middle|>
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- set tool_response_queue = namespace(ids=[]) -%}
|
||||
{%- set tool_call_counter = namespace(value=1) -%}
|
||||
|
||||
{%- macro render_toolcalls(message) -%}
|
||||
<|tool_calls_section_begin|>
|
||||
{%- for tool_call in message['tool_calls'] -%}
|
||||
{%- if tool_call['id'] is defined -%}
|
||||
{%- set formatted_id = tool_call['id'] -%}
|
||||
{%- else -%}
|
||||
{%- set formatted_id = 'functions.' + tool_call['function']['name'] + ':' + (tool_call_counter.value | string) -%}
|
||||
{%- set tool_call_counter.value = tool_call_counter.value + 1 -%}
|
||||
{%- endif -%}
|
||||
{%- set _ = tool_response_queue.ids.append(formatted_id) -%}
|
||||
<|tool_call_begin|>{{ formatted_id }}<|tool_call_argument_begin|>{% if tool_call['function']['arguments'] is string %}{{ tool_call['function']['arguments'] }}{% else %}{{ tool_call['function']['arguments'] | tojson }}{% endif %}<|tool_call_end|>
|
||||
{%- endfor -%}
|
||||
<|tool_calls_section_end|>
|
||||
{%- endmacro -%}
|
||||
|
||||
|
||||
{# Find last non-tool-call assisitant message #}
|
||||
{%- set ns = namespace(last_non_tool_call_assistant_msg=-1) -%}
|
||||
{%- for idx in range(messages|length-1, -1, -1) -%}
|
||||
{%- if messages[idx]['role'] == 'assistant' and not messages[idx].get('tool_calls') -%}
|
||||
{%- set ns.last_non_tool_call_assistant_msg = idx -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{# split all messages into history & suffix, reasoning_content in suffix should be reserved.#}
|
||||
{%- set hist_msgs = messages[:ns.last_non_tool_call_assistant_msg+1] -%}
|
||||
{%- set suffix_msgs = messages[ns.last_non_tool_call_assistant_msg+1:] -%}
|
||||
|
||||
{%- if tools -%}
|
||||
<|im_system|>tool_declare<|im_middle|>{{ tools | tojson }}<|im_end|>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if messages|length == 0 or messages[0]['role'] != 'system' -%}
|
||||
<|im_system|>system<|im_middle|>You are Kimi, an AI assistant created by Moonshot AI.<|im_end|>
|
||||
{%- endif -%}
|
||||
|
||||
{%- for message in hist_msgs -%}
|
||||
{{set_roles(message)}}
|
||||
{%- if message['role'] == 'assistant' -%}
|
||||
<think></think>{{render_content(message)}}
|
||||
{%- if message.get('tool_calls') -%}
|
||||
{{render_toolcalls(message)}}
|
||||
{%- endif -%}
|
||||
{%- elif message['role'] == 'tool' -%}
|
||||
{%- if tool_response_queue.ids -%}
|
||||
{%- set tool_call_id = tool_response_queue.ids.pop(0) -%}
|
||||
{%- else -%}
|
||||
{%- set tool_call_id = 'functions.' + message.get('name', 'unknown') + ':' + (tool_call_counter.value | string) -%}
|
||||
{%- endif -%}
|
||||
## Return of {{ tool_call_id }}
|
||||
{{render_content(message)}}
|
||||
{%- elif message['content'] is not none -%}
|
||||
{{render_content(message)}}
|
||||
{%- endif -%}
|
||||
<|im_end|>
|
||||
{%- endfor -%}
|
||||
|
||||
{%- for message in suffix_msgs -%}
|
||||
{{set_roles(message)}}
|
||||
{%- if message['role'] == 'assistant' -%}
|
||||
{%- set rc = message.get('reasoning_content', '') -%}
|
||||
<think>{{rc}}</think>{{render_content(message)}}
|
||||
{%- if message.get('tool_calls') -%}
|
||||
{{render_toolcalls(message)}}
|
||||
{%- endif -%}
|
||||
{%- elif message['role'] == 'tool' -%}
|
||||
{%- if tool_response_queue.ids -%}
|
||||
{%- set tool_call_id = tool_response_queue.ids.pop(0) -%}
|
||||
{%- else -%}
|
||||
{%- set tool_call_id = 'functions.' + message.get('name', 'unknown') + ':' + (tool_call_counter.value | string) -%}
|
||||
{%- endif -%}
|
||||
## Return of {{ tool_call_id }}
|
||||
{{render_content(message)}}
|
||||
{%- elif message['content'] is not none -%}
|
||||
{{render_content(message)}}
|
||||
{%- endif -%}
|
||||
<|im_end|>
|
||||
{%- endfor -%}
|
||||
|
||||
|
||||
{%- if add_generation_prompt -%}
|
||||
<|im_assistant|>assistant<|im_middle|>
|
||||
{%- endif -%}
|
||||
54
models/templates/MiMo-VL.jinja
Normal file
54
models/templates/MiMo-VL.jinja
Normal file
@@ -0,0 +1,54 @@
|
||||
{%- if tools %}
|
||||
{{- '<|im_start|>system\n' }}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{{- messages[0]['content'] }}
|
||||
{%- else %}
|
||||
{{- 'You are MiMo, an AI assistant developed by Xiaomi.' }}
|
||||
{%- endif %}
|
||||
{{- "\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
|
||||
{%- for tool in tools %}
|
||||
{{- "\n" }}
|
||||
{{- tool | tojson }}
|
||||
{%- endfor %}
|
||||
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
|
||||
{%- else %}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{{- '<|im_start|>system\n' + messages[0]['content'] + '<|im_end|>\n' }}
|
||||
{%- else %}
|
||||
{{- '<|im_start|>system\nYou are MiMo, an AI assistant developed by Xiaomi.<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- for message in messages %}
|
||||
{%- if (message.role == "user") or (message.role == "system" and not loop.first) or (message.role == "assistant" and not message.tool_calls) %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
|
||||
{%- elif message.role == "assistant" %}
|
||||
{{- '<|im_start|>' + message.role }}
|
||||
{%- if message.content %}
|
||||
{{- '\n' + message.content }}
|
||||
{%- endif %}
|
||||
{%- for tool_call in message.tool_calls %}
|
||||
{%- if tool_call.function is defined %}
|
||||
{%- set tool_call = tool_call.function %}
|
||||
{%- endif %}
|
||||
{{- '\n<tool_call>\n{"name": "' }}
|
||||
{{- tool_call.name }}
|
||||
{{- '", "arguments": ' }}
|
||||
{{- tool_call.arguments | tojson }}
|
||||
{{- '}\n</tool_call>' }}
|
||||
{%- endfor %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- elif message.role == "tool" %}
|
||||
{%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != "tool") %}
|
||||
{{- '<|im_start|>user' }}
|
||||
{%- endif %}
|
||||
{{- '\n<tool_response>\n' }}
|
||||
{{- message.content }}
|
||||
{{- '\n</tool_response>' }}
|
||||
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %}
|
||||
159
models/templates/MiniMax-M2.jinja
Normal file
159
models/templates/MiniMax-M2.jinja
Normal file
@@ -0,0 +1,159 @@
|
||||
{# ----------‑‑‑ special token variables ‑‑‑---------- #}
|
||||
{%- set toolcall_begin_token = '<minimax:tool_call>' -%}
|
||||
{%- set toolcall_end_token = '</minimax:tool_call>' -%}
|
||||
{#- Tool Rendering Functions ============================================== -#}
|
||||
{%- macro render_tool_namespace(namespace_name, tool_list) -%}
|
||||
{%- for tool in tool_list -%}
|
||||
<tool>{{ tool.function | tojson(ensure_ascii=False) }}</tool>
|
||||
{% endfor -%}
|
||||
{%- endmacro -%}
|
||||
{%- macro visible_text(content) -%}
|
||||
{%- if content is string -%}
|
||||
{{ content }}
|
||||
{%- elif content is iterable and content is not mapping -%}
|
||||
{%- for item in content -%}
|
||||
{%- if item is mapping and item.type == 'text' -%}
|
||||
{{- item.text }}
|
||||
{%- elif item is string -%}
|
||||
{{- item }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- else -%}
|
||||
{{- content }}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
{#- System Message Construction ============================================ -#}
|
||||
{%- macro build_system_message(system_message) -%}
|
||||
{%- if system_message and system_message.content -%}
|
||||
{{- visible_text(system_message.content) }}
|
||||
{%- else -%}
|
||||
{%- if model_identity is not defined -%}
|
||||
{%- set model_identity = "You are a helpful assistant." -%}
|
||||
{%- endif -%}
|
||||
{{- model_identity }}
|
||||
{%- endif -%}
|
||||
|
||||
{#- Handle current_date -#}
|
||||
{%- if system_message and system_message.current_date -%}
|
||||
{{- '\n' ~ 'Current date: ' + system_message.current_date }}
|
||||
{%- endif -%}
|
||||
{#- Handle current_location -#}
|
||||
{%- if system_message and system_message.current_location -%}
|
||||
{{- '\n' ~ 'Current location: ' + system_message.current_location }}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
{#- Main Template Logic ================================================= -#}
|
||||
{#- Extract system message (only first message if it's system) -#}
|
||||
{%- set system_message = none -%}
|
||||
{%- set conversation_messages = messages -%}
|
||||
{%- if messages and messages[0].role == "system" -%}
|
||||
{%- set system_message = messages[0] -%}
|
||||
{%- set conversation_messages = messages[1:] -%}
|
||||
{%- endif -%}
|
||||
{#- Get the last user message turn, for interleved thinking -#}
|
||||
{%- set ns = namespace(last_user_index=-1) %}
|
||||
{% for m in conversation_messages %}
|
||||
{%- if m.role == 'user' %}
|
||||
{% set ns.last_user_index = loop.index0 -%}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{#- Render system message -#}
|
||||
{{- ']~!b[' ~ ']~b]system' ~ '\n' }}
|
||||
{{- build_system_message(system_message) }}
|
||||
{#- Render tools if available -#}
|
||||
{%- if tools -%}
|
||||
{{- '\n\n' ~ '# Tools' ~ '\n' ~ 'You may call one or more tools to assist with the user query.\nHere are the tools available in JSONSchema format:' ~ '\n' }}
|
||||
{{- '\n' ~ '<tools>' ~ '\n' }}
|
||||
{{- render_tool_namespace("functions", tools) }}
|
||||
{{- '</tools>' ~ '\n\n' }}
|
||||
{{- 'When making tool calls, use XML format to invoke tools and pass parameters:' ~ '\n' }}
|
||||
{{- '\n' ~ toolcall_begin_token }}
|
||||
<invoke name="tool-name-1">
|
||||
<parameter name="param-key-1">param-value-1</parameter>
|
||||
<parameter name="param-key-2">param-value-2</parameter>
|
||||
...
|
||||
</invoke>
|
||||
{{- '\n' ~ toolcall_end_token }}
|
||||
{%- endif -%}
|
||||
{{- '[e~[\n' }}
|
||||
|
||||
{#- Render messages -#}
|
||||
{%- set last_tool_call = namespace(name=none) -%}
|
||||
{%- for message in conversation_messages -%}
|
||||
{%- if message.role == 'assistant' -%}
|
||||
{#- Only render reasoning_content if no user message follows -#}
|
||||
{{- ']~b]ai' ~ '\n' }}
|
||||
|
||||
{%- set reasoning_content = '' %}
|
||||
{%- set content = visible_text(message.content) %}
|
||||
{%- if message.reasoning_content is string %}
|
||||
{%- set reasoning_content = message.reasoning_content %}
|
||||
{%- else %}
|
||||
{%- if '</think>' in content %}
|
||||
{%- set reasoning_content = content.split('</think>')[0].strip('\n').split('<think>')[-1].strip('\n') %}
|
||||
{%- set content = content.split('</think>')[-1].strip('\n') %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if reasoning_content and loop.index0 > ns.last_user_index -%}
|
||||
{{- '<think>' ~ '\n' ~ reasoning_content ~ '\n' ~ '</think>' ~ '\n\n' }}
|
||||
{%- endif -%}
|
||||
{%- if content -%}
|
||||
{{- content }}
|
||||
{%- endif -%}
|
||||
{%- if message.tool_calls -%}
|
||||
{{- '\n' ~ toolcall_begin_token ~ '\n' }}
|
||||
|
||||
{%- for tool_call in message.tool_calls -%}
|
||||
{%- if tool_call.function %}
|
||||
{%- set tool_call = tool_call.function %}
|
||||
{%- endif %}
|
||||
{{- '<invoke name="' + tool_call.name + '">' }}
|
||||
{% set _args = tool_call.arguments %}
|
||||
{%- for k, v in _args.items() %}
|
||||
{{- '<parameter name="' + k + '">' }}
|
||||
{{- v | tojson(ensure_ascii=False) if v is not string else v }}
|
||||
{{- '</parameter>' }}
|
||||
{% endfor %}
|
||||
{{- '</invoke>' ~ '\n' }}
|
||||
{%- endfor -%}
|
||||
|
||||
{{- toolcall_end_token}}
|
||||
{%- set last_tool_call.name = message.tool_calls[-1].function.name -%}
|
||||
{%- else -%}
|
||||
{%- set last_tool_call.name = none -%}
|
||||
{%- endif -%}
|
||||
{{- '[e~[' ~ '\n' }}
|
||||
|
||||
{%- elif message.role == 'tool' -%}
|
||||
{%- if last_tool_call.name is none -%}
|
||||
{{- raise_exception("Message has tool role, but there was no previous assistant message with a tool call!") }}
|
||||
{%- endif -%}
|
||||
{%- if loop.first or (conversation_messages[loop.index0 - 1].role != 'tool') -%}
|
||||
{{- ']~b]tool' }}
|
||||
{%- endif -%}
|
||||
{%- if message.content is string -%}
|
||||
{{- '\n<response>' }}
|
||||
{{- message.content }}
|
||||
{{- '</response>' }}
|
||||
{%- else -%}
|
||||
{%- for tr in message.content -%}
|
||||
{{- '\n<response>' }}
|
||||
{{- tr.output if tr.output is defined else (tr.text if tr.type == 'text' and tr.text is defined else tr) }}
|
||||
{{- '\n</response>' }}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- if loop.last or (conversation_messages[loop.index0 + 1].role != 'tool') -%}
|
||||
{{- '[e~[\n' -}}
|
||||
{%- endif -%}
|
||||
|
||||
{%- elif message.role == 'user' -%}
|
||||
{{- ']~b]user' ~ '\n' }}
|
||||
{{- visible_text(message.content) }}
|
||||
{{- '[e~[' ~ '\n' }}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{#- Generation prompt -#}
|
||||
{%- if add_generation_prompt -%}
|
||||
{{- ']~b]ai' ~ '\n' ~ '<think>' ~ '\n' }}
|
||||
{%- endif -%}
|
||||
117
models/templates/Qwen3-Coder.jinja
Normal file
117
models/templates/Qwen3-Coder.jinja
Normal file
@@ -0,0 +1,117 @@
|
||||
{% macro render_extra_keys(json_dict, handled_keys) %}
|
||||
{%- if json_dict is mapping %}
|
||||
{%- for json_key in json_dict if json_key not in handled_keys %}
|
||||
{%- if json_dict[json_key] is mapping or (json_dict[json_key] is sequence and json_dict[json_key] is not string) %}
|
||||
{{- '\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | tojson | safe) ~ '</' ~ json_key ~ '>' }}
|
||||
{%- else %}
|
||||
{{-'\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | string) ~ '</' ~ json_key ~ '>' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{%- if messages[0]["role"] == "system" %}
|
||||
{%- set system_message = messages[0]["content"] %}
|
||||
{%- set loop_messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{%- set loop_messages = messages %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if not tools is defined %}
|
||||
{%- set tools = [] %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if system_message is defined %}
|
||||
{{- "<|im_start|>system\n" + system_message }}
|
||||
{%- else %}
|
||||
{%- if tools is iterable and tools | length > 0 %}
|
||||
{{- "<|im_start|>system\nYou are Qwen, a helpful AI assistant that can interact with a computer to solve tasks." }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if tools is iterable and tools | length > 0 %}
|
||||
{{- "\n\n# Tools\n\nYou have access to the following functions:\n\n" }}
|
||||
{{- "<tools>" }}
|
||||
{%- for tool in tools %}
|
||||
{%- if tool.function is defined %}
|
||||
{%- set tool = tool.function %}
|
||||
{%- endif %}
|
||||
{{- "\n<function>\n<name>" ~ tool.name ~ "</name>" }}
|
||||
{%- if tool.description is defined %}
|
||||
{{- '\n<description>' ~ (tool.description | trim) ~ '</description>' }}
|
||||
{%- endif %}
|
||||
{{- '\n<parameters>' }}
|
||||
{%- if tool.parameters is defined and tool.parameters is mapping and tool.parameters.properties is defined and tool.parameters.properties is mapping %}
|
||||
{%- for param_name, param_fields in tool.parameters.properties|items %}
|
||||
{{- '\n<parameter>' }}
|
||||
{{- '\n<name>' ~ param_name ~ '</name>' }}
|
||||
{%- if param_fields.type is defined %}
|
||||
{{- '\n<type>' ~ (param_fields.type | string) ~ '</type>' }}
|
||||
{%- endif %}
|
||||
{%- if param_fields.description is defined %}
|
||||
{{- '\n<description>' ~ (param_fields.description | trim) ~ '</description>' }}
|
||||
{%- endif %}
|
||||
{%- set handled_keys = ['name', 'type', 'description'] %}
|
||||
{{- render_extra_keys(param_fields, handled_keys) }}
|
||||
{{- '\n</parameter>' }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{% set handled_keys = ['type', 'properties'] %}
|
||||
{{- render_extra_keys(tool.parameters, handled_keys) }}
|
||||
{{- '\n</parameters>' }}
|
||||
{%- set handled_keys = ['type', 'name', 'description', 'parameters'] %}
|
||||
{{- render_extra_keys(tool, handled_keys) }}
|
||||
{{- '\n</function>' }}
|
||||
{%- endfor %}
|
||||
{{- "\n</tools>" }}
|
||||
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
||||
{%- endif %}
|
||||
{%- if system_message is defined %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- else %}
|
||||
{%- if tools is iterable and tools | length > 0 %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- for message in loop_messages %}
|
||||
{%- if message.role == "assistant" and message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
||||
{{- '<|im_start|>' + message.role }}
|
||||
{%- if message.content is defined and message.content is string and message.content | trim | length > 0 %}
|
||||
{{- '\n' + message.content | trim + '\n' }}
|
||||
{%- endif %}
|
||||
{%- for tool_call in message.tool_calls %}
|
||||
{%- if tool_call.function is defined %}
|
||||
{%- set tool_call = tool_call.function %}
|
||||
{%- endif %}
|
||||
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
||||
{%- if tool_call.arguments is defined %}
|
||||
{%- for args_name, args_value in tool_call.arguments|items %}
|
||||
{{- '<parameter=' + args_name + '>\n' }}
|
||||
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
||||
{{- args_value }}
|
||||
{{- '\n</parameter>\n' }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{{- '</function>\n</tool_call>' }}
|
||||
{%- endfor %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- elif message.role == "user" or message.role == "system" or message.role == "assistant" %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
|
||||
{%- elif message.role == "tool" %}
|
||||
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
||||
{{- '<|im_start|>user\n' }}
|
||||
{%- endif %}
|
||||
{{- '<tool_response>\n' }}
|
||||
{{- message.content }}
|
||||
{{- '\n</tool_response>\n' }}
|
||||
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- elif loop.last %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %}
|
||||
126
models/templates/unsloth-Apriel-1.5.jinja
Normal file
126
models/templates/unsloth-Apriel-1.5.jinja
Normal file
@@ -0,0 +1,126 @@
|
||||
{# Unsloth template fixes #}
|
||||
{%- set available_tools_string = '' -%}
|
||||
{%- set thought_instructions = '' -%}
|
||||
{%- set add_tool_id = true -%}
|
||||
{%- set tool_output_format = "default" -%}
|
||||
{%- if tools is not none and tools|length > 0 -%}
|
||||
{%- set available_tools_string -%}
|
||||
You are provided with function signatures within <available_tools></available_tools> XML tags. You may call one or more functions to assist with the user query. Don't make assumptions about the arguments. You should infer the argument values from previous user responses and the system message. Here are the available tools:
|
||||
<available_tools>
|
||||
{% for tool in tools %}
|
||||
{{ tool|string }}
|
||||
{% endfor %}
|
||||
</available_tools>
|
||||
{%- endset -%}
|
||||
{%- endif -%}
|
||||
{%- if tool_output_format is none or tool_output_format == "default" -%}
|
||||
{%- set tool_output_instructions -%}
|
||||
Return all function calls as a list of json objects within <tool_call></tool_call> XML tags. Each json object should contain a function name and arguments as follows:
|
||||
<tool_calls>[{"name": <function-name-1>, "arguments": <args-dict-1>}, {"name": <function-name-2>, "arguments": <args-dict-2>},...]</tool_calls>
|
||||
{%- endset -%}
|
||||
{%- elif tool_output_format == "yaml" -%}
|
||||
{%- set tool_output_instructions -%}
|
||||
Return all function calls as a list of yaml objects within <tool_call></tool_call> XML tags. Each yaml object should contain a function name and arguments as follows:
|
||||
<tool_calls>
|
||||
- name: <function-name-1>
|
||||
arguments: <args-dict-1>
|
||||
- name: <function-name-2>
|
||||
arguments: <args-dict-2>
|
||||
...
|
||||
</tool_calls>
|
||||
{%- endset -%}
|
||||
{%- endif -%}
|
||||
{%- if add_thoughts -%}
|
||||
{%- set thought_instructions -%}
|
||||
Prior to generating the function calls, you should generate the reasoning for why you're calling the function. Please generate these reasoning thoughts between <thinking> and </thinking> XML tags.
|
||||
{%- endset -%}
|
||||
{%- endif -%}
|
||||
{{- bos_token -}}
|
||||
{%- set reasoning_prompt='You are a thoughtful and systematic AI assistant built by ServiceNow Language Models (SLAM) lab. Before providing an answer, analyze the problem carefully and present your reasoning step by step. After explaining your thought process, provide the final solution in the following format: [BEGIN FINAL RESPONSE] ... [END FINAL RESPONSE].' -%}
|
||||
{%- if messages[0]['role'] != 'system' and tools is not none and tools|length > 0 -%}
|
||||
{{- '<|system|>\n' + reasoning_prompt + available_tools_string + "\n" + tool_output_instructions + '\n<|end|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- if messages|selectattr('role', 'equalto', 'system')|list|length == 0 -%}
|
||||
{{- '<|system|>\n' + reasoning_prompt + '\n<|end|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- for message in messages -%}
|
||||
{%- if message['role'] == 'user' -%}
|
||||
{{- '<|user|>\n' }}
|
||||
{%- if message['content'] is not string %}
|
||||
{%- for chunk in message['content'] %}
|
||||
{%- if chunk['type'] == 'text' %}
|
||||
{{- chunk['text'] }}
|
||||
{%- elif chunk['type'] == 'image' or chunk['type'] == 'image_url'%}
|
||||
{{- '[IMG]' }}
|
||||
{%- else %}
|
||||
{{- raise_exception('Unrecognized content type!') }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- else %}
|
||||
{{- message['content'] }}
|
||||
{%- endif %}
|
||||
{{- '\n<|end|>\n' }}
|
||||
{%- elif message['role'] == 'content' -%}
|
||||
{%- if message['content'] is not string %}
|
||||
{{- '<|content|>\n' + message['content'][0]['text'] + '\n<|end|>\n' -}}
|
||||
{%- else %}
|
||||
{{- '<|content|>\n' + message['content'] + '\n<|end|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- elif message['role'] == 'system' -%}
|
||||
{%- if message['content'] is not none and message['content']|length > 0 %}
|
||||
{%- if message['content'] is string %}
|
||||
{%- set system_message = message['content'] %}
|
||||
{%- else %}
|
||||
{%- set system_message = message['content'][0]['text'] %}
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{%- set system_message = '' %}
|
||||
{%- endif %}
|
||||
{%- if tools is not none and tools|length > 0 -%}
|
||||
{{- '<|system|>\n' + reasoning_prompt + system_message + '\n' + available_tools_string + '\n<|end|>\n' -}}
|
||||
{%- else -%}
|
||||
{{- '<|system|>\n' + reasoning_prompt + system_message + '\n<|end|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- elif message['role'] == 'assistant' -%}
|
||||
{%- if loop.last -%}
|
||||
{%- set add_tool_id = false -%}
|
||||
{%- endif -%}
|
||||
{{- '<|assistant|>\n' -}}
|
||||
{%- if message['content'] is not none and message['content']|length > 0 -%}
|
||||
{%- if message['content'] is not string and message['content'][0]['text'] is not none %}
|
||||
{{- message['content'][0]['text'] }}
|
||||
{%- else %}
|
||||
{{- message['content'] -}}
|
||||
{%- endif -%}
|
||||
{%- elif message['chosen'] is not none and message['chosen']|length > 0 -%}
|
||||
{{- message['chosen'][0] -}}
|
||||
{%- endif -%}
|
||||
{%- if add_thoughts and 'thought' in message and message['thought'] is not none -%}
|
||||
{{- '<thinking>' + message['thought'] + '</thinking>' -}}
|
||||
{%- endif -%}
|
||||
{%- if message['tool_calls'] is not none and message['tool_calls']|length > 0 -%}
|
||||
{{- '\n<tool_calls>[' -}}
|
||||
{%- for tool_call in message["tool_calls"] -%}
|
||||
{{- '{"name": "' + tool_call['function']['name'] + '", "arguments": ' + tool_call['function']['arguments']|string -}}
|
||||
{%- if add_tool_id == true -%}
|
||||
{{- ', "id": "' + tool_call['id'] + '"' -}}
|
||||
{%- endif -%}
|
||||
{{- '}' -}}
|
||||
{%- if not loop.last -%}{{- ', ' -}}{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{{- ']</tool_calls>' -}}
|
||||
{%- endif -%}
|
||||
{{- '\n<|end|>\n' + eos_token -}}
|
||||
{%- elif message['role'] == 'tool' -%}
|
||||
{%- if message['content'] is string %}
|
||||
{%- set tool_message = message['content'] %}
|
||||
{%- else %}
|
||||
{%- set tool_message = message['content'][0]['text'] %}
|
||||
{%- endif -%}
|
||||
{{- '<|tool_result|>\n' + tool_message|string + '\n<|end|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- if loop.last and add_generation_prompt and message['role'] != 'assistant' -%}
|
||||
{{- '<|assistant|>\n' -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{# Copyright 2025-present Unsloth. Apache 2.0 License. #}
|
||||
Reference in New Issue
Block a user