mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(cli): strip all reasoning tag variants from /resume recap
HermesCLI._display_resumed_history() calls the module-level _strip_reasoning_tags() to clean assistant content before rendering the recap panel. The tag list was missing <thought> (Gemma 4) and there was no pass for stray orphan </tag> closes, so those variants leaked internal reasoning into the recap display (#11316). - Add <thought> to _REASONING_TAGS. - Add a third regex pass that strips orphan close tags (e.g. 'stuff</think>answer' → 'stuffanswer'). - Apply IGNORECASE to closed-pair and unclosed-pair passes so mixed-case variants (<THINK>, <Thinking>) are handled uniformly — previously both 'THINKING' and 'thinking' had to be listed explicitly as distinct tuple entries, which missed <Thinking>. 7 new regression tests in tests/cli/test_resume_display.py covering: <think>, <thinking>, <reasoning>, <thought>, unclosed <think>, multiple interleaved blocks, and orphan </think> close. Resolves #11316. Originally proposed as PR #11366.
This commit is contained in:
parent
ec48ec5530
commit
bd01ec7885
2 changed files with 159 additions and 4 deletions
42
cli.py
42
cli.py
|
|
@ -83,17 +83,51 @@ load_hermes_dotenv(hermes_home=_hermes_home, project_env=_project_env)
|
|||
_REASONING_TAGS = (
|
||||
"REASONING_SCRATCHPAD",
|
||||
"think",
|
||||
"reasoning",
|
||||
"THINKING",
|
||||
"thinking",
|
||||
"reasoning",
|
||||
"thought",
|
||||
)
|
||||
|
||||
|
||||
def _strip_reasoning_tags(text: str) -> str:
|
||||
"""Remove reasoning/thinking blocks from displayed text.
|
||||
|
||||
Handles every case:
|
||||
* Closed pairs ``<tag>…</tag>`` (case-insensitive, multi-line).
|
||||
* Unterminated open tags that run to end-of-text (e.g. truncated
|
||||
generations on NIM/MiniMax where the close tag is dropped).
|
||||
* Stray orphan close tags (``stuff</think>answer``) left behind by
|
||||
partial-content dumps.
|
||||
|
||||
Covers the variants emitted by reasoning models today: ``<think>``,
|
||||
``<thinking>``, ``<reasoning>``, ``<REASONING_SCRATCHPAD>``, and
|
||||
``<thought>`` (Gemma 4). Must stay in sync with
|
||||
``run_agent.py::_strip_think_blocks`` and the stream consumer's
|
||||
``_OPEN_THINK_TAGS`` / ``_CLOSE_THINK_TAGS`` tuples.
|
||||
"""
|
||||
cleaned = text
|
||||
for tag in _REASONING_TAGS:
|
||||
cleaned = re.sub(rf"<{tag}>.*?</{tag}>\s*", "", cleaned, flags=re.DOTALL)
|
||||
cleaned = re.sub(rf"<{tag}>.*$", "", cleaned, flags=re.DOTALL)
|
||||
# Closed pair — case-insensitive so <THINK>…</THINK> is handled too.
|
||||
cleaned = re.sub(
|
||||
rf"<{tag}>.*?</{tag}>\s*",
|
||||
"",
|
||||
cleaned,
|
||||
flags=re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
# Unterminated open tag — strip from the tag to end of text.
|
||||
cleaned = re.sub(
|
||||
rf"<{tag}>.*$",
|
||||
"",
|
||||
cleaned,
|
||||
flags=re.DOTALL | re.IGNORECASE,
|
||||
)
|
||||
# Stray orphan close tag left behind by partial dumps.
|
||||
cleaned = re.sub(
|
||||
rf"</{tag}>\s*",
|
||||
"",
|
||||
cleaned,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
return cleaned.strip()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue