From 1db1e9e200b8e610811d562c0bd0d354308a4334 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 30 Jun 2026 02:12:51 +0000 Subject: [PATCH 1/3] fix(catalog): omitted disabled thinking for kimi code Kimi K2.7 Code rejects disabled thinking on native Kimi endpoints, so route caller disable requests through the omit mode and let the model default to required thinking. Added regression coverage for the title-generator-style Kimi Code request, Moonshot K2.7 Code variants, and K2.6's still-supported disabled-thinking path. Fixes #3852 --- .../__tests__/kimi-code-thinking.test.ts | 52 +++ packages/catalog/CHANGELOG.md | 4 + packages/catalog/src/compat/openai.ts | 12 +- packages/catalog/src/models.json | 438 ++++++++++-------- .../src/provider-models/openai-compat.ts | 2 +- 5 files changed, 303 insertions(+), 205 deletions(-) create mode 100644 packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts diff --git a/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts new file mode 100644 index 000000000..0b3c08de4 --- /dev/null +++ b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "bun:test"; +import { getBundledModel } from "@oh-my-pi/pi-catalog"; +import { + applyChatCompletionsCompatPolicy, + type OpenAICompletionsParams, + resolveOpenAICompatPolicy, +} from "../openai-shared"; + +const BASE_CHAT_COMPLETIONS_PARAMS: OpenAICompletionsParams = { messages: [], model: "unused", stream: true }; + +describe("Kimi K2.7 Code thinking policy", () => { + it("omits disabled thinking for title-generator-style Kimi Code requests", () => { + const model = getBundledModel("kimi-code", "kimi-for-coding"); + const policy = resolveOpenAICompatPolicy(model, { + endpoint: "chat-completions", + disableReasoning: true, + toolChoice: { type: "tool", name: "set_title" }, + }); + const params = { ...BASE_CHAT_COMPLETIONS_PARAMS }; + + applyChatCompletionsCompatPolicy(params, policy); + + expect("thinking" in params).toBe(false); + }); + + it("omits disabled thinking for native Moonshot Kimi K2.7 Code variants", () => { + for (const modelId of ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"]) { + const model = getBundledModel("moonshot", modelId); + const policy = resolveOpenAICompatPolicy(model, { + endpoint: "chat-completions", + disableReasoning: true, + }); + const params = { ...BASE_CHAT_COMPLETIONS_PARAMS }; + applyChatCompletionsCompatPolicy(params, policy); + + expect("thinking" in params).toBe(false); + } + }); + + it("keeps explicit disabled thinking for Kimi K2.6", () => { + const model = getBundledModel("moonshot", "kimi-k2.6"); + const policy = resolveOpenAICompatPolicy(model, { + endpoint: "chat-completions", + disableReasoning: true, + }); + const params = { ...BASE_CHAT_COMPLETIONS_PARAMS }; + + applyChatCompletionsCompatPolicy(params, policy); + + expect(params.thinking).toEqual({ type: "disabled" }); + }); +}); diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 09b0580c6..dc60dc92a 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Kimi K2.7 Code compatibility to avoid sending disabled thinking to native Kimi endpoints that require thinking mode. ([#3852](https://github.com/can1357/oh-my-pi/issues/3852)) + ## [16.2.6] - 2026-06-29 ### Fixed diff --git a/packages/catalog/src/compat/openai.ts b/packages/catalog/src/compat/openai.ts index 26ae8b357..42ab2f305 100644 --- a/packages/catalog/src/compat/openai.ts +++ b/packages/catalog/src/compat/openai.ts @@ -39,6 +39,13 @@ const GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS = 600_000; const DEEPSEEK_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000; /** Kimi K2.6 can spend several minutes reasoning before the first visible token. */ const KIMI_K26_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000; +/** Native Kimi K2.7 Code requires thinking; sending `thinking: { type: "disabled" }` 400s. */ +const KIMI_K27_CODE_MODEL_PATTERN = /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i; + +function requiresKimiK27CodeEnabledThinking(spec: ModelSpec<"openai-completions">): boolean { + if (KIMI_K27_CODE_MODEL_PATTERN.test(spec.id)) return true; + return spec.provider === "kimi-code" && spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); +} /** Xiaomi MiMo Pro on api.xiaomimimo.com can stall ~2min before the first event (issue #1770). */ const XIAOMI_MIMO_STREAM_IDLE_TIMEOUT_MS = 300_000; /** Alibaba Coding Plan (coding-intl.dashscope) qwen models idle before the first event (issue #1770). */ @@ -231,6 +238,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv const isKimiModel = isKimiModelId(spec.id); const isMoonshotNative = modelMatchesHost(hostModel, "moonshotNative"); const isMoonshotKimi = isKimiModel && isMoonshotNative; + const requiresEnabledThinking = requiresKimiK27CodeEnabledThinking(spec); const usesMoonshotKimiPreservedThinking = isMoonshotKimi && isKimiK26ModelId(spec.id); const isAnthropicModel = modelMatchesHost(hostModel, "anthropic") || isClaudeModelId(spec.id) || isAnthropicNamespacedModelId(spec.id); @@ -509,7 +517,9 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv applyCompatOverrides(compat, spec.compat); if (spec.compat?.reasoningDisableMode === undefined) { - compat.reasoningDisableMode = resolveReasoningDisableMode(compat.thinkingFormat); + compat.reasoningDisableMode = requiresEnabledThinking + ? "omit" + : resolveReasoningDisableMode(compat.thinkingFormat); } if (spec.compat?.omitReasoningEffort === undefined && !compat.supportsReasoningEffort) { compat.omitReasoningEffort = true; diff --git a/packages/catalog/src/models.json b/packages/catalog/src/models.json index 492404b0c..877830895 100644 --- a/packages/catalog/src/models.json +++ b/packages/catalog/src/models.json @@ -2476,7 +2476,7 @@ "api": "openai-completions", "provider": "aimlapi", "baseUrl": "https://api.aimlapi.com/v1", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -2487,24 +2487,7 @@ "cacheWrite": 0 }, "contextWindow": 163840, - "maxTokens": 16384, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ], - "effortMap": { - "minimal": "high", - "low": "high", - "medium": "high", - "high": "high", - "xhigh": "max" - } - } + "maxTokens": 16384 }, "deepseek/deepseek-chat-v3.1": { "id": "deepseek/deepseek-chat-v3.1", @@ -10620,6 +10603,35 @@ ] } }, + "xai.grok-4.3": { + "id": "xai.grok-4.3", + "name": "Grok 4.3", + "api": "bedrock-converse-stream", + "provider": "amazon-bedrock", + "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 1.25, + "output": 2.5, + "cacheRead": 0.2, + "cacheWrite": 0 + }, + "contextWindow": 1000000, + "maxTokens": 131072, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high" + ] + } + }, "zai.glm-4.7": { "id": "zai.glm-4.7", "name": "GLM-4.7", @@ -13672,7 +13684,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -13683,17 +13695,7 @@ "cacheWrite": 0 }, "contextWindow": 128000, - "maxTokens": 128000, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ] - } + "maxTokens": 128000 }, "meta-llama/Llama-3.3-70B-Instruct": { "id": "meta-llama/Llama-3.3-70B-Instruct", @@ -13701,7 +13703,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -13712,17 +13714,7 @@ "cacheWrite": 0 }, "contextWindow": 128000, - "maxTokens": 128000, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ] - } + "maxTokens": 128000 }, "meta-llama/Llama-4-Scout-17B-16E-Instruct": { "id": "meta-llama/Llama-4-Scout-17B-16E-Instruct", @@ -13730,7 +13722,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": true, + "reasoning": false, "input": [ "text", "image" @@ -13742,17 +13734,7 @@ "cacheWrite": 0 }, "contextWindow": 64000, - "maxTokens": 64000, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ] - } + "maxTokens": 64000 }, "microsoft/Phi-4-mini-instruct": { "id": "microsoft/Phi-4-mini-instruct", @@ -13760,7 +13742,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -13771,17 +13753,7 @@ "cacheWrite": 0 }, "contextWindow": 128000, - "maxTokens": 128000, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ] - } + "maxTokens": 128000 }, "MiniMaxAI/MiniMax-M2.5": { "id": "MiniMaxAI/MiniMax-M2.5", @@ -13789,7 +13761,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": false, + "reasoning": true, "input": [ "text" ], @@ -13800,7 +13772,16 @@ "cacheWrite": 0 }, "contextWindow": 196608, - "maxTokens": 196608 + "maxTokens": 196608, + "thinking": { + "mode": "effort", + "efforts": [ + "low", + "medium", + "high" + ], + "requiresEffort": true + } }, "moonshotai/Kimi-K2.5": { "id": "moonshotai/Kimi-K2.5", @@ -13898,7 +13879,7 @@ "api": "openai-completions", "provider": "coreweave", "baseUrl": "https://api.inference.wandb.ai/v1", - "reasoning": false, + "reasoning": true, "input": [ "text" ], @@ -13909,7 +13890,17 @@ "cacheWrite": 0 }, "contextWindow": 262144, - "maxTokens": 262144 + "maxTokens": 262144, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } }, "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B": { "id": "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B", @@ -21734,7 +21725,7 @@ "cost": { "input": 0.075, "output": 0.3, - "cacheRead": 0.037, + "cacheRead": 0, "cacheWrite": 0 }, "contextWindow": 131072, @@ -22420,13 +22411,13 @@ "text" ], "cost": { - "input": 0, - "output": 0, + "input": 0.25, + "output": 0.69, "cacheRead": 0, "cacheWrite": 0 }, "contextWindow": 131072, - "maxTokens": 65536, + "maxTokens": 32768, "thinking": { "mode": "effort", "efforts": [ @@ -25048,7 +25039,7 @@ "api": "openai-completions", "provider": "kilo", "baseUrl": "https://api.kilo.ai/api/gateway", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -25059,24 +25050,7 @@ "cacheWrite": 0 }, "contextWindow": 163840, - "maxTokens": 65536, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ], - "effortMap": { - "minimal": "high", - "low": "high", - "medium": "high", - "high": "high", - "xhigh": "max" - } - } + "maxTokens": 65536 }, "deepseek/deepseek-chat-v3.1": { "id": "deepseek/deepseek-chat-v3.1", @@ -30816,6 +30790,7 @@ "supportsStrictMode": false, "toolStrictMode": "mixed", "stripDeepseekSpecialTokens": false, + "streamMarkupHealingPattern": "thinking", "reasoningDeltasMayBeCumulative": false, "emptyLengthFinishIsContextError": false, "usesOpenAIToolCallIdLimit": false, @@ -54444,6 +54419,36 @@ "requiresEffort": true } }, + "minimaxai/minimax-m3": { + "id": "minimaxai/minimax-m3", + "name": "MiniMax-M3", + "api": "openai-completions", + "provider": "nvidia", + "baseUrl": "https://integrate.api.nvidia.com/v1", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 1000000, + "maxTokens": 16384, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, "mistralai/codestral-22b-instruct-v0.1": { "id": "mistralai/codestral-22b-instruct-v0.1", "name": "Codestral 22b Instruct V0.1", @@ -62195,9 +62200,9 @@ "image" ], "cost": { - "input": 0.66, - "output": 3.41, - "cacheRead": 0.144, + "input": 0.55, + "output": 3.1999999999999997, + "cacheRead": 0.11, "cacheWrite": 0 }, "contextWindow": 262144, @@ -63522,7 +63527,7 @@ "api": "openrouter", "baseUrl": "https://openrouter.ai/api/v1", "provider": "openrouter", - "reasoning": true, + "reasoning": false, "input": [ "text" ], @@ -63533,22 +63538,7 @@ "cacheWrite": 0 }, "contextWindow": 163840, - "maxTokens": 16384, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high" - ], - "effortMap": { - "minimal": "high", - "low": "high", - "medium": "high", - "high": "high" - } - } + "maxTokens": 16384 }, "deepseek/deepseek-chat-v3.1": { "id": "deepseek/deepseek-chat-v3.1", @@ -65751,7 +65741,7 @@ "cacheWrite": 0 }, "contextWindow": 131072, - "maxTokens": 32768 + "maxTokens": 100352 }, "moonshotai/kimi-k2-0905": { "id": "moonshotai/kimi-k2-0905", @@ -65770,7 +65760,7 @@ "cacheWrite": 0 }, "contextWindow": 262144, - "maxTokens": 262144 + "maxTokens": 100352 }, "moonshotai/kimi-k2-0905:exacto": { "id": "moonshotai/kimi-k2-0905:exacto", @@ -65861,9 +65851,9 @@ "image" ], "cost": { - "input": 0.66, - "output": 3.41, - "cacheRead": 0.144, + "input": 0.55, + "output": 3.1999999999999997, + "cacheRead": 0.11, "cacheWrite": 0 }, "contextWindow": 262144, @@ -69347,8 +69337,8 @@ "image" ], "cost": { - "input": 0.28850000000000003, - "output": 2.65, + "input": 0.2596, + "output": 2.3850000000000002, "cacheRead": 0.15, "cacheWrite": 0 }, @@ -69758,13 +69748,13 @@ "text" ], "cost": { - "input": 0.09, + "input": 0.09999999999999999, "output": 0.3, "cacheRead": 0.02, "cacheWrite": 0 }, "contextWindow": 262144, - "maxTokens": 16384, + "maxTokens": 65536, "thinking": { "mode": "effort", "efforts": [ @@ -70846,8 +70836,8 @@ "text" ], "cost": { - "input": 0.98, - "output": 3.08, + "input": 0.975, + "output": 4.300000000000001, "cacheRead": 0.182, "cacheWrite": 0 }, @@ -70874,7 +70864,7 @@ "text" ], "cost": { - "input": 0.95, + "input": 0.94, "output": 3, "cacheRead": 0.18, "cacheWrite": 0 @@ -71096,7 +71086,7 @@ "synthetic": { "hf:MiniMaxAI/MiniMax-M3": { "id": "hf:MiniMaxAI/MiniMax-M3", - "name": "MiniMaxAI/MiniMax-M3", + "name": "MiniMax-M3", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71111,7 +71101,7 @@ "cacheRead": 0.6, "cacheWrite": 0 }, - "contextWindow": 262144, + "contextWindow": 524288, "maxTokens": 65536, "thinking": { "mode": "effort", @@ -71126,7 +71116,7 @@ }, "hf:moonshotai/Kimi-K2.6": { "id": "hf:moonshotai/Kimi-K2.6", - "name": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71156,7 +71146,7 @@ }, "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4": { "id": "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4", - "name": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4", + "name": "Nemotron 3 Super 120B A12B", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71185,7 +71175,7 @@ }, "hf:openai/gpt-oss-120b": { "id": "hf:openai/gpt-oss-120b", - "name": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71196,7 +71186,7 @@ "cost": { "input": 0.1, "output": 0.1, - "cacheRead": 0, + "cacheRead": 0.1, "cacheWrite": 0 }, "contextWindow": 131072, @@ -71212,7 +71202,7 @@ }, "hf:Qwen/Qwen3.5-397B-A17B": { "id": "hf:Qwen/Qwen3.5-397B-A17B", - "name": "Qwen/Qwen3.5-397B-A17B", + "name": "Qwen3.5 397B-A17B", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71223,7 +71213,7 @@ ], "cost": { "input": 0.6, - "output": 3, + "output": 3.6, "cacheRead": 0.6, "cacheWrite": 0 }, @@ -71241,26 +71231,36 @@ }, "hf:Qwen/Qwen3.6-27B": { "id": "hf:Qwen/Qwen3.6-27B", - "name": "Qwen/Qwen3.6-27B", + "name": "Qwen3.6 27B", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", - "reasoning": false, + "reasoning": true, "input": [ - "text" + "text", + "image" ], "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, + "input": 0.45, + "output": 3.6, + "cacheRead": 0.45, "cacheWrite": 0 }, "contextWindow": 262144, - "maxTokens": 8192 + "maxTokens": 65536, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high" + ] + } }, "hf:zai-org/GLM-4.7": { "id": "hf:zai-org/GLM-4.7", - "name": "zai-org/GLM-4.7", + "name": "GLM-4.7", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71269,13 +71269,13 @@ "text" ], "cost": { - "input": 0.55, + "input": 0.45, "output": 2.19, - "cacheRead": 0, + "cacheRead": 0.45, "cacheWrite": 0 }, "contextWindow": 202752, - "maxTokens": 64000, + "maxTokens": 65536, "thinking": { "mode": "effort", "efforts": [ @@ -71289,7 +71289,7 @@ }, "hf:zai-org/GLM-4.7-Flash": { "id": "hf:zai-org/GLM-4.7-Flash", - "name": "zai-org/GLM-4.7-Flash", + "name": "GLM-4.7-Flash", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71298,9 +71298,9 @@ "text" ], "cost": { - "input": 0.06, - "output": 0.4, - "cacheRead": 0.06, + "input": 0.1, + "output": 0.5, + "cacheRead": 0.1, "cacheWrite": 0 }, "contextWindow": 196608, @@ -71318,7 +71318,7 @@ }, "hf:zai-org/GLM-5.1": { "id": "hf:zai-org/GLM-5.1", - "name": "zai-org/GLM-5.1", + "name": "GLM-5.1", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", @@ -71347,22 +71347,32 @@ }, "hf:zai-org/GLM-5.2": { "id": "hf:zai-org/GLM-5.2", - "name": "zai-org/GLM-5.2", + "name": "GLM-5.2", "api": "openai-completions", "provider": "synthetic", "baseUrl": "https://api.synthetic.new/openai/v1", - "reasoning": false, + "reasoning": true, "input": [ "text" ], "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, + "input": 1.4, + "output": 4.4, + "cacheRead": 1.4, "cacheWrite": 0 }, "contextWindow": 524288, - "maxTokens": 8192 + "maxTokens": 65536, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } }, "syn:large:text": { "id": "syn:large:text", @@ -71682,7 +71692,7 @@ "api": "openai-completions", "provider": "together", "baseUrl": "https://api.together.xyz/v1", - "reasoning": true, + "reasoning": false, "input": [ "text", "image" @@ -71694,17 +71704,7 @@ "cacheWrite": 0.18 }, "contextWindow": 10000000, - "maxTokens": 32768, - "thinking": { - "mode": "effort", - "efforts": [ - "minimal", - "low", - "medium", - "high", - "xhigh" - ] - } + "maxTokens": 32768 }, "MiniMaxAI/MiniMax-M2.5": { "id": "MiniMaxAI/MiniMax-M2.5", @@ -72381,6 +72381,38 @@ "escapeBuiltinToolNames": true } }, + "umans-glm-5.2-nvfp4": { + "id": "umans-glm-5.2-nvfp4", + "name": "Umans GLM 5.2 NVFP4 (experimental, short test from Jun 29)", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "thinking": { + "mode": "anthropic-budget-effort", + "efforts": [ + "high", + "xhigh" + ], + "effortMap": { + "xhigh": "max" + } + }, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 405504, + "maxTokens": 131071, + "compat": { + "escapeBuiltinToolNames": true + } + }, "umans-kimi-k2.7": { "id": "umans-kimi-k2.7", "name": "Umans Kimi K2.7 Code", @@ -80633,7 +80665,7 @@ }, "zai/glm-4.5": { "id": "zai/glm-4.5", - "name": "GLM-4.5", + "name": "GLM 4.5", "api": "anthropic-messages", "baseUrl": "https://ai-gateway.vercel.sh", "provider": "vercel-ai-gateway", @@ -82062,6 +82094,14 @@ }, "contextWindow": 2000000, "maxTokens": 2000000, + "compat": { + "reasoningEffortMap": { + "minimal": "low" + }, + "includeEncryptedReasoning": false, + "filterReasoningHistory": true, + "omitReasoningEffort": false + }, "thinking": { "mode": "effort", "efforts": [ @@ -82074,14 +82114,6 @@ "effortMap": { "minimal": "low" } - }, - "compat": { - "reasoningEffortMap": { - "minimal": "low" - }, - "includeEncryptedReasoning": false, - "filterReasoningHistory": true, - "omitReasoningEffort": false } }, "grok-4.3": { @@ -82103,6 +82135,14 @@ }, "contextWindow": 1000000, "maxTokens": 1000000, + "compat": { + "reasoningEffortMap": { + "minimal": "low" + }, + "includeEncryptedReasoning": false, + "filterReasoningHistory": true, + "omitReasoningEffort": false + }, "thinking": { "mode": "effort", "efforts": [ @@ -82115,14 +82155,6 @@ "effortMap": { "minimal": "low" } - }, - "compat": { - "reasoningEffortMap": { - "minimal": "low" - }, - "includeEncryptedReasoning": false, - "filterReasoningHistory": true, - "omitReasoningEffort": false } }, "grok-build": { @@ -82174,12 +82206,12 @@ "contextWindow": 256000, "maxTokens": 256000, "compat": { - "includeEncryptedReasoning": false, - "filterReasoningHistory": true, - "omitReasoningEffort": true, "reasoningEffortMap": { "minimal": "low" }, + "includeEncryptedReasoning": false, + "filterReasoningHistory": true, + "omitReasoningEffort": true, "supportsReasoningEffort": false } }, @@ -82223,9 +82255,9 @@ "text" ], "cost": { - "input": 0.1, - "output": 0.3, - "cacheRead": 0.01, + "input": 0.14, + "output": 0.28, + "cacheRead": 0.0028, "cacheWrite": 0 }, "contextWindow": 262144, @@ -82259,9 +82291,9 @@ "image" ], "cost": { - "input": 0.4, - "output": 2, - "cacheRead": 0.08, + "input": 0.14, + "output": 0.28, + "cacheRead": 0.0028, "cacheWrite": 0 }, "contextWindow": 262144, @@ -82294,9 +82326,9 @@ "text" ], "cost": { - "input": 1, - "output": 3, - "cacheRead": 0.2, + "input": 0.435, + "output": 0.87, + "cacheRead": 0.0036, "cacheWrite": 0 }, "contextWindow": 1048576, @@ -82330,9 +82362,9 @@ "image" ], "cost": { - "input": 0.4, - "output": 2, - "cacheRead": 0.08, + "input": 0.14, + "output": 0.28, + "cacheRead": 0.0028, "cacheWrite": 0 }, "contextWindow": 1048576, @@ -82365,9 +82397,9 @@ "text" ], "cost": { - "input": 1, - "output": 3, - "cacheRead": 0.2, + "input": 0.435, + "output": 0.87, + "cacheRead": 0.0036, "cacheWrite": 0 }, "contextWindow": 1048576, diff --git a/packages/catalog/src/provider-models/openai-compat.ts b/packages/catalog/src/provider-models/openai-compat.ts index 12776ea48..ea448e488 100644 --- a/packages/catalog/src/provider-models/openai-compat.ts +++ b/packages/catalog/src/provider-models/openai-compat.ts @@ -1294,7 +1294,7 @@ export function clampFireworksKimiMaxTokens(modelId: string, candidate: number | export const KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS = 32_768; export function isKimiK27CodeModelId(modelId: string): boolean { - return /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code$/i.test(modelId); + return /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i.test(modelId); } export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number): number; From fa2e3a807f48a35440b427373c0a0da757849934 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 30 Jun 2026 02:13:17 +0000 Subject: [PATCH 2/3] style: bun run fix --- packages/ai/CHANGELOG.md | 4 ++ .../__tests__/kimi-code-thinking.test.ts | 55 ++++++++++++++++++- packages/ai/src/providers/anthropic.ts | 11 ++-- packages/catalog/CHANGELOG.md | 2 +- packages/catalog/src/compat/anthropic.ts | 11 +++- packages/catalog/src/compat/openai.ts | 2 +- packages/catalog/src/models.json | 1 + packages/catalog/src/types.ts | 5 ++ .../test/agent-session-handoff.test.ts | 12 +++- 9 files changed, 90 insertions(+), 13 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 3a4f8c78d..4b1fc6925 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Kimi Code's Anthropic-compatible request path to keep thinking enabled and downgrade forced tool choice for Kimi K2.7 Code title generation. ([#3852](https://github.com/can1357/oh-my-pi/issues/3852)) + ## [16.2.6] - 2026-06-29 ### Fixed diff --git a/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts index 0b3c08de4..4dcab4218 100644 --- a/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts +++ b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "bun:test"; import { getBundledModel } from "@oh-my-pi/pi-catalog"; +import type { Context } from "../../types"; +import type { MessageCreateParamsStreaming } from "../anthropic-wire"; +import { streamOpenAIAnthropicShim } from "../openai-anthropic-shim"; import { applyChatCompletionsCompatPolicy, type OpenAICompletionsParams, @@ -7,10 +10,26 @@ import { } from "../openai-shared"; const BASE_CHAT_COMPLETIONS_PARAMS: OpenAICompletionsParams = { messages: [], model: "unused", stream: true }; +const TITLE_CONTEXT: Context = { + systemPrompt: ["Generate a title."], + messages: [{ role: "user", content: "Explain the login failure", timestamp: 0 }], + tools: [ + { + name: "set_title", + description: "Set title", + parameters: { + type: "object", + properties: { title: { type: "string" } }, + required: ["title"], + additionalProperties: false, + }, + }, + ], +}; describe("Kimi K2.7 Code thinking policy", () => { it("omits disabled thinking for title-generator-style Kimi Code requests", () => { - const model = getBundledModel("kimi-code", "kimi-for-coding"); + const model = getBundledModel<"openai-completions">("kimi-code", "kimi-for-coding"); const policy = resolveOpenAICompatPolicy(model, { endpoint: "chat-completions", disableReasoning: true, @@ -21,11 +40,40 @@ describe("Kimi K2.7 Code thinking policy", () => { applyChatCompletionsCompatPolicy(params, policy); expect("thinking" in params).toBe(false); + expect(model.compat.supportsForcedToolChoice).toBe(false); + }); + + it("enables thinking and downgrades forced tool choice on Kimi Code's Anthropic endpoint", async () => { + const model = getBundledModel<"openai-completions">("kimi-code", "kimi-for-coding"); + let payload: MessageCreateParamsStreaming | undefined; + const stream = streamOpenAIAnthropicShim( + model, + TITLE_CONTEXT, + { + apiKey: "test-key", + maxTokens: 1024, + disableReasoning: true, + toolChoice: { type: "tool", name: "set_title" }, + onPayload: body => { + payload = body as MessageCreateParamsStreaming; + throw new Error("stop after payload capture"); + }, + }, + { + anthropicBaseUrl: "https://api.kimi.com/coding", + defaultFormat: "anthropic", + }, + ); + + await stream.result(); + + expect(payload?.thinking?.type).toBe("enabled"); + expect(payload?.tool_choice).toEqual({ type: "auto" }); }); it("omits disabled thinking for native Moonshot Kimi K2.7 Code variants", () => { for (const modelId of ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"]) { - const model = getBundledModel("moonshot", modelId); + const model = getBundledModel<"openai-completions">("moonshot", modelId); const policy = resolveOpenAICompatPolicy(model, { endpoint: "chat-completions", disableReasoning: true, @@ -34,11 +82,12 @@ describe("Kimi K2.7 Code thinking policy", () => { applyChatCompletionsCompatPolicy(params, policy); expect("thinking" in params).toBe(false); + expect(model.compat.supportsForcedToolChoice).toBe(false); } }); it("keeps explicit disabled thinking for Kimi K2.6", () => { - const model = getBundledModel("moonshot", "kimi-k2.6"); + const model = getBundledModel<"openai-completions">("moonshot", "kimi-k2.6"); const policy = resolveOpenAICompatPolicy(model, { endpoint: "chat-completions", disableReasoning: true, diff --git a/packages/ai/src/providers/anthropic.ts b/packages/ai/src/providers/anthropic.ts index 442cfd59e..4e9dbf81d 100644 --- a/packages/ai/src/providers/anthropic.ts +++ b/packages/ai/src/providers/anthropic.ts @@ -2876,9 +2876,10 @@ function buildParams( let thinking: MessageCreateParamsStreaming["thinking"] | undefined; let outputConfigEffort: AnthropicOutputEffort | undefined; if (model.reasoning) { - if (options?.thinkingEnabled) { + if (options?.thinkingEnabled || model.compat.requiresThinkingEnabled) { + const thinkingOptions = options ?? {}; const mode = model.thinking?.mode; - const effort = resolveAnthropicAdaptiveEffort(model, options); + const effort = resolveAnthropicAdaptiveEffort(model, thinkingOptions); const compat = model.compat; if (mode === "anthropic-adaptive" && !compat.disableAdaptiveThinking) { const adaptive: { type: "adaptive"; display?: AnthropicThinkingDisplay } = { type: "adaptive" }; @@ -2889,15 +2890,15 @@ function buildParams( // support: Opus 4.6 / Sonnet 4.6+ reject it with a 400, so an explicit // `thinkingDisplay` MUST NOT force it onto a model that can't accept it. if (model.thinking?.supportsDisplay) { - adaptive.display = options.thinkingDisplay ?? "summarized"; + adaptive.display = thinkingOptions.thinkingDisplay ?? "summarized"; } thinking = adaptive; if (effort && effort !== "adaptive") outputConfigEffort = effort; } else { thinking = { type: "enabled", - budget_tokens: options.thinkingBudgetTokens || 1024, - display: options.thinkingDisplay ?? "summarized", + budget_tokens: thinkingOptions.thinkingBudgetTokens || 1024, + display: thinkingOptions.thinkingDisplay ?? "summarized", }; if (mode === "anthropic-budget-effort" && effort && effort !== "adaptive") outputConfigEffort = effort; } diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index dc60dc92a..4d2edc7d5 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed -- Fixed Kimi K2.7 Code compatibility to avoid sending disabled thinking to native Kimi endpoints that require thinking mode. ([#3852](https://github.com/can1357/oh-my-pi/issues/3852)) +- Fixed Kimi K2.7 Code compatibility to avoid disabled thinking and forced tool choice on native Kimi endpoints that require thinking mode. ([#3852](https://github.com/can1357/oh-my-pi/issues/3852)) ## [16.2.6] - 2026-06-29 diff --git a/packages/catalog/src/compat/anthropic.ts b/packages/catalog/src/compat/anthropic.ts index 93390b0e3..876f11f3c 100644 --- a/packages/catalog/src/compat/anthropic.ts +++ b/packages/catalog/src/compat/anthropic.ts @@ -28,6 +28,13 @@ export function isOfficialAnthropicApiUrl(baseUrl?: string): boolean { return lower === OFFICIAL_ANTHROPIC_URL || lower.startsWith(`${OFFICIAL_ANTHROPIC_URL}/`); } +const KIMI_K27_CODE_MODEL_PATTERN = /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i; + +function requiresKimiK27CodeEnabledThinking(spec: ModelSpec<"anthropic-messages">): boolean { + if (KIMI_K27_CODE_MODEL_PATTERN.test(spec.id)) return true; + return spec.provider === "kimi-code" && spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); +} + /** Build the resolved anthropic-messages compat record for a model spec. */ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): ResolvedAnthropicCompat { const baseUrl = spec.baseUrl; @@ -40,6 +47,7 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res // doesn't whitelist the `fine-grained-tool-streaming-2025-05-14` beta either // (issue #2558), so eager tool-input streaming is unavailable on this host. const isCopilot = modelMatchesHost(spec, "githubCopilot"); + const requiresThinkingEnabled = requiresKimiK27CodeEnabledThinking(spec); const compat: ResolvedAnthropicCompat = { officialEndpoint: official, disableStrictTools: false, @@ -53,12 +61,13 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res // detection requires the canonical api.anthropic.com host plus a // supported model id. supportsMidConversationSystem: official && supportsMidConversationSystemMessages(spec.id), - supportsForcedToolChoice: !isAnthropicFableOrMythosModel(spec.id), + supportsForcedToolChoice: !requiresThinkingEnabled && !isAnthropicFableOrMythosModel(spec.id), // Opus 4.7+ and Fable/Mythos reject temperature/top_p/top_k with a 400. supportsSamplingParams: !hasOpus47ApiRestrictions(spec.id), // Z.AI workaround (issue #814): its proxy deserializes tool_result blocks // into a class that reads `.id`. requiresToolResultId: isZai, + requiresThinkingEnabled, // Official Anthropic enforces signature-based thinking-chain integrity, so // unsigned thinking blocks must stay text there. Anthropic-compatible // reasoning endpoints commonly emit unsigned thinking blocks while still diff --git a/packages/catalog/src/compat/openai.ts b/packages/catalog/src/compat/openai.ts index 42ab2f305..c3ae7e2d6 100644 --- a/packages/catalog/src/compat/openai.ts +++ b/packages/catalog/src/compat/openai.ts @@ -411,7 +411,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv disableReasoningOnForcedToolChoice: isKimiModel || isAnthropicModel, disableReasoningOnToolChoice: isDeepseekFamily && Boolean(spec.reasoning) && !isOpenRouter, supportsToolChoice: !isDirectDeepseekReasoning, - supportsForcedToolChoice: true, + supportsForcedToolChoice: !requiresEnabledThinking, supportsNamedToolChoice: provider !== "llama.cpp", maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens", requiresToolResultName: isMistral, diff --git a/packages/catalog/src/models.json b/packages/catalog/src/models.json index 877830895..a95d863af 100644 --- a/packages/catalog/src/models.json +++ b/packages/catalog/src/models.json @@ -75779,6 +75779,7 @@ "supportsForcedToolChoice": true, "supportsSamplingParams": true, "requiresToolResultId": false, + "requiresThinkingEnabled": false, "replayUnsignedThinking": false, "escapeBuiltinToolNames": false } diff --git a/packages/catalog/src/types.ts b/packages/catalog/src/types.ts index 29c0a9571..c41d76d3c 100644 --- a/packages/catalog/src/types.ts +++ b/packages/catalog/src/types.ts @@ -420,6 +420,11 @@ export interface AnthropicCompat { * Default: auto-detected from provider/baseUrl and `model.reasoning`. */ replayUnsignedThinking?: boolean; + /** + * Whether the endpoint requires `thinking.type: "enabled"` whenever the + * model reasons. Use for models that reject omitted or disabled thinking. + */ + requiresThinkingEnabled?: boolean; /** * Prefix Anthropic built-in tool names (`web_search`, `code_execution`, ...) * when they are ordinary client tools. Some Anthropic-compatible gateways diff --git a/packages/coding-agent/test/agent-session-handoff.test.ts b/packages/coding-agent/test/agent-session-handoff.test.ts index d8e82aa54..516a4f906 100644 --- a/packages/coding-agent/test/agent-session-handoff.test.ts +++ b/packages/coding-agent/test/agent-session-handoff.test.ts @@ -452,7 +452,11 @@ describe("AgentSession handoff", () => { const fixedPreparation: compactionModule.CompactionPreparation = { firstKeptEntryId: lastEntryId, messagesToSummarize: [ - { role: "user", content: [{ type: "text", text: UNRENDERABLE_SNAPCOMPACT_TEXT.repeat(100) }], timestamp: 1 }, + { + role: "user", + content: [{ type: "text", text: UNRENDERABLE_SNAPCOMPACT_TEXT.repeat(100) }], + timestamp: 1, + }, ], turnPrefixMessages: [], recentMessages: [], @@ -479,7 +483,11 @@ describe("AgentSession handoff", () => { const fixedPreparation: compactionModule.CompactionPreparation = { firstKeptEntryId: lastEntryId, messagesToSummarize: [ - { role: "user", content: [{ type: "text", text: UNRENDERABLE_SNAPCOMPACT_TEXT.repeat(100) }], timestamp: 1 }, + { + role: "user", + content: [{ type: "text", text: UNRENDERABLE_SNAPCOMPACT_TEXT.repeat(100) }], + timestamp: 1, + }, ], turnPrefixMessages: [], recentMessages: [], From 7cab14d963941a45fca3065c9c04d5cd34f29c68 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 30 Jun 2026 02:30:40 +0000 Subject: [PATCH 3/3] fix(catalog): gated kimi K2.7 Code thinking-required policy on native Moonshot/Kimi-code host Reviewer flagged that the omit/forced-tool gate matched every public id, regressing Fireworks and OpenRouter Kimi K2.7 Code (non-zai dialects). Added Fireworks + OpenRouter regression coverage. --- .../__tests__/kimi-code-thinking.test.ts | 11 +++++++++++ packages/catalog/src/compat/anthropic.ts | 7 ++++--- packages/catalog/src/compat/openai.ts | 15 +++++++++++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts index 4dcab4218..b76cbd6cb 100644 --- a/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts +++ b/packages/ai/src/providers/__tests__/kimi-code-thinking.test.ts @@ -86,6 +86,17 @@ describe("Kimi K2.7 Code thinking policy", () => { } }); + it("keeps the openai disable shape for non-native Kimi K2.7 Code aliases", () => { + for (const { provider, id } of [ + { provider: "fireworks", id: "kimi-k2.7-code" }, + { provider: "openrouter", id: "moonshotai/kimi-k2.7-code" }, + ] as const) { + const model = getBundledModel<"openai-completions">(provider, id); + expect(model.compat.supportsForcedToolChoice).toBe(true); + expect(model.compat.reasoningDisableMode).not.toBe("omit"); + } + }); + it("keeps explicit disabled thinking for Kimi K2.6", () => { const model = getBundledModel<"openai-completions">("moonshot", "kimi-k2.6"); const policy = resolveOpenAICompatPolicy(model, { diff --git a/packages/catalog/src/compat/anthropic.ts b/packages/catalog/src/compat/anthropic.ts index 876f11f3c..cdff05ec2 100644 --- a/packages/catalog/src/compat/anthropic.ts +++ b/packages/catalog/src/compat/anthropic.ts @@ -28,11 +28,12 @@ export function isOfficialAnthropicApiUrl(baseUrl?: string): boolean { return lower === OFFICIAL_ANTHROPIC_URL || lower.startsWith(`${OFFICIAL_ANTHROPIC_URL}/`); } +/** Mirrors `compat/openai.ts`; native-only host gating is the caller's responsibility. */ const KIMI_K27_CODE_MODEL_PATTERN = /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i; -function requiresKimiK27CodeEnabledThinking(spec: ModelSpec<"anthropic-messages">): boolean { +function matchesKimiK27CodeFamily(spec: ModelSpec<"anthropic-messages">): boolean { if (KIMI_K27_CODE_MODEL_PATTERN.test(spec.id)) return true; - return spec.provider === "kimi-code" && spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); + return spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); } /** Build the resolved anthropic-messages compat record for a model spec. */ @@ -47,7 +48,7 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res // doesn't whitelist the `fine-grained-tool-streaming-2025-05-14` beta either // (issue #2558), so eager tool-input streaming is unavailable on this host. const isCopilot = modelMatchesHost(spec, "githubCopilot"); - const requiresThinkingEnabled = requiresKimiK27CodeEnabledThinking(spec); + const requiresThinkingEnabled = modelMatchesHost(spec, "moonshotNative") && matchesKimiK27CodeFamily(spec); const compat: ResolvedAnthropicCompat = { officialEndpoint: official, disableStrictTools: false, diff --git a/packages/catalog/src/compat/openai.ts b/packages/catalog/src/compat/openai.ts index c3ae7e2d6..d0ca9447e 100644 --- a/packages/catalog/src/compat/openai.ts +++ b/packages/catalog/src/compat/openai.ts @@ -39,12 +39,19 @@ const GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS = 600_000; const DEEPSEEK_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000; /** Kimi K2.6 can spend several minutes reasoning before the first visible token. */ const KIMI_K26_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000; -/** Native Kimi K2.7 Code requires thinking; sending `thinking: { type: "disabled" }` 400s. */ +/** + * Native Kimi K2.7 Code requires `thinking.type: "enabled"` and rejects + * disabled thinking. Match the public id, its Fast variant, and the + * `kimi-code/kimi-for-coding` alias (which keeps the family name). + * Caller-disabled requests on non-native dialects (Fireworks `openai`, + * OpenRouter `openrouter`, …) MUST keep their per-dialect disable shape — + * gating on `isMoonshotKimi` is the caller's responsibility. + */ const KIMI_K27_CODE_MODEL_PATTERN = /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i; -function requiresKimiK27CodeEnabledThinking(spec: ModelSpec<"openai-completions">): boolean { +function matchesKimiK27CodeFamily(spec: ModelSpec<"openai-completions">): boolean { if (KIMI_K27_CODE_MODEL_PATTERN.test(spec.id)) return true; - return spec.provider === "kimi-code" && spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); + return spec.id === "kimi-for-coding" && /k2\.?7 code/i.test(spec.name ?? ""); } /** Xiaomi MiMo Pro on api.xiaomimimo.com can stall ~2min before the first event (issue #1770). */ const XIAOMI_MIMO_STREAM_IDLE_TIMEOUT_MS = 300_000; @@ -238,7 +245,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv const isKimiModel = isKimiModelId(spec.id); const isMoonshotNative = modelMatchesHost(hostModel, "moonshotNative"); const isMoonshotKimi = isKimiModel && isMoonshotNative; - const requiresEnabledThinking = requiresKimiK27CodeEnabledThinking(spec); + const requiresEnabledThinking = isMoonshotKimi && matchesKimiK27CodeFamily(spec); const usesMoonshotKimiPreservedThinking = isMoonshotKimi && isKimiK26ModelId(spec.id); const isAnthropicModel = modelMatchesHost(hostModel, "anthropic") || isClaudeModelId(spec.id) || isAnthropicNamespacedModelId(spec.id);