fix(ai): prune deprecated direct minimax models

This commit is contained in:
Mario Zechner
2026-03-25 22:44:17 +01:00
parent 14dedf8a9b
commit 6dc43d6dd1
12 changed files with 175 additions and 236 deletions

View File

@@ -2,6 +2,10 @@
## [Unreleased]
### Changed
- Changed direct `minimax` and `minimax-cn` model catalogs to keep only `MiniMax-M2.7` and `MiniMax-M2.7-highspeed`, and updated MiniMax and abort coverage to current provider model IDs and usage behavior ([#2596](https://github.com/badlogic/pi-mono/pull/2596) by [@liyuan97](https://github.com/liyuan97))
### Fixed
- Fixed GitHub Copilot OpenAI Responses requests to omit the `reasoning` field entirely when no reasoning effort is requested, avoiding `400` errors from Copilot `gpt-5-mini` rejecting `reasoning: { effort: "none" }` during internal summary calls ([#2567](https://github.com/badlogic/pi-mono/issues/2567))

View File

@@ -914,44 +914,26 @@ async function generateModels() {
});
}
const minimaxDirectProviders = ["minimax", "minimax-cn"] as const;
const minimaxAnthropicIds = new Set([
"MiniMax-M2",
"MiniMax-M2.1",
"MiniMax-M2.1-highspeed",
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
]);
const minimaxDirectSupportedIds = new Set(["MiniMax-M2.7", "MiniMax-M2.7-highspeed"]);
for (const candidate of allModels) {
if (
(candidate.provider === "minimax" || candidate.provider === "minimax-cn") &&
minimaxAnthropicIds.has(candidate.id)
minimaxDirectSupportedIds.has(candidate.id)
) {
candidate.contextWindow = 204800;
candidate.maxTokens = 131072;
}
}
for (const provider of minimaxDirectProviders) {
const baseModel = allModels.find((m) => m.provider === provider && m.id === "MiniMax-M2.1");
if (!baseModel) continue;
if (allModels.some((m) => m.provider === provider && m.id === "MiniMax-M2.1-highspeed")) continue;
allModels.push({
...baseModel,
id: "MiniMax-M2.1-highspeed",
name: "MiniMax-M2.1-highspeed",
cost: {
...baseModel.cost,
input: baseModel.cost.input * 2,
output: baseModel.cost.output * 2,
},
contextWindow: 204800,
maxTokens: 131072,
});
for (let i = allModels.length - 1; i >= 0; i--) {
const candidate = allModels[i];
if (
(candidate.provider === "minimax" || candidate.provider === "minimax-cn") &&
!minimaxDirectSupportedIds.has(candidate.id)
) {
allModels.splice(i, 1);
}
}
// OpenAI Codex (ChatGPT OAuth) models

View File

@@ -804,6 +804,23 @@ export const MODELS = {
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"bedrock-converse-stream">,
"minimax.minimax-m2.5": {
id: "minimax.minimax-m2.5",
name: "MiniMax M2.5",
api: "bedrock-converse-stream",
provider: "amazon-bedrock",
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 196608,
maxTokens: 98304,
} satisfies Model<"bedrock-converse-stream">,
"mistral.devstral-2-123b": {
id: "mistral.devstral-2-123b",
name: "Devstral 2 123B",
@@ -1042,6 +1059,23 @@ export const MODELS = {
contextWindow: 128000,
maxTokens: 4096,
} satisfies Model<"bedrock-converse-stream">,
"nvidia.nemotron-super-3-120b": {
id: "nvidia.nemotron-super-3-120b",
name: "NVIDIA Nemotron 3 Super 120B A12B",
api: "bedrock-converse-stream",
provider: "amazon-bedrock",
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
reasoning: true,
input: ["text"],
cost: {
input: 0.15,
output: 0.65,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 131072,
} satisfies Model<"bedrock-converse-stream">,
"openai.gpt-oss-120b-1:0": {
id: "openai.gpt-oss-120b-1:0",
name: "gpt-oss-120b",
@@ -1416,6 +1450,23 @@ export const MODELS = {
contextWindow: 200000,
maxTokens: 131072,
} satisfies Model<"bedrock-converse-stream">,
"zai.glm-5": {
id: "zai.glm-5",
name: "GLM-5",
api: "bedrock-converse-stream",
provider: "amazon-bedrock",
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 202752,
maxTokens: 101376,
} satisfies Model<"bedrock-converse-stream">,
},
"anthropic": {
"claude-3-5-haiku-20241022": {
@@ -4498,91 +4549,6 @@ export const MODELS = {
} satisfies Model<"anthropic-messages">,
},
"minimax": {
"MiniMax-M2": {
id: "MiniMax-M2",
name: "MiniMax-M2",
api: "anthropic-messages",
provider: "minimax",
baseUrl: "https://api.minimax.io/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.1": {
id: "MiniMax-M2.1",
name: "MiniMax-M2.1",
api: "anthropic-messages",
provider: "minimax",
baseUrl: "https://api.minimax.io/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.1-highspeed": {
id: "MiniMax-M2.1-highspeed",
name: "MiniMax-M2.1-highspeed",
api: "anthropic-messages",
provider: "minimax",
baseUrl: "https://api.minimax.io/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.6,
output: 2.4,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.5": {
id: "MiniMax-M2.5",
name: "MiniMax-M2.5",
api: "anthropic-messages",
provider: "minimax",
baseUrl: "https://api.minimax.io/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0.03,
cacheWrite: 0.375,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.5-highspeed": {
id: "MiniMax-M2.5-highspeed",
name: "MiniMax-M2.5-highspeed",
api: "anthropic-messages",
provider: "minimax",
baseUrl: "https://api.minimax.io/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.6,
output: 2.4,
cacheRead: 0.06,
cacheWrite: 0.375,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.7": {
id: "MiniMax-M2.7",
name: "MiniMax-M2.7",
@@ -4619,91 +4585,6 @@ export const MODELS = {
} satisfies Model<"anthropic-messages">,
},
"minimax-cn": {
"MiniMax-M2": {
id: "MiniMax-M2",
name: "MiniMax-M2",
api: "anthropic-messages",
provider: "minimax-cn",
baseUrl: "https://api.minimaxi.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.1": {
id: "MiniMax-M2.1",
name: "MiniMax-M2.1",
api: "anthropic-messages",
provider: "minimax-cn",
baseUrl: "https://api.minimaxi.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.1-highspeed": {
id: "MiniMax-M2.1-highspeed",
name: "MiniMax-M2.1-highspeed",
api: "anthropic-messages",
provider: "minimax-cn",
baseUrl: "https://api.minimaxi.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.6,
output: 2.4,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.5": {
id: "MiniMax-M2.5",
name: "MiniMax-M2.5",
api: "anthropic-messages",
provider: "minimax-cn",
baseUrl: "https://api.minimaxi.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.3,
output: 1.2,
cacheRead: 0.03,
cacheWrite: 0.375,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.5-highspeed": {
id: "MiniMax-M2.5-highspeed",
name: "MiniMax-M2.5-highspeed",
api: "anthropic-messages",
provider: "minimax-cn",
baseUrl: "https://api.minimaxi.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.6,
output: 2.4,
cacheRead: 0.06,
cacheWrite: 0.375,
},
contextWindow: 204800,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.7": {
id: "MiniMax-M2.7",
name: "MiniMax-M2.7",
@@ -8295,13 +8176,13 @@ export const MODELS = {
reasoning: false,
input: ["text"],
cost: {
input: 0.55,
output: 2.2,
input: 0.5700000000000001,
output: 2.3,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 131000,
maxTokens: 4096,
contextWindow: 131072,
maxTokens: 131072,
} satisfies Model<"openai-completions">,
"moonshotai/kimi-k2-0905": {
id: "moonshotai/kimi-k2-0905",
@@ -8363,8 +8244,8 @@ export const MODELS = {
reasoning: false,
input: ["text"],
cost: {
input: 0.27,
output: 1,
input: 0.135,
output: 0.5,
cacheRead: 0,
cacheWrite: 0,
},
@@ -8450,7 +8331,7 @@ export const MODELS = {
cost: {
input: 0.09999999999999999,
output: 0.5,
cacheRead: 0.04,
cacheRead: 0.09999999999999999,
cacheWrite: 0,
},
contextWindow: 262144,
@@ -9542,7 +9423,7 @@ export const MODELS = {
cacheWrite: 0,
},
contextWindow: 32768,
maxTokens: 4096,
maxTokens: 32768,
} satisfies Model<"openai-completions">,
"qwen/qwen-max": {
id: "qwen/qwen-max",
@@ -10205,7 +10086,7 @@ export const MODELS = {
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 4096,
maxTokens: 65536,
} satisfies Model<"openai-completions">,
"qwen/qwen3.5-flash-02-23": {
id: "qwen/qwen3.5-flash-02-23",
@@ -10258,6 +10139,23 @@ export const MODELS = {
contextWindow: 131072,
maxTokens: 131072,
} satisfies Model<"openai-completions">,
"reka/reka-edge": {
id: "reka/reka-edge",
name: "Reka Edge",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: false,
input: ["text", "image"],
cost: {
input: 0.19999999999999998,
output: 0.19999999999999998,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 16384,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"relace/relace-search": {
id: "relace/relace-search",
name: "Relace: Relace Search",
@@ -10794,9 +10692,9 @@ export const MODELS = {
reasoning: true,
input: ["text"],
cost: {
input: 0.96,
output: 3.1999999999999997,
cacheRead: 0.192,
input: 1.2,
output: 4,
cacheRead: 0.24,
cacheWrite: 0,
},
contextWindow: 202752,
@@ -12802,8 +12700,8 @@ export const MODELS = {
reasoning: false,
input: ["text", "image"],
cost: {
input: 1,
output: 1,
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
@@ -12819,8 +12717,8 @@ export const MODELS = {
reasoning: false,
input: ["text", "image"],
cost: {
input: 3,
output: 15,
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
@@ -13014,6 +12912,23 @@ export const MODELS = {
contextWindow: 2000000,
maxTokens: 30000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-multi-agent": {
id: "xai/grok-4.20-multi-agent",
name: "Grok 4.20 Multi-Agent",
api: "anthropic-messages",
provider: "vercel-ai-gateway",
baseUrl: "https://ai-gateway.vercel.sh",
reasoning: true,
input: ["text"],
cost: {
input: 2,
output: 6,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
contextWindow: 2000000,
maxTokens: 2000000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-multi-agent-beta": {
id: "xai/grok-4.20-multi-agent-beta",
name: "Grok 4.20 Multi Agent Beta",
@@ -13031,6 +12946,23 @@ export const MODELS = {
contextWindow: 2000000,
maxTokens: 2000000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-non-reasoning": {
id: "xai/grok-4.20-non-reasoning",
name: "Grok 4.20 Non-Reasoning",
api: "anthropic-messages",
provider: "vercel-ai-gateway",
baseUrl: "https://ai-gateway.vercel.sh",
reasoning: false,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
contextWindow: 2000000,
maxTokens: 2000000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-non-reasoning-beta": {
id: "xai/grok-4.20-non-reasoning-beta",
name: "Grok 4.20 Beta Non-Reasoning",
@@ -13048,6 +12980,23 @@ export const MODELS = {
contextWindow: 2000000,
maxTokens: 2000000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-reasoning": {
id: "xai/grok-4.20-reasoning",
name: "Grok 4.20 Reasoning",
api: "anthropic-messages",
provider: "vercel-ai-gateway",
baseUrl: "https://ai-gateway.vercel.sh",
reasoning: true,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
contextWindow: 2000000,
maxTokens: 2000000,
} satisfies Model<"anthropic-messages">,
"xai/grok-4.20-reasoning-beta": {
id: "xai/grok-4.20-reasoning-beta",
name: "Grok 4.20 Beta Reasoning",

View File

@@ -182,7 +182,7 @@ describe("AI Providers Abort Tests", () => {
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider Abort", () => {
const llm = getModel("minimax", "MiniMax-M2.1");
const llm = getModel("minimax", "MiniMax-M2.7");
it("should abort mid-stream", { retry: 3 }, async () => {
await testAbortSignal(llm);

View File

@@ -436,8 +436,8 @@ describe("Context overflow error handling", () => {
// =============================================================================
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax", () => {
it("MiniMax-M2.1 - should detect overflow via isContextOverflow", async () => {
const model = getModel("minimax", "MiniMax-M2.1");
it("MiniMax-M2.7 - should detect overflow via isContextOverflow", async () => {
const model = getModel("minimax", "MiniMax-M2.7");
const result = await testContextOverflow(model, process.env.MINIMAX_API_KEY!);
logResult(result);

View File

@@ -93,7 +93,7 @@ const PROVIDER_MODEL_PAIRS: ProviderModelPair[] = [
// Mistral
{ provider: "mistral", model: "devstral-medium-latest", label: "mistral-devstral-medium" },
// MiniMax
{ provider: "minimax", model: "MiniMax-M2.1", label: "minimax-m2.1" },
{ provider: "minimax", model: "MiniMax-M2.7", label: "minimax-m2.7" },
// OpenCode Zen
{ provider: "opencode", model: "big-pickle", label: "zen-big-pickle" },
{ provider: "opencode", model: "claude-sonnet-4-5", label: "zen-claude-sonnet-4-5" },

View File

@@ -369,7 +369,7 @@ describe("AI Providers Empty Message Tests", () => {
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider Empty Messages", () => {
const llm = getModel("minimax", "MiniMax-M2.1");
const llm = getModel("minimax", "MiniMax-M2.7");
it("should handle empty content array", { retry: 3, timeout: 30000 }, async () => {
await testEmptyMessage(llm);

View File

@@ -795,8 +795,8 @@ describe("Generate E2E Tests", () => {
});
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider (MiniMax-M2.1 via Anthropic Messages)", () => {
const llm = getModel("minimax", "MiniMax-M2.1");
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider (MiniMax-M2.7 via Anthropic Messages)", () => {
const llm = getModel("minimax", "MiniMax-M2.7");
it("should complete basic text generation", { retry: 3 }, async () => {
await basicTextGeneration(llm);

View File

@@ -52,7 +52,7 @@ async function testTokensOnAbort<TApi extends Api>(llm: Model<TApi>, options: St
// OpenAI providers, OpenAI Codex, Gemini CLI, zai, Amazon Bedrock, and the GPT-OSS model on Antigravity only send usage in the final chunk,
// so when aborted they have no token stats. Anthropic and Google send usage information early in the stream.
// MiniMax reports input tokens but not output tokens when aborted.
// MiniMax and Kimi report input tokens but not output tokens differently on aborted requests.
if (
llm.api === "openai-completions" ||
llm.api === "mistral-conversations" ||
@@ -68,7 +68,11 @@ async function testTokensOnAbort<TApi extends Api>(llm: Model<TApi>, options: St
expect(msg.usage.input).toBe(0);
expect(msg.usage.output).toBe(0);
} else if (llm.provider === "minimax") {
// MiniMax reports input tokens early but output tokens only in final chunk
// MiniMax M2.7 does not report token usage for aborted requests.
expect(msg.usage.input).toBe(0);
expect(msg.usage.output).toBe(0);
} else if (llm.provider === "kimi-coding") {
// Kimi reports input tokens early but output tokens only in the final chunk.
expect(msg.usage.input).toBeGreaterThan(0);
expect(msg.usage.output).toBe(0);
} else {
@@ -106,10 +110,10 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.OPENAI_API_KEY)("OpenAI Responses Provider", () => {
const llm = getModel("openai", "gpt-5-mini");
const llm = getModel("openai", "gpt-5.4-mini");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
await testTokensOnAbort(llm, { reasoningEffort: "low" });
});
});
@@ -124,7 +128,7 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic Provider", () => {
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
const llm = getModel("anthropic", "claude-sonnet-4-6");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
@@ -148,7 +152,7 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.CEREBRAS_API_KEY)("Cerebras Provider", () => {
const llm = getModel("cerebras", "gpt-oss-120b");
const llm = getModel("cerebras", "qwen-3-235b-a22b-instruct-2507");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
@@ -180,7 +184,7 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider", () => {
const llm = getModel("minimax", "MiniMax-M2.1");
const llm = getModel("minimax", "MiniMax-M2.7");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
@@ -188,7 +192,7 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding Provider", () => {
const llm = getModel("kimi-coding", "kimi-k2-thinking");
const llm = getModel("kimi-coding", "k2p5");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
@@ -208,7 +212,7 @@ describe("Token Statistics on Abort", () => {
// =========================================================================
describe("Anthropic OAuth Provider", () => {
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
const llm = getModel("anthropic", "claude-sonnet-4-6");
it.skipIf(!anthropicOAuthToken)(
"should include token stats when aborted mid-stream",
@@ -261,10 +265,10 @@ describe("Token Statistics on Abort", () => {
);
it.skipIf(!antigravityToken)(
"claude-sonnet-4-5 - should include token stats when aborted mid-stream",
"claude-sonnet-4-6 - should include token stats when aborted mid-stream",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("google-antigravity", "claude-sonnet-4-5");
const llm = getModel("google-antigravity", "claude-sonnet-4-6");
await testTokensOnAbort(llm, { apiKey: antigravityToken });
},
);

View File

@@ -193,7 +193,7 @@ describe("Tool Call Without Result Tests", () => {
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider", () => {
const model = getModel("minimax", "MiniMax-M2.1");
const model = getModel("minimax", "MiniMax-M2.7");
it("should filter out tool calls without corresponding tool results", { retry: 3, timeout: 30000 }, async () => {
await testToolCallWithoutResult(model);

View File

@@ -377,10 +377,10 @@ describe("totalTokens field", () => {
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax", () => {
it(
"MiniMax-M2.1 - should return totalTokens equal to sum of components",
"MiniMax-M2.7 - should return totalTokens equal to sum of components",
{ retry: 3, timeout: 60000 },
async () => {
const llm = getModel("minimax", "MiniMax-M2.1");
const llm = getModel("minimax", "MiniMax-M2.7");
console.log(`\nMiniMax / ${llm.id}:`);
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MINIMAX_API_KEY });

View File

@@ -660,7 +660,7 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => {
});
describe.skipIf(!process.env.MINIMAX_API_KEY)("MiniMax Provider Unicode Handling", () => {
const llm = getModel("minimax", "MiniMax-M2.1");
const llm = getModel("minimax", "MiniMax-M2.7");
it("should handle emoji in tool results", { retry: 3, timeout: 30000 }, async () => {
await testEmojiInToolResults(llm);