Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd60ac08d3 |
@@ -245,18 +245,25 @@ def extract_responses_text(response: Any) -> str:
|
||||
|
||||
_THINK_BLOCK_RE = re.compile(r"<think>.*?</think>", flags=re.IGNORECASE | re.DOTALL)
|
||||
_THINK_START_RE = re.compile(r"<think>.*$", flags=re.IGNORECASE | re.DOTALL)
|
||||
_THINK_CLOSE_RE = re.compile(r"</think>", flags=re.IGNORECASE)
|
||||
|
||||
|
||||
def strip_think_content(text: str | None) -> str:
|
||||
"""
|
||||
Remove reasoning blocks enclosed in <think>...</think>.
|
||||
Also removes dangling <think> blocks without a closing tag.
|
||||
Also removes dangling <think> blocks without a closing tag and any
|
||||
orphan </think> closing tags left by nested or malformed think blocks.
|
||||
"""
|
||||
if not text:
|
||||
return ""
|
||||
|
||||
cleaned = _THINK_BLOCK_RE.sub("", text)
|
||||
cleaned = _THINK_START_RE.sub("", cleaned)
|
||||
# After the above two passes, any remaining </think> tags are orphaned
|
||||
# closing tags produced by nested blocks (e.g. <think><think>…</think></think>).
|
||||
# The non-greedy block regex only removes up to the *first* </think>, leaving
|
||||
# the outer one behind; strip it here so it never appears in visible output.
|
||||
cleaned = _THINK_CLOSE_RE.sub("", cleaned)
|
||||
return cleaned.strip()
|
||||
|
||||
|
||||
|
||||
@@ -64,3 +64,18 @@ def test_strip_think_content_removes_dangling_start_tag() -> None:
|
||||
# strip() removes the trailing real newline left after the dangling block is excised.
|
||||
text = "Visible output\n<think>unfinished"
|
||||
assert client.strip_think_content(text) == "Visible output"
|
||||
|
||||
|
||||
def test_strip_think_content_nested_think_tags_no_orphan_close() -> None:
|
||||
# When a think block contains nested <think> tags (e.g. a model quoting the tag
|
||||
# in its own reasoning), the non-greedy regex matches up to the *first* </think>,
|
||||
# which would leave the outer closing tag as an orphan in the visible output.
|
||||
# The fix strips any remaining </think> tags after the block/dangling passes.
|
||||
assert client.strip_think_content("<think><think>nested</think></think>") == ""
|
||||
# Regression: visible text surrounding nested blocks must be preserved intact.
|
||||
result = client.strip_think_content(
|
||||
"Before <think>reasoning <think>deep</think></think> after"
|
||||
)
|
||||
assert "</think>" not in result
|
||||
assert "Before" in result
|
||||
assert "after" in result
|
||||
|
||||
Reference in New Issue
Block a user