From 77ca68e1f0ce7f2bca24ac0450602fcebf7d162f Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Sat, 19 Sep 2026 22:46:59 +0800 Subject: [PATCH 1/3] fix: preserve system prompts for Kimi Coding --- .../resolution/local-model-resolver.test.ts | 35 +++++++++++++++++-- third_party/pi-mono/MINIMAX_CHANGES.md | 8 +++++ .../ai/src/providers/openai-completions.ts | 3 +- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts index 1187b5710..15c483616 100644 --- a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts +++ b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts @@ -1384,19 +1384,22 @@ describe('LocalModelResolver custom provider compat overrides', () => { }); // The incident was a wire-level symptom: pi chooses the system prompt role from the - // resolved compat, so these two cases pin the request pi would actually send. + // resolved compat, so these cases pin the request pi would actually send. const reasoningModelConfig: LocalModelConfig = { reasoning: true, thinking: { effortOptions: ['low', 'light', 'max'] }, }; - const systemPromptRoleFor = async (compat: LocalModelConfig['compat']) => { + const systemPromptRoleFor = async ( + compat: LocalModelConfig['compat'], + baseURL = 'https://gateway.example/v1', + ) => { const resolver = new LocalModelResolver({ byokConfigGetter: () => ({ custom_provider: { gateway: { api: 'openai-completions', - options: { apiKey: 'gateway-key', baseURL: 'https://gateway.example/v1' }, + options: { apiKey: 'gateway-key', baseURL }, models: { 'kimi-k2-thinking': { ...reasoningModelConfig, compat } }, }, }, @@ -1443,4 +1446,30 @@ describe('LocalModelResolver custom provider compat overrides', () => { it('still sends `developer` when the gateway declares nothing, reproducing the incident', async () => { expect(await systemPromptRoleFor(undefined)).toBe('developer'); }); + + it.each([ + 'https://api.kimi.com/coding/v1', + 'https://api.kimi.ai/coding/v1', + ])('keeps the system role for a thinking model at %s', async (baseURL) => { + expect(await systemPromptRoleFor(undefined, baseURL)).toBe('system'); + }); + + it.each([ + ['https://api.openai.com/v1', 'developer'], + ['https://api.moonshot.cn/v1', 'system'], + ['https://api.moonshot.ai/v1', 'system'], + ['https://api.kimi.com.example/v1', 'developer'], + ['https://gateway.example/api.kimi.com/coding/v1', 'developer'], + ])('preserves the system prompt role for %s', async (baseURL, role) => { + expect(await systemPromptRoleFor(undefined, baseURL)).toBe(role); + }); + + it('honors an explicit developer-role override on a Kimi Coding endpoint', async () => { + expect( + await systemPromptRoleFor( + { supportsDeveloperRole: true }, + 'https://api.kimi.com/coding/v1', + ), + ).toBe('developer'); + }); }); diff --git a/third_party/pi-mono/MINIMAX_CHANGES.md b/third_party/pi-mono/MINIMAX_CHANGES.md index 02ca14e7b..1fb61c3f4 100644 --- a/third_party/pi-mono/MINIMAX_CHANGES.md +++ b/third_party/pi-mono/MINIMAX_CHANGES.md @@ -13,6 +13,14 @@ This directory vendors `pi-mono` as source so MiniMax can patch, validate, and s No upstream source files are changed in the baseline import. +### 2026-09-19 — preserve the system role for Kimi Coding + +- Reason: Kimi Coding endpoints were treated as supporting the `developer` role, causing thinking-enabled OpenAI-compatible conversations to send an unsupported system-prompt role. +- Affected package: `packages/ai` (`@earendil-works/pi-ai`), OpenAI Completions compatibility detection. +- Change: default to the `system` role for `api.kimi.com` and `api.kimi.ai`. Preserve thinking, other request options, explicit compatibility overrides, and other providers. +- Upstream PR: not opened. +- Validation: offline request-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`. Live Kimi Coding validation requires a Coding Plan key and remains untested. + ### 2026-08-31 — Windows PowerShell ConstrainedLanguage compatibility - Reason: the Windows PowerShell 5.1 stdin wrapper called `Parser.ParseInput`, `ScriptBlock.Create`, and other restricted .NET APIs before user commands. Under enterprise App Control / AppLocker `ConstrainedLanguage`, the wrapper therefore failed before commands such as Python could run. diff --git a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts index 20f3b9711..26026fd1b 100644 --- a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts +++ b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts @@ -1090,6 +1090,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet const isTogether = provider === "together" || baseUrl.includes("api.together.ai") || baseUrl.includes("api.together.xyz"); const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot."); + const isKimiCoding = /^https?:\/\/api\.kimi\.(?:com|ai)(?::\d+)?(?:\/|$)/i.test(baseUrl); const isOpenRouter = provider === "openrouter" || baseUrl.includes("openrouter.ai"); const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com"); const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com"); @@ -1124,7 +1125,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet return { supportsStore: !isNonStandard, - supportsDeveloperRole: isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter), + supportsDeveloperRole: !isKimiCoding && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)), supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing, supportsUsageInStreaming: true, From d8d60619426af90a5563fc11494ffe10e28b9ea1 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Sat, 19 Sep 2026 23:09:21 +0800 Subject: [PATCH 2/3] fix: preserve system prompts for DashScope --- .../resolution/local-model-resolver.test.ts | 12 ++++++++++-- third_party/pi-mono/MINIMAX_CHANGES.md | 8 ++++---- .../packages/ai/src/providers/openai-completions.ts | 4 +++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts index 15c483616..cd6db63ee 100644 --- a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts +++ b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts @@ -1450,6 +1450,9 @@ describe('LocalModelResolver custom provider compat overrides', () => { it.each([ 'https://api.kimi.com/coding/v1', 'https://api.kimi.ai/coding/v1', + 'https://dashscope.aliyuncs.com/compatible-mode/v1', + 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', + 'https://dashscope-us.aliyuncs.com/compatible-mode/v1', ])('keeps the system role for a thinking model at %s', async (baseURL) => { expect(await systemPromptRoleFor(undefined, baseURL)).toBe('system'); }); @@ -1460,15 +1463,20 @@ describe('LocalModelResolver custom provider compat overrides', () => { ['https://api.moonshot.ai/v1', 'system'], ['https://api.kimi.com.example/v1', 'developer'], ['https://gateway.example/api.kimi.com/coding/v1', 'developer'], + ['https://dashscope.aliyuncs.com.example/v1', 'developer'], + ['https://gateway.example/dashscope.aliyuncs.com/v1', 'developer'], ])('preserves the system prompt role for %s', async (baseURL, role) => { expect(await systemPromptRoleFor(undefined, baseURL)).toBe(role); }); - it('honors an explicit developer-role override on a Kimi Coding endpoint', async () => { + it.each([ + 'https://api.kimi.com/coding/v1', + 'https://dashscope.aliyuncs.com/compatible-mode/v1', + ])('honors an explicit developer-role override on %s', async (baseURL) => { expect( await systemPromptRoleFor( { supportsDeveloperRole: true }, - 'https://api.kimi.com/coding/v1', + baseURL, ), ).toBe('developer'); }); diff --git a/third_party/pi-mono/MINIMAX_CHANGES.md b/third_party/pi-mono/MINIMAX_CHANGES.md index 1fb61c3f4..516974d0d 100644 --- a/third_party/pi-mono/MINIMAX_CHANGES.md +++ b/third_party/pi-mono/MINIMAX_CHANGES.md @@ -13,13 +13,13 @@ This directory vendors `pi-mono` as source so MiniMax can patch, validate, and s No upstream source files are changed in the baseline import. -### 2026-09-19 — preserve the system role for Kimi Coding +### 2026-09-19 — preserve the system role for Kimi Coding and DashScope -- Reason: Kimi Coding endpoints were treated as supporting the `developer` role, causing thinking-enabled OpenAI-compatible conversations to send an unsupported system-prompt role. +- Reason: Kimi Coding and DashScope endpoints were treated as supporting the `developer` role, causing thinking-enabled OpenAI-compatible conversations to send an unsupported system-prompt role. - Affected package: `packages/ai` (`@earendil-works/pi-ai`), OpenAI Completions compatibility detection. -- Change: default to the `system` role for `api.kimi.com` and `api.kimi.ai`. Preserve thinking, other request options, explicit compatibility overrides, and other providers. +- Change: default to the `system` role for `api.kimi.com`, `api.kimi.ai`, and the standard DashScope regional hosts (`dashscope`, `dashscope-intl`, and `dashscope-us` under `aliyuncs.com`). Preserve thinking, other request options, explicit compatibility overrides, and other providers. - Upstream PR: not opened. -- Validation: offline request-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`. Live Kimi Coding validation requires a Coding Plan key and remains untested. +- Validation: offline request-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`. A minimal live `qwen3.5-plus` role comparison on the China DashScope endpoint returned HTTP 200 for `system` and HTTP 400 for `developer`. Live Kimi Coding and other DashScope regions remain untested; this does not establish full CLI acceptance. ### 2026-08-31 — Windows PowerShell ConstrainedLanguage compatibility diff --git a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts index 26026fd1b..cda65b5fa 100644 --- a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts +++ b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts @@ -1091,6 +1091,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet provider === "together" || baseUrl.includes("api.together.ai") || baseUrl.includes("api.together.xyz"); const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot."); const isKimiCoding = /^https?:\/\/api\.kimi\.(?:com|ai)(?::\d+)?(?:\/|$)/i.test(baseUrl); + const isDashScope = /^https?:\/\/dashscope(?:-intl|-us)?\.aliyuncs\.com(?::\d+)?(?:\/|$)/i.test(baseUrl); const isOpenRouter = provider === "openrouter" || baseUrl.includes("openrouter.ai"); const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com"); const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com"); @@ -1125,7 +1126,8 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet return { supportsStore: !isNonStandard, - supportsDeveloperRole: !isKimiCoding && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)), + supportsDeveloperRole: + !isKimiCoding && !isDashScope && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)), supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing, supportsUsageInStreaming: true, From e0193bdecd57ac59e22595995c9ccf9c04db4dcb Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Sat, 19 Sep 2026 23:14:32 +0800 Subject: [PATCH 3/3] Revert "fix: preserve system prompts for DashScope" This reverts commit d8d60619426af90a5563fc11494ffe10e28b9ea1. --- .../resolution/local-model-resolver.test.ts | 12 ++---------- third_party/pi-mono/MINIMAX_CHANGES.md | 8 ++++---- .../packages/ai/src/providers/openai-completions.ts | 4 +--- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts index cd6db63ee..15c483616 100644 --- a/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts +++ b/packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts @@ -1450,9 +1450,6 @@ describe('LocalModelResolver custom provider compat overrides', () => { it.each([ 'https://api.kimi.com/coding/v1', 'https://api.kimi.ai/coding/v1', - 'https://dashscope.aliyuncs.com/compatible-mode/v1', - 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', - 'https://dashscope-us.aliyuncs.com/compatible-mode/v1', ])('keeps the system role for a thinking model at %s', async (baseURL) => { expect(await systemPromptRoleFor(undefined, baseURL)).toBe('system'); }); @@ -1463,20 +1460,15 @@ describe('LocalModelResolver custom provider compat overrides', () => { ['https://api.moonshot.ai/v1', 'system'], ['https://api.kimi.com.example/v1', 'developer'], ['https://gateway.example/api.kimi.com/coding/v1', 'developer'], - ['https://dashscope.aliyuncs.com.example/v1', 'developer'], - ['https://gateway.example/dashscope.aliyuncs.com/v1', 'developer'], ])('preserves the system prompt role for %s', async (baseURL, role) => { expect(await systemPromptRoleFor(undefined, baseURL)).toBe(role); }); - it.each([ - 'https://api.kimi.com/coding/v1', - 'https://dashscope.aliyuncs.com/compatible-mode/v1', - ])('honors an explicit developer-role override on %s', async (baseURL) => { + it('honors an explicit developer-role override on a Kimi Coding endpoint', async () => { expect( await systemPromptRoleFor( { supportsDeveloperRole: true }, - baseURL, + 'https://api.kimi.com/coding/v1', ), ).toBe('developer'); }); diff --git a/third_party/pi-mono/MINIMAX_CHANGES.md b/third_party/pi-mono/MINIMAX_CHANGES.md index 516974d0d..1fb61c3f4 100644 --- a/third_party/pi-mono/MINIMAX_CHANGES.md +++ b/third_party/pi-mono/MINIMAX_CHANGES.md @@ -13,13 +13,13 @@ This directory vendors `pi-mono` as source so MiniMax can patch, validate, and s No upstream source files are changed in the baseline import. -### 2026-09-19 — preserve the system role for Kimi Coding and DashScope +### 2026-09-19 — preserve the system role for Kimi Coding -- Reason: Kimi Coding and DashScope endpoints were treated as supporting the `developer` role, causing thinking-enabled OpenAI-compatible conversations to send an unsupported system-prompt role. +- Reason: Kimi Coding endpoints were treated as supporting the `developer` role, causing thinking-enabled OpenAI-compatible conversations to send an unsupported system-prompt role. - Affected package: `packages/ai` (`@earendil-works/pi-ai`), OpenAI Completions compatibility detection. -- Change: default to the `system` role for `api.kimi.com`, `api.kimi.ai`, and the standard DashScope regional hosts (`dashscope`, `dashscope-intl`, and `dashscope-us` under `aliyuncs.com`). Preserve thinking, other request options, explicit compatibility overrides, and other providers. +- Change: default to the `system` role for `api.kimi.com` and `api.kimi.ai`. Preserve thinking, other request options, explicit compatibility overrides, and other providers. - Upstream PR: not opened. -- Validation: offline request-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`. A minimal live `qwen3.5-plus` role comparison on the China DashScope endpoint returned HTTP 200 for `system` and HTTP 400 for `developer`. Live Kimi Coding and other DashScope regions remain untested; this does not establish full CLI acceptance. +- Validation: offline request-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`. Live Kimi Coding validation requires a Coding Plan key and remains untested. ### 2026-08-31 — Windows PowerShell ConstrainedLanguage compatibility diff --git a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts index cda65b5fa..26026fd1b 100644 --- a/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts +++ b/third_party/pi-mono/packages/ai/src/providers/openai-completions.ts @@ -1091,7 +1091,6 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet provider === "together" || baseUrl.includes("api.together.ai") || baseUrl.includes("api.together.xyz"); const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot."); const isKimiCoding = /^https?:\/\/api\.kimi\.(?:com|ai)(?::\d+)?(?:\/|$)/i.test(baseUrl); - const isDashScope = /^https?:\/\/dashscope(?:-intl|-us)?\.aliyuncs\.com(?::\d+)?(?:\/|$)/i.test(baseUrl); const isOpenRouter = provider === "openrouter" || baseUrl.includes("openrouter.ai"); const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com"); const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com"); @@ -1126,8 +1125,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet return { supportsStore: !isNonStandard, - supportsDeveloperRole: - !isKimiCoding && !isDashScope && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)), + supportsDeveloperRole: !isKimiCoding && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)), supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing, supportsUsageInStreaming: true,