diff --git a/open-sse/handlers/responseSanitizer/reasoning.ts b/open-sse/handlers/responseSanitizer/reasoning.ts index 867d455c84..b1a385003a 100644 --- a/open-sse/handlers/responseSanitizer/reasoning.ts +++ b/open-sse/handlers/responseSanitizer/reasoning.ts @@ -130,6 +130,7 @@ export function isTextualReasoningTagNativeRoute(providerId: string, modelId: st /deepseek[-_/]?r1\b/.test(routeId) || /r1[-_/]?distill\b/.test(routeId) || /(?:^|[/:_-])qwq(?:[/._:-]|$)/.test(routeId) || + (providerId === "kimi-coding" && /(?:^|[/_-])k3(?:[/._:-]|$)/.test(modelId)) || // 9router#2231: MiniMax M3 leaks raw ... into `content` on its // OpenAI-format provider tiers (trae, huggingchat, bazaarlink, ollama-cloud, // opencode, cline, opencode-zen, codebuddy-cn). The direct minimax/minimax-cn diff --git a/tests/unit/responsesanitizer-reasoning-split.test.ts b/tests/unit/responsesanitizer-reasoning-split.test.ts index 1517d5db3b..f89529b21e 100644 --- a/tests/unit/responsesanitizer-reasoning-split.test.ts +++ b/tests/unit/responsesanitizer-reasoning-split.test.ts @@ -52,6 +52,21 @@ describe("responseSanitizer/reasoning — shouldParseTextualReasoningTags", () = }); }); +describe("responseSanitizer/reasoning — Kimi Code K3 textual reasoning-tag route", () => { + it("extracts blocks from the Kimi Code K3 route", () => { + const chunk = { + choices: [{ index: 0, delta: { content: "reasoning herefinal answer" } }], + }; + const sanitized = sanitizeOpenAIResponse(chunk, { + parseTextualReasoningTags: shouldParseTextualReasoningTags("kimi-coding", "k3"), + }) as { choices: Array<{ delta: { content: string; reasoning_content?: string } }> }; + + const delta = sanitized.choices[0].delta; + assert.equal(delta.content, "final answer"); + assert.equal(delta.reasoning_content, "reasoning here"); + }); +}); + // ── MiniMax M3 textual reasoning-tag route (9router#2231) ────────────────────── // // MiniMax M3 leaks raw ... into `content` instead of a separate