MiniMax: add M2.7 models and update default to M2.7 (#49691)

* MiniMax: add M2.7 models and update default to M2.7

- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to provider catalog and model definitions
- Update default model from MiniMax-M2.5 to MiniMax-M2.7 across onboard, portal, and provider configs
- Update isModernMiniMaxModel to recognize M2.7 prefix
- Update all test fixtures to reflect M2.7 as default

Made-with: Cursor

* MiniMax: add extension test for model definitions

* update 2.7

* feat: add MiniMax M2.7 models and update default (#49691) (thanks @liyuan97)

---------

Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
This commit is contained in:
liyuan97 2026-03-19 00:24:37 +08:00 committed by GitHub
parent 823a09acbe
commit b64f4e313d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 148 additions and 75 deletions

View File

@ -42,6 +42,7 @@ Docs: https://docs.openclaw.ai
- Control UI/appearance: unify theme border radii across Claw, Knot, and Dash, and add a Roundness slider to the Appearance settings so users can adjust corner radius from sharp to fully rounded. Thanks @BunsDev.
- Control UI/chat: add an expand-to-canvas button on assistant chat bubbles and in-app session navigation from Sessions and Cron views. Thanks @BunsDev.
- Plugins/context engines: expose `delegateCompactionToRuntime(...)` on the public plugin SDK, refactor the legacy engine to use the shared helper, and clarify `ownsCompaction` delegation semantics for non-owning engines. (#49061) Thanks @jalehman.
- Plugins/MiniMax: add MiniMax-M2.7 and MiniMax-M2.7-highspeed models and update the default model from M2.5 to M2.7. (#49691) Thanks @liyuan97.
### Fixes

View File

@ -23,7 +23,7 @@ import { buildMinimaxPortalProvider, buildMinimaxProvider } from "./provider-cat
const API_PROVIDER_ID = "minimax";
const PORTAL_PROVIDER_ID = "minimax-portal";
const PROVIDER_LABEL = "MiniMax";
const DEFAULT_MODEL = "MiniMax-M2.5";
const DEFAULT_MODEL = "MiniMax-M2.7";
const DEFAULT_BASE_URL_CN = "https://api.minimaxi.com/anthropic";
const DEFAULT_BASE_URL_GLOBAL = "https://api.minimax.io/anthropic";
@ -40,7 +40,8 @@ function portalModelRef(modelId: string): string {
}
function isModernMiniMaxModel(modelId: string): boolean {
return modelId.trim().toLowerCase().startsWith("minimax-m2.5");
const lower = modelId.trim().toLowerCase();
return lower.startsWith("minimax-m2.7") || lower.startsWith("minimax-m2.5");
}
function buildPortalProviderCatalog(params: { baseUrl: string; apiKey: string }) {
@ -129,6 +130,10 @@ function createOAuthHandler(region: MiniMaxRegion) {
agents: {
defaults: {
models: {
[portalModelRef("MiniMax-M2.7")]: { alias: "minimax-m2.7" },
[portalModelRef("MiniMax-M2.7-highspeed")]: {
alias: "minimax-m2.7-highspeed",
},
[portalModelRef("MiniMax-M2.5")]: { alias: "minimax-m2.5" },
[portalModelRef("MiniMax-M2.5-highspeed")]: {
alias: "minimax-m2.5-highspeed",
@ -190,7 +195,7 @@ export default definePluginEntry({
choiceHint: "Global endpoint - api.minimax.io",
groupId: "minimax",
groupLabel: "MiniMax",
groupHint: "M2.5 (recommended)",
groupHint: "M2.7 (recommended)",
},
}),
createProviderApiKeyAuthMethod({
@ -214,7 +219,7 @@ export default definePluginEntry({
choiceHint: "CN endpoint - api.minimaxi.com",
groupId: "minimax",
groupLabel: "MiniMax",
groupHint: "M2.5 (recommended)",
groupHint: "M2.7 (recommended)",
},
}),
],
@ -253,7 +258,7 @@ export default definePluginEntry({
choiceHint: "Global endpoint - api.minimax.io",
groupId: "minimax",
groupLabel: "MiniMax",
groupHint: "M2.5 (recommended)",
groupHint: "M2.7 (recommended)",
},
run: createOAuthHandler("global"),
},
@ -268,7 +273,7 @@ export default definePluginEntry({
choiceHint: "CN endpoint - api.minimaxi.com",
groupId: "minimax",
groupLabel: "MiniMax",
groupHint: "M2.5 (recommended)",
groupHint: "M2.7 (recommended)",
},
run: createOAuthHandler("cn"),
},

View File

@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import {
buildMinimaxApiModelDefinition,
buildMinimaxModelDefinition,
DEFAULT_MINIMAX_CONTEXT_WINDOW,
DEFAULT_MINIMAX_MAX_TOKENS,
MINIMAX_API_COST,
MINIMAX_HOSTED_MODEL_ID,
} from "./model-definitions.js";
describe("minimax model definitions", () => {
it("uses M2.7 as default hosted model", () => {
expect(MINIMAX_HOSTED_MODEL_ID).toBe("MiniMax-M2.7");
});
it("builds catalog model with name and reasoning from catalog", () => {
const model = buildMinimaxModelDefinition({
id: "MiniMax-M2.7",
cost: MINIMAX_API_COST,
contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
});
expect(model).toMatchObject({
id: "MiniMax-M2.7",
name: "MiniMax M2.7",
reasoning: true,
});
});
it("builds API model definition with standard cost", () => {
const model = buildMinimaxApiModelDefinition("MiniMax-M2.7");
expect(model.cost).toEqual(MINIMAX_API_COST);
expect(model.contextWindow).toBe(DEFAULT_MINIMAX_CONTEXT_WINDOW);
expect(model.maxTokens).toBe(DEFAULT_MINIMAX_MAX_TOKENS);
});
it("falls back to generated name for unknown model id", () => {
const model = buildMinimaxApiModelDefinition("MiniMax-Future");
expect(model.name).toBe("MiniMax MiniMax-Future");
expect(model.reasoning).toBe(false);
});
});

View File

@ -3,7 +3,7 @@ import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-models"
export const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
export const MINIMAX_API_BASE_URL = "https://api.minimax.io/anthropic";
export const MINIMAX_CN_API_BASE_URL = "https://api.minimaxi.com/anthropic";
export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.5";
export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.7";
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
export const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000;
export const DEFAULT_MINIMAX_MAX_TOKENS = 8192;
@ -28,6 +28,8 @@ export const MINIMAX_LM_STUDIO_COST = {
};
const MINIMAX_MODEL_CATALOG = {
"MiniMax-M2.7": { name: "MiniMax M2.7", reasoning: true },
"MiniMax-M2.7-highspeed": { name: "MiniMax M2.7 Highspeed", reasoning: true },
"MiniMax-M2.5": { name: "MiniMax M2.5", reasoning: true },
"MiniMax-M2.5-highspeed": { name: "MiniMax M2.5 Highspeed", reasoning: true },
} as const;

View File

@ -61,7 +61,7 @@ function applyMinimaxApiConfigWithBaseUrl(
export function applyMinimaxApiProviderConfig(
cfg: OpenClawConfig,
modelId: string = "MiniMax-M2.5",
modelId: string = "MiniMax-M2.7",
): OpenClawConfig {
return applyMinimaxApiProviderConfigWithBaseUrl(cfg, {
providerId: "minimax",
@ -72,7 +72,7 @@ export function applyMinimaxApiProviderConfig(
export function applyMinimaxApiConfig(
cfg: OpenClawConfig,
modelId: string = "MiniMax-M2.5",
modelId: string = "MiniMax-M2.7",
): OpenClawConfig {
return applyMinimaxApiConfigWithBaseUrl(cfg, {
providerId: "minimax",
@ -83,7 +83,7 @@ export function applyMinimaxApiConfig(
export function applyMinimaxApiProviderConfigCn(
cfg: OpenClawConfig,
modelId: string = "MiniMax-M2.5",
modelId: string = "MiniMax-M2.7",
): OpenClawConfig {
return applyMinimaxApiProviderConfigWithBaseUrl(cfg, {
providerId: "minimax",
@ -94,7 +94,7 @@ export function applyMinimaxApiProviderConfigCn(
export function applyMinimaxApiConfigCn(
cfg: OpenClawConfig,
modelId: string = "MiniMax-M2.5",
modelId: string = "MiniMax-M2.7",
): OpenClawConfig {
return applyMinimaxApiConfigWithBaseUrl(cfg, {
providerId: "minimax",

View File

@ -14,7 +14,7 @@
"choiceHint": "Global endpoint - api.minimax.io",
"groupId": "minimax",
"groupLabel": "MiniMax",
"groupHint": "M2.5 (recommended)"
"groupHint": "M2.7 (recommended)"
},
{
"provider": "minimax",
@ -24,7 +24,7 @@
"choiceHint": "Global endpoint - api.minimax.io",
"groupId": "minimax",
"groupLabel": "MiniMax",
"groupHint": "M2.5 (recommended)",
"groupHint": "M2.7 (recommended)",
"optionKey": "minimaxApiKey",
"cliFlag": "--minimax-api-key",
"cliOption": "--minimax-api-key <key>",
@ -38,7 +38,7 @@
"choiceHint": "CN endpoint - api.minimaxi.com",
"groupId": "minimax",
"groupLabel": "MiniMax",
"groupHint": "M2.5 (recommended)"
"groupHint": "M2.7 (recommended)"
},
{
"provider": "minimax",
@ -48,7 +48,7 @@
"choiceHint": "CN endpoint - api.minimaxi.com",
"groupId": "minimax",
"groupLabel": "MiniMax",
"groupHint": "M2.5 (recommended)",
"groupHint": "M2.7 (recommended)",
"optionKey": "minimaxApiKey",
"cliFlag": "--minimax-api-key",
"cliOption": "--minimax-api-key <key>",

View File

@ -4,7 +4,7 @@ import type {
} from "openclaw/plugin-sdk/provider-models";
const MINIMAX_PORTAL_BASE_URL = "https://api.minimax.io/anthropic";
export const MINIMAX_DEFAULT_MODEL_ID = "MiniMax-M2.5";
export const MINIMAX_DEFAULT_MODEL_ID = "MiniMax-M2.7";
const MINIMAX_DEFAULT_VISION_MODEL_ID = "MiniMax-VL-01";
const MINIMAX_DEFAULT_CONTEXT_WINDOW = 200000;
const MINIMAX_DEFAULT_MAX_TOKENS = 8192;
@ -50,6 +50,16 @@ function buildMinimaxCatalog(): ModelDefinitionConfig[] {
}),
buildMinimaxTextModel({
id: MINIMAX_DEFAULT_MODEL_ID,
name: "MiniMax M2.7",
reasoning: true,
}),
buildMinimaxTextModel({
id: "MiniMax-M2.7-highspeed",
name: "MiniMax M2.7 Highspeed",
reasoning: true,
}),
buildMinimaxTextModel({
id: "MiniMax-M2.5",
name: "MiniMax M2.5",
reasoning: true,
}),

View File

@ -7,7 +7,7 @@ import {
describe("live model error helpers", () => {
it("detects generic model-not-found messages", () => {
expect(isModelNotFoundErrorMessage('{"code":404,"message":"model not found"}')).toBe(true);
expect(isModelNotFoundErrorMessage("model: MiniMax-M2.5-highspeed not found")).toBe(true);
expect(isModelNotFoundErrorMessage("model: MiniMax-M2.7-highspeed not found")).toBe(true);
expect(isModelNotFoundErrorMessage("request ended without sending any chunks")).toBe(false);
});

View File

@ -4,7 +4,7 @@ import { isTruthyEnvValue } from "../infra/env.js";
const MINIMAX_KEY = process.env.MINIMAX_API_KEY ?? "";
const MINIMAX_BASE_URL = process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/anthropic";
const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.5";
const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.7";
const LIVE = isTruthyEnvValue(process.env.MINIMAX_LIVE_TEST) || isTruthyEnvValue(process.env.LIVE);
const describeLive = LIVE && MINIMAX_KEY ? describe : describe.skip;

View File

@ -368,14 +368,14 @@ describe("isModernModelRef", () => {
expect(isModernModelRef({ provider: "opencode", id: "gemini-3-pro" })).toBe(true);
expect(isModernModelRef({ provider: "opencode-go", id: "kimi-k2.5" })).toBe(true);
expect(isModernModelRef({ provider: "opencode-go", id: "glm-5" })).toBe(true);
expect(isModernModelRef({ provider: "opencode-go", id: "minimax-m2.5" })).toBe(true);
expect(isModernModelRef({ provider: "opencode-go", id: "minimax-m2.7" })).toBe(true);
});
it("excludes provider-declined modern models", () => {
providerRuntimeMocks.resolveProviderModernModelRef.mockImplementation(({ provider, context }) =>
provider === "opencode" && context.modelId === "minimax-m2.5" ? false : undefined,
provider === "opencode" && context.modelId === "minimax-m2.7" ? false : undefined,
);
expect(isModernModelRef({ provider: "opencode", id: "minimax-m2.5" })).toBe(false);
expect(isModernModelRef({ provider: "opencode", id: "minimax-m2.7" })).toBe(false);
});
});

View File

@ -308,8 +308,8 @@ describe("models-config", () => {
api: "anthropic-messages",
models: [
{
id: "MiniMax-M2.5",
name: "MiniMax M2.5",
id: "MiniMax-M2.7",
name: "MiniMax M2.7",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@ -454,7 +454,7 @@ describe("models-config", () => {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "STALE_AGENT_KEY", // pragma: allowlist secret
api: "anthropic-messages",
models: [{ id: "MiniMax-M2.5", name: "MiniMax M2.5", input: ["text"] }],
models: [{ id: "MiniMax-M2.7", name: "MiniMax M2.7", input: ["text"] }],
},
},
});

View File

@ -21,7 +21,7 @@ type ModelsJson = {
};
const MINIMAX_ENV_KEY = "MINIMAX_API_KEY";
const MINIMAX_MODEL_ID = "MiniMax-M2.5";
const MINIMAX_MODEL_ID = "MiniMax-M2.7";
const MINIMAX_TEST_KEY = "sk-minimax-test";
const baseMinimaxProvider = {
@ -50,8 +50,8 @@ async function generateAndReadMinimaxModel(cfg: OpenClawConfig): Promise<ModelEn
}
describe("models-config: explicit reasoning override", () => {
it("preserves user reasoning:false when built-in catalog has reasoning:true (MiniMax-M2.5)", async () => {
// MiniMax-M2.5 has reasoning:true in the built-in catalog.
it("preserves user reasoning:false when built-in catalog has reasoning:true (MiniMax-M2.7)", async () => {
// MiniMax-M2.7 has reasoning:true in the built-in catalog.
// User explicitly sets reasoning:false to avoid message-ordering conflicts.
await withTempHome(async () => {
await withMinimaxApiKey(async () => {
@ -63,7 +63,7 @@ describe("models-config: explicit reasoning override", () => {
models: [
{
id: MINIMAX_MODEL_ID,
name: "MiniMax M2.5",
name: "MiniMax M2.7",
reasoning: false, // explicit override: user wants to disable reasoning
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@ -84,15 +84,15 @@ describe("models-config: explicit reasoning override", () => {
});
});
it("falls back to built-in reasoning:true when user omits the field (MiniMax-M2.5)", async () => {
it("falls back to built-in reasoning:true when user omits the field (MiniMax-M2.7)", async () => {
// When the user does not set reasoning at all, the built-in catalog value
// (true for MiniMax-M2.5) should be used so the model works out of the box.
// (true for MiniMax-M2.7) should be used so the model works out of the box.
await withTempHome(async () => {
await withMinimaxApiKey(async () => {
// Omit 'reasoning' to simulate a user config that doesn't set it.
const modelWithoutReasoning = {
id: MINIMAX_MODEL_ID,
name: "MiniMax M2.5",
name: "MiniMax M2.7",
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1_000_000,

View File

@ -37,11 +37,15 @@ describe("minimax provider catalog", () => {
const providers = await resolveImplicitProvidersForTest({ agentDir });
expect(providers?.minimax?.models?.map((model) => model.id)).toEqual([
"MiniMax-VL-01",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
]);
expect(providers?.["minimax-portal"]?.models?.map((model) => model.id)).toEqual([
"MiniMax-VL-01",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
]);

View File

@ -98,7 +98,7 @@ describe("models-config", () => {
providerKey: "minimax",
expectedBaseUrl: "https://api.minimax.io/anthropic",
expectedApiKeyRef: "MINIMAX_API_KEY", // pragma: allowlist secret
expectedModelIds: ["MiniMax-M2.5", "MiniMax-VL-01"],
expectedModelIds: ["MiniMax-M2.7", "MiniMax-VL-01"],
});
});
});

View File

@ -199,11 +199,11 @@ describe("openclaw-tools: subagents (sessions_spawn model + thinking)", () => {
await expectSpawnUsesConfiguredModel({
config: {
session: { mainKey: "main", scope: "per-sender" },
agents: { defaults: { subagents: { model: "minimax/MiniMax-M2.5" } } },
agents: { defaults: { subagents: { model: "minimax/MiniMax-M2.7" } } },
},
runId: "run-default-model",
callId: "call-default-model",
expectedModel: "minimax/MiniMax-M2.5",
expectedModel: "minimax/MiniMax-M2.7",
});
});
@ -220,7 +220,7 @@ describe("openclaw-tools: subagents (sessions_spawn model + thinking)", () => {
config: {
session: { mainKey: "main", scope: "per-sender" },
agents: {
defaults: { subagents: { model: "minimax/MiniMax-M2.5" } },
defaults: { subagents: { model: "minimax/MiniMax-M2.7" } },
list: [{ id: "research", subagents: { model: "opencode/claude" } }],
},
},
@ -235,7 +235,7 @@ describe("openclaw-tools: subagents (sessions_spawn model + thinking)", () => {
config: {
session: { mainKey: "main", scope: "per-sender" },
agents: {
defaults: { model: { primary: "minimax/MiniMax-M2.5" } },
defaults: { model: { primary: "minimax/MiniMax-M2.7" } },
list: [{ id: "research", model: { primary: "opencode/claude" } }],
},
},

View File

@ -685,7 +685,7 @@ describe("applyExtraParamsToAgent", () => {
agent,
undefined,
"siliconflow",
"Pro/MiniMaxAI/MiniMax-M2.5",
"Pro/MiniMaxAI/MiniMax-M2.7",
undefined,
"off",
);
@ -693,7 +693,7 @@ describe("applyExtraParamsToAgent", () => {
const model = {
api: "openai-completions",
provider: "siliconflow",
id: "Pro/MiniMaxAI/MiniMax-M2.5",
id: "Pro/MiniMaxAI/MiniMax-M2.7",
} as Model<"openai-completions">;
const context: Context = { messages: [] };
void agent.streamFn?.(model, context, {});

View File

@ -142,7 +142,7 @@ function createMinimaxImageConfig(): OpenClawConfig {
return {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.5" },
model: { primary: "minimax/MiniMax-M2.7" },
imageModel: { primary: "minimax/MiniMax-VL-01" },
},
},
@ -272,7 +272,7 @@ describe("image tool implicit imageModel config", () => {
vi.stubEnv("OPENAI_API_KEY", "openai-test");
vi.stubEnv("ANTHROPIC_API_KEY", "anthropic-test");
const cfg: OpenClawConfig = {
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.5" } } },
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.7" } } },
};
expect(resolveImageModelConfigForTool({ cfg, agentDir })).toEqual(
createDefaultImageFallbackExpectation("minimax/MiniMax-VL-01"),
@ -298,7 +298,7 @@ describe("image tool implicit imageModel config", () => {
vi.stubEnv("OPENAI_API_KEY", "openai-test");
vi.stubEnv("ANTHROPIC_API_KEY", "anthropic-test");
const cfg: OpenClawConfig = {
agents: { defaults: { model: { primary: "minimax-portal/MiniMax-M2.5" } } },
agents: { defaults: { model: { primary: "minimax-portal/MiniMax-M2.7" } } },
};
expect(resolveImageModelConfigForTool({ cfg, agentDir })).toEqual(
createDefaultImageFallbackExpectation("minimax-portal/MiniMax-VL-01"),
@ -356,7 +356,7 @@ describe("image tool implicit imageModel config", () => {
const cfg: OpenClawConfig = {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.5" },
model: { primary: "minimax/MiniMax-M2.7" },
imageModel: { primary: "openai/gpt-5-mini" },
},
},
@ -584,7 +584,7 @@ describe("image tool implicit imageModel config", () => {
vi.stubEnv("OPENAI_API_KEY", "openai-test");
const cfg: OpenClawConfig = {
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.5" } } },
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.7" } } },
};
const tool = createRequiredImageTool({ config: cfg, agentDir, sandbox });
@ -651,7 +651,7 @@ describe("image tool implicit imageModel config", () => {
const cfg: OpenClawConfig = {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.5" },
model: { primary: "minimax/MiniMax-M2.7" },
imageModel: { primary: "minimax/MiniMax-VL-01" },
},
},
@ -704,7 +704,7 @@ describe("image tool MiniMax VLM routing", () => {
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-minimax-vlm-"));
vi.stubEnv("MINIMAX_API_KEY", "minimax-test");
const cfg: OpenClawConfig = {
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.5" } } },
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.7" } } },
};
const tool = createRequiredImageTool({ config: cfg, agentDir });
return { fetch, tool };

View File

@ -183,7 +183,7 @@ describe("directive behavior", () => {
primary: "anthropic/claude-opus-4-5",
fallbacks: ["openai/gpt-4.1-mini"],
},
imageModel: { primary: "minimax/MiniMax-M2.5" },
imageModel: { primary: "minimax/MiniMax-M2.7" },
models: undefined,
},
});
@ -206,7 +206,7 @@ describe("directive behavior", () => {
models: {
"anthropic/claude-opus-4-5": {},
"openai/gpt-4.1-mini": {},
"minimax/MiniMax-M2.5": { alias: "minimax" },
"minimax/MiniMax-M2.7": { alias: "minimax" },
},
},
extra: {
@ -216,14 +216,17 @@ describe("directive behavior", () => {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
api: "anthropic-messages",
models: [{ id: "MiniMax-M2.5", name: "MiniMax M2.5" }],
models: [
{ id: "MiniMax-M2.7", name: "MiniMax M2.7" },
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
],
},
},
},
},
});
expect(configOnlyProviderText).toContain("Models (minimax");
expect(configOnlyProviderText).toContain("minimax/MiniMax-M2.5");
expect(configOnlyProviderText).toContain("minimax/MiniMax-M2.7");
const missingAuthText = await runModelDirectiveText(home, "/model list", {
defaults: {

View File

@ -119,9 +119,10 @@ describe("directive behavior", () => {
config: {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.5" },
model: { primary: "minimax/MiniMax-M2.7" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.7": {},
"minimax/MiniMax-M2.5": {},
"minimax/MiniMax-M2.5-highspeed": {},
"lmstudio/minimax-m2.5-gs32": {},
@ -135,7 +136,10 @@ describe("directive behavior", () => {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "sk-test", // pragma: allowlist secret
api: "anthropic-messages",
models: [makeModelDefinition("MiniMax-M2.5", "MiniMax M2.5")],
models: [
makeModelDefinition("MiniMax-M2.7", "MiniMax M2.7"),
makeModelDefinition("MiniMax-M2.5", "MiniMax M2.5"),
],
},
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
@ -153,9 +157,10 @@ describe("directive behavior", () => {
config: {
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M2.5" },
model: { primary: "minimax/MiniMax-M2.7" },
workspace: path.join(home, "openclaw"),
models: {
"minimax/MiniMax-M2.7": {},
"minimax/MiniMax-M2.5": {},
"minimax/MiniMax-M2.5-highspeed": {},
},
@ -169,6 +174,7 @@ describe("directive behavior", () => {
apiKey: "sk-test", // pragma: allowlist secret
api: "anthropic-messages",
models: [
makeModelDefinition("MiniMax-M2.7", "MiniMax M2.7"),
makeModelDefinition("MiniMax-M2.5", "MiniMax M2.5"),
makeModelDefinition("MiniMax-M2.5-highspeed", "MiniMax M2.5 Highspeed"),
],

View File

@ -80,7 +80,7 @@ const modelCatalogMocks = vi.hoisted(() => ({
{ provider: "openai", id: "gpt-4.1-mini", name: "GPT-4.1 mini" },
{ provider: "openai", id: "gpt-5.2", name: "GPT-5.2" },
{ provider: "openai-codex", id: "gpt-5.2", name: "GPT-5.2 (Codex)" },
{ provider: "minimax", id: "MiniMax-M2.5", name: "MiniMax M2.5" },
{ provider: "minimax", id: "MiniMax-M2.7", name: "MiniMax M2.7" },
]),
resetModelCatalogCacheForTest: vi.fn(),
}));

View File

@ -24,7 +24,7 @@ vi.mock("../../agents/session-write-lock.js", () => ({
vi.mock("../../agents/model-catalog.js", () => ({
loadModelCatalog: vi.fn(async () => [
{ provider: "minimax", id: "m2.5", name: "M2.5" },
{ provider: "minimax", id: "m2.7", name: "M2.7" },
{ provider: "openai", id: "gpt-4o-mini", name: "GPT-4o mini" },
]),
}));
@ -1288,7 +1288,7 @@ describe("applyResetModelOverride", () => {
});
expect(sessionEntry.providerOverride).toBe("minimax");
expect(sessionEntry.modelOverride).toBe("m2.5");
expect(sessionEntry.modelOverride).toBe("m2.7");
expect(sessionCtx.BodyStripped).toBe("summarize");
});

View File

@ -1423,7 +1423,7 @@ describe("applyAuthChoice", () => {
profileId: "minimax-portal:default",
baseUrl: "https://api.minimax.io/anthropic",
api: "anthropic-messages",
defaultModel: "minimax-portal/MiniMax-M2.5",
defaultModel: "minimax-portal/MiniMax-M2.7",
apiKey: "minimax-oauth", // pragma: allowlist secret
},
];

View File

@ -88,7 +88,7 @@ function createApplyAuthChoiceConfig(includeMinimaxProvider = false) {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
api: "anthropic-messages",
models: [{ id: "MiniMax-M2.5", name: "MiniMax M2.5" }],
models: [{ id: "MiniMax-M2.7", name: "MiniMax M2.7" }],
},
}
: {}),
@ -127,7 +127,7 @@ describe("promptAuthConfig", () => {
"anthropic/claude-sonnet-4",
]);
expect(result.models?.providers?.minimax?.models?.map((model) => model.id)).toEqual([
"MiniMax-M2.5",
"MiniMax-M2.7",
]);
});

View File

@ -386,8 +386,8 @@ describe("applyMinimaxApiConfig", () => {
});
});
it("keeps reasoning enabled for MiniMax-M2.5", () => {
const cfg = applyMinimaxApiConfig({}, "MiniMax-M2.5");
it("keeps reasoning enabled for MiniMax-M2.7", () => {
const cfg = applyMinimaxApiConfig({}, "MiniMax-M2.7");
expect(cfg.models?.providers?.minimax?.models[0]?.reasoning).toBe(true);
});
@ -397,7 +397,7 @@ describe("applyMinimaxApiConfig", () => {
agents: {
defaults: {
models: {
"minimax/MiniMax-M2.5": {
"minimax/MiniMax-M2.7": {
alias: "MiniMax",
params: { custom: "value" },
},
@ -405,9 +405,9 @@ describe("applyMinimaxApiConfig", () => {
},
},
},
"MiniMax-M2.5",
"MiniMax-M2.7",
);
expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.5"]).toMatchObject({
expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.7"]).toMatchObject({
alias: "Minimax",
params: { custom: "value" },
});
@ -426,7 +426,7 @@ describe("applyMinimaxApiConfig", () => {
expect(cfg.models?.providers?.minimax?.apiKey).toBe("old-key");
expect(cfg.models?.providers?.minimax?.models.map((m) => m.id)).toEqual([
"old-model",
"MiniMax-M2.5",
"MiniMax-M2.7",
]);
});
@ -669,8 +669,8 @@ describe("provider alias defaults", () => {
it("adds expected alias for provider defaults", () => {
const aliasCases = [
{
applyConfig: () => applyMinimaxApiConfig({}, "MiniMax-M2.5"),
modelRef: "minimax/MiniMax-M2.5",
applyConfig: () => applyMinimaxApiConfig({}, "MiniMax-M2.7"),
modelRef: "minimax/MiniMax-M2.7",
alias: "Minimax",
},
{

View File

@ -236,7 +236,7 @@ describe("onboard (non-interactive): provider auth", () => {
expect(cfg.auth?.profiles?.["minimax:global"]?.provider).toBe("minimax");
expect(cfg.auth?.profiles?.["minimax:global"]?.mode).toBe("api_key");
expect(cfg.models?.providers?.minimax?.baseUrl).toBe(MINIMAX_API_BASE_URL);
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.5");
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.7");
await expectApiKeyProfile({
profileId: "minimax:global",
provider: "minimax",
@ -255,7 +255,7 @@ describe("onboard (non-interactive): provider auth", () => {
expect(cfg.auth?.profiles?.["minimax:cn"]?.provider).toBe("minimax");
expect(cfg.auth?.profiles?.["minimax:cn"]?.mode).toBe("api_key");
expect(cfg.models?.providers?.minimax?.baseUrl).toBe(MINIMAX_CN_API_BASE_URL);
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.5");
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.7");
await expectApiKeyProfile({
profileId: "minimax:cn",
provider: "minimax",

View File

@ -131,8 +131,8 @@ describe("config identity defaults", () => {
api: "anthropic-messages",
models: [
{
id: "MiniMax-M2.5",
name: "MiniMax M2.5",
id: "MiniMax-M2.7",
name: "MiniMax M2.7",
reasoning: false,
input: ["text"],
cost: {

View File

@ -415,7 +415,7 @@ describe("resolveSessionModelRef", () => {
test("preserves openrouter provider when model contains vendor prefix", () => {
const cfg = createModelDefaultsConfig({
primary: "openrouter/minimax/minimax-m2.5",
primary: "openrouter/minimax/minimax-m2.7",
});
const resolved = resolveSessionModelRef(cfg, {

View File

@ -458,7 +458,7 @@ describe("provider discovery contract", () => {
authHeader: true,
apiKey: "minimax-key",
models: expect.arrayContaining([
expect.objectContaining({ id: "MiniMax-M2.5" }),
expect.objectContaining({ id: "MiniMax-M2.7" }),
expect.objectContaining({ id: "MiniMax-VL-01" }),
]),
},
@ -499,7 +499,7 @@ describe("provider discovery contract", () => {
api: "anthropic-messages",
authHeader: true,
apiKey: "minimax-oauth",
models: expect.arrayContaining([expect.objectContaining({ id: "MiniMax-M2.5" })]),
models: expect.arrayContaining([expect.objectContaining({ id: "MiniMax-M2.7" })]),
},
});
});

View File

@ -104,7 +104,7 @@ describe("tui session actions", () => {
sessions: [
{
key: "agent:main:main",
model: "Minimax-M2.5",
model: "Minimax-M2.7",
modelProvider: "minimax",
},
],
@ -112,7 +112,7 @@ describe("tui session actions", () => {
await second;
expect(state.sessionInfo.model).toBe("Minimax-M2.5");
expect(state.sessionInfo.model).toBe("Minimax-M2.7");
expect(updateAutocompleteProvider).toHaveBeenCalledTimes(2);
expect(updateFooter).toHaveBeenCalledTimes(2);
expect(requestRender).toHaveBeenCalledTimes(2);