fix: make streaming reasoning-tag filter case-insensitive

The streaming think-tag suppressors in cli.py (_stream_delta) and
gateway/stream_consumer.py (_filter_and_accumulate) matched tag names
with case-sensitive str.find(), so only the exact-case literals in the
tag tuples were caught. Mixed-case variants a model may emit — <Think>,
<ThInK>, <REASONING>, <Thought> — slipped through and leaked raw
reasoning into the user-visible stream.

Match against a lowercased view of the buffer with lowercased tag names
at all three sites (open-tag boundary search, partial-tag hold-back,
close-tag search) in both paths. Only KNOWN tag names are matched — no
substring matching — and the block-boundary gating that protects prose
mentions of <think> is preserved.

- 6 parametrized case-insensitive regression tests in each of
  tests/gateway/test_stream_consumer.py and
  tests/cli/test_stream_delta_think_tag.py.

Salvaged from PR #27289 by @YLChen-007.
This commit is contained in:
YLChen-007 2026-07-01 03:11:53 -07:00 committed by Teknium
parent f049227f31
commit e23f723389
5 changed files with 46 additions and 7 deletions

View file

@ -1,6 +1,9 @@
"""Tests for _stream_delta's handling of <think> tags in prose vs real reasoning blocks."""
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
@ -110,6 +113,18 @@ class TestRealReasoningBlock:
cli._stream_delta(" <think>")
assert cli._in_reasoning_block
@pytest.mark.parametrize(
"tag",
["THINK", "Think", "ThInK", "THOUGHT", "REASONING", "Thinking"],
)
def test_reasoning_tags_are_case_insensitive(self, tag):
cli = _make_cli_stub()
cli._stream_delta(f"<{tag}>hidden reasoning</{tag}>Visible answer")
assert not cli._in_reasoning_block
full = "".join(cli._emitted)
assert full == "Visible answer"
assert "hidden reasoning" not in full
class TestFlushRecovery:
"""_flush_stream should recover content from false-positive reasoning blocks."""