fix(agent): preserve reasoning_content on Xiaomi MiMo thinking endpoints#24737
Open
xx9090950 wants to merge 1 commit into
Open
fix(agent): preserve reasoning_content on Xiaomi MiMo thinking endpoints#24737xx9090950 wants to merge 1 commit into
xx9090950 wants to merge 1 commit into
Conversation
Xiaomi MiMo (mimo-v2.5-pro, mimo-v2.5, ...) is a DeepSeek-derived
thinking-capable model family. Both its OpenAI-compatible
``api.xiaomimimo.com/v1`` endpoint and its Anthropic-compatible
``token-plan-*.xiaomimimo.com/anthropic`` endpoint enforce the same
reasoning_content echo-back contract as DeepSeek and Kimi: when a
persisted session replays an assistant tool-call turn that was recorded
without reasoning_content, Xiaomi rejects the next request with HTTP
400::
The reasoning_content in the thinking mode must be passed back
to the API.
But the existing guards only matched DeepSeek and Kimi:
- ``_preserve_unsigned_thinking`` (agent/anthropic_adapter.py) — drops
reasoning_content-derived thinking blocks for any non-whitelisted
third-party /anthropic endpoint, so Xiaomi history loses its thinking
blocks on replay.
- ``_needs_thinking_reasoning_pad`` (run_agent.py) — doesn't pad/preserve
reasoning_content on tool-call turns for Xiaomi, so even new turns
are persisted without the field.
Add Xiaomi to both whitelists, mirroring the DeepSeek policy:
- ``_is_xiaomi_anthropic_endpoint`` — pinned to the ``/anthropic`` path
(host match ``xiaomimimo.com`` covers ``token-plan-{cn,sgp,ams,...}``
regional subdomains). The OpenAI-compatible ``/v1`` base URL never
reaches this adapter, so detection fails closed there.
- ``_needs_xiaomi_tool_reasoning`` — three detection signals matching
the DeepSeek shape: ``provider == "xiaomi"``, model starts with
``mimo-``, or host matches ``xiaomimimo.com`` (covers both /v1 and
/anthropic transports).
Coverage:
- ``tests/agent/test_xiaomi_anthropic_thinking.py`` — unsigned thinking
block survival, signed-block stripping, OpenAI-base fail-closed,
substring false-positive guard.
- ``tests/run_agent/test_xiaomi_reasoning_content_echo.py`` — detection
signals, replay padding, build-time pinning, cross-provider history
guard, parametrized pad scenarios.
Tested on macOS Darwin 25.2.0, Python 3.11.14; 32 new tests pass,
existing DeepSeek/Kimi/xiaomi-provider suites (82 tests) unchanged.
Collaborator
This was referenced May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Xiaomi MiMo (mimo-v2.5-pro, mimo-v2.5, …) is a DeepSeek-derived thinking-capable model family. Both its OpenAI-compatible
api.xiaomimimo.com/v1endpoint and its Anthropic-compatibletoken-plan-*.xiaomimimo.com/anthropicendpoint enforce the same reasoning_content echo-back contract as DeepSeek and Kimi: when a persisted session replays an assistant tool-call turn that was recorded withoutreasoning_content, Xiaomi rejects the next request with HTTP 400:But the existing guards only matched DeepSeek and Kimi:
_preserve_unsigned_thinking(agent/anthropic_adapter.py) drops reasoning_content-derived thinking blocks for any non-whitelisted third-party/anthropicendpoint, so Xiaomi history loses its thinking blocks on replay._needs_thinking_reasoning_pad(run_agent.py) doesn't pad/preserve reasoning_content on tool-call turns for Xiaomi, so even new turns are persisted without the field.This PR adds Xiaomi to both whitelists, mirroring the DeepSeek policy verbatim. Same strip-signed / keep-unsigned policy, same three detection signals, same regression test layout — Xiaomi-shaped scenarios were just missing from the whitelist.
Related Issue
Fixes an unfiled bug; reproduction is straightforward (see How to Test). Happy to file a tracking issue if maintainers prefer one.
Type of Change
Changes Made
agent/anthropic_adapter.py_is_xiaomi_anthropic_endpoint(base_url)— pinned to the/anthropicpath (host matchxiaomimimo.comcoverstoken-plan-{cn,sgp,ams,…}regional subdomains). The OpenAI-compatible/v1base URL never reaches this adapter, so detection fails closed there._is_xiaomi_anthropic_endpoint(base_url)to the_preserve_unsigned_thinkingwhitelist inconvert_messages_to_anthropic, alongside_is_kimi_family_endpointand_is_deepseek_anthropic_endpoint.run_agent.py_needs_xiaomi_tool_reasoning()— three detection signals matching the DeepSeek shape:provider == "xiaomi", model starts withmimo-, or host matchesxiaomimimo.com(covers both/v1and/anthropictransports)._needs_thinking_reasoning_pad().tests/agent/test_xiaomi_anthropic_thinking.py— new: unsigned thinking block survival on/anthropic, signed-block stripping, OpenAI-base fail-closed, substring false-positive guard. (10 tests)tests/run_agent/test_xiaomi_reasoning_content_echo.py— new: detection signals, replay padding, build-time pinning, cross-provider history guard, parametrized pad scenarios. (22 tests)How to Test
Reproduce the bug (before this fix)
Run any multi-turn session that uses
terminalor another tool (a cron job that fetches a URL is a good reproducer). On the second tool-call turn, the upstream call fails with:Verify the fix
pytest tests/agent/test_xiaomi_anthropic_thinking.py tests/run_agent/test_xiaomi_reasoning_content_echo.py -q— 32 new tests pass.pytest tests/run_agent/test_deepseek_reasoning_content_echo.py tests/agent/test_deepseek_anthropic_thinking.py tests/agent/test_kimi_coding_anthropic_thinking.py tests/hermes_cli/test_xiaomi_provider.py -q— 114 existing tests still pass (no regression on DeepSeek / Kimi / xiaomi provider).token-plan-cn.xiaomimimo.com/anthropic— the next tool-call turn now carries the synthesised thinking block on replay and the 400 disappears.Checklist
Code
fix(agent): …)main—test_switch_model_preserves_config_context_lengthandtest_interrupt_child_during_api_call— are unrelated to this change and reproduce on a clean checkout)Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — N/A (no new config keys)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/A (no architectural change)