Skip to content

Fix Xiaomi MiMo reasoning-only completions#60304

Merged
steipete merged 1 commit into
openclaw:mainfrom
HiddenPuppy:codex/issue-60261-xiaomi-reasoning-content-fallback
May 15, 2026
Merged

Fix Xiaomi MiMo reasoning-only completions#60304
steipete merged 1 commit into
openclaw:mainfrom
HiddenPuppy:codex/issue-60261-xiaomi-reasoning-content-fallback

Conversation

@HiddenPuppy

@HiddenPuppy HiddenPuppy commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes Xiaomi MiMo V2 replies that appear blank when the provider returns the final visible answer in reasoning_content.
  • Keeps request-side MiMo reasoning replay/passthrough intact for multi-turn tool calls.
  • Adds response-side promotion only for terminal thinking-only mimo-v2-pro / mimo-v2-omni outputs, including custom Xiaomi-compatible OpenAI-completions proxy routes.
  • Leaves stock OpenAI calls and non-target providers unchanged.

Changed

  • extensions/xiaomi/stream.ts: direct Xiaomi wrapper composes replay/passthrough with terminal reasoning-only visible-text promotion.
  • src/plugin-sdk/provider-stream-shared.ts: shared stream helper promotes final thinking-only assistant output to text while preserving existing text and tool-call turns.
  • src/agents/pi-embedded-runner/extra-params.ts: custom proxy fallback applies the same MiMo V2 response-side promotion when the Xiaomi plugin wrapper is not in the provider path.
  • extensions/xiaomi/index.test.ts and src/agents/pi-embedded-runner-extraparams.test.ts: regression coverage for direct Xiaomi and proxy routes.

Real behavior proof

Behavior addressed: Xiaomi MiMo mimo-v2-pro / mimo-v2-omni can return the final user-visible answer in reasoning_content; OpenClaw parsed that as thinking-only content, so the rendered reply could appear blank.

Real environment tested: local OpenClaw checkout plus a real Xiaomi console API key created temporarily for this verification and revoked afterward.

Exact steps or command run after this patch:

  • OPENCLAW_LIVE_TEST=1 XIAOMI_API_KEY=<temporary Xiaomi key> pnpm test extensions/xiaomi/xiaomi-chat.tmp.test.ts
  • pnpm test extensions/xiaomi/index.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/plugin-sdk/provider-stream-shared.test.ts
  • pnpm check:changed
  • pnpm build
  • codex-review --mode local --parallel-tests "pnpm test extensions/xiaomi/index.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/plugin-sdk/provider-stream-shared.test.ts"

Evidence after fix: live mimo-v2-pro returned the requested marker XIAOMI_VISIBLE_E2E_OK as visible assistant text; focused tests passed; Codex review reported no actionable findings; changed gate passed on Blacksmith Testbox tbx_01krnvk8eq4ncvaqhnkg4swy2a with Actions run https://github.com/openclaw/openclaw/actions/runs/25919175341.

Observed result after fix: reasoning-only terminal MiMo V2 outputs are converted to visible text, while replay/tool-call reasoning and already-visible text remain unchanged.

What was not tested: live mimo-v2-omni; it is covered by the same unit wrapper path as mimo-v2-pro.

Linked Issue

Closes #60261

@greptile-apps

greptile-apps Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Xiaomi-specific stream compatibility wrapper in extensions/xiaomi/stream.ts that promotes reasoning_content (surfaced as thinking blocks) to text blocks for mimo-v2-pro and mimo-v2-omni when the final message contains reasoning output but no visible text. The fix is correctly scoped to those two model IDs via wrapStreamFn, leaving all other Xiaomi models and providers untouched. Remaining notes are both P2: the early break on hasRenderableText creates a subtle asymmetry in the detection loop that could mislead future contributors, and mimo-v2-omni has no dedicated test case (though the shared logic covers it).

Confidence Score: 5/5

Safe to merge — no P0/P1 issues, all remaining findings are minor style suggestions.

The core normalization logic is correct and well-guarded. Both the iterator and result() rewrite paths are exercised by tests. The only open items are a stylistic early-break asymmetry and missing direct coverage of mimo-v2-omni, neither of which affects runtime behavior.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/xiaomi/stream.ts
Line: 62-65

Comment:
**Early `break` may shadow `hasToolCalls` detection**

The `break` fires as soon as any renderable text block is found, so subsequent `toolCall` blocks are never visited — `hasToolCalls` stays `false` for those iterations. Correctness is preserved because `hasRenderableText = true` already causes the function to bail on line 78, but the loop's three booleans read as symmetric flags when they aren't. If the loop body ever grows (e.g. another bypass condition), this implicit ordering could silently create a bug. Consider removing the `break` or adding a comment explaining that only `hasRenderableText` needs to short-circuit.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/xiaomi/index.test.ts
Line: 62-104

Comment:
**`mimo-v2-omni` not covered by tests**

The `XIAOMI_REASONING_AS_TEXT_MODEL_IDS` Set lists both `mimo-v2-pro` and `mimo-v2-omni` as target models, but the tests only exercise `mimo-v2-pro`. Adding a parallel case for `mimo-v2-omni` (or parameterizing the existing test) would lock in the full set of affected models against future `XIAOMI_REASONING_AS_TEXT_MODEL_IDS` edits.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Normalize Xiaomi reasoning-only completi..." | Re-trigger Greptile

Re-review progress:

Comment thread extensions/xiaomi/stream.ts Outdated
Comment on lines +62 to +65
) {
hasRenderableText = true;
break;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Early break may shadow hasToolCalls detection

The break fires as soon as any renderable text block is found, so subsequent toolCall blocks are never visited — hasToolCalls stays false for those iterations. Correctness is preserved because hasRenderableText = true already causes the function to bail on line 78, but the loop's three booleans read as symmetric flags when they aren't. If the loop body ever grows (e.g. another bypass condition), this implicit ordering could silently create a bug. Consider removing the break or adding a comment explaining that only hasRenderableText needs to short-circuit.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/xiaomi/stream.ts
Line: 62-65

Comment:
**Early `break` may shadow `hasToolCalls` detection**

The `break` fires as soon as any renderable text block is found, so subsequent `toolCall` blocks are never visited — `hasToolCalls` stays `false` for those iterations. Correctness is preserved because `hasRenderableText = true` already causes the function to bail on line 78, but the loop's three booleans read as symmetric flags when they aren't. If the loop body ever grows (e.g. another bypass condition), this implicit ordering could silently create a bug. Consider removing the `break` or adding a comment explaining that only `hasRenderableText` needs to short-circuit.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread extensions/xiaomi/index.test.ts Outdated
Comment on lines +62 to +104
it("normalizes reasoning-only final assistant messages into text for MiMo reasoning models", async () => {
const stream = runWrappedXiaomiStream({
modelId: "mimo-v2-pro",
events: [
{
type: "done",
reason: "stop",
message: {
role: "assistant",
content: [{ type: "thinking", thinking: "MiMo final answer" }],
stopReason: "stop",
},
},
],
resultMessage: {
role: "assistant",
content: [{ type: "thinking", thinking: "MiMo final answer" }],
stopReason: "stop",
},
});

const events: unknown[] = [];
for await (const event of stream) {
events.push(event);
}

await expect(stream.result()).resolves.toEqual({
role: "assistant",
content: [{ type: "text", text: "MiMo final answer" }],
stopReason: "stop",
});
expect(events).toEqual([
{
type: "done",
reason: "stop",
message: {
role: "assistant",
content: [{ type: "text", text: "MiMo final answer" }],
stopReason: "stop",
},
},
]);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 mimo-v2-omni not covered by tests

The XIAOMI_REASONING_AS_TEXT_MODEL_IDS Set lists both mimo-v2-pro and mimo-v2-omni as target models, but the tests only exercise mimo-v2-pro. Adding a parallel case for mimo-v2-omni (or parameterizing the existing test) would lock in the full set of affected models against future XIAOMI_REASONING_AS_TEXT_MODEL_IDS edits.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/xiaomi/index.test.ts
Line: 62-104

Comment:
**`mimo-v2-omni` not covered by tests**

The `XIAOMI_REASONING_AS_TEXT_MODEL_IDS` Set lists both `mimo-v2-pro` and `mimo-v2-omni` as target models, but the tests only exercise `mimo-v2-pro`. Adding a parallel case for `mimo-v2-omni` (or parameterizing the existing test) would lock in the full set of affected models against future `XIAOMI_REASONING_AS_TEXT_MODEL_IDS` edits.

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper

clawsweeper Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Summary
The PR adds a shared stream wrapper, Xiaomi provider wiring, proxy-route fallback, tests, and a changelog entry to promote mimo-v2-pro and mimo-v2-omni thinking-only final output into visible text.

Reproducibility: yes. at source level. Current main exposes the target MiMo models as reasoning OpenAI-completions models, maps reasoning_content into thinking blocks, and visible extraction does not turn thinking-only content into normal assistant text.

Real behavior proof
Sufficient (live_output): A maintainer comment reports after-fix live Xiaomi mimo-v2-pro proof with a visible marker response, plus targeted tests, build, and changed-check proof.

Next step before merge
No ClawSweeper repair job is needed; the remaining action is normal maintainer review/merge judgment for an open implementation PR with no current blocking finding.

Security
Cleared: The diff is limited to provider stream-normalization code, tests, and changelog text; it does not add dependencies, workflows, install scripts, secret handling, or publishing changes.

Review details

Best possible solution:

Land the scoped Xiaomi/provider-family normalization after normal maintainer review, preserving the existing replay fix and keeping the final-output promotion limited to MiMo V2 reasoning-only terminal responses.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level. Current main exposes the target MiMo models as reasoning OpenAI-completions models, maps reasoning_content into thinking blocks, and visible extraction does not turn thinking-only content into normal assistant text.

Is this the best way to solve the issue?

Yes. A Xiaomi-owned wrapper plus a shared provider-family helper for custom proxy routes is the narrow maintainable fix, and the force-pushed version preserves tool-call reasoning replay and already-visible text.

Acceptance criteria:

  • pnpm test extensions/xiaomi/index.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/plugin-sdk/provider-stream-shared.test.ts
  • pnpm check:changed
  • pnpm build
  • OPENCLAW_LIVE_TEST=1 with a redacted Xiaomi console API key against real mimo-v2-pro

What I checked:

  • Current main lacks response-side promotion: Current main's Xiaomi stream wrapper only applies the DeepSeek-style request/replay wrapper; it does not wrap final thinking-only responses into text. (extensions/xiaomi/stream.ts:9, 6de8563827ad)
  • Affected models are reasoning OpenAI-completions models: The Xiaomi manifest exposes mimo-v2-pro and mimo-v2-omni as reasoning models on the OpenAI-completions catalog, matching the PR's target surface. (extensions/xiaomi/openclaw.plugin.json:28, 6de8563827ad)
  • Current parser routes reasoning_content to thinking: The OpenAI-completions parser still reads reasoning_content and emits it as a thinking delta rather than visible text, which source-reproduces the blank visible-output path. (src/agents/openai-transport-stream.ts:2137, 6de8563827ad)
  • PR head adds narrow promotion helper: PR head 1056c81f151ab69bab13487604ff2a49421d5527 promotes only terminal messages with no visible text, no tool call, and at least one thinking block; it rewrites both stream events and result(). (src/plugin-sdk/provider-stream-shared.ts:237, 1056c81f151a)
  • PR head wires Xiaomi and custom proxy routes: The direct Xiaomi wrapper is gated to provider xiaomi plus mimo-v2-pro/mimo-v2-omni, and the core fallback applies the same visible-text promotion to normalized MiMo V2 OpenAI-completions proxy model IDs. (extensions/xiaomi/stream.ts:23, 1056c81f151a)
  • Maintainer proof and checks supplied in PR discussion: A May 15 maintainer comment on the PR says the rewrite was tested with OPENCLAW_LIVE_TEST=1 against real mimo-v2-pro and returned marker XIAOMI_VISIBLE_E2E_OK; it also reports targeted Vitest shards, pnpm check:changed, and pnpm build passing. (1056c81f151a)

Likely related people:

  • jimdawdy-hub: Authored the recent current-main Xiaomi MiMo thinking profile, stream wrapper, replay hooks, and unowned-proxy fallback that this PR builds on. (role: recent area contributor; confidence: high; commits: e3ad37c24f8b, f97645428e4e, aeb06bf4ef62; files: extensions/xiaomi/index.ts, extensions/xiaomi/stream.ts, extensions/xiaomi/thinking.ts)
  • steipete: Recent current-main history includes the central inbound-history/reasoning replay work, and the PR head was force-pushed by this account with live proof and validation notes. (role: recent adjacent contributor and PR rewrite author; confidence: high; commits: 2d8339529b58, 3537d8a613dd, a1a6cd6508cb; files: src/agents/openai-transport-stream.ts, src/plugin-sdk/provider-stream-shared.ts, extensions/xiaomi/stream.ts)
  • DJjjjhao: Prior ClawSweeper review context ties this account to the Xiaomi MiMo V2 Pro/Omni OpenAI-completions catalog path involved in the report. (role: introduced affected model surface; confidence: medium; commits: 4f00b3b534f2, 69abdd111ab8; files: extensions/xiaomi/provider-catalog.ts, docs/providers/xiaomi.md)

Remaining risk / open question:

  • mimo-v2-omni has unit coverage through the same wrapper path, but the PR discussion only reports live proof for mimo-v2-pro.
  • This read-only review did not run the PR branch tests locally; it relies on source inspection plus maintainer-reported live proof and checks.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 6de8563827ad.

@IvinWu

IvinWu commented May 13, 2026

Copy link
Copy Markdown

Hit this on v2026.5.7 with xiaomi/mimo-v2-pro thinking=medium. Same error: Param Incorrect: The reasoning_content in the thinking mode must be passed back to the API. Disabling thinking works around it. Bumping for visibility — this PR has been open for a month.

@steipete

steipete commented May 15, 2026

Copy link
Copy Markdown
Contributor

Maintainer rewrite pushed in 0de9f69.

Changed:

  • Kept MiMo request replay/passthrough on the existing DeepSeek-style reasoning_content wire format.
  • Added a response-side fallback that only promotes terminal thinking-only MiMo V2 output to visible text, and only for mimo-v2-pro / mimo-v2-omni.
  • Left tool-call reasoning replay untouched: messages with tool calls or already-visible text are not promoted.
  • Added the same response-side fallback for custom Xiaomi-compatible OpenAI-completions proxy routes, where the xiaomi plugin wrapper is not in the provider path.
  • This does not change stock OpenAI calls. The new core fallback is gated to normalized MiMo model IDs, and the direct plugin path requires provider=xiaomi.

Code refs:

  • extensions/xiaomi/stream.ts:39
  • src/plugin-sdk/provider-stream-shared.ts:333
  • src/agents/pi-embedded-runner/extra-params.ts:758
  • extensions/xiaomi/index.test.ts:344
  • src/agents/pi-embedded-runner-extraparams.test.ts:728

Tested:

  • pnpm test extensions/xiaomi/index.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/plugin-sdk/provider-stream-shared.test.ts: passed 3 Vitest shards in 15.12s after rebase.
  • OPENCLAW_LIVE_TEST=1 with a temporary Xiaomi console API key against real mimo-v2-pro: prompt returned the requested visible marker XIAOMI_VISIBLE_E2E_OK; temp key was revoked afterward.
  • /Users/steipete/Projects/agent-scripts/skills/codex-review/scripts/codex-review --mode local --parallel-tests "pnpm test extensions/xiaomi/index.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/plugin-sdk/provider-stream-shared.test.ts": clean, no actionable findings; parallel tests passed.
  • pnpm check:changed: passed on Blacksmith Testbox tbx_01krnvk8eq4ncvaqhnkg4swy2a, GitHub Actions run https://github.com/openclaw/openclaw/actions/runs/25919175341.
  • pnpm build: passed.
  • PR CI for 0de9f69: green, including Real behavior proof, Critical Quality, Security High, build-artifacts, and check shards.

Not live-tested:

  • mimo-v2-omni live call. It is covered by the same unit wrapper path as mimo-v2-pro.

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@steipete
steipete force-pushed the codex/issue-60261-xiaomi-reasoning-content-fallback branch from 1056c81 to 0de9f69 Compare May 15, 2026 13:07
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 15, 2026
@steipete
steipete merged commit 8cc1aee into openclaw:main May 15, 2026
120 of 125 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling extensions: xiaomi proof: supplied External PR includes structured after-fix real behavior proof. size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Xiaomi/MiMo models output to reasoning_content instead of content field

3 participants