fix: buffer split reasoning wrapper deltas

maintainer edit: native reasoning streams can split <thinking> wrapper tags across chunks. Buffer the stream and emit only cleaned incremental reasoning so raw partial tags do not reach WebUI.
This commit is contained in:
chengyongru
2026-06-24 15:45:34 +08:00
committed by Xubin Ren
parent 35bd1be109
commit 98dd883ce8
3 changed files with 16 additions and 5 deletions
+9 -3
View File
@@ -771,18 +771,24 @@ class AgentRunner:
await live_file_edits.update(delta)
if wants_streaming:
thinking_buf = ""
async def _stream(delta: str) -> None:
if delta:
context.streamed_content = True
await hook.on_stream(context, delta)
async def _thinking(delta: str) -> None:
nonlocal thinking_buf
if not delta:
return
delta = strip_reasoning_tags(delta)
if delta:
prev_clean = strip_reasoning_tags(thinking_buf)
thinking_buf += delta
new_clean = strip_reasoning_tags(thinking_buf)
incremental = new_clean[len(prev_clean):]
if incremental:
context.streamed_reasoning = True
await hook.emit_reasoning(delta)
await hook.emit_reasoning(incremental)
async def _stream_recover() -> None:
await hook.on_stream_end(context, resuming=True)
+5
View File
@@ -134,10 +134,15 @@ def strip_reasoning_tags(text: object) -> str:
"""Remove wrapper tags from text that is already known to be reasoning."""
if not isinstance(text, str):
return ""
partial_reasoning_tag = (
r"</?(?:t|th|thi|thin|think|thinki|thinkin|thinking|tho|thou|thoug|though|thought)>?"
)
text = re.sub(rf"^\s*(?:{partial_reasoning_tag})$", "", text)
text = re.sub(r"^\s*<(?:think|thinking|thought)/>\s*", "", text)
text = re.sub(r"\s*<(?:think|thinking|thought)/>\s*$", "", text)
text = re.sub(r"^\s*<(?:think|thinking|thought)>\s*", "", text)
text = re.sub(r"\s*</(?:think|thinking|thought)>\s*$", "", text)
text = re.sub(rf"\s*(?:{partial_reasoning_tag})$", "", text)
return text.strip()
+2 -2
View File
@@ -381,8 +381,8 @@ async def test_runner_strips_thinking_tags_from_native_thinking_deltas():
*, on_content_delta=None, on_thinking_delta=None, **kwargs
):
if on_thinking_delta:
await on_thinking_delta("<thinking>")
await on_thinking_delta("Preparing final response")
await on_thinking_delta("<thinking")
await on_thinking_delta(">Preparing final response")
await on_thinking_delta("</thinking>")
if on_content_delta:
await on_content_delta("done")