Repository navigation
fix(minimax): stop capping MiniMax-M3 / M2.7 max_tokens at 8192 (#3141) #3163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
| } | ||
|
|
||
| // 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 | ||
| // 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; | ||
|
Comment on lines
369
to
390
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Performance & Simplicity Optimization for
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SUGGESTION: Comment language inconsistency
This comment is in Portuguese ("Buscas por alias") while the rest of the codebase uses English comments. Should be "Alias lookups (case-insensitive)" for consistency.