fix: thinking card no longer mirrors main response — v0.50.154 (closes #852)
Remove early return in _streamDisplay() bypassing think-block stripping when reasoningText populated.
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Hermes Web UI -- Changelog
|
# Hermes Web UI -- Changelog
|
||||||
|
|
||||||
|
## [v0.50.154] — 2026-04-22
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Thinking card no longer mirrors main response** — removed early return in `_streamDisplay()` that bypassed think-block stripping when `reasoningText` was populated. (`static/messages.js`) (closes #852)
|
||||||
|
|
||||||
## [v0.50.153] — 2026-04-22
|
## [v0.50.153] — 2026-04-22
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -279,7 +279,10 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
|||||||
}
|
}
|
||||||
function _streamDisplay(){
|
function _streamDisplay(){
|
||||||
const raw=_stripXmlToolCalls(assistantText);
|
const raw=_stripXmlToolCalls(assistantText);
|
||||||
if(reasoningText) return raw;
|
// Always run think-block stripping even when reasoningText is populated.
|
||||||
|
// Some providers emit reasoning content via on_reasoning AND wrap it in
|
||||||
|
// <think> tags in the token stream — the early-return caused the thinking
|
||||||
|
// card and main response to show identical content (closes #852).
|
||||||
for(const {open,close} of _thinkPairs){
|
for(const {open,close} of _thinkPairs){
|
||||||
// Trim leading whitespace before checking for the open tag — some models
|
// Trim leading whitespace before checking for the open tag — some models
|
||||||
// (e.g. MiniMax) emit newlines before <think>.
|
// (e.g. MiniMax) emit newlines before <think>.
|
||||||
|
|||||||
58
tests/test_issue852_thinking_card_mirror.py
Normal file
58
tests/test_issue852_thinking_card_mirror.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
"""Regression tests for #852 — thinking card must not mirror the main response.
|
||||||
|
|
||||||
|
The `_streamDisplay()` function in messages.js had an early return
|
||||||
|
`if(reasoningText) return raw` that bypassed think-block stripping when
|
||||||
|
the reasoning SSE event had populated `reasoningText`. Providers that emit
|
||||||
|
reasoning via BOTH `on_reasoning` AND `<think>` tags in the token stream
|
||||||
|
then showed identical content in the thinking card and the main response.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
_SRC = os.path.join(os.path.dirname(__file__), "..")
|
||||||
|
|
||||||
|
|
||||||
|
def _read(name):
|
||||||
|
return open(os.path.join(_SRC, name), encoding="utf-8").read()
|
||||||
|
|
||||||
|
|
||||||
|
class TestStreamDisplayStripsThinkBlocksAlways:
|
||||||
|
|
||||||
|
def test_early_return_on_reasoning_text_is_gone(self):
|
||||||
|
"""Regression guard: the bypass that caused the thinking card to
|
||||||
|
mirror the main response must stay removed."""
|
||||||
|
js = _read("static/messages.js")
|
||||||
|
m = re.search(r'function _streamDisplay\(\)\{.*?\n \}', js, re.DOTALL)
|
||||||
|
assert m, "_streamDisplay not found"
|
||||||
|
fn = m.group(0)
|
||||||
|
assert "if(reasoningText) return raw" not in fn, (
|
||||||
|
"The early-return `if(reasoningText) return raw;` must remain "
|
||||||
|
"removed (#852) — it caused the thinking card to mirror the main "
|
||||||
|
"response when providers emit <think> tags AND reasoning SSE events."
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_think_pair_stripping_still_runs(self):
|
||||||
|
"""The `_thinkPairs` stripping loop must still be present so the
|
||||||
|
fix actually strips think blocks."""
|
||||||
|
js = _read("static/messages.js")
|
||||||
|
m = re.search(r'function _streamDisplay\(\)\{.*?\n \}', js, re.DOTALL)
|
||||||
|
assert m
|
||||||
|
fn = m.group(0)
|
||||||
|
assert "_thinkPairs" in fn, (
|
||||||
|
"_streamDisplay must iterate _thinkPairs to strip think blocks"
|
||||||
|
)
|
||||||
|
assert "trimmed.startsWith(open)" in fn, (
|
||||||
|
"the think-block stripping must check for the open tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_still_handles_incomplete_think_tag_partial_prefix(self):
|
||||||
|
"""Existing behaviour preserved: partial `<thi`, `<think` prefixes
|
||||||
|
must still be suppressed so users don't see them mid-stream."""
|
||||||
|
js = _read("static/messages.js")
|
||||||
|
m = re.search(r'function _streamDisplay\(\)\{.*?\n \}', js, re.DOTALL)
|
||||||
|
assert m
|
||||||
|
fn = m.group(0)
|
||||||
|
assert "open.startsWith(trimmed)" in fn, (
|
||||||
|
"Partial-tag suppression must still be present"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user