| {%- 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 string -%} |
| {{- '\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | string) ~ '</' ~ json_key ~ '>' }} |
| {%- else -%} |
| {{- '\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | tojson | safe) ~ '</' ~ json_key ~ '>' }} |
| {%- endif -%} |
| {%- endfor -%} |
| {%- endif -%} |
| {%- endmacro %} |
| |
| {%- macro render_content(content, is_system_content=false) -%} |
| {%- if content is string -%} |
| {{- content }} |
| {%- elif content is iterable and content is not mapping -%} |
| {%- for item in content -%} |
| {%- if item is mapping -%} |
| {%- if item.type is defined and item.type == 'text' -%} |
| {{- item.text if item.text is defined else '' }} |
| {%- elif 'text' in item -%} |
| {{- item.text }} |
| {%- elif 'image' in item or 'image_url' in item or 'video' in item -%} |
| {{- raise_exception('This template is text-only and does not support image/video content.') }} |
| {%- else -%} |
| {{- raise_exception('Unexpected item type in content.') }} |
| {%- endif -%} |
| {%- elif item is string -%} |
| {{- item }} |
| {%- else -%} |
| {{- raise_exception('Unexpected non-text item in content.') }} |
| {%- endif -%} |
| {%- endfor -%} |
| {%- elif content is none or content is undefined -%} |
| {{- '' }} |
| {%- else -%} |
| {{- raise_exception('Unexpected content type.') }} |
| {%- endif -%} |
| {%- endmacro %} |
| |
| {%- if not messages or messages|length == 0 -%} |
| {{- raise_exception('No messages provided.') }} |
| {%- endif -%} |
| |
| {%- if not tools is defined or tools is none -%} |
| {%- set tools = [] -%} |
| {%- endif -%} |
| |
| {%- if messages[0].role == 'system' -%} |
| {%- set system_message = render_content(messages[0].content, true) | trim -%} |
| {%- set loop_messages = messages[1:] -%} |
| {%- else -%} |
| {%- set system_message = none -%} |
| {%- set loop_messages = messages -%} |
| {%- endif -%} |
| |
| |
| {%- if tools and tools is iterable and tools is not mapping and tools | length > 0 -%} |
| {{- '<|im_start|>system\n' }} |
| {%- if system_message -%} |
| {{- system_message }} |
| {{- '\n\n' }} |
| {%- else -%} |
| {{- 'You are Qwen, a helpful AI assistant that can interact with a computer to solve tasks.' }} |
| {{- '\n\n' }} |
| {%- endif -%} |
| |
| {{- '# 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- Do not add any text after a function call\n- If there is no function call available, answer the question normally using your current knowledge and do not mention function calling\n</IMPORTANT>' }} |
| |
| {{- '<|im_end|>\n' }} |
| {%- else -%} |
| {%- if system_message is not none -%} |
| {{- '<|im_start|>system\n' + system_message + '<|im_end|>\n' }} |
| {%- endif -%} |
| {%- endif -%} |
| |
| |
| {%- for message in loop_messages -%} |
| {%- if message.role == 'user' -%} |
| {%- set content = render_content(message.content) | trim -%} |
| {{- '<|im_start|>user\n' + content + '<|im_end|>\n' }} |
| |
| {%- elif message.role == 'assistant' -%} |
| {%- set content = render_content(message.content) | trim -%} |
| {{- '<|im_start|>assistant' }} |
| |
| {%- if content -%} |
| {{- '\n' + content }} |
| {%- endif -%} |
| |
| {%- if message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls is not mapping and message.tool_calls | length > 0 -%} |
| {%- for tool_call in message.tool_calls -%} |
| {%- if tool_call.function is defined -%} |
| {%- set tool_call = tool_call.function -%} |
| {%- endif -%} |
| |
| {%- if content or not loop.first -%} |
| {{- '\n' }} |
| {%- endif -%} |
| |
| {{- '<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 if args_value is string else args_value | tojson | safe -%} |
| {{- args_value }} |
| {{- '\n</parameter>\n' }} |
| {%- endfor -%} |
| {%- endif -%} |
| {{- '</function>\n</tool_call>' }} |
| {%- endfor -%} |
| {%- endif -%} |
| |
| {{- '<|im_end|>\n' }} |
| |
| {%- elif message.role == 'tool' -%} |
| {%- set content = render_content(message.content) | trim -%} |
| {%- if loop.previtem and loop.previtem.role != 'tool' -%} |
| {{- '<|im_start|>user' }} |
| {%- endif -%} |
| {{- '\n<tool_response>\n' }} |
| {{- content }} |
| {{- '\n</tool_response>' }} |
| {%- if not loop.last and loop.nextitem.role != 'tool' -%} |
| {{- '<|im_end|>\n' }} |
| {%- elif loop.last -%} |
| {{- '<|im_end|>\n' }} |
| {%- endif -%} |
| |
| {%- elif message.role == 'system' -%} |
| {{- raise_exception('System message must be at the beginning.') }} |
| |
| {%- else -%} |
| {{- raise_exception('Unexpected message role.') }} |
| {%- endif -%} |
| {%- endfor -%} |
| |
| {%- if add_generation_prompt -%} |
| {{- '<|im_start|>assistant\n' }} |
| {%- endif -%} |
| |