Skip to content

Commit 7820677

Browse files
committed
feat: enhance Qwen35ChatHandler with preserve_thinking and Qwen3.6 template fixes
- Add `preserve_thinking` parameter to optionally retain `<think>` reasoning blocks across all historical conversational turns (defaults to False to save tokens). - Improve template robustness by adding an `is defined` safety check for `enable_thinking`. - Simplify JSON serialization logic for tool call arguments in the Jinja template. - Update class docstring to explicitly indicate support for Qwen 3.5 and Qwen 3.6 models. - Include `preserve_thinking` state in verbose processing logs.
1 parent 9e00017 commit 7820677

2 files changed

Lines changed: 19 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ Below are the supported multi-modal models and their respective chat handlers (P
834834
| [qwen2.5-vl](https://huggingface.co/unsloth/Qwen2.5-VL-3B-Instruct-GGUF) | `Qwen25VLChatHandler` | `qwen2.5-vl` |
835835
| [qwen3-vl](https://huggingface.co/unsloth/Qwen3-VL-8B-Thinking-GGUF) | `Qwen3VLChatHandler` | `qwen3-vl` |
836836
| [qwen3.5](https://huggingface.co/unsloth/Qwen3.5-27B-GGUF) | `Qwen35ChatHandler` | `qwen3.5` |
837+
| [qwen3.6](https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF) | `Qwen35ChatHandler` | `qwen3.6` |
837838
| [step3-vl](https://huggingface.co/JamePeng2023/Step3-VL-10B-GGUF) | `Step3VLChatHandler` | `step3-vl` |
838839
839840
Then you'll need to use a custom chat handler to load the clip model and process the chat messages and images.

llama_cpp/llama_chat_format.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5387,6 +5387,9 @@ def __call__(self, **kwargs):
53875387
return super().__call__(**kwargs)
53885388

53895389
class Qwen35ChatHandler(MTMDChatHandler):
5390+
"""
5391+
Handler for Qwen3.5/Qwen3.6 models.
5392+
"""
53905393
CHAT_FORMAT = (
53915394
"{%- set image_count = namespace(value=0) -%}"
53925395
"{%- set video_count = namespace(value=0) -%}"
@@ -5494,7 +5497,7 @@ class Qwen35ChatHandler(MTMDChatHandler):
54945497
" {%- set content = content.split('</think>')[-1].lstrip('\n') -%}"
54955498
" {%- endif -%}"
54965499
" {%- set reasoning_content = reasoning_content | trim -%}"
5497-
" {%- if loop.index0 > ns.last_query_index -%}"
5500+
" {%- if (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > ns.last_query_index) -%}"
54985501
" {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content -}}"
54995502
" {%- else -%}"
55005503
" {{- '<|im_start|>' + message.role + '\n' + content -}}"
@@ -5516,7 +5519,7 @@ class Qwen35ChatHandler(MTMDChatHandler):
55165519
" {%- if tool_call.arguments is defined -%}"
55175520
" {%- for (args_name, args_value) in tool_call.arguments | items -%}"
55185521
" {{- '<parameter=' + args_name + '>\n' -}}"
5519-
" {%- set args_value = args_value | tojson | safe if args_value is mapping or args_value is sequence and args_value is not string else args_value | string -%}"
5522+
" {%- set args_value = args_value | string if args_value is string else args_value | tojson | safe %}"
55205523
" {{- args_value -}}"
55215524
" {{- '\n</parameter>' -}}"
55225525
" {%- endfor -%}"
@@ -5543,7 +5546,7 @@ class Qwen35ChatHandler(MTMDChatHandler):
55435546
"{%- endfor -%}"
55445547
"{%- if add_generation_prompt -%}"
55455548
" {{- '<|im_start|>assistant\n' -}}"
5546-
" {%- if enable_thinking is false -%}"
5549+
" {%- if enable_thinking is defined and enable_thinking is false -%}"
55475550
" {{- '<think>\n\n</think>\n\n' -}}"
55485551
" {%- else -%}"
55495552
" {{- '<think>\n' -}}"
@@ -5553,23 +5556,29 @@ class Qwen35ChatHandler(MTMDChatHandler):
55535556

55545557
def __init__(
55555558
self,
5556-
enable_thinking: bool = True,
55575559
add_vision_id: bool = True,
5560+
enable_thinking: bool = True,
5561+
preserve_thinking: bool = False,
55585562
**kwargs,
55595563
):
55605564
"""
55615565
Parameters:
5562-
- enable_thinking (bool):
5563-
- True (default): Enables reasoning for better results.
5564-
- False: Disables reasoning for faster results.
55655566
- add_vision_id (bool):
55665567
- True (default): Count all the images. Recommended for multi-image.
55675568
- False: Doesn't count the images. Can save tokens with single-image.
5569+
- enable_thinking (bool):
5570+
- True (default): Enables reasoning for better results.
5571+
- False: Disables reasoning for faster results.
5572+
- preserve_thinking (bool):
5573+
- True: Keeps <think> reasoning process for ALL historical conversational turns.
5574+
- False (default): Only keeps <think> for the latest assistant reply to save tokens.
55685575
"""
55695576
super().__init__(**kwargs)
55705577
self.enable_thinking = enable_thinking
5571-
self.extra_template_arguments["enable_thinking"] = enable_thinking
5578+
self.preserve_thinking = preserve_thinking
55725579
self.extra_template_arguments["add_vision_id"] = add_vision_id
5580+
self.extra_template_arguments["enable_thinking"] = enable_thinking
5581+
self.extra_template_arguments["preserve_thinking"] = preserve_thinking
55735582

55745583
def __call__(self, **kwargs):
55755584
llama = kwargs['llama']
@@ -5578,7 +5587,7 @@ def __call__(self, **kwargs):
55785587
llama.input_ids.fill(0)
55795588

55805589
if self.verbose:
5581-
print(f"{self.log_prefix}(enable_thinking={self.enable_thinking}) - Start processing")
5590+
print(f"{self.log_prefix}(enable_thinking={self.enable_thinking}, preserve_thinking={self.preserve_thinking}) - Start processing")
55825591

55835592
# Use parent implementation
55845593
return super().__call__(**kwargs)

0 commit comments

Comments
 (0)