I am posting this in case it might be of some help to LM Studio users.

#36
by savior714 - opened

Thank you for always providing so much help. I was able to resolve a tool call parsing error that occurred while using the v20 template by making some minor adjustments myself. Since the issue is now gone, I am sharing this modified template here for other LM Studio and VS Code users who might be struggling with a similar problem. I hope this will be helpful when the next version is released.

(I am currently using a combination of LM Studio + stock qwen 3.6 35b a3b 8-bit + VS Code + Kilo Code.)

{%- set template_version = "qwen3.6-froggeric-v20" %}
{%- 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 = true %}
{%- set _preserve_thinking = preserve_thinking if preserve_thinking is defined else true %}
{%- 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 ns_state = namespace(thinking=enable_thinking) %}
{%- 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 _first_role = messages[0].role %}
{%- if _first_role == 'system' or _first_role == 'developer' %}
{%- set _sys_msg = messages[0] %}
{%- set _msgs = messages[1:] %}
{%- else %}
{%- set _sys_msg = none %}
{%- set _msgs = messages %}
{%- endif %}
{%- set _sc = '' %}
{%- if _sys_msg is not none %}
{%- set _sc = render_content(_sys_msg.content, false, true) | trim %}
{%- if '<|think_off|>' in _sc %}
{%- set ns_state.thinking = false %}
{%- set _sc = _sc.split('<|think_off|>') | join('') | trim %}
{%- elif '<|think_on|>' in _sc %}
{%- set ns_state.thinking = true %}
{%- set _sc = _sc.split('<|think_on|>') | join('') | trim %}
{%- endif %}
{%- endif %}

{%- if _has_tools and auto_disable_thinking_with_tools %}
{%- set ns_state.thinking = false %}
{%- endif %}

{%- if _has_tools %}
{{- '<|im_start|>system\n' }}
{{- '# Tools\n\nYou have access to the following functions:\n\n' }}
{%- for tool in tools %}
{{- '\n' }}
{{- tool | tojson }}
{%- endfor %}
{{- '\n' }}
{%- set tool_instructions %}
CRITICAL:

The ONLY valid tool format is:

value

Never output:

TOOL_REQUEST
END_TOOL_REQUEST
{"name": "..."}
{"tool": "..."}

If a tool is needed:
call the tool directly.

If a tool is not needed:
answer the user normally.
{%- endset %}
{{- '\n\n' ~ tool_instructions | trim }}
{%- if _sc %}
{{- '\n\n' + _sc }}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- else %}
{%- if _sc %}
{{- '<|im_start|>system\n' + _sc + '<|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('') and _rc.endswith('')) %}
{%- set ns.multi_step_tool = false %}
{%- set ns.last_query_index = index %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- if ns.multi_step_tool %}
{{- raise_exception('No user query found in messages.') }}
{%- 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 '<|think_off|>' in content %}
{%- set ns_state.thinking = false %}
{%- set content = content.split('<|think_off|>') | join('') | trim %}
{%- elif '<|think_on|>' in content %}
{%- set ns_state.thinking = true %}
{%- set content = content.split('<|think_on|>') | join('') | trim %}
{%- 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 = '' %}
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
{%- if message.reasoning_content is string %}
{%- set reasoning_content = message.reasoning_content %}
{%- else %}
{%- set reasoning_content = message.reasoning_content | string %}
{%- endif %}
{%- else %}
{# FIX: deduplicated variants — original had two identical conditions #}
{%- set _think_end = '' %}
{%- if '' in content %}
{%- set _think_end = '' %}
{%- elif '</ think>' in content %}
{%- set _think_end = '</ think>' %}
{%- elif '' in content %}
{%- set _think_end = '' %}
{%- endif %}
{%- if _think_end %}
{%- set _think_start = '' %}
{%- 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 %}
{%- set _has_tool_calls = message.tool_calls is defined and message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
{%- set _assistant_content = '' if _has_tool_calls else content %}
{%- if (_preserve_thinking or loop.index0 > ns.last_query_index) and reasoning_content %}
{{- '<|im_start|>assistant\n\n' + reasoning_content + '\n\n\n' + _assistant_content }}
{%- else %}
{{- '<|im_start|>assistant\n' + _assistant_content }}
{%- endif %}
{%- if _has_tool_calls %}
{%- 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 %}
{%- if loop.first %}
{{- '\n<function=' + tc.name + '>\n' }}
{%- else %}
{{- '\n\n\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 mapping or (args_value is sequence and args_value is not string) %}
{%- set _av = args_value | tojson %}
{%- else %}
{%- set _av = args_value | string %}
{%- 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\n' }}
{%- endfor %}
{%- elif tc.arguments is string and tc.arguments %}
{{- tc.arguments }}
{%- endif %}
{%- endif %}
{%- if loop.last %}
{{- '\n\n' }}
{%- else %}
{{- '\n' }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- elif message.role == 'tool' %}
{%- set _content_lower = content | lower %}
{%- if content | length < 500 and '$ ' not in content and 'took ' not in _content_lower and ('"error":' in _content_lower or 'error:' in _content_lower or 'exception:' in _content_lower or 'traceback' in _content_lower or 'command not found' in _content_lower or 'invalid syntax' in _content_lower or 'failed to' in _content_lower) %}
{%- 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 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\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' }}
{%- 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 ns_state.thinking and not _has_tools %}
{{- '\n' }}
{%- endif %}
{%- endif %}

Interesting, I've had issues with V19 and V20. I ended up falling back on V17 which has a 3.6 version and works excellent for my use case (same as yours but opencode and hermes agent). I will give this a spin and see if it resolves my v20 issues.

I found that it works a little better in opencode.

In conclusion, use a model that is close to the stock version, and also use the stock fixed version template uploaded by froggeric.

In conclusion, use a model that is close to the stock version, and also use the stock fixed version template uploaded by froggeric.

savior714 changed discussion status to closed

Sign up or log in to comment