Bug Description
Some models (e.g. Kimi K2.5 on Alibaba's OpenAI-compatible endpoint) emit reasoning as plain text followed by a closing </think> tag without a matching opening <think>. _strip_think_blocks() uses paired-tag regexes (<think>.*?</think>) that require both tags — orphaned </think> passes through unmatched and appears in user-facing responses.
Steps to Reproduce
- Configure Kimi K2.5 via Alibaba's OpenAI-compatible endpoint (
/v1)
- Run gateway via systemd (
hermes gateway)
- Send a message via Telegram that triggers model reasoning (e.g. ask about available models)
- Model outputs reasoning as plain text, then
</think>, then the actual response
Expected Behavior
No raw XML tags in user-facing responses. _strip_think_blocks() should remove orphaned tags.
Actual Behavior
</think> appears in the Telegram message:
The user wants to see available models. I can list them via the gateway command. However I don't have permission to run this...You're currently using kimi-k2.5.
Verified from session data — reasoning and reasoning_content fields are both empty. The model does not use structured reasoning; the </think> appears as literal text in assistant message content.
Affected Component
Agent Core (conversation loop, context compression, memory), Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
Telegram
Operating System
Ubuntu 24.04
Python Version
Python 3.12
Hermes Version
Hermes v0.6.0
Relevant Logs / Traceback
No traceback. The orphaned tag appears as literal text in the assistant message content. Session log excerpt:
...I can list them via the gateway command. However I don't have permission...</think>You're currently using **kimi-k2.5**.
Message metadata: reasoning=None, reasoning_content=None
Root Cause Analysis (optional)
_strip_think_blocks() has four paired-tag regexes:
re.sub(r'<think>.*?</think>', ...)
re.sub(r'<thinking>.*?</thinking>', ...)
re.sub(r'<reasoning>.*?</reasoning>', ...)
re.sub(r'<REASONING_SCRATCHPAD>.*?</REASONING_SCRATCHPAD>', ...)
All require matching open+close pairs. When a model writes </think> without <think>, none of these match.
Proposed Fix (optional)
Add a catch-all regex after the existing paired-block removal:
content = re.sub(r'</?(?:think|thinking|reasoning|REASONING_SCRATCHPAD)>\s*', '', content, flags=re.IGNORECASE)
Are you willing to submit a PR for this?
Bug Description
Some models (e.g. Kimi K2.5 on Alibaba's OpenAI-compatible endpoint) emit reasoning as plain text followed by a closing
</think>tag without a matching opening<think>._strip_think_blocks()uses paired-tag regexes (<think>.*?</think>) that require both tags — orphaned</think>passes through unmatched and appears in user-facing responses.Steps to Reproduce
/v1)hermes gateway)</think>, then the actual responseExpected Behavior
No raw XML tags in user-facing responses.
_strip_think_blocks()should remove orphaned tags.Actual Behavior
</think>appears in the Telegram message:The user wants to see available models. I can list them via the gateway command. However I don't have permission to run this...You're currently using kimi-k2.5.
Verified from session data —
reasoningandreasoning_contentfields are both empty. The model does not use structured reasoning; the</think>appears as literal text in assistant message content.Affected Component
Agent Core (conversation loop, context compression, memory), Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
Telegram
Operating System
Ubuntu 24.04
Python Version
Python 3.12
Hermes Version
Hermes v0.6.0
Relevant Logs / Traceback
Root Cause Analysis (optional)
_strip_think_blocks()has four paired-tag regexes:re.sub(r'<think>.*?</think>', ...)re.sub(r'<thinking>.*?</thinking>', ...)re.sub(r'<reasoning>.*?</reasoning>', ...)re.sub(r'<REASONING_SCRATCHPAD>.*?</REASONING_SCRATCHPAD>', ...)All require matching open+close pairs. When a model writes
</think>without<think>, none of these match.Proposed Fix (optional)
Add a catch-all regex after the existing paired-block removal:
Are you willing to submit a PR for this?