{# ========================= Qwen 3.6 Multimodal Interleaved-Thinking Chat Template - Multimodal: image/video tokens - Tools: XML tool_call + tool_response wrapping - M2.5-style interleaved thinking: * historical assistant reasoning before the last real user query is hidden * assistant turns after the last real user query keep * generation always starts in - Reference: https://huggingface.co/raydelossantos/Qwen3.5-40B-Claude-4.6-Opus-Deckard-Heretic-GPTQ-Int4/blob/main/chat_template.jinja ========================= #} {%- set image_count = namespace(value=0) -%} {%- set video_count = namespace(value=0) -%} {# Macro to render individual tool parameters #} {%- macro render_tool_parameter(args_name, args_value) -%} {{- '\n' -}} {%- set args_value = args_value | tojson(ensure_ascii=False) | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string -%} {{- args_value -}} {{- '\n\n' -}} {%- endmacro -%} {# Macro to render all tool arguments #} {%- macro render_tool_arguments(arguments) -%} {%- if arguments is undefined or arguments is none -%} {%- elif arguments is mapping -%} {%- for args_name, args_value in arguments|items -%} {{- render_tool_parameter(args_name, args_value) -}} {%- endfor -%} {%- elif arguments is string -%} {%- set trimmed_arguments = arguments | trim -%} {%- if trimmed_arguments -%} {%- set parsed = trimmed_arguments | from_json -%} {%- if parsed is mapping -%} {%- for args_name, args_value in parsed | items -%} {{- render_tool_parameter(args_name, args_value) -}} {%- endfor -%} {%- else -%} {{- trimmed_arguments -}} {%- if not trimmed_arguments.endswith('\n') -%} {{- '\n' -}} {%- endif -%} {%- endif -%} {%- endif -%} {%- else -%} {{- raise_exception('Unexpected tool_call.arguments type.') -}} {%- endif -%} {%- endmacro -%} {# Core content renderer (Handles Text + Vision) #} {%- macro render_content(content, do_vision_count, is_system_content=false) -%} {%- if content is string -%} {{- content -}} {%- elif content is iterable and content is not mapping -%} {%- for item in content -%} {%- if 'image' in item or 'image_url' in item or item.type == 'image' -%} {%- if is_system_content -%} {{- raise_exception('System message cannot contain images.') -}} {%- endif -%} {%- if do_vision_count -%} {%- set image_count.value = image_count.value + 1 -%} {%- endif -%} {%- if add_vision_id -%} {{- 'Picture ' ~ image_count.value ~ ': ' -}} {%- endif -%} {{- '<|vision_start|><|image_pad|><|vision_end|>' -}} {%- elif 'video' in item or item.type == 'video' -%} {%- if is_system_content -%} {{- raise_exception('System message cannot contain videos.') -}} {%- endif -%} {%- if do_vision_count -%} {%- set video_count.value = video_count.value + 1 -%} {%- endif -%} {%- if add_vision_id -%} {{- 'Video ' ~ video_count.value ~ ': ' -}} {%- endif -%} {{- '<|vision_start|><|video_pad|><|vision_end|>' -}} {%- elif 'text' in item -%} {{- item.text -}} {%- else -%} {{- raise_exception('Unexpected item type in content.') -}} {%- endif -%} {%- endfor -%} {%- elif content is none or content is undefined -%} {{- '' -}} {%- else -%} {{- raise_exception('Unexpected content type.') -}} {%- endif -%} {%- endmacro -%} {%- if not messages -%} {{- raise_exception('No messages provided.') -}} {%- endif -%} {# -------- Tools & System Header -------- #} {%- if tools and tools is iterable and tools is not mapping -%} {{- '<|im_start|>system\n' -}} {{- "# Tools\n\nYou have access to the following functions:\n\n" -}} {%- for tool in tools -%} {{- "\n" -}} {{- tool | tojson(ensure_ascii=False) -}} {%- endfor -%} {{- "\n" -}} {{- '\n\nIMPORTANT: The assistant MUST always close its thinking with before emitting any . Reasoning inside a or its parameters is strictly forbidden and will cause a fatal error. When calling a tool, output one or more XML blocks in exactly this format and nothing after the final :\n\n\n\n\nvalue_1\n\n\nThis is the value for the second parameter\nthat can span\nmultiple lines\n\n\n\n\nRules:\n- If a suitable tool exists for the user request, use it instead of answering from memory.\n- If the user is modifying an existing object and a prior tool response contains its id, prefer the matching update_* tool and reuse that id.\n- Put any reasoning or natural language BEFORE the first , never after the last .\n- Include every required parameter.\n- Preserve user-provided string values verbatim when possible for ids, names, titles, subjects, emails, and search queries.\n- For object or array parameter values, write valid JSON inside the parameter body.\n- If no tool is needed, answer normally.\n\nFollow-up update example:\n\n{"status":"scheduled","id":"r-100"}\n\nIf the user then says "Actually make it 10:00 instead.", call:\n\n\n\nr-100\n\n\n2026-03-12T10:00:00\n\n\nAsia/Taipei\n\n\n' -}} {%- if messages[0].role == 'system' -%} {%- set sysc = render_content(messages[0].content, false, true)|trim -%} {%- if sysc -%} {{- '\n\n' ~ sysc -}} {%- endif -%} {%- endif -%} {{- '<|im_end|>\n' -}} {%- else -%} {%- if messages[0].role == 'system' -%} {%- set sysc = render_content(messages[0].content, false, true)|trim -%} {{- '<|im_start|>system\n' ~ sysc ~ '<|im_end|>\n' -}} {%- endif -%} {%- endif -%} {# -------- Interleaved-thinking boundary: last REAL user query -------- #} {%- set ns = namespace(last_user_index=-1) -%} {%- for m in messages -%} {%- if m.role == "user" -%} {%- set uc = render_content(m.content, false)|trim -%} {%- if not (uc.startswith('') and uc.endswith('')) -%} {%- set ns.last_user_index = loop.index0 -%} {%- endif -%} {%- endif -%} {%- endfor -%} {# -------- Render messages -------- #} {%- for message in messages -%} {%- set content = render_content(message.content, true)|trim -%} {%- if message.role == "system" -%} {%- if not loop.first -%} {{- raise_exception('System message must be at the beginning.') -}} {%- endif -%} {%- elif message.role == "user" -%} {{- '<|im_start|>user\n' ~ content ~ '<|im_end|>\n' -}} {%- elif message.role == "assistant" -%} {%- set content = render_content(message.content, true)|trim -%} {# --- 完美自癒:處理任意數量 未閉合的情況 --- #} {%- if '' in content and '' in content -%} {%- set last_think = content.rfind('') -%} {%- set last_close = content.rfind('') -%} {%- set tool_pos = content.find('') -%} {%- if last_close < last_think or last_close == -1 -%} {%- if tool_pos > last_think -%} {%- set content = content[:tool_pos] + '' + content[tool_pos:] -%} {%- else -%} {%- set content = content + '' -%} {%- endif -%} {%- endif -%} {%- endif -%} {%- set reasoning_content = '' -%} {%- if message.reasoning_content is string -%} {%- set reasoning_content = message.reasoning_content -%} {%- else -%} {%- if '' in content -%} {%- set reasoning_content = content.split('')[0].rstrip('\n').split('')[-1].lstrip('\n') -%} {%- set content = content.split('')[-1].lstrip('\n') -%} {%- endif -%} {%- endif -%} {%- set reasoning_content = reasoning_content|trim -%} {# --- 總是輸出閉合的 think 塊,即使 reasoning 為空,確保 tool_call 永遠在外面 --- #} {%- if (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > ns.last_user_index) %} {{- '<|im_start|>assistant\n\n' ~ reasoning_content ~ '\n\n\n' ~ content -}} {%- else -%} {{- '<|im_start|>assistant\n' ~ content -}} {%- endif -%} {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping -%} {%- for tool_call in message.tool_calls -%} {%- if tool_call.function is defined -%} {%- set tool_call = tool_call.function -%} {%- endif -%} {%- if loop.first -%} {%- if content|trim -%} {{- '\n\n\n\n' -}} {%- else -%} {{- '\n\n' -}} {%- endif -%} {%- else -%} {{- '\n\n\n' -}} {%- endif -%} {{- render_tool_arguments(tool_call.arguments) -}} {{- '\n' -}} {%- endfor -%} {%- endif -%} {{- '<|im_end|>\n' -}} {%- elif message.role == "tool" -%} {%- if loop.index0 == 0 or messages[loop.index0 - 1].role != "tool" -%} {{- '<|im_start|>user' -}} {%- endif -%} {{- '\n\n' ~ content ~ '\n' -}} {%- if loop.last or messages[loop.index0 + 1].role != "tool" -%} {{- '\n<|im_end|>\n' -}} {%- endif -%} {%- else -%} {{- raise_exception('Unexpected message role.') -}} {%- endif -%} {%- endfor -%} {# -------- Generation prompt -------- #} {%- if add_generation_prompt -%} {{- '<|im_start|>assistant\n' -}} {%- if enable_thinking is defined and enable_thinking is false -%} {{- '\n\n\n\n' -}} {%- else -%} {{- '\n' -}} {%- endif -%} {%- endif -%}