feat: A better logic for detecting the thinking prefix in the response - #423
feat: A better logic for detecting the thinking prefix in the response#423Vinay-Umrethe wants to merge 16 commits into
Conversation
In case if the model adds <think> at the end of user prompt and only generates </think> end, then the detection goes wrong.
| for cot_initializer, closed_cot_block in settings.chain_of_thought_skips: | ||
| if settings.response_prefix.startswith(cot_initializer): | ||
| settings.response_prefix = closed_cot_block | ||
| if dummy_prompt.endswith( |
There was a problem hiding this comment.
This is too brittle. For example if the chat template ends with <think>\n instead of <think> this logic fails.
There was a problem hiding this comment.
good catch, I think applying rstrip() might be enough for it.
and I also expect this to fix CoT detection for cases like qwen3.5 models (which I almost never saw get properly detected) because qwen3.5 template has:
{%- if add_generation_prompt %}
{{- '<|im_start|>assistant\n' }}
{%- if enable_thinking is defined and enable_thinking is false %}
{{- '<think>\n\n</think>\n\n' }}
{%- else %}
{{- '<think>\n' }}
{%- endif %}
{%- endif %}I'll test it and push if it works...
There was a problem hiding this comment.
rstrip should do the trick in this case, but it's basically a hack. For example, the template could end with </think>\nOkay,, and we're back to zero. The correct approach would be to get the complete text (prompt + response) and then do a regex match. But this is probably not worth the complexity.
At some point we should revisit trying to set enable_thinking to False in the template, which would make the whole prefix detection unnecessary for many models. I've looked into this in the past, but there were several inconsistencies that might have been fixed in the meantime.
Tested
|
Checking for common response prefix...
* Closed Chain-of-Thought block: '</think>\n'
* Rechecking with prefix...got this now for @p-e-w yet I have still not decided what to do with hashes for now. |
A major change that affects reproducibility.
This Is A Major Change
Checking for common response prefix...
* Closed Chain-of-Thought block: '[/THINK]Your thoughts or/and draft, like
working through an exercise on scratch paper. Be as casual and as long as you
want until you are confident to generate the response to the user.[/THINK]Here,
provide a self-contained response.[/SYSTEM_PROMPT][INST][/INST]'
* Rechecking with prefix...
* Extended prefix found: '[/THINK]Your thoughts or/and draft, like working
through an exercise on scratch paper. Be as casual and as long as you want until
you are confident to generate the response to the user.[/THINK]Here, provide a
self-contained response.[/SYSTEM_PROMPT][INST][/INST]Ġ'
Checking for common response prefix...
* Closed Chain-of-Thought block: '</think>\n'
* Rechecking with prefix...Until now, these models and many more almost always gave:Checking for common response prefix...
* None foundSo the point is that this PR comes with a major change which highly affects reproducibility of the models for the upcoming v2.0 of heretic and its thinking prefix detecting logic. So the Not yet decided if this is better than the existing logic before I merge. |
|
The output is pretty misleading though. Those are not "closed chain-of-though blocks". |
because mistral-3 as additional reasoning instructions in its chat template. And I suppose many other models can have it too.
|
That's sensible enough, I didn't knew Mistral 3 had additional instructions for the model about telling it "how to think" in its chat template. so I don't think there's a better way to detect the thinking tags for such ambiguous templates (without using hacks with more regex), so it is better that it fallbacks to inference rather than dummy prompt.
* Closed Chain-of-Thought block: '<think></think>\n'
* Prefix found: 'Ġ'
Checking for common response prefix...
* Closed Chain-of-Thought block: '<think></think>'
* Rechecking with prefix... |
What is going on here? That looks like a fragment from a wrongly-decoded vocabulary token. |
In case if the model adds
<think>at the end of user prompt and only generates</think>end in the model response, then the detection goes wrong.LiquidAI/LFM2.5-2.6B generates response like this:
USER_PROMPT + <think>tag added to it at last fromchat_templateviatokenizer.apply_chat_template:For fixing, we just create a dummy prompt to see whatever model is used with heretic...
"does its
apply_chat_template(add_generation_prompt=True)method adds the tag or not to the prompt".If yes, then we extract only the end part (like
</think>) and set it as theresponse_prefix.Before
After