diff --git a/changelog.d/fixes/7098-mimo-thinking-model-reasoning-content.md b/changelog.d/fixes/7098-mimo-thinking-model-reasoning-content.md new file mode 100644 index 0000000000..ddd454f252 --- /dev/null +++ b/changelog.d/fixes/7098-mimo-thinking-model-reasoning-content.md @@ -0,0 +1 @@ +- **fix(sse):** xiaomi-tokenplan `mimo` models (e.g. `mimo-v2.5-pro`) are now recognized as thinking-mode upstreams that require `reasoning_content` echoed back on every assistant turn, fixing a persistent `400 reasoning_content must be passed back` error on multi-turn conversations ([#7098](https://github.com/diegosouzapw/OmniRoute/pull/7098)) — thanks @xxue-z diff --git a/open-sse/utils/reasoningContentInjector.ts b/open-sse/utils/reasoningContentInjector.ts index 8fd9b13bd4..c2e8318be4 100644 --- a/open-sse/utils/reasoningContentInjector.ts +++ b/open-sse/utils/reasoningContentInjector.ts @@ -1,5 +1,6 @@ /** - * Thinking-mode upstreams (DeepSeek V4 Flash, Kimi, MiniMax, ...) require + * Thinking-mode upstreams (DeepSeek V4 Flash, Kimi, MiniMax, xiaomi-tokenplan + * mimo, ...) require * `reasoning_content` to be echoed back on every assistant message in the * conversation history. Standard OpenAI clients do not preserve that field * across turns, so we inject a non-empty placeholder before forwarding. @@ -26,6 +27,7 @@ const THINKING_MODEL_PATTERNS: RegExp[] = [ /\bkimi\b/i, /\bk2\b/i, // moonshot kimi k2 family alias /\bminimax\b/i, + /\bmimo\b/i, // xiaomi-tokenplan mimo family (e.g. xiaomi-tokenplan/mimo-v2.5-pro) ]; export function isThinkingMessageModel(model: string | undefined | null): boolean { diff --git a/tests/unit/reasoningContentInjector.test.ts b/tests/unit/reasoningContentInjector.test.ts new file mode 100644 index 0000000000..134843e774 --- /dev/null +++ b/tests/unit/reasoningContentInjector.test.ts @@ -0,0 +1,44 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +import { + isThinkingMessageModel, + injectReasoningContentForThinkingModel, +} from "../../open-sse/utils/reasoningContentInjector.ts"; + +describe("reasoningContentInjector — xiaomi-tokenplan mimo family (9router#1321)", () => { + it("recognizes xiaomi-tokenplan/mimo-v2.5-pro as a thinking-mode model", () => { + assert.equal(isThinkingMessageModel("xiaomi-tokenplan/mimo-v2.5-pro"), true); + }); + + it("recognizes bare mimo model ids as thinking-mode models", () => { + assert.equal(isThinkingMessageModel("mimo-v2.5-pro"), true); + }); + + it("still recognizes the existing thinking-mode families (deepseek/kimi/k2/minimax)", () => { + assert.equal(isThinkingMessageModel("deepseek-v4-flash"), true); + assert.equal(isThinkingMessageModel("kimi-k2"), true); + assert.equal(isThinkingMessageModel("minimax-m2"), true); + }); + + it("does not flag unrelated model ids", () => { + assert.equal(isThinkingMessageModel("gpt-4o"), false); + }); + + it("injects a reasoning_content placeholder for assistant messages when routed to mimo", () => { + const body = { + model: "xiaomi-tokenplan/mimo-v2.5-pro", + messages: [ + { role: "user", content: "hi" }, + { role: "assistant", content: "hello" }, + ], + }; + + // Simulate the executor gate: only inject when the model is a thinking model. + assert.equal(isThinkingMessageModel(body.model), true); + + const result = injectReasoningContentForThinkingModel(body) as typeof body; + const assistantMsg = result.messages[1] as Record; + assert.equal(assistantMsg.reasoning_content, " "); + }); +});