diff --git a/nanobot/utils/helpers.py b/nanobot/utils/helpers.py index 5a6dc129d..3ce15ad62 100644 --- a/nanobot/utils/helpers.py +++ b/nanobot/utils/helpers.py @@ -32,6 +32,8 @@ def strip_think(text: str) -> str: explanatory prose that mentions these tokens. 5. Orphan closing tags `` / `` **at the very start or end of the text** only, for the same reason. + 6. Trailing partial control tags split across stream chunks, such as + ` str: text = re.sub(r"\s*\s*$", "", text) # Edge-only channel markers (harmony / Gemma 4 variant leaks). text = re.sub(r"^\s*<\|?channel\|?>\s*", "", text) + # Stream chunks may end in the middle of a control tag. Strip only known + # control-token prefixes at the very end. + partial_control_tag = ( + r"hiddenWorld") + return LLMResponse(content="Hello hiddenWorld", tool_calls=[], usage={}) + + loop.provider.chat_stream_with_retry = chat_stream_with_retry + + async def on_stream(delta: str) -> None: + deltas.append(delta) + + final_content, _, _, _, _ = await loop._run_agent_loop([], on_stream=on_stream) + + assert final_content == "Hello World" + assert deltas == ["Hello", " World"] + + @pytest.mark.asyncio async def test_loop_retries_think_only_final_response(tmp_path): loop = _make_loop(tmp_path) diff --git a/tests/utils/test_strip_think.py b/tests/utils/test_strip_think.py index 1eda89eb5..7adbe7bce 100644 --- a/tests/utils/test_strip_think.py +++ b/tests/utils/test_strip_think.py @@ -102,6 +102,14 @@ class TestStripThinkMalformedLeaks: assert strip_think("喷泉策略:09:00 开启") == ("喷泉策略:09:00 开启") assert strip_think("<|channel|>answer") == "answer" + def test_partial_trailing_think_tag_after_visible_text(self): + assert strip_think("喷泉策略说明