From 28ec3af6974c0f04ed9eb3cb4667a307a4493645 Mon Sep 17 00:00:00 2001 From: Frank Song Date: Thu, 14 May 2026 08:42:57 +0800 Subject: [PATCH] fix: strip only leading user-asking wrapper line Refs #2215 Fix B: remove the mid-response stripping hazard without losing leading multi-line wrapper cleanup. The pattern now strips only a leading 'the user is asking' wrapper line and preserves the visible answer that follows. Add regression coverage for both the leading-wrapper and mid-response prose cases. --- api/streaming.py | 2 +- tests/test_issue607.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/api/streaming.py b/api/streaming.py index bf473d90..0d1334e0 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -695,7 +695,7 @@ def _strip_thinking_markup(text: str) -> str: s = re.sub(r'^\s*.*?\s*', ' ', s, flags=re.IGNORECASE | re.DOTALL) s = re.sub(r'^\s*<\|channel\|?>thought\n?.*?\s*', ' ', s, flags=re.IGNORECASE | re.DOTALL) s = re.sub(r'^\s*<\|turn\|>thinking\n.*?\s*', ' ', s, flags=re.IGNORECASE | re.DOTALL) # Gemma 4 - s = re.sub(r'^\s*(the|ther)\s+user\s+is\s+asking.*$', ' ', s, flags=re.IGNORECASE | re.MULTILINE) + s = re.sub(r'^\s*(the|ther)\s+user\s+is\s+asking[^\n]*(?:\n|$)', ' ', s, flags=re.IGNORECASE) # Strip plain-text thinking preambles from models that don't use tags (e.g. Qwen3). # These appear as the very first sentence of the assistant response and are not useful as titles. s = re.sub( diff --git a/tests/test_issue607.py b/tests/test_issue607.py index 0e950c3d..112fe916 100644 --- a/tests/test_issue607.py +++ b/tests/test_issue607.py @@ -88,6 +88,20 @@ class TestGemma4ThinkingTokenStrip: result = _strip_thinking_markup(raw) assert result == raw + def test_leading_the_user_is_asking_wrapper_line_is_stripped(self): + """A leading title-wrapper line is metadata; the visible answer should remain.""" + raw = "The user is asking about Python imports.\nUse importlib for dynamic imports." + result = _strip_thinking_markup(raw) + assert result == "Use importlib for dynamic imports." + + def test_mid_response_the_user_is_asking_line_is_preserved(self): + """Only the leading wrapper line is stripped; normal mid-response prose survives.""" + raw = "First, define the context.\nThe user is asking us to be patient.\nThen answer." + result = _strip_thinking_markup(raw) + assert "First, define the context." in result + assert "The user is asking us to be patient." in result + assert "Then answer." in result + def test_minimax_channel_without_second_pipe_still_stripped_at_start(self): """The client-facing MiniMax <|channel>thought shape is stripped when leading.""" raw = "<|channel>thought\nSome reasoningAnswer"