gdevenyi commited on
Commit
8d0bb79
·
verified ·
1 Parent(s): 3be7669

Read the assistant `reasoning` field (vLLM / Responses API name) when rendering history

Browse files

## Summary

The history renderer reads `message.reasoning_content`, then `message.thinking`, then in-content `<think>` tags. It does not read `message.reasoning`, which is the field name vLLM now returns first (`reasoning_content` is marked deprecated in vLLM's `ChatMessage`) and the name the OpenAI Responses API uses.

vLLM itself is not affected (it renames an incoming `reasoning_content` to `reasoning` and then sets both keys before rendering). But a client that echoes vLLM's `message.reasoning` back to llama.cpp, LM Studio or oMLX loses the thought: the template renders an empty `<think>\n\n</think>` block in place of it.

## Changes

- `chat_template.jinja`: add `message.reasoning` to the extraction chain (after `reasoning_content`, before `thinking`), same string/non-string handling as the other two fields.
- `chat_template_oneline.txt`: regenerated with `scripts/minify_jinja.py`.
- `scripts/test_v22.py`: new test 24b covering the `reasoning` field.
- `README.md`: one phrase in the feature table.

## Verification

`python3 scripts/test_v22.py` → 101/101, `python3 scripts/test_v21.py` → 9/9, `python3 scripts/fuzz_template.py --cases 200` → clean.

README.md CHANGED
@@ -33,7 +33,7 @@ The v22 generation delivers full Qwen 3.8 support with all official bugs resolve
33
  |---|---|---|
34
  | **Default Reasoning Baseline** | Hardcodes `xhigh` by default, often exhausting the token budget on reasoning before generating answers. | Defaults to `medium` (zero injected tokens), preserving KV cache parity with v21 and preventing empty-content timeouts. |
35
  | **Non-Reasoning Fast Mode** | Throws a fatal exception if `enable_thinking=false`. | Full non-reasoning mode restored via kwargs (`enable_thinking=false`, `reasoning_effort="none"`) or inline `<\|think_off\|>`. |
36
- | **History Reasoning Extraction** | Drops in-content thinking extraction, prepending blank `<think></think>` blocks to real thoughts in chat history. | Extracts reasoning across OpenAI (`reasoning_content`), Anthropic (`thinking`), and in-content tags (`<think>`) without tag duplication. |
37
  | **Tool Argument Wire Format** | Crashes on serialized JSON strings from standard OpenAI API clients. | Canonical XML default with universal argument handling: safely renders stringified JSON arguments in history without syntax crashes or KV cache invalidation. |
38
  | **Client Reasoning Aliases** | Rejects non-standard effort names. | Maps OpenAI, Claude Code, Cursor, and Cline aliases automatically: `high`, `max`, `ultracode`, `extreme` $\to$ `xhigh`; `minimal` $\to$ `low`; `none`, `off` $\to$ disabled. |
39
  | **Per-Turn Inline Chat Tags** | Not supported. | Inline steering via chat text: `<\|think_low\|>`, `<\|think_medium\|>`, `<\|think_xhigh\|>`, `<\|think_ultracode\|>`, `<\|think_off\|>`. Tags are stripped before inference. |
 
33
  |---|---|---|
34
  | **Default Reasoning Baseline** | Hardcodes `xhigh` by default, often exhausting the token budget on reasoning before generating answers. | Defaults to `medium` (zero injected tokens), preserving KV cache parity with v21 and preventing empty-content timeouts. |
35
  | **Non-Reasoning Fast Mode** | Throws a fatal exception if `enable_thinking=false`. | Full non-reasoning mode restored via kwargs (`enable_thinking=false`, `reasoning_effort="none"`) or inline `<\|think_off\|>`. |
36
+ | **History Reasoning Extraction** | Drops in-content thinking extraction, prepending blank `<think></think>` blocks to real thoughts in chat history. | Extracts reasoning across OpenAI (`reasoning_content`, and the newer `reasoning` field used by vLLM and the Responses API), Anthropic (`thinking`), and in-content tags (`<think>`) without tag duplication. |
37
  | **Tool Argument Wire Format** | Crashes on serialized JSON strings from standard OpenAI API clients. | Canonical XML default with universal argument handling: safely renders stringified JSON arguments in history without syntax crashes or KV cache invalidation. |
38
  | **Client Reasoning Aliases** | Rejects non-standard effort names. | Maps OpenAI, Claude Code, Cursor, and Cline aliases automatically: `high`, `max`, `ultracode`, `extreme` $\to$ `xhigh`; `minimal` $\to$ `low`; `none`, `off` $\to$ disabled. |
39
  | **Per-Turn Inline Chat Tags** | Not supported. | Inline steering via chat text: `<\|think_low\|>`, `<\|think_medium\|>`, `<\|think_xhigh\|>`, `<\|think_ultracode\|>`, `<\|think_off\|>`. Tags are stripped before inference. |
chat_template.jinja CHANGED
@@ -240,6 +240,12 @@
240
  {%- else %}
241
  {%- set _explicit_reasoning = message.reasoning_content | string %}
242
  {%- endif %}
 
 
 
 
 
 
243
  {%- elif message.thinking is defined and message.thinking is not none %}
244
  {%- if message.thinking is string %}
245
  {%- set _explicit_reasoning = message.thinking %}
 
240
  {%- else %}
241
  {%- set _explicit_reasoning = message.reasoning_content | string %}
242
  {%- endif %}
243
+ {%- elif message.reasoning is defined and message.reasoning is not none %}
244
+ {%- if message.reasoning is string %}
245
+ {%- set _explicit_reasoning = message.reasoning %}
246
+ {%- else %}
247
+ {%- set _explicit_reasoning = message.reasoning | string %}
248
+ {%- endif %}
249
  {%- elif message.thinking is defined and message.thinking is not none %}
250
  {%- if message.thinking is string %}
251
  {%- set _explicit_reasoning = message.thinking %}
chat_template_oneline.txt CHANGED
@@ -1 +1 @@
1
- {%- set template_version = "qwen3.8-froggeric-v22.3" %}{%- set _tool_format = tool_call_format if tool_call_format is defined else 'xml' %}{%- set image_count = namespace(value=0) %}{%- set video_count = namespace(value=0) %}{%- set add_vision_id = add_vision_id if add_vision_id is defined else false %}{%- set enable_thinking = enable_thinking if enable_thinking is defined else true %}{%- set auto_disable_thinking_with_tools = auto_disable_thinking_with_tools if auto_disable_thinking_with_tools is defined else false %}{%- if preserve_reasoning is defined and preserve_reasoning is not none %}{%- set _preserve_thinking = preserve_reasoning %}{%- elif preserve_thinking is defined and preserve_thinking is not none %}{%- set _preserve_thinking = preserve_thinking %}{%- else %}{%- set _preserve_thinking = true %}{%- endif %}{%- set max_tool_arg_chars = max_tool_arg_chars if max_tool_arg_chars is defined else 0 %}{%- set max_tool_response_chars = max_tool_response_chars if max_tool_response_chars is defined else 0 %}{%- set _has_tools = (tools is defined and tools and tools is iterable and tools is not mapping) %}{%- set _effort_raw = (reasoning_effort | string | lower) if reasoning_effort is defined and reasoning_effort is not none else 'medium' %}{%- set _initial_thinking = enable_thinking %}{%- set _initial_effort = 'medium' %}{%- if _effort_raw in ('none', 'off') %}{%- set _initial_thinking = false %}{%- set _initial_effort = 'medium' %}{%- elif _effort_raw in ('minimal', 'low') %}{%- set _initial_effort = 'low' %}{%- elif _effort_raw in ('high', 'xhigh', 'max', 'ultracode', 'extreme') %}{%- set _initial_effort = 'xhigh' %}{%- else %}{%- set _initial_effort = 'medium' %}{%- endif %}{%- set ns_state = namespace(thinking=_initial_thinking, effort=_initial_effort) %}{%- if auto_disable_thinking_with_tools and _has_tools %}{%- set ns_state.thinking = false %}{%- endif %}{%- for msg in messages %}{%- if msg.role == 'system' or msg.role == 'developer' or msg.role == 'user' %}{%- if msg.content is string %}{%- if '<|think_off|>' in msg.content %}{%- set ns_state.thinking = false %}{%- elif '<|think_on|>' in msg.content %}{%- set ns_state.thinking = true %}{%- elif '<|think_xhigh|>' in msg.content or '<|think_high|>' in msg.content or '<|think_ultracode|>' in msg.content or '<|think_extreme|>' in msg.content or '<|think_max|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'xhigh' %}{%- elif '<|think_low|>' in msg.content or '<|think_minimal|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'low' %}{%- elif '<|think_medium|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'medium' %}{%- endif %}{%- elif msg.content is iterable and msg.content is not mapping %}{%- for item in msg.content %}{%- if item is string %}{%- set _item_text = item %}{%- elif item is mapping and 'text' in item and item.text is string %}{%- set _item_text = item.text %}{%- else %}{%- set _item_text = '' %}{%- endif %}{%- if _item_text %}{%- if '<|think_off|>' in _item_text %}{%- set ns_state.thinking = false %}{%- elif '<|think_on|>' in _item_text %}{%- set ns_state.thinking = true %}{%- elif '<|think_xhigh|>' in _item_text or '<|think_high|>' in _item_text or '<|think_ultracode|>' in _item_text or '<|think_extreme|>' in _item_text or '<|think_max|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'xhigh' %}{%- elif '<|think_low|>' in _item_text or '<|think_minimal|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'low' %}{%- elif '<|think_medium|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'medium' %}{%- endif %}{%- endif %}{%- endfor %}{%- endif %}{%- endif %}{%- endfor %}{%- set reasoning_instructions = '' %}{%- if ns_state.thinking %}{%- if ns_state.effort == 'xhigh' %}{%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}{%- elif ns_state.effort == 'low' %}{%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}{%- endif %}{%- endif %}{%- 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 item is mapping %}{%- if item.type == 'image' or 'image' in item or 'image_url' in item %}{%- 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 item.type == 'video' or 'video' in item %}{%- 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 %}{%- else %}{{- item | string }}{%- 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 %}{%- set head = namespace(count=0, seen_non_system=false) %}{%- for message in messages %}{%- set _is_sys = (message.role == 'system' or message.role == 'developer') %}{%- if _is_sys and not head.seen_non_system %}{%- set head.count = head.count + 1 %}{%- else %}{%- set head.seen_non_system = true %}{%- endif %}{%- endfor %}{%- set sys_state = namespace(content='') %}{%- for message in messages[:head.count] %}{%- set _part = render_content(message.content, false, true) | trim %}{%- if '<|think_off|>' in _part %}{%- set _part = _part.split('<|think_off|>') | join('') | trim %}{%- endif %}{%- if '<|think_on|>' in _part %}{%- set _part = _part.split('<|think_on|>') | join('') | trim %}{%- endif %}{%- if '<|think_xhigh|>' in _part %}{%- set _part = _part.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}{%- if '<|think_high|>' in _part %}{%- set _part = _part.split('<|think_high|>') | join('') | trim %}{%- endif %}{%- if '<|think_ultracode|>' in _part %}{%- set _part = _part.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}{%- if '<|think_extreme|>' in _part %}{%- set _part = _part.split('<|think_extreme|>') | join('') | trim %}{%- endif %}{%- if '<|think_max|>' in _part %}{%- set _part = _part.split('<|think_max|>') | join('') | trim %}{%- endif %}{%- if '<|think_medium|>' in _part %}{%- set _part = _part.split('<|think_medium|>') | join('') | trim %}{%- endif %}{%- if '<|think_low|>' in _part %}{%- set _part = _part.split('<|think_low|>') | join('') | trim %}{%- endif %}{%- if '<|think_minimal|>' in _part %}{%- set _part = _part.split('<|think_minimal|>') | join('') | trim %}{%- endif %}{%- if _part %}{%- if sys_state.content %}{%- set sys_state.content = sys_state.content ~ '\n\n' ~ _part %}{%- else %}{%- set sys_state.content = _part %}{%- endif %}{%- endif %}{%- endfor %}{%- set _sc = sys_state.content %}{%- set _msgs = messages[head.count:] %}{%- if _has_tools %}{{- '<|im_start|>system\n' }}{%- if reasoning_instructions %}{{- reasoning_instructions + '\n\n' }}{%- endif %}{{- '# Tools\n\nYou have access to the following functions:\n\n<tools>' }}{%- for tool in tools %}{{- '\n' }}{{- tool | tojson }}{%- endfor %}{{- '\n</tools>' }}{%- if _tool_format == 'json' %}{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<think>\nBrief explanation of tool call\n</think>\n<tool_call>\n{"name": "example_function_name", "arguments": {"example_parameter_1": "value_1", "example_parameter_2": "This is the value for the second parameter"}}\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n- Function calls MUST follow the specified format: a single JSON object with "name" and "arguments" keys inside <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY after thinking, with NO conversational text before it.\n- The <tool_call> tag MUST be at the very beginning of a new line, with NO spaces or indentation before it.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}{%- else %}{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<think>\nBrief explanation of tool call\n</think>\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- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY after thinking, with NO conversational text before it.\n- The <tool_call> and <function> tags MUST be at the very beginning of a new line, with NO spaces or indentation before them.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}{%- endif %}{%- if _sc %}{{- '\n\n' + _sc }}{%- endif %}{{- '<|im_end|>\n' }}{%- else %}{%- if _sc %}{{- '<|im_start|>system\n' + (reasoning_instructions + '\n\n' if reasoning_instructions else '') + _sc + '<|im_end|>\n' }}{%- elif reasoning_instructions %}{{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}{%- endif %}{%- endif %}{%- set _last_idx = _msgs | length - 1 %}{%- set ns = namespace(multi_step_tool=true, last_query_index=_last_idx) %}{%- for message in _msgs[::-1] %}{%- set index = (_msgs | length - 1) - loop.index0 %}{%- if ns.multi_step_tool and message.role == 'user' %}{%- set _rc = render_content(message.content, false) | trim %}{%- if not (_rc.startswith('<tool_response>') and _rc.endswith('</tool_response>')) %}{%- set ns.multi_step_tool = false %}{%- set ns.last_query_index = index %}{%- endif %}{%- endif %}{%- endfor %}{%- if ns.multi_step_tool %}{%- if _last_idx > 50 %}{%- set ns.last_query_index = _last_idx %}{%- else %}{%- set ns.last_query_index = 0 %}{%- endif %}{%- endif %}{%- set ns2 = namespace(prev_role='', consecutive_failures=0) %}{%- for message in _msgs %}{%- set is_system = (message.role == "system" or message.role == "developer") %}{%- set content = render_content(message.content, true, is_system) | trim %}{%- if is_system or message.role == 'user' %}{%- if '<|think_off|>' in content %}{%- set content = content.split('<|think_off|>') | join('') | trim %}{%- endif %}{%- if '<|think_on|>' in content %}{%- set content = content.split('<|think_on|>') | join('') | trim %}{%- endif %}{%- if '<|think_xhigh|>' in content %}{%- set content = content.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}{%- if '<|think_high|>' in content %}{%- set content = content.split('<|think_high|>') | join('') | trim %}{%- endif %}{%- if '<|think_ultracode|>' in content %}{%- set content = content.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}{%- if '<|think_extreme|>' in content %}{%- set content = content.split('<|think_extreme|>') | join('') | trim %}{%- endif %}{%- if '<|think_max|>' in content %}{%- set content = content.split('<|think_max|>') | join('') | trim %}{%- endif %}{%- if '<|think_medium|>' in content %}{%- set content = content.split('<|think_medium|>') | join('') | trim %}{%- endif %}{%- if '<|think_low|>' in content %}{%- set content = content.split('<|think_low|>') | join('') | trim %}{%- endif %}{%- if '<|think_minimal|>' in content %}{%- set content = content.split('<|think_minimal|>') | join('') | trim %}{%- endif %}{%- endif %}{%- if is_system %}{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}{%- elif message.role == 'user' %}{%- set ns2.consecutive_failures = 0 %}{{- '<|im_start|>user\n' + content + '<|im_end|>\n' }}{%- elif message.role == 'assistant' %}{%- set reasoning_content = '' %}{%- set _explicit_reasoning = '' %}{%- if message.reasoning_content is defined and message.reasoning_content is not none %}{%- if message.reasoning_content is string %}{%- set _explicit_reasoning = message.reasoning_content %}{%- else %}{%- set _explicit_reasoning = message.reasoning_content | string %}{%- endif %}{%- elif message.thinking is defined and message.thinking is not none %}{%- if message.thinking is string %}{%- set _explicit_reasoning = message.thinking %}{%- else %}{%- set _explicit_reasoning = message.thinking | string %}{%- endif %}{%- endif %}{%- if _explicit_reasoning %}{%- set _lead_end = '' %}{%- if content.startswith('<think>') and '</think>' in content %}{%- set _lead_end = '</think>' %}{%- elif content.startswith('<thinking>') and '</thinking>' in content %}{%- set _lead_end = '</thinking>' %}{%- elif content.startswith('</think>') %}{%- set _lead_end = '</think>' %}{%- elif content.startswith('</thinking>') %}{%- set _lead_end = '</thinking>' %}{%- endif %}{%- if _lead_end %}{%- set content = content.split(_lead_end)[-1].lstrip('\n') %}{%- endif %}{%- set reasoning_content = _explicit_reasoning %}{%- else %}{%- set _think_end = '' %}{%- if content.startswith('</think>') %}{%- set _think_end = '</think>' %}{%- elif content.startswith('</thinking>') %}{%- set _think_end = '</thinking>' %}{%- elif '\n</think>' in content %}{%- set _think_end = '\n</think>' %}{%- elif '\n</thinking>' in content %}{%- set _think_end = '\n</thinking>' %}{%- elif '\n</ think>' in content %}{%- set _think_end = '\n</ think>' %}{%- elif '\n</think >' in content %}{%- set _think_end = '\n</think >' %}{%- elif content.startswith('<think>') and '</think>' in content %}{%- set _think_end = '</think>' %}{%- elif content.startswith('<thinking>') and '</thinking>' in content %}{%- set _think_end = '</thinking>' %}{%- endif %}{%- if _think_end %}{%- if 'thinking' in _think_end %}{%- set _think_start = '<thinking>' %}{%- else %}{%- set _think_start = '<think>' %}{%- endif %}{%- set reasoning_content = content.split(_think_end)[0].rstrip('\n') %}{%- if _think_start in reasoning_content %}{%- set reasoning_content = reasoning_content.split(_think_start)[-1].lstrip('\n') %}{%- endif %}{%- set content = content.split(_think_end)[-1].lstrip('\n') %}{%- endif %}{%- endif %}{%- set reasoning_content = reasoning_content | trim %}{%- if (_preserve_thinking or loop.index0 > ns.last_query_index) %}{{- '<|im_start|>assistant\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}{%- else %}{{- '<|im_start|>assistant\n' + content }}{%- endif %}{%- if message.tool_calls is defined and 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 and tool_call.function is not none %}{%- set tc = tool_call.function %}{%- else %}{%- set tc = tool_call %}{%- endif %}{%- set tc_name = tc.name if (tc.name is defined and tc.name is not none) else '' %}{%- if _tool_format == 'json' %}{%- if not loop.first or content | trim %}{{- '\n\n' }}{%- endif %}{%- set _args = '{}' %}{%- if tc.arguments is defined and tc.arguments is not none %}{%- if tc.arguments is mapping %}{%- set _args = tc.arguments | tojson %}{%- elif tc.arguments is string %}{%- if tc.arguments %}{%- set _args = tc.arguments %}{%- endif %}{%- else %}{%- set _args = tc.arguments | tojson %}{%- endif %}{%- endif %}{{- '<tool_call>\n{"name": ' }}{{- tc_name | tojson }}{{- ', "arguments": ' }}{{- _args }}{{- '}\n</tool_call>' }}{%- else %}{%- if loop.first %}{%- if content | trim %}{{- '\n\n<tool_call>\n<function=' + tc_name + '>\n' }}{%- else %}{{- '<tool_call>\n<function=' + tc_name + '>\n' }}{%- endif %}{%- else %}{{- '\n\n<tool_call>\n<function=' + tc_name + '>\n' }}{%- endif %}{%- if tc.arguments is defined and tc.arguments is not none %}{%- if tc.arguments is mapping %}{%- for args_name, args_value in tc.arguments.items() %}{{- '<parameter=' + args_name + '>\n' }}{%- if args_value is string %}{%- set _av = args_value %}{%- else %}{%- set _av = args_value | tojson %}{%- endif %}{%- if max_tool_arg_chars > 0 and _av | length > max_tool_arg_chars %}{{- _av[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_av | length | string) ~ ' chars]' }}{%- else %}{{- _av }}{%- endif %}{{- '\n</parameter>\n' }}{%- endfor %}{%- else %}{%- if tc.arguments is string %}{%- set _raw_args = tc.arguments %}{%- else %}{%- set _raw_args = tc.arguments | tojson %}{%- endif %}{%- if _raw_args %}{%- if max_tool_arg_chars > 0 and _raw_args | length > max_tool_arg_chars %}{{- _raw_args[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_raw_args | length | string) ~ ' chars]' }}{%- else %}{{- _raw_args }}{%- endif %}{%- endif %}{%- endif %}{%- endif %}{{- '</function>\n</tool_call>' }}{%- endif %}{%- endfor %}{%- endif %}{{- '<|im_end|>\n' }}{%- elif message.role == 'tool' %}{%- set _content_lower = content | lower %}{%- set _content_head = _content_lower[:120] %}{%- set _is_code_or_grep = ('throw new ' in _content_lower or 'throw error' in _content_lower or 'console.error' in _content_lower or 'logger.error' in _content_lower or 'logging.error' in _content_lower or 'import ' in _content_head or 'def ' in _content_head or 'function ' in _content_head) %}{%- set _exit_code_zero = ('exit code: 0' in _content_head or 'process exited with code 0' in _content_head) %}{%- set _error_field_ok = ('"error": null' in _content_head or '"error":null' in _content_head or '"error": false' in _content_head or '"error":false' in _content_head or '"error": ""' in _content_head or '"error":""' in _content_head) %}{%- set _strong_error = (('"error":' in _content_head and not _error_field_ok) or '"status": "error"' in _content_head or '"status":"error"' in _content_head or 'traceback (most recent call last):' in _content_head or 'command not found' in _content_head or 'invalid syntax' in _content_head or 'fatal:' in _content_head or (('exit code: ' in _content_head or 'process exited with code' in _content_head) and not _exit_code_zero) or _content_head.startswith('exception:') or _content_head.startswith('failed to ')) %}{%- set _weak_error = ('error:' in _content_head or 'err!' in _content_head) %}{%- set _weak_suppressed = ('$ ' in _content_head or 'took ' in _content_head or content | length >= 600) %}{%- if not _is_code_or_grep and (_strong_error or (_weak_error and not _weak_suppressed)) %}{%- set ns2.consecutive_failures = ns2.consecutive_failures + 1 %}{%- else %}{%- set ns2.consecutive_failures = 0 %}{%- endif %}{%- if ns2.prev_role != 'tool' %}{{- '<|im_start|>user' }}{%- endif %}{%- if _tool_format != 'json' and max_tool_response_chars > 0 and content | length > max_tool_response_chars %}{%- set content = content[:max_tool_response_chars] + '\n[TRUNCATED - original length ' ~ (content | length | string) ~ ' chars]' %}{%- endif %}{{- '\n<tool_response>\n' + content }}{%- if ns2.consecutive_failures >= 2 %}{{- '\n\n⚠️ SYSTEM WARNING: ' ~ ns2.consecutive_failures ~ ' consecutive tool errors detected. Your previous approach is incorrect. You MUST use a fundamentally different approach or corrected arguments.' }}{%- elif ns2.consecutive_failures == 1 %}{{- '\n\n⚠️ SYSTEM WARNING: The previous tool call returned an error. Diagnose the failure and retry with completely corrected arguments.' }}{%- endif %}{{- '\n</tool_response>' }}{%- if loop.last %}{{- '<|im_end|>\n' }}{%- else %}{%- set _next_role = _msgs[loop.index0 + 1].role %}{%- if _next_role != 'tool' %}{{- '<|im_end|>\n' }}{%- endif %}{%- endif %}{%- else %}{{- '<|im_start|>user\n[' + message.role + ']: ' + content + '<|im_end|>\n' }}{%- endif %}{%- set ns2.prev_role = message.role %}{%- endfor %}{%- if add_generation_prompt %}{{- '<|im_start|>assistant\n' }}{%- if not ns_state.thinking %}{{- '<think>\n\n</think>\n\n' }}{%- else %}{{- '<think>\n' }}{%- endif %}{%- endif %}
 
1
+ {%- set template_version = "qwen3.8-froggeric-v22.3" %}{%- set _tool_format = tool_call_format if tool_call_format is defined else 'xml' %}{%- set image_count = namespace(value=0) %}{%- set video_count = namespace(value=0) %}{%- set add_vision_id = add_vision_id if add_vision_id is defined else false %}{%- set enable_thinking = enable_thinking if enable_thinking is defined else true %}{%- set auto_disable_thinking_with_tools = auto_disable_thinking_with_tools if auto_disable_thinking_with_tools is defined else false %}{%- if preserve_reasoning is defined and preserve_reasoning is not none %}{%- set _preserve_thinking = preserve_reasoning %}{%- elif preserve_thinking is defined and preserve_thinking is not none %}{%- set _preserve_thinking = preserve_thinking %}{%- else %}{%- set _preserve_thinking = true %}{%- endif %}{%- set max_tool_arg_chars = max_tool_arg_chars if max_tool_arg_chars is defined else 0 %}{%- set max_tool_response_chars = max_tool_response_chars if max_tool_response_chars is defined else 0 %}{%- set _has_tools = (tools is defined and tools and tools is iterable and tools is not mapping) %}{%- set _effort_raw = (reasoning_effort | string | lower) if reasoning_effort is defined and reasoning_effort is not none else 'medium' %}{%- set _initial_thinking = enable_thinking %}{%- set _initial_effort = 'medium' %}{%- if _effort_raw in ('none', 'off') %}{%- set _initial_thinking = false %}{%- set _initial_effort = 'medium' %}{%- elif _effort_raw in ('minimal', 'low') %}{%- set _initial_effort = 'low' %}{%- elif _effort_raw in ('high', 'xhigh', 'max', 'ultracode', 'extreme') %}{%- set _initial_effort = 'xhigh' %}{%- else %}{%- set _initial_effort = 'medium' %}{%- endif %}{%- set ns_state = namespace(thinking=_initial_thinking, effort=_initial_effort) %}{%- if auto_disable_thinking_with_tools and _has_tools %}{%- set ns_state.thinking = false %}{%- endif %}{%- for msg in messages %}{%- if msg.role == 'system' or msg.role == 'developer' or msg.role == 'user' %}{%- if msg.content is string %}{%- if '<|think_off|>' in msg.content %}{%- set ns_state.thinking = false %}{%- elif '<|think_on|>' in msg.content %}{%- set ns_state.thinking = true %}{%- elif '<|think_xhigh|>' in msg.content or '<|think_high|>' in msg.content or '<|think_ultracode|>' in msg.content or '<|think_extreme|>' in msg.content or '<|think_max|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'xhigh' %}{%- elif '<|think_low|>' in msg.content or '<|think_minimal|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'low' %}{%- elif '<|think_medium|>' in msg.content %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'medium' %}{%- endif %}{%- elif msg.content is iterable and msg.content is not mapping %}{%- for item in msg.content %}{%- if item is string %}{%- set _item_text = item %}{%- elif item is mapping and 'text' in item and item.text is string %}{%- set _item_text = item.text %}{%- else %}{%- set _item_text = '' %}{%- endif %}{%- if _item_text %}{%- if '<|think_off|>' in _item_text %}{%- set ns_state.thinking = false %}{%- elif '<|think_on|>' in _item_text %}{%- set ns_state.thinking = true %}{%- elif '<|think_xhigh|>' in _item_text or '<|think_high|>' in _item_text or '<|think_ultracode|>' in _item_text or '<|think_extreme|>' in _item_text or '<|think_max|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'xhigh' %}{%- elif '<|think_low|>' in _item_text or '<|think_minimal|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'low' %}{%- elif '<|think_medium|>' in _item_text %}{%- set ns_state.thinking = true %}{%- set ns_state.effort = 'medium' %}{%- endif %}{%- endif %}{%- endfor %}{%- endif %}{%- endif %}{%- endfor %}{%- set reasoning_instructions = '' %}{%- if ns_state.thinking %}{%- if ns_state.effort == 'xhigh' %}{%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}{%- elif ns_state.effort == 'low' %}{%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}{%- endif %}{%- endif %}{%- 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 item is mapping %}{%- if item.type == 'image' or 'image' in item or 'image_url' in item %}{%- 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 item.type == 'video' or 'video' in item %}{%- 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 %}{%- else %}{{- item | string }}{%- 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 %}{%- set head = namespace(count=0, seen_non_system=false) %}{%- for message in messages %}{%- set _is_sys = (message.role == 'system' or message.role == 'developer') %}{%- if _is_sys and not head.seen_non_system %}{%- set head.count = head.count + 1 %}{%- else %}{%- set head.seen_non_system = true %}{%- endif %}{%- endfor %}{%- set sys_state = namespace(content='') %}{%- for message in messages[:head.count] %}{%- set _part = render_content(message.content, false, true) | trim %}{%- if '<|think_off|>' in _part %}{%- set _part = _part.split('<|think_off|>') | join('') | trim %}{%- endif %}{%- if '<|think_on|>' in _part %}{%- set _part = _part.split('<|think_on|>') | join('') | trim %}{%- endif %}{%- if '<|think_xhigh|>' in _part %}{%- set _part = _part.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}{%- if '<|think_high|>' in _part %}{%- set _part = _part.split('<|think_high|>') | join('') | trim %}{%- endif %}{%- if '<|think_ultracode|>' in _part %}{%- set _part = _part.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}{%- if '<|think_extreme|>' in _part %}{%- set _part = _part.split('<|think_extreme|>') | join('') | trim %}{%- endif %}{%- if '<|think_max|>' in _part %}{%- set _part = _part.split('<|think_max|>') | join('') | trim %}{%- endif %}{%- if '<|think_medium|>' in _part %}{%- set _part = _part.split('<|think_medium|>') | join('') | trim %}{%- endif %}{%- if '<|think_low|>' in _part %}{%- set _part = _part.split('<|think_low|>') | join('') | trim %}{%- endif %}{%- if '<|think_minimal|>' in _part %}{%- set _part = _part.split('<|think_minimal|>') | join('') | trim %}{%- endif %}{%- if _part %}{%- if sys_state.content %}{%- set sys_state.content = sys_state.content ~ '\n\n' ~ _part %}{%- else %}{%- set sys_state.content = _part %}{%- endif %}{%- endif %}{%- endfor %}{%- set _sc = sys_state.content %}{%- set _msgs = messages[head.count:] %}{%- if _has_tools %}{{- '<|im_start|>system\n' }}{%- if reasoning_instructions %}{{- reasoning_instructions + '\n\n' }}{%- endif %}{{- '# Tools\n\nYou have access to the following functions:\n\n<tools>' }}{%- for tool in tools %}{{- '\n' }}{{- tool | tojson }}{%- endfor %}{{- '\n</tools>' }}{%- if _tool_format == 'json' %}{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<think>\nBrief explanation of tool call\n</think>\n<tool_call>\n{"name": "example_function_name", "arguments": {"example_parameter_1": "value_1", "example_parameter_2": "This is the value for the second parameter"}}\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n- Function calls MUST follow the specified format: a single JSON object with "name" and "arguments" keys inside <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY after thinking, with NO conversational text before it.\n- The <tool_call> tag MUST be at the very beginning of a new line, with NO spaces or indentation before it.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}{%- else %}{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<think>\nBrief explanation of tool call\n</think>\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- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY after thinking, with NO conversational text before it.\n- The <tool_call> and <function> tags MUST be at the very beginning of a new line, with NO spaces or indentation before them.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}{%- endif %}{%- if _sc %}{{- '\n\n' + _sc }}{%- endif %}{{- '<|im_end|>\n' }}{%- else %}{%- if _sc %}{{- '<|im_start|>system\n' + (reasoning_instructions + '\n\n' if reasoning_instructions else '') + _sc + '<|im_end|>\n' }}{%- elif reasoning_instructions %}{{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}{%- endif %}{%- endif %}{%- set _last_idx = _msgs | length - 1 %}{%- set ns = namespace(multi_step_tool=true, last_query_index=_last_idx) %}{%- for message in _msgs[::-1] %}{%- set index = (_msgs | length - 1) - loop.index0 %}{%- if ns.multi_step_tool and message.role == 'user' %}{%- set _rc = render_content(message.content, false) | trim %}{%- if not (_rc.startswith('<tool_response>') and _rc.endswith('</tool_response>')) %}{%- set ns.multi_step_tool = false %}{%- set ns.last_query_index = index %}{%- endif %}{%- endif %}{%- endfor %}{%- if ns.multi_step_tool %}{%- if _last_idx > 50 %}{%- set ns.last_query_index = _last_idx %}{%- else %}{%- set ns.last_query_index = 0 %}{%- endif %}{%- endif %}{%- set ns2 = namespace(prev_role='', consecutive_failures=0) %}{%- for message in _msgs %}{%- set is_system = (message.role == "system" or message.role == "developer") %}{%- set content = render_content(message.content, true, is_system) | trim %}{%- if is_system or message.role == 'user' %}{%- if '<|think_off|>' in content %}{%- set content = content.split('<|think_off|>') | join('') | trim %}{%- endif %}{%- if '<|think_on|>' in content %}{%- set content = content.split('<|think_on|>') | join('') | trim %}{%- endif %}{%- if '<|think_xhigh|>' in content %}{%- set content = content.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}{%- if '<|think_high|>' in content %}{%- set content = content.split('<|think_high|>') | join('') | trim %}{%- endif %}{%- if '<|think_ultracode|>' in content %}{%- set content = content.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}{%- if '<|think_extreme|>' in content %}{%- set content = content.split('<|think_extreme|>') | join('') | trim %}{%- endif %}{%- if '<|think_max|>' in content %}{%- set content = content.split('<|think_max|>') | join('') | trim %}{%- endif %}{%- if '<|think_medium|>' in content %}{%- set content = content.split('<|think_medium|>') | join('') | trim %}{%- endif %}{%- if '<|think_low|>' in content %}{%- set content = content.split('<|think_low|>') | join('') | trim %}{%- endif %}{%- if '<|think_minimal|>' in content %}{%- set content = content.split('<|think_minimal|>') | join('') | trim %}{%- endif %}{%- endif %}{%- if is_system %}{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}{%- elif message.role == 'user' %}{%- set ns2.consecutive_failures = 0 %}{{- '<|im_start|>user\n' + content + '<|im_end|>\n' }}{%- elif message.role == 'assistant' %}{%- set reasoning_content = '' %}{%- set _explicit_reasoning = '' %}{%- if message.reasoning_content is defined and message.reasoning_content is not none %}{%- if message.reasoning_content is string %}{%- set _explicit_reasoning = message.reasoning_content %}{%- else %}{%- set _explicit_reasoning = message.reasoning_content | string %}{%- endif %}{%- elif message.reasoning is defined and message.reasoning is not none %}{%- if message.reasoning is string %}{%- set _explicit_reasoning = message.reasoning %}{%- else %}{%- set _explicit_reasoning = message.reasoning | string %}{%- endif %}{%- elif message.thinking is defined and message.thinking is not none %}{%- if message.thinking is string %}{%- set _explicit_reasoning = message.thinking %}{%- else %}{%- set _explicit_reasoning = message.thinking | string %}{%- endif %}{%- endif %}{%- if _explicit_reasoning %}{%- set _lead_end = '' %}{%- if content.startswith('<think>') and '</think>' in content %}{%- set _lead_end = '</think>' %}{%- elif content.startswith('<thinking>') and '</thinking>' in content %}{%- set _lead_end = '</thinking>' %}{%- elif content.startswith('</think>') %}{%- set _lead_end = '</think>' %}{%- elif content.startswith('</thinking>') %}{%- set _lead_end = '</thinking>' %}{%- endif %}{%- if _lead_end %}{%- set content = content.split(_lead_end)[-1].lstrip('\n') %}{%- endif %}{%- set reasoning_content = _explicit_reasoning %}{%- else %}{%- set _think_end = '' %}{%- if content.startswith('</think>') %}{%- set _think_end = '</think>' %}{%- elif content.startswith('</thinking>') %}{%- set _think_end = '</thinking>' %}{%- elif '\n</think>' in content %}{%- set _think_end = '\n</think>' %}{%- elif '\n</thinking>' in content %}{%- set _think_end = '\n</thinking>' %}{%- elif '\n</ think>' in content %}{%- set _think_end = '\n</ think>' %}{%- elif '\n</think >' in content %}{%- set _think_end = '\n</think >' %}{%- elif content.startswith('<think>') and '</think>' in content %}{%- set _think_end = '</think>' %}{%- elif content.startswith('<thinking>') and '</thinking>' in content %}{%- set _think_end = '</thinking>' %}{%- endif %}{%- if _think_end %}{%- if 'thinking' in _think_end %}{%- set _think_start = '<thinking>' %}{%- else %}{%- set _think_start = '<think>' %}{%- endif %}{%- set reasoning_content = content.split(_think_end)[0].rstrip('\n') %}{%- if _think_start in reasoning_content %}{%- set reasoning_content = reasoning_content.split(_think_start)[-1].lstrip('\n') %}{%- endif %}{%- set content = content.split(_think_end)[-1].lstrip('\n') %}{%- endif %}{%- endif %}{%- set reasoning_content = reasoning_content | trim %}{%- if (_preserve_thinking or loop.index0 > ns.last_query_index) %}{{- '<|im_start|>assistant\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}{%- else %}{{- '<|im_start|>assistant\n' + content }}{%- endif %}{%- if message.tool_calls is defined and 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 and tool_call.function is not none %}{%- set tc = tool_call.function %}{%- else %}{%- set tc = tool_call %}{%- endif %}{%- set tc_name = tc.name if (tc.name is defined and tc.name is not none) else '' %}{%- if _tool_format == 'json' %}{%- if not loop.first or content | trim %}{{- '\n\n' }}{%- endif %}{%- set _args = '{}' %}{%- if tc.arguments is defined and tc.arguments is not none %}{%- if tc.arguments is mapping %}{%- set _args = tc.arguments | tojson %}{%- elif tc.arguments is string %}{%- if tc.arguments %}{%- set _args = tc.arguments %}{%- endif %}{%- else %}{%- set _args = tc.arguments | tojson %}{%- endif %}{%- endif %}{{- '<tool_call>\n{"name": ' }}{{- tc_name | tojson }}{{- ', "arguments": ' }}{{- _args }}{{- '}\n</tool_call>' }}{%- else %}{%- if loop.first %}{%- if content | trim %}{{- '\n\n<tool_call>\n<function=' + tc_name + '>\n' }}{%- else %}{{- '<tool_call>\n<function=' + tc_name + '>\n' }}{%- endif %}{%- else %}{{- '\n\n<tool_call>\n<function=' + tc_name + '>\n' }}{%- endif %}{%- if tc.arguments is defined and tc.arguments is not none %}{%- if tc.arguments is mapping %}{%- for args_name, args_value in tc.arguments.items() %}{{- '<parameter=' + args_name + '>\n' }}{%- if args_value is string %}{%- set _av = args_value %}{%- else %}{%- set _av = args_value | tojson %}{%- endif %}{%- if max_tool_arg_chars > 0 and _av | length > max_tool_arg_chars %}{{- _av[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_av | length | string) ~ ' chars]' }}{%- else %}{{- _av }}{%- endif %}{{- '\n</parameter>\n' }}{%- endfor %}{%- else %}{%- if tc.arguments is string %}{%- set _raw_args = tc.arguments %}{%- else %}{%- set _raw_args = tc.arguments | tojson %}{%- endif %}{%- if _raw_args %}{%- if max_tool_arg_chars > 0 and _raw_args | length > max_tool_arg_chars %}{{- _raw_args[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_raw_args | length | string) ~ ' chars]' }}{%- else %}{{- _raw_args }}{%- endif %}{%- endif %}{%- endif %}{%- endif %}{{- '</function>\n</tool_call>' }}{%- endif %}{%- endfor %}{%- endif %}{{- '<|im_end|>\n' }}{%- elif message.role == 'tool' %}{%- set _content_lower = content | lower %}{%- set _content_head = _content_lower[:120] %}{%- set _is_code_or_grep = ('throw new ' in _content_lower or 'throw error' in _content_lower or 'console.error' in _content_lower or 'logger.error' in _content_lower or 'logging.error' in _content_lower or 'import ' in _content_head or 'def ' in _content_head or 'function ' in _content_head) %}{%- set _exit_code_zero = ('exit code: 0' in _content_head or 'process exited with code 0' in _content_head) %}{%- set _error_field_ok = ('"error": null' in _content_head or '"error":null' in _content_head or '"error": false' in _content_head or '"error":false' in _content_head or '"error": ""' in _content_head or '"error":""' in _content_head) %}{%- set _strong_error = (('"error":' in _content_head and not _error_field_ok) or '"status": "error"' in _content_head or '"status":"error"' in _content_head or 'traceback (most recent call last):' in _content_head or 'command not found' in _content_head or 'invalid syntax' in _content_head or 'fatal:' in _content_head or (('exit code: ' in _content_head or 'process exited with code' in _content_head) and not _exit_code_zero) or _content_head.startswith('exception:') or _content_head.startswith('failed to ')) %}{%- set _weak_error = ('error:' in _content_head or 'err!' in _content_head) %}{%- set _weak_suppressed = ('$ ' in _content_head or 'took ' in _content_head or content | length >= 600) %}{%- if not _is_code_or_grep and (_strong_error or (_weak_error and not _weak_suppressed)) %}{%- set ns2.consecutive_failures = ns2.consecutive_failures + 1 %}{%- else %}{%- set ns2.consecutive_failures = 0 %}{%- endif %}{%- if ns2.prev_role != 'tool' %}{{- '<|im_start|>user' }}{%- endif %}{%- if _tool_format != 'json' and max_tool_response_chars > 0 and content | length > max_tool_response_chars %}{%- set content = content[:max_tool_response_chars] + '\n[TRUNCATED - original length ' ~ (content | length | string) ~ ' chars]' %}{%- endif %}{{- '\n<tool_response>\n' + content }}{%- if ns2.consecutive_failures >= 2 %}{{- '\n\n⚠️ SYSTEM WARNING: ' ~ ns2.consecutive_failures ~ ' consecutive tool errors detected. Your previous approach is incorrect. You MUST use a fundamentally different approach or corrected arguments.' }}{%- elif ns2.consecutive_failures == 1 %}{{- '\n\n⚠️ SYSTEM WARNING: The previous tool call returned an error. Diagnose the failure and retry with completely corrected arguments.' }}{%- endif %}{{- '\n</tool_response>' }}{%- if loop.last %}{{- '<|im_end|>\n' }}{%- else %}{%- set _next_role = _msgs[loop.index0 + 1].role %}{%- if _next_role != 'tool' %}{{- '<|im_end|>\n' }}{%- endif %}{%- endif %}{%- else %}{{- '<|im_start|>user\n[' + message.role + ']: ' + content + '<|im_end|>\n' }}{%- endif %}{%- set ns2.prev_role = message.role %}{%- endfor %}{%- if add_generation_prompt %}{{- '<|im_start|>assistant\n' }}{%- if not ns_state.thinking %}{{- '<think>\n\n</think>\n\n' }}{%- else %}{{- '<think>\n' }}{%- endif %}{%- endif %}
scripts/test_v22.py CHANGED
@@ -524,6 +524,17 @@ execute_test(
524
  # 4. Tool Calling (XML & JSON)
525
  # ==========================================
526
 
 
 
 
 
 
 
 
 
 
 
 
527
  # 25. Tool calling with dict arguments (XML)
528
  execute_test(
529
  "25. Tool calling with dict arguments (XML)",
 
524
  # 4. Tool Calling (XML & JSON)
525
  # ==========================================
526
 
527
+ # 24b. OpenAI `reasoning` field (the name vLLM returns and the Responses API uses)
528
+ execute_test(
529
+ "24b. OpenAI reasoning field (vLLM / Responses API name)",
530
+ messages=[
531
+ {"role": "user", "content": "Q1"},
532
+ {"role": "assistant", "content": "Answer 1", "reasoning": "Thought via reasoning field"},
533
+ {"role": "user", "content": "Q2"}
534
+ ],
535
+ expected_in=["<|im_start|>assistant\n<think>\nThought via reasoning field\n</think>\n\nAnswer 1<|im_end|>"]
536
+ )
537
+
538
  # 25. Tool calling with dict arguments (XML)
539
  execute_test(
540
  "25. Tool calling with dict arguments (XML)",