ModelEngine-Group / ModelEngine-Group/nexent

`remove_think_blocks` regex deletes content that is not in a `<think>` block

Open Beginner friendly
#3,689 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.9k
Forks
731
Avg merge
19h 34m
Merged PRs (30d)
172

Description

backend/utils/str_utils.py:9:

return re.sub(r"(?:<think>)?.*?</think>", "", text, flags=re.DOTALL | re.IGNORECASE)

The intent (per the docstring) is to remove <think>…</think> blocks including their inner content. The actual behaviour is broader: because (?:<think>)? makes the opening tag optional while .*?</think> still requires a closing tag, the regex will happily eat anything that ends with a </think> even if there's no opening tag.

Repro
>>> from utils.str_utils import remove_think_blocks
>>> remove_think_blocks("The model said: hello </think> world")
' world'
>>> remove_think_blocks("regular content ending with </think> stray tag")
' stray tag'
>>> remove_think_blocks("<think>secret</think>visible<think>second</think>tail")
'visibletail'  # OK - matches intent

This matters for the streaming path in _process_thinking_tokens (backend/utils/llm_utils.py:15) which handles half-tokens around <think>/</think>; a stray closing tag emitted by a misbehaving provider will cause everything before it on the same chunk to be silently dropped.

Suggested fix
return re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL | re.IGNORECASE)

If the optional-open intent really was deliberate (e.g., to recover from a provider that streams only the closing tag), keep that behaviour but make it explicit, anchor it, and add a comment + a test.

Severity: Low — the failure mode requires a malformed/partial provider stream, but when it happens the user-visible content is silently truncated.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with backend/utils/str_utils.py:9 and compare remove_think_blocks with the documented behavior and the reproductions in this issue. Read the related streaming handling at backend/utils/llm_utils.py:15, then add a regression test covering tagged blocks and stray closing tags. Done means content outside a complete think block is preserved.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.