fix(minimax): stop capping MiniMax-M3 / M2.7 max_tokens at 8192 (#3141) (#3163)

MiniMax-M3 had no MODEL_SPECS entry and capitalized MiniMax-M2.7 missed
its lowercase spec (case-sensitive lookup) → both fell to the 8192 default
cap. Add the M3 spec (512K output), alias the capitalized ids, and make
getModelSpec lookups case-insensitive.

Co-authored-by: totaltube <totaltube@users.noreply.github.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-04 16:57:52 -03:00
committed by GitHub
parent f8fb3d524d
commit cb94bf695d
2 changed files with 105 additions and 4 deletions

View File

@@ -310,12 +310,24 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
supportsTools: true,
},
// ── MiniMax M3 (1M context, 512K max output) ─────────────────────
// max output verified against MiniMax docs / OpenRouter / Artificial
// Analysis (Nov 2025 launch): 1,048,576-token context, up to 512K output.
"minimax-m3": {
maxOutputTokens: 512000,
contextWindow: 1048576,
supportsThinking: true,
supportsTools: true,
aliases: ["MiniMax-M3", "MiniMaxAI/MiniMax-M3"],
},
// ── MiniMax M2.x (200K context family) ───────────────────────────
"minimax-m2.7": {
maxOutputTokens: 131072,
contextWindow: 204800,
supportsThinking: true,
supportsTools: true,
aliases: ["MiniMax-M2.7", "MiniMaxAI/MiniMax-M2.7"],
},
"minimax-m2.5": {
maxOutputTokens: 131072,
@@ -356,14 +368,23 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
export function getModelSpec(modelId: string): ModelSpec | undefined {
if (MODEL_SPECS[modelId]) return MODEL_SPECS[modelId];
// Buscas por alias
// Case-insensitive lookups: upstream model ids are often capitalized
// (e.g. "MiniMax-M2.7") while specs/aliases use lowercase ids (#3141).
const lower = modelId.toLowerCase();
// Exact match (case-insensitive)
for (const [canonical, spec] of Object.entries(MODEL_SPECS)) {
if (spec.aliases?.includes(modelId)) return spec;
if (canonical.toLowerCase() === lower) return spec;
}
// Prefix matching
// Buscas por alias (case-insensitive)
for (const [, spec] of Object.entries(MODEL_SPECS)) {
if (spec.aliases?.some((alias) => alias.toLowerCase() === lower)) return spec;
}
// Prefix matching (case-insensitive)
for (const [key, spec] of Object.entries(MODEL_SPECS)) {
if (key !== "__default__" && modelId.startsWith(key)) return spec;
if (key !== "__default__" && lower.startsWith(key.toLowerCase())) return spec;
}
return undefined;

View File

@@ -0,0 +1,80 @@
/**
* tests/unit/minimax-m3-maxtokens.test.ts
*
* Regression for issue #3141 — "Omniroute sets max_tokens = 8192 for
* minimax/MiniMax-M3" (reporter @totaltube).
*
* minimax-coding / minimax route through the Claude translator, which calls
* fitThinkingToMaxTokens(model, callerMaxTokens, …) → capMaxOutputTokens().
* The caller's larger max_tokens was silently capped to the 8192
* MODEL_SPECS.__default__ ceiling because:
* (a) MiniMax-M3 had no MODEL_SPECS entry at all, and the registry models
* declare no maxOutputTokens, so resolution fell through to __default__;
* (b) MiniMax-M2.7 (the upstream, capitalized id) never matched its existing
* lowercase "minimax-m2.7" spec because getModelSpec was case-SENSITIVE.
*
* Both lookups must now resolve to the real family ceiling (well above 8192).
*/
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-minimax-m3-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const modelCapabilities = await import("../../src/lib/modelCapabilities.ts");
const { getModelSpec } = await import("../../src/shared/constants/modelSpecs.ts");
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
const DEFAULT_CAP = 8192;
test("#3141 MiniMax-M3 max_tokens is not capped to the 8192 default", () => {
const cap = modelCapabilities.capMaxOutputTokens({ provider: "minimax", model: "MiniMax-M3" });
assert.ok(
cap > DEFAULT_CAP,
`expected MiniMax-M3 maxOutputTokens > ${DEFAULT_CAP}, got ${cap}`
);
});
test("#3141 MiniMaxAI/MiniMax-M3 (prefixed id) resolves above the 8192 default", () => {
const cap = modelCapabilities.capMaxOutputTokens({
provider: "minimax",
model: "MiniMaxAI/MiniMax-M3",
});
assert.ok(
cap > DEFAULT_CAP,
`expected MiniMaxAI/MiniMax-M3 maxOutputTokens > ${DEFAULT_CAP}, got ${cap}`
);
});
test("#3141 capitalized MiniMax-M2.7 resolves to its lowercase spec (case-insensitive)", () => {
const cap = modelCapabilities.capMaxOutputTokens({ provider: "minimax", model: "MiniMax-M2.7" });
assert.ok(
cap > DEFAULT_CAP,
`expected MiniMax-M2.7 maxOutputTokens > ${DEFAULT_CAP}, got ${cap}`
);
});
test("#3141 lowercase minimax-m2.7 spec is unchanged", () => {
const cap = modelCapabilities.capMaxOutputTokens({ provider: "minimax", model: "minimax-m2.7" });
assert.ok(
cap > DEFAULT_CAP,
`expected minimax-m2.7 maxOutputTokens > ${DEFAULT_CAP}, got ${cap}`
);
});
test("#3141 getModelSpec resolves MiniMax-M3 and capitalized M2.7 to family specs", () => {
assert.ok(getModelSpec("MiniMax-M3"), "MiniMax-M3 should have a spec");
assert.ok(
getModelSpec("MiniMax-M2.7"),
"capitalized MiniMax-M2.7 should resolve to the minimax-m2.7 spec"
);
});