September 3, 2026

LM Studio preset for thinking mode

I was trying Qwen 3.8 27B on my Mac Mini M4 Pro which has 64GB but it is still pretty slow, especially when it is in thinking mode.

So I looked up the web, and there is a way in LM studio to create presets to enable/disable thinking.

I switched also to Qwen 3.6 35B A3B in the meantime to have a better performance.

In order to have two presets, what you can do is go to My Models > select Qwen 3.6 > Prompt Template (Jinja).

Then add at the top:

{%- set config = namespace(
    enable_thinking=(enable_thinking if enable_thinking is defined else true)
) %}

{%- if messages and messages[0].role == 'system' %}
    {%- if messages[0].content is string and '<|think_off|>' in messages[0].content %}
        {%- set config.enable_thinking = false %}
    {%- elif messages[0].content is iterable and messages[0].content is not mapping %}
        {%- for item in messages[0].content %}
            {%- if 'text' in item and '<|think_off|>' in item.text %}
                {%- set config.enable_thinking = false %}
            {%- endif %}
        {%- endfor %}
    {%- endif %}
{%- endif %}

After all that, when you are in a chat, you can go to Model Parameters > Preset and you can create two.

  • Qwen 3.6 - Thinking
    • System prompt: leave empty
    • Temperature: 1
    • Top K Sampling: 20
    • Repeat Penalty: 1
    • Top P Sampling: 0.95
    • Min P Sampling: 0
  • Qwen 3.6 - No thinking
    • System prompt: <|think_off|>
    • Temperature: 0.7
    • Top K Sampling: 20
    • Repeat Penalty: 1
    • Top P Sampling: 0.8
    • Min P Sampling: 0

Then save them.

Once back in the chat you can switch to use the preset you’d like.

Qwen 3.6 - Thinking

Explain integrals and primitives like I’m 5 in one paragraph max.

Thought for 20.62 seconds

Imagine building a tall tower out of invisible Lego bricks; an integral is just the math way of carefully adding up all those tiny, hidden bricks to find the tower’s total height. A primitive (or antiderivative) works the exact opposite way—it’s like a “reverse recipe” that takes your step-by-step changes and undoes them, showing how the whole tower started and grew before you stacked each new piece. Together, they’re just two sides of the same puzzle: one that slowly adds up bits to find a total, and one that steps backward from how something changed to reveal what it began as.

60.34 tok/sec - 1373 tokens - 0.67s

Qwen 3.6 - No thinking

Explain integrals and primitives like I’m 5 in one paragraph max.

Imagine you have a giant pile of tiny Lego bricks; an integral is like counting all those bricks to find the total size of the pile, while a primitive is like a special key that helps you figure out that total count quickly without having to count each brick one by one. Think of it this way: if you are driving and want to know how far you went, the distance is the “integral” (the total area under your speed graph), and the “primitive” is the odometer in your car that automatically adds up your speed over time to show you that final number. So, integration is the process of adding up tiny pieces to find a whole, and a primitive is the formula or function that tells you the answer instantly.

61.61 tok/sec - 149 tokens - 0.53s

It used less tokens (149 vs 1373) and avoided thinking for 20 seconds before answering, and to me the second answer seems better since it got the relationship right as it presents the primitive as a shortcut for computing the integral, while the first answer (thinking mode) is misleading in saying that the primitive works the opposite way right after describing the integral, it would be like suggesting integration and antidifferentiation are opposites while they are not.

Of course not a good benchmark at all :)

Might work for Qwen 3.8 and other thiking models.

Full Prompt Template for reference
{%- set config = namespace(
    enable_thinking=(enable_thinking if enable_thinking is defined else true)
) %}

{%- if messages and messages[0].role == 'system' %}
    {%- if messages[0].content is string and '<|think_off|>' in messages[0].content %}
        {%- set config.enable_thinking = false %}
    {%- elif messages[0].content is iterable and messages[0].content is not mapping %}
        {%- for item in messages[0].content %}
            {%- if 'text' in item and '<|think_off|>' in item.text %}
                {%- set config.enable_thinking = false %}
            {%- endif %}
        {%- endfor %}
    {%- endif %}
{%- endif %}

{%- set image_count = namespace(value=0) %}
{%- set video_count = namespace(value=0) %}

{%- macro render_content(content, do_vision_count, is_system_content=false) %}
    {%- if content is string %}
        {%- if is_system_content %}
            {{- content | replace('<|think_off|>', '') }}
        {%- else %}
            {{- content }}
        {%- endif %}

    {%- 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 %}
                {%- if is_system_content %}
                    {{- item.text | replace('<|think_off|>', '') }}
                {%- else %}
                    {{- item.text }}
                {%- endif %}

            {%- 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 %}

{%- 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<tools>" }}

    {%- for tool in tools %}
        {{- "\n" }}
        {{- tool | tojson }}
    {%- 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>' }}

    {%- if messages[0].role == 'system' %}
        {%- set content = render_content(messages[0].content, false, true)|trim %}

        {%- if content %}
            {{- '\n\n' + content }}
        {%- endif %}
    {%- endif %}

    {{- '<|im_end|>\n' }}

{%- else %}
    {%- if messages[0].role == 'system' %}
        {%- set content = render_content(messages[0].content, false, true)|trim %}
        {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
    {%- endif %}
{%- endif %}


{%- set ns = namespace(
    multi_step_tool=true,
    last_query_index=messages|length - 1
) %}

{%- for message in messages[::-1] %}
    {%- set index = (messages|length - 1) - loop.index0 %}

    {%- if ns.multi_step_tool and message.role == "user" %}
        {%- set content = render_content(message.content, false)|trim %}

        {%- if not(
            content.startswith('<tool_response>')
            and content.endswith('</tool_response>')
        ) %}
            {%- 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 %}


{%- 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|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}


    {%- elif message.role == "assistant" %}

        {%- set reasoning_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]
                    .rstrip('\n')
                    .split('<think>')[-1]
                    .lstrip('\n')
                %}

                {%- set content =
                    content.split('</think>')[-1]
                    .lstrip('\n')
                %}
            {%- endif %}
        {%- endif %}

        {%- set reasoning_content = reasoning_content|trim %}

        {%- if
            (preserve_thinking is defined and preserve_thinking is true)
            or (loop.index0 > ns.last_query_index)
        %}
            {{- '<|im_start|>'
                + message.role
                + '\n<think>\n'
                + reasoning_content
                + '\n</think>\n\n'
                + content
            }}
        {%- else %}
            {{- '<|im_start|>'
                + message.role
                + '\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<tool_call>\n<function='
                            + tool_call.name
                            + '>\n'
                        }}
                    {%- else %}
                        {{- '<tool_call>\n<function='
                            + tool_call.name
                            + '>\n'
                        }}
                    {%- endif %}

                {%- else %}

                    {{- '\n<tool_call>\n<function='
                        + tool_call.name
                        + '>\n'
                    }}

                {%- endif %}


                {%- 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 | string
                            if args_value is string
                            else args_value | tojson
                        %}

                        {{- args_value }}
                        {{- '\n</parameter>\n' }}

                    {%- endfor %}

                {%- endif %}

                {{- '</function>\n</tool_call>' }}

            {%- endfor %}
        {%- endif %}

        {{- '<|im_end|>\n' }}


    {%- elif message.role == "tool" %}

        {%- 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 %}


    {%- else %}

        {{- raise_exception('Unexpected message role.') }}

    {%- endif %}
{%- endfor %}


{%- if add_generation_prompt %}
    {{- '<|im_start|>assistant\n' }}

    {%- if config.enable_thinking is false %}
        {{- '<think>\n\n</think>\n\n' }}
    {%- else %}
        {{- '<think>\n' }}
    {%- endif %}
{%- endif %}

Until next time!