fix(ai): minimax thinking

MiniMax descriptors carried thinkingFormat: "zai", which made
openai-completions emit thinking: { type: "enabled" } in plan mode.
MiniMax's OpenAI-compatible endpoint rejects that field with
`400 invalid params, invalid chat setting (2013)`. Drop thinkingFormat
from both minimax-coding-plan descriptors and add
supportsReasoningEffort: false (MiniMax also ignores reasoning_effort).
Normalize bundled minimax-code/minimax-code-cn entries inside
applyGeneratedModelPolicy so a regenerated models.json cannot
reintroduce the bad flag.

Fixes #955
This commit is contained in:
can1357
2026-05-09 04:07:39 +02:00
parent 05274585d8
commit 4e8d69076d
4 changed files with 79 additions and 18 deletions
+10
View File
@@ -314,6 +314,16 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
model.maxTokens = copilotLimits.maxTokens;
}
if (model.api === "openai-completions" && (model.provider === "minimax-code" || model.provider === "minimax-code-cn")) {
model.compat = {
...model.compat,
supportsStore: false,
supportsDeveloperRole: false,
supportsReasoningEffort: false,
reasoningContentField: "reasoning_content",
};
delete model.compat.thinkingFormat;
}
const parsedModel = parseKnownModel(model.id);
const applyPatchToolType = inferGeneratedApplyPatchToolType(model, parsedModel);
if (applyPatchToolType) {
+16 -16
View File
@@ -18451,7 +18451,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18481,7 +18481,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18508,7 +18508,7 @@
},
"compat": {
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"contextWindow": 1000000,
@@ -18540,7 +18540,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18570,7 +18570,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18597,7 +18597,7 @@
},
"compat": {
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"contextWindow": 204800,
@@ -18629,7 +18629,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18659,7 +18659,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18691,7 +18691,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18721,7 +18721,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18748,7 +18748,7 @@
},
"compat": {
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"contextWindow": 1000000,
@@ -18780,7 +18780,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18810,7 +18810,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18837,7 +18837,7 @@
},
"compat": {
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"contextWindow": 204800,
@@ -18869,7 +18869,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -18899,7 +18899,7 @@
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"thinkingFormat": "zai",
"supportsReasoningEffort": false,
"reasoningContentField": "reasoning_content"
},
"thinking": {
@@ -2112,7 +2112,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDe
compat: {
supportsStore: false,
supportsDeveloperRole: false,
thinkingFormat: "zai",
supportsReasoningEffort: false,
reasoningContentField: "reasoning_content",
},
}),
@@ -2120,7 +2120,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDe
compat: {
supportsStore: false,
supportsDeveloperRole: false,
thinkingFormat: "zai",
supportsReasoningEffort: false,
reasoningContentField: "reasoning_content",
},
}),
+51
View File
@@ -0,0 +1,51 @@
import { afterEach, describe, expect, it } from "bun:test";
import { getBundledModel } from "@oh-my-pi/pi-ai/models";
import { streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
import type { Context, Model } from "@oh-my-pi/pi-ai/types";
const originalFetch = global.fetch;
afterEach(() => {
global.fetch = originalFetch;
});
const context: Context = {
systemPrompt: ["stable instructions", "cacheable policy"],
messages: [{ role: "user", content: "hello", timestamp: Date.now() }],
};
function abortedSignal(): AbortSignal {
const controller = new AbortController();
controller.abort();
return controller.signal;
}
async function capturePayload(model: Model<"openai-completions">): Promise<Record<string, unknown>> {
const { promise, resolve } = Promise.withResolvers<Record<string, unknown>>();
streamOpenAICompletions(model, context, {
apiKey: "test-key",
signal: abortedSignal(),
reasoning: "high",
onPayload: payload => resolve(payload as Record<string, unknown>),
});
return await promise;
}
describe("issue #955 — MiniMax coding-plan plan mode payload", () => {
it.each([
["minimax-code", "MiniMax-M2.5"],
["minimax-code-cn", "MiniMax-M2.5"],
] as const)("omits unsupported thinking fields for %s/%s", async (provider, modelId) => {
const model = getBundledModel(provider, modelId) as Model<"openai-completions">;
const body = await capturePayload(model);
expect(body.model).toBe(modelId);
expect(body.messages).toEqual([
{ role: "system", content: "stable instructions\n\ncacheable policy" },
{ role: "user", content: "hello" },
]);
expect(body.thinking).toBeUndefined();
expect(body.reasoning).toBeUndefined();
expect(body.reasoning_effort).toBeUndefined();
});
});