Skip to content

Commit 8a792c5

Browse files
committed
fix: preserve system prompts for Mistral Chat Completions
1 parent a5639bc commit 8a792c5

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

‎packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,19 +1384,22 @@ describe('LocalModelResolver custom provider compat overrides', () => {
13841384
});
13851385

13861386
// The incident was a wire-level symptom: pi chooses the system prompt role from the
1387-
// resolved compat, so these two cases pin the request pi would actually send.
1387+
// resolved compat, so these cases pin the request pi would actually send.
13881388
const reasoningModelConfig: LocalModelConfig = {
13891389
reasoning: true,
13901390
thinking: { effortOptions: ['low', 'light', 'max'] },
13911391
};
13921392

1393-
const systemPromptRoleFor = async (compat: LocalModelConfig['compat']) => {
1393+
const systemPromptRoleFor = async (
1394+
compat: LocalModelConfig['compat'],
1395+
baseURL = 'https://gateway.example/v1',
1396+
) => {
13941397
const resolver = new LocalModelResolver({
13951398
byokConfigGetter: () => ({
13961399
custom_provider: {
13971400
gateway: {
13981401
api: 'openai-completions',
1399-
options: { apiKey: 'gateway-key', baseURL: 'https://gateway.example/v1' },
1402+
options: { apiKey: 'gateway-key', baseURL },
14001403
models: { 'kimi-k2-thinking': { ...reasoningModelConfig, compat } },
14011404
},
14021405
},
@@ -1443,4 +1446,23 @@ describe('LocalModelResolver custom provider compat overrides', () => {
14431446
it('still sends `developer` when the gateway declares nothing, reproducing the incident', async () => {
14441447
expect(await systemPromptRoleFor(undefined)).toBe('developer');
14451448
});
1449+
1450+
it('keeps the system role for a thinking model on the Mistral API', async () => {
1451+
expect(await systemPromptRoleFor(undefined, 'https://api.mistral.ai/v1')).toBe('system');
1452+
});
1453+
1454+
it.each([
1455+
['https://api.openai.com/v1', 'developer'],
1456+
['https://api.deepseek.com/v1', 'system'],
1457+
['https://api.mistral.ai.example/v1', 'developer'],
1458+
['https://gateway.example/api.mistral.ai/v1', 'developer'],
1459+
])('preserves the system prompt role for %s', async (baseURL, role) => {
1460+
expect(await systemPromptRoleFor(undefined, baseURL)).toBe(role);
1461+
});
1462+
1463+
it('honors an explicit developer-role override on the Mistral API', async () => {
1464+
expect(
1465+
await systemPromptRoleFor({ supportsDeveloperRole: true }, 'https://api.mistral.ai/v1'),
1466+
).toBe('developer');
1467+
});
14461468
});

‎third_party/pi-mono/MINIMAX_CHANGES.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ This directory vendors `pi-mono` as source so MiniMax can patch, validate, and s
1313

1414
No upstream source files are changed in the baseline import.
1515

16+
### 2026-09-19 — preserve the system role for Mistral Chat Completions
17+
18+
- Reason: thinking-enabled custom OpenAI-compatible connections to `api.mistral.ai` emitted `developer`, which is absent from the [Mistral Chat Completions message contract](https://docs.mistral.ai/api/endpoint/chat). [OpenClaw's compatibility defaults](https://github.com/openclaw/openclaw/blob/e2bcb1614de060927121bd72de850cee3a08d308/packages/ai/src/transports/openai-completions-compat.ts#L184-L210) also disable this role for the Mistral public endpoint.
19+
- Affected package: `packages/ai` (`@earendil-works/pi-ai`), OpenAI Completions compatibility detection.
20+
- Change: default to `system` only on the exact Mistral API host. Preserve thinking, explicit compatibility overrides, all other request options, and other endpoints. Native Mistral transport is unchanged.
21+
- Upstream PR: not opened.
22+
- Validation: outgoing-payload regressions in `packages/local-runtime-v2/src/service/model-system/resolution/local-model-resolver.test.ts`; the endpoint regression fails before the fix. No live Mistral requests or full CLI acceptance were performed; the decision is based on the official contract and current upstream implementation.
23+
1624
### 2026-08-31 — Windows PowerShell ConstrainedLanguage compatibility
1725

1826
- 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.

‎third_party/pi-mono/packages/ai/src/providers/openai-completions.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
10901090
const isTogether =
10911091
provider === "together" || baseUrl.includes("api.together.ai") || baseUrl.includes("api.together.xyz");
10921092
const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot.");
1093+
const isMistralApi = /^https?:\/\/api\.mistral\.ai(?::\d+)?(?:\/|$)/i.test(baseUrl);
10931094
const isOpenRouter = provider === "openrouter" || baseUrl.includes("openrouter.ai");
10941095
const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com");
10951096
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
@@ -1124,7 +1125,8 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
11241125

11251126
return {
11261127
supportsStore: !isNonStandard,
1127-
supportsDeveloperRole: isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter),
1128+
supportsDeveloperRole:
1129+
!isMistralApi && (isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter)),
11281130
supportsReasoningEffort:
11291131
!isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing,
11301132
supportsUsageInStreaming: true,

0 commit comments

Comments
 (0)