Skip to content

fix(agent): recover inline reasoning stripped by the think scrubber - #90417

Open
SayHell0W0rld wants to merge 2 commits into
NousResearch:mainfrom
SayHell0W0rld:fix/minimax-reasoning-extraction
Open

SayHell0W0rld wants to merge 2 commits into
NousResearch:mainfrom
SayHell0W0rld:fix/minimax-reasoning-extraction

Conversation

@SayHell0W0rld

@SayHell0W0rld SayHell0W0rld commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Fixes #89647: providers that inline reasoning (MiniMax-M3 streams <think>…</think> inside content) never send a reasoning_content delta, so the desktop reasoning pane stays dead even though reasoning happened.

Approach

  • StreamingThinkScrubber (agent/think_scrubber.py, [Bug] MiniMax reasoning content leaks to CLI output even with reasoning_effort: none #17924) already strips inline think blocks from streamed content but discarded the text. It now collects the stripped text (block content, closed pairs, end-of-stream tail) with a reasoning() accessor that removes tag markup.
  • _relay_final_response (agent/chat_completion_helpers.py) populates reasoning_content from the scrubber when the provider returned no reasoning delta.

Relation to #43827 / #43836

Test Plan

  • 5 new tests in TestReasoningCollection (tests/agent/test_think_scrubber.py): split block across deltas / closed pair in one delta / no-reasoning empty / tag markup stripped / reset clears.
  • Full file: 25/25 pass. ruff check clean.

Risk / Exclusions

References #89647


Update (2026-08-29): This PR now also carries the credential-pool heal fix (formerly #95351, closed). Both changes edit _relay_final_response's success path, so they were merged into one branch to avoid a merge conflict between overlapping edits of the same function:

  1. Recover inline reasoning stripped by the think scrubber ([Bug] Hermes Desktop reasoning pane dead for MiniMax-M3 (Plus subscription) — reasoning inline in content, never extracted to reasoning_content #89647)
  2. Heal credential-pool last_status after a successful served request (xai-oauth: usage/limits visibility parity with openai-codex (fetch_usage), and heal credential_pool last_status on served requests #95166)

Combined branch: fix/minimax-reasoning-extraction (2 commits). 87/87 tests pass.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/minimax MiniMax (Anthropic transport) P2 Medium — degraded but workaround exists labels Aug 20, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

  1. agent/think_scrubber.py:reasoning() — Nit: parts are joined with "\n", so a sentence streamed across deltas ("Let me check" + " their config") gains a newline where none existed ("Let me check\ntheir config"). Harmless for model consumption, mildly lossy if a UI renders this as the model's literal reasoning prose. Suggestion: join with "" and let the natural delta boundaries carry whitespace (the scrubber already suppresses nothing but tags), or document the reflow.

  2. Overall — Positive: the collection hooks cover all four suppression sites (in-flight block buffer, block-close content, single-delta closed pairs, unterminated flush), reasoning() strips tag markup including case variants, and the relay fallback engages only when no genuine reasoning deltas arrived — so providers with native reasoning streams are untouched. Reset-clears-collected is tested, which matters because a stale turn's reasoning leaking into the next turn's field would be its own bug. Clean fix for the MiniMax-M3 inline-think class of providers.

Providers that inline reasoning (MiniMax-M3 streams <think>…</think> in
content) never send a reasoning delta, so the desktop reasoning pane stays
dead even though the reasoning happened (NousResearch#89647).

StreamingThinkScrubber (NousResearch#17924) already strips inline blocks from streamed
content but discarded the text. It now collects the stripped text
(reasoning() accessor, tag markup removed), and _relay_final_response
populates reasoning_content from the scrubber when the provider returned
no reasoning delta.
…d request

A credential marked exhausted by a transient rate-limit/billing wall stays
exhausted even after the wall lifts and kernel-mediated requests succeed again
(issue NousResearch#95166, gap 2). Add _mark_healthy() (symmetric to _mark_exhausted) and
call it from _relay_final_response — the unified success exit for
chat-completions mode — so pool readers don't see a stale exhausted verdict.

3 regression tests; 62/62 file pass; ruff + Windows footgun lint clean.
@SayHell0W0rld

Copy link
Copy Markdown
Author

Requesting review — this PR is a small, self-contained fix with passing tests (full file green, ruff/footgun clean). Happy to address any feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/minimax MiniMax (Anthropic transport) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Hermes Desktop reasoning pane dead for MiniMax-M3 (Plus subscription) — reasoning inline in content, never extracted to reasoning_content

3 participants