diff --git a/apps/tui/lib/ai/src/cache-pricing.ts b/apps/tui/lib/ai/src/cache-pricing.ts index 0def44a18..17dd20ef6 100644 --- a/apps/tui/lib/ai/src/cache-pricing.ts +++ b/apps/tui/lib/ai/src/cache-pricing.ts @@ -15,7 +15,7 @@ export function hasStandardAnthropicCachePricing(model: Model< const modelId = model.id.toLowerCase(); const isAnthropicModel = model.provider === "anthropic" || modelId.startsWith("anthropic/") || modelId.startsWith("claude-"); - if (!isAnthropicModel) { + if (!isAnthropicModel || model.cost.cacheWrite === undefined) { return false; } diff --git a/apps/tui/lib/ai/src/models.generated.ts b/apps/tui/lib/ai/src/models.generated.ts index ff0f33240..3534227e5 100644 --- a/apps/tui/lib/ai/src/models.generated.ts +++ b/apps/tui/lib/ai/src/models.generated.ts @@ -1129,57 +1129,6 @@ export const MODELS = { contextWindow: 3500000, maxTokens: 16384, } satisfies Model<"bedrock-converse-stream">, - "minimax.minimax-m2": { - id: "minimax.minimax-m2", - name: "MiniMax M2", - 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: 204608, - maxTokens: 128000, - } satisfies Model<"bedrock-converse-stream">, - "minimax.minimax-m2.1": { - id: "minimax.minimax-m2.1", - name: "MiniMax M2.1", - 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: 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", @@ -5620,78 +5569,6 @@ export const MODELS = { } satisfies Model<"openai-completions">, }, "huggingface": { - "MiniMaxAI/MiniMax-M2": { - id: "MiniMaxAI/MiniMax-M2", - name: "MiniMax-M2", - api: "openai-completions", - provider: "huggingface", - baseUrl: "https://router.huggingface.co/v1", - compat: {"supportsDeveloperRole":false}, - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "MiniMaxAI/MiniMax-M2.1": { - id: "MiniMaxAI/MiniMax-M2.1", - name: "MiniMax-M2.1", - api: "openai-completions", - provider: "huggingface", - baseUrl: "https://router.huggingface.co/v1", - compat: {"supportsDeveloperRole":false}, - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "MiniMaxAI/MiniMax-M2.5": { - id: "MiniMaxAI/MiniMax-M2.5", - name: "MiniMax-M2.5", - api: "openai-completions", - provider: "huggingface", - baseUrl: "https://router.huggingface.co/v1", - compat: {"supportsDeveloperRole":false}, - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.03, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "MiniMaxAI/MiniMax-M2.7": { - id: "MiniMaxAI/MiniMax-M2.7", - name: "MiniMax-M2.7", - api: "openai-completions", - provider: "huggingface", - baseUrl: "https://router.huggingface.co/v1", - compat: {"supportsDeveloperRole":false}, - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, "MiniMaxAI/MiniMax-M3": { id: "MiniMaxAI/MiniMax-M3", name: "MiniMax-M3", @@ -6977,75 +6854,119 @@ export const MODELS = { } satisfies Model<"anthropic-messages">, }, "minimax": { - "MiniMax-M2.7": { - id: "MiniMax-M2.7", - name: "MiniMax-M2.7", - api: "anthropic-messages", - provider: "minimax", - baseUrl: "https://api.minimax.io/anthropic", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"anthropic-messages">, - "MiniMax-M2.7-highspeed": { - id: "MiniMax-M2.7-highspeed", - name: "MiniMax-M2.7-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, + "MiniMax-M3": { + "id": "MiniMax-M3", + "name": "MiniMax-M3", + "api": "anthropic-messages", + "provider": "minimax", + "baseUrl": "https://api.minimax.io/anthropic", + "reasoning": true, + "thinkingLevelMap": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "high", + "xhigh": null, + "max": null + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.3, + "output": 1.2, + "cacheRead": 0.06 + }, + "contextWindow": 1000000, + "maxTokens": 524288 + } satisfies Model<"anthropic-messages">, + "MiniMax-M3.1-Flash": { + "id": "MiniMax-M3.1-Flash", + "name": "MiniMax-M3.1-Flash", + "api": "anthropic-messages", + "provider": "minimax", + "baseUrl": "https://api.minimax.io/anthropic", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": null, + "low": "low", + "medium": "medium", + "high": "high", + "xhigh": "xhigh", + "max": "max" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 + }, + "contextWindow": 1000000, + "maxTokens": 524288 } satisfies Model<"anthropic-messages">, }, "minimax-cn": { - "MiniMax-M2.7": { - id: "MiniMax-M2.7", - name: "MiniMax-M2.7", - 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.06, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"anthropic-messages">, - "MiniMax-M2.7-highspeed": { - id: "MiniMax-M2.7-highspeed", - name: "MiniMax-M2.7-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, + "MiniMax-M3": { + "id": "MiniMax-M3", + "name": "MiniMax-M3", + "api": "anthropic-messages", + "provider": "minimax-cn", + "baseUrl": "https://api.minimaxi.com/anthropic", + "reasoning": true, + "thinkingLevelMap": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "high", + "xhigh": null, + "max": null + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.3, + "output": 1.2, + "cacheRead": 0.06 + }, + "contextWindow": 1000000, + "maxTokens": 524288 + } satisfies Model<"anthropic-messages">, + "MiniMax-M3.1-Flash": { + "id": "MiniMax-M3.1-Flash", + "name": "MiniMax-M3.1-Flash", + "api": "anthropic-messages", + "provider": "minimax-cn", + "baseUrl": "https://api.minimaxi.com/anthropic", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": null, + "low": "low", + "medium": "medium", + "high": "high", + "xhigh": "xhigh", + "max": "max" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 + }, + "contextWindow": 1000000, + "maxTokens": 524288 } satisfies Model<"anthropic-messages">, }, "mistral": { @@ -9965,40 +9886,6 @@ export const MODELS = { contextWindow: 200000, maxTokens: 32000, } satisfies Model<"openai-completions">, - "minimax-m2.5": { - id: "minimax-m2.5", - name: "MiniMax-M2.5", - api: "openai-completions", - provider: "opencode", - baseUrl: "https://opencode.ai/zen/v1", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "minimax-m2.7": { - id: "minimax-m2.7", - name: "MiniMax-M2.7", - api: "openai-completions", - provider: "opencode", - baseUrl: "https://opencode.ai/zen/v1", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, "minimax-m3": { id: "minimax-m3", name: "MiniMax-M3", @@ -10451,23 +10338,6 @@ export const MODELS = { contextWindow: 1048576, maxTokens: 128000, } satisfies Model<"openai-completions">, - "minimax-m2.7": { - id: "minimax-m2.7", - name: "MiniMax-M2.7", - api: "openai-completions", - provider: "opencode-go", - baseUrl: "https://opencode.ai/zen/go/v1", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, "minimax-m3": { id: "minimax-m3", name: "MiniMax-M3", @@ -12304,120 +12174,6 @@ export const MODELS = { contextWindow: 1048576, maxTokens: 943718, } satisfies Model<"openai-completions">, - "minimax/minimax-m1": { - id: "minimax/minimax-m1", - name: "MiniMax: MiniMax M1", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.55, - output: 2.2, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 1000000, - maxTokens: 40000, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2": { - id: "minimax/minimax-m2", - name: "MiniMax: MiniMax M2", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.255, - output: 1.02, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2.1": { - id: "minimax/minimax-m2.1", - name: "MiniMax: MiniMax M2.1", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.03, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2.5": { - id: "minimax/minimax-m2.5", - name: "MiniMax: MiniMax M2.5", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.27, - output: 1.08, - cacheRead: 0.027, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 128000, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2.7": { - id: "minimax/minimax-m2.7", - name: "MiniMax: MiniMax M2.7", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2.7:free": { - id: "minimax/minimax-m2.7:free", - name: "MiniMax: MiniMax M2.7 (free)", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - compat: {"supportsReasoningEffort":false}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 196608, - maxTokens: 176947, - } satisfies Model<"openai-completions">, "minimax/minimax-m3": { id: "minimax/minimax-m3", name: "MiniMax: MiniMax M3", @@ -16702,44 +16458,6 @@ export const MODELS = { contextWindow: 1048576, maxTokens: 115200, } satisfies Model<"openai-completions">, - "minimax/minimax-m2.5": { - id: "minimax/minimax-m2.5", - name: "Minimax M2.5", - api: "openai-completions", - provider: "prime-inference", - baseUrl: "https://api.pinference.ai/api/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","supportsStrictMode":false,"thinkingFormat":"openrouter"}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.6, - output: 2.4, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 128000, - } satisfies Model<"openai-completions">, - "minimax/minimax-m2.7": { - id: "minimax/minimax-m2.7", - name: "Minimax M2.7", - api: "openai-completions", - provider: "prime-inference", - baseUrl: "https://api.pinference.ai/api/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","supportsStrictMode":false,"thinkingFormat":"openrouter"}, - reasoning: true, - thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null,"max":null}, - input: ["text"], - cost: { - input: 0.6, - output: 2.4, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"openai-completions">, "minimax/minimax-m3": { id: "minimax/minimax-m3", name: "Minimax M3", @@ -19840,142 +19558,6 @@ export const MODELS = { contextWindow: 1048576, maxTokens: 1048576, } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2": { - id: "minimax/minimax-m2", - name: "MiniMax M2", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.03, - cacheWrite: 0.375, - }, - contextWindow: 205000, - maxTokens: 205000, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.1": { - id: "minimax/minimax-m2.1", - name: "MiniMax M2.1", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - 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/minimax-m2.1-lightning": { - id: "minimax/minimax-m2.1-lightning", - name: "MiniMax M2.1 Lightning", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 2.4, - cacheRead: 0.03, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131072, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.5": { - id: "minimax/minimax-m2.5", - name: "MiniMax M2.5", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.03, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131000, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.5-highspeed": { - id: "minimax/minimax-m2.5-highspeed", - name: "MiniMax M2.5 High Speed", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.6, - output: 2.4, - cacheRead: 0.03, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131000, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.7": { - id: "minimax/minimax-m2.7", - name: "MiniMax M2.7", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.3, - output: 1.2, - cacheRead: 0.06, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131000, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.7-free": { - id: "minimax/minimax-m2.7-free", - name: "MiniMax M2.7 (Free)", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 196608, - maxTokens: 196608, - } satisfies Model<"anthropic-messages">, - "minimax/minimax-m2.7-highspeed": { - id: "minimax/minimax-m2.7-highspeed", - name: "MiniMax M2.7 High Speed", - api: "anthropic-messages", - provider: "vercel-ai-gateway", - baseUrl: "https://ai-gateway.vercel.sh", - reasoning: true, - input: ["text"], - cost: { - input: 0.6, - output: 2.4, - cacheRead: 0.06, - cacheWrite: 0.375, - }, - contextWindow: 204800, - maxTokens: 131100, - } satisfies Model<"anthropic-messages">, "minimax/minimax-m3": { id: "minimax/minimax-m3", name: "MiniMax M3", diff --git a/apps/tui/lib/ai/src/models.ts b/apps/tui/lib/ai/src/models.ts index 892fd608f..123e8b3a8 100644 --- a/apps/tui/lib/ai/src/models.ts +++ b/apps/tui/lib/ai/src/models.ts @@ -57,7 +57,7 @@ export function calculateCost( usage.cost.input = (model.cost.input / 1000000) * usage.input; usage.cost.output = (model.cost.output / 1000000) * usage.output; usage.cost.cacheRead = (model.cost.cacheRead / 1000000) * usage.cacheRead; - usage.cost.cacheWrite = ((overrides?.cacheWrite ?? model.cost.cacheWrite) / 1000000) * usage.cacheWrite; + usage.cost.cacheWrite = ((overrides?.cacheWrite ?? model.cost.cacheWrite ?? 0) / 1000000) * usage.cacheWrite; usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite; return usage.cost; } diff --git a/apps/tui/lib/ai/src/providers/anthropic.ts b/apps/tui/lib/ai/src/providers/anthropic.ts index 0be96d24e..c1edc67c5 100644 --- a/apps/tui/lib/ai/src/providers/anthropic.ts +++ b/apps/tui/lib/ai/src/providers/anthropic.ts @@ -71,7 +71,7 @@ function getCacheControl( cacheRetention?: CacheRetention, ): { retention: CacheRetention; cacheControl?: CacheControlEphemeral } { const retention = resolveCacheRetention(cacheRetention); - if (retention === "none") { + if (retention === "none" || model.id === "MiniMax-M3" || model.id === "MiniMax-M3.1-Flash") { return { retention }; } const ttl = retention === "long" && getAnthropicCompat(model).supportsLongCacheRetention ? "1h" : undefined; @@ -761,6 +761,8 @@ function isAlwaysOnAdaptiveThinkingModel(modelId: string): boolean { function supportsAdaptiveThinking(modelId: string): boolean { // Adaptive-thinking model IDs (with or without date suffix). return ( + modelId === "MiniMax-M3" || + modelId === "MiniMax-M3.1-Flash" || modelId.includes("opus-4-6") || modelId.includes("opus-4.6") || modelId.includes("opus-5") || @@ -1025,7 +1027,14 @@ function buildParams( // Configure thinking mode: adaptive (Opus 4.6+ and Sonnet 4.6), // budget-based (older models), or explicitly disabled. - if (model.reasoning) { + if (model.id === "MiniMax-M3.1-Flash") { + params.thinking = { type: "adaptive" }; + params.output_config = { effort: options?.effort ?? "max" } as NonNullable< + MessageCreateParamsStreaming["output_config"] + >; + } else if (model.id === "MiniMax-M3") { + params.thinking = { type: options?.thinkingEnabled ? "adaptive" : "disabled" }; + } else if (model.reasoning) { if (options?.thinkingEnabled) { // Default to "summarized" so Opus 4.7 and Mythos Preview behave like // older Claude 4 models (whose API default is also "summarized"). diff --git a/apps/tui/lib/ai/src/types.ts b/apps/tui/lib/ai/src/types.ts index f5744ba97..10d60df27 100644 --- a/apps/tui/lib/ai/src/types.ts +++ b/apps/tui/lib/ai/src/types.ts @@ -441,7 +441,7 @@ export interface Model { input: number; // $/million tokens output: number; // $/million tokens cacheRead: number; // $/million tokens - cacheWrite: number; // $/million tokens + cacheWrite?: number; // $/million tokens, omitted when unpublished }; contextWindow: number; maxTokens: number; diff --git a/apps/tui/lib/coding-agent/src/core/model-resolver.ts b/apps/tui/lib/coding-agent/src/core/model-resolver.ts index d6a41202a..084dce738 100644 --- a/apps/tui/lib/coding-agent/src/core/model-resolver.ts +++ b/apps/tui/lib/coding-agent/src/core/model-resolver.ts @@ -35,8 +35,8 @@ export const defaultModelPerProvider: Record = { cerebras: "gpt-oss-120b", zai: "glm-5.3", mistral: "devstral-medium-latest", - minimax: "MiniMax-M2.7", - "minimax-cn": "MiniMax-M2.7", + minimax: "MiniMax-M3.1-Flash", + "minimax-cn": "MiniMax-M3.1-Flash", moonshotai: "kimi-k2.6", "moonshotai-cn": "kimi-k2.6", huggingface: "moonshotai/Kimi-K2.6", diff --git a/apps/tui/src/minimax-models.test.ts b/apps/tui/src/minimax-models.test.ts new file mode 100644 index 000000000..eed539a23 --- /dev/null +++ b/apps/tui/src/minimax-models.test.ts @@ -0,0 +1,88 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { test } from "node:test"; +import { calculateCost, getModel, getModels, getProviders, getSupportedThinkingLevels } from "../lib/ai/src/models.js"; +import { streamSimpleAnthropic } from "../lib/ai/src/providers/anthropic.js"; +import type { SimpleStreamOptions, Usage } from "../lib/ai/src/types.js"; +import { defaultModelPerProvider } from "../lib/coding-agent/src/core/model-resolver.js"; + +for (const provider of ["minimax", "minimax-cn"] as const) { + test(`${provider} offers only M3 and Flash with supported input and thinking modes`, () => { + assert.deepEqual(getModels(provider).map((model) => model.id).sort(), ["MiniMax-M3", "MiniMax-M3.1-Flash"]); + assert.equal(defaultModelPerProvider[provider], "MiniMax-M3.1-Flash"); + for (const model of getModels(provider)) { + assert.equal(model.contextWindow, 1_000_000); + assert.equal(model.maxTokens, 524_288); + assert.deepEqual(model.input, ["text", "image"]); + assert.equal(model.cost.cacheWrite, undefined); + } + assert.deepEqual(getSupportedThinkingLevels(getModel(provider, "MiniMax-M3")), ["off", "high"]); + const flash = getModel(provider, "MiniMax-M3.1-Flash"); + assert.deepEqual(getSupportedThinkingLevels(flash), ["low", "medium", "high", "xhigh", "max"]); + assert.deepEqual(flash.cost, { input: 0.15, output: 0.6, cacheRead: 0.015 }); + }); +} + +test("all builtin routes exclude retired MiniMax models", () => { + for (const provider of getProviders()) { + for (const model of getModels(provider)) { + assert.doesNotMatch(model.id, /minimax[./-]m[12](?:\D|$)/i); + } + } + const catalog = JSON.parse(readFileSync(new URL("../../../crates/core/providers.json", import.meta.url), "utf8")); + for (const [provider, entry] of Object.entries(catalog.provider) as [string, { models: Record }][]) { + for (const id of Object.keys(entry.models)) assert.doesNotMatch(id, /minimax[./-]m[12](?:\D|$)/i); + if (provider === "minimax" || provider === "minimax-cn") { + assert.deepEqual(Object.keys(entry.models).sort(), ["MiniMax-M3", "MiniMax-M3.1-Flash"]); + assert.equal(entry.models["MiniMax-M3.1-Flash"].default_reasoning_selection, "max"); + assert.equal(entry.models["MiniMax-M3.1-Flash"].thinking_level_map.off, null); + assert.equal(entry.models["MiniMax-M3"].default_reasoning_selection, "off"); + } + } +}); + +async function payload(modelId: "MiniMax-M3" | "MiniMax-M3.1-Flash", reasoning?: SimpleStreamOptions["reasoning"]) { + let captured: any; + const stream = streamSimpleAnthropic(getModel("minimax", modelId), { + systemPrompt: "Be concise.", + messages: [{ role: "user", content: "Hello", timestamp: 0 }], + }, { + apiKey: "test-placeholder", + reasoning, + serviceTier: "priority", + onPayload: (body) => { + captured = body; + throw new Error("Captured before network request"); + }, + }); + await stream.result(); + assert.ok(captured, "request was captured before network access"); + assert.equal(JSON.stringify(captured).includes("cache_control"), false); + assert.equal(captured.service_tier, undefined); + return captured; +} + +test("Flash always sends adaptive thinking, defaults to max, and preserves supported efforts", async () => { + for (const reasoning of [undefined, "off", "low", "medium", "high", "xhigh", "max"] as const) { + const body = await payload("MiniMax-M3.1-Flash", reasoning); + assert.deepEqual(body.thinking, { type: "adaptive" }); + assert.deepEqual(body.output_config, { effort: reasoning && reasoning !== "off" ? reasoning : "max" }); + } +}); + +test("M3 defaults off, enables adaptive thinking, and omits ignored effort controls", async () => { + assert.deepEqual((await payload("MiniMax-M3")).thinking, { type: "disabled" }); + const body = await payload("MiniMax-M3", "high"); + assert.deepEqual(body.thinking, { type: "adaptive" }); + assert.equal(body.output_config, undefined); +}); + +test("unpublished cache-write pricing does not produce NaN cost totals", () => { + const usage: Usage = { + input: 1_000_000, output: 1_000_000, cacheRead: 1_000_000, cacheWrite: 0, totalTokens: 3_000_000, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }; + const cost = calculateCost(getModel("minimax", "MiniMax-M3.1-Flash"), usage); + assert.equal(cost.total, 0.765); + assert.equal(cost.cacheWrite, 0); +}); diff --git a/crates/core/catalog/pi-ai-models.snapshot.json b/crates/core/catalog/pi-ai-models.snapshot.json index 68d1a7c3a..8d3b8f1c7 100644 --- a/crates/core/catalog/pi-ai-models.snapshot.json +++ b/crates/core/catalog/pi-ai-models.snapshot.json @@ -1416,63 +1416,6 @@ "contextWindow": 3500000, "maxTokens": 16384 }, - "minimax.minimax-m2": { - "id": "minimax.minimax-m2", - "name": "MiniMax M2", - "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": 204608, - "maxTokens": 128000 - }, - "minimax.minimax-m2.1": { - "id": "minimax.minimax-m2.1", - "name": "MiniMax M2.1", - "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": 204800, - "maxTokens": 131072 - }, - "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 - }, "mistral.devstral-2-123b": { "id": "mistral.devstral-2-123b", "name": "Devstral 2 123B", @@ -7207,94 +7150,6 @@ } }, "huggingface": { - "MiniMaxAI/MiniMax-M2": { - "id": "MiniMaxAI/MiniMax-M2", - "name": "MiniMax-M2", - "api": "openai-completions", - "provider": "huggingface", - "baseUrl": "https://router.huggingface.co/v1", - "compat": { - "supportsDeveloperRole": false - }, - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "MiniMaxAI/MiniMax-M2.1": { - "id": "MiniMaxAI/MiniMax-M2.1", - "name": "MiniMax-M2.1", - "api": "openai-completions", - "provider": "huggingface", - "baseUrl": "https://router.huggingface.co/v1", - "compat": { - "supportsDeveloperRole": false - }, - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "MiniMaxAI/MiniMax-M2.5": { - "id": "MiniMaxAI/MiniMax-M2.5", - "name": "MiniMax-M2.5", - "api": "openai-completions", - "provider": "huggingface", - "baseUrl": "https://router.huggingface.co/v1", - "compat": { - "supportsDeveloperRole": false - }, - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "MiniMaxAI/MiniMax-M2.7": { - "id": "MiniMaxAI/MiniMax-M2.7", - "name": "MiniMax-M2.7", - "api": "openai-completions", - "provider": "huggingface", - "baseUrl": "https://router.huggingface.co/v1", - "compat": { - "supportsDeveloperRole": false - }, - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, "MiniMaxAI/MiniMax-M3": { "id": "MiniMaxAI/MiniMax-M3", "name": "MiniMax-M3", @@ -8916,83 +8771,119 @@ } }, "minimax": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", + "MiniMax-M3": { + "id": "MiniMax-M3", + "name": "MiniMax-M3", "api": "anthropic-messages", "provider": "minimax", "baseUrl": "https://api.minimax.io/anthropic", "reasoning": true, + "thinkingLevelMap": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "high", + "xhigh": null, + "max": null + }, "input": [ - "text" + "text", + "image" ], "cost": { "input": 0.3, "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 + "cacheRead": 0.06 }, - "contextWindow": 204800, - "maxTokens": 131072 + "contextWindow": 1000000, + "maxTokens": 524288 }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", + "MiniMax-M3.1-Flash": { + "id": "MiniMax-M3.1-Flash", + "name": "MiniMax-M3.1-Flash", "api": "anthropic-messages", "provider": "minimax", "baseUrl": "https://api.minimax.io/anthropic", "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": null, + "low": "low", + "medium": "medium", + "high": "high", + "xhigh": "xhigh", + "max": "max" + }, "input": [ - "text" + "text", + "image" ], "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 }, - "contextWindow": 204800, - "maxTokens": 131072 + "contextWindow": 1000000, + "maxTokens": 524288 } }, "minimax-cn": { - "MiniMax-M2.7": { - "id": "MiniMax-M2.7", - "name": "MiniMax-M2.7", + "MiniMax-M3": { + "id": "MiniMax-M3", + "name": "MiniMax-M3", "api": "anthropic-messages", "provider": "minimax-cn", "baseUrl": "https://api.minimaxi.com/anthropic", "reasoning": true, + "thinkingLevelMap": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "high", + "xhigh": null, + "max": null + }, "input": [ - "text" + "text", + "image" ], "cost": { "input": 0.3, "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 + "cacheRead": 0.06 }, - "contextWindow": 204800, - "maxTokens": 131072 + "contextWindow": 1000000, + "maxTokens": 524288 }, - "MiniMax-M2.7-highspeed": { - "id": "MiniMax-M2.7-highspeed", - "name": "MiniMax-M2.7-highspeed", + "MiniMax-M3.1-Flash": { + "id": "MiniMax-M3.1-Flash", + "name": "MiniMax-M3.1-Flash", "api": "anthropic-messages", "provider": "minimax-cn", "baseUrl": "https://api.minimaxi.com/anthropic", "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": null, + "low": "low", + "medium": "medium", + "high": "high", + "xhigh": "xhigh", + "max": "max" + }, "input": [ - "text" + "text", + "image" ], "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 }, - "contextWindow": 204800, - "maxTokens": 131072 + "contextWindow": 1000000, + "maxTokens": 524288 } }, "mistral": { @@ -12764,44 +12655,6 @@ "contextWindow": 200000, "maxTokens": 32000 }, - "minimax-m2.5": { - "id": "minimax-m2.5", - "name": "MiniMax-M2.5", - "api": "openai-completions", - "provider": "opencode", - "baseUrl": "https://opencode.ai/zen/v1", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax-m2.7": { - "id": "minimax-m2.7", - "name": "MiniMax-M2.7", - "api": "openai-completions", - "provider": "opencode", - "baseUrl": "https://opencode.ai/zen/v1", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, "minimax-m3": { "id": "minimax-m3", "name": "MiniMax-M3", @@ -13364,25 +13217,6 @@ "contextWindow": 1048576, "maxTokens": 128000 }, - "minimax-m2.7": { - "id": "minimax-m2.7", - "name": "MiniMax-M2.7", - "api": "openai-completions", - "provider": "opencode-go", - "baseUrl": "https://opencode.ai/zen/go/v1", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, "minimax-m3": { "id": "minimax-m3", "name": "MiniMax-M3", @@ -16117,9 +15951,9 @@ "contextWindow": 1048576, "maxTokens": 943718 }, - "minimax/minimax-m1": { - "id": "minimax/minimax-m1", - "name": "MiniMax: MiniMax M1", + "minimax/minimax-m3": { + "id": "minimax/minimax-m3", + "name": "MiniMax: MiniMax M3", "api": "openai-completions", "provider": "openrouter", "baseUrl": "https://openrouter.ai/api/v1", @@ -16136,193 +15970,8 @@ "max": null }, "input": [ - "text" - ], - "cost": { - "input": 0.55, - "output": 2.2, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 40000 - }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax: MiniMax M2", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.255, - "output": 1.02, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax: MiniMax M2.1", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax: MiniMax M2.5", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.27, - "output": 1.08, - "cacheRead": 0.027, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 128000 - }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "MiniMax: MiniMax M2.7", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax/minimax-m2.7:free": { - "id": "minimax/minimax-m2.7:free", - "name": "MiniMax: MiniMax M2.7 (free)", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 196608, - "maxTokens": 176947 - }, - "minimax/minimax-m3": { - "id": "minimax/minimax-m3", - "name": "MiniMax: MiniMax M3", - "api": "openai-completions", - "provider": "openrouter", - "baseUrl": "https://openrouter.ai/api/v1", - "compat": { - "supportsReasoningEffort": false - }, - "reasoning": true, - "thinkingLevelMap": { - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text", - "image" + "text", + "image" ], "cost": { "input": 0.3, @@ -22801,78 +22450,6 @@ "contextWindow": 1048576, "maxTokens": 115200 }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "Minimax M2.5", - "api": "openai-completions", - "provider": "prime-inference", - "baseUrl": "https://api.pinference.ai/api/v1", - "compat": { - "supportsStore": false, - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens", - "supportsStrictMode": false, - "thinkingFormat": "openrouter" - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 128000 - }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "Minimax M2.7", - "api": "openai-completions", - "provider": "prime-inference", - "baseUrl": "https://api.pinference.ai/api/v1", - "compat": { - "supportsStore": false, - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens", - "supportsStrictMode": false, - "thinkingFormat": "openrouter" - }, - "reasoning": true, - "thinkingLevelMap": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "input": [ - "text" - ], - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, "minimax/minimax-m3": { "id": "minimax/minimax-m3", "name": "Minimax M3", @@ -27313,158 +26890,6 @@ "contextWindow": 1048576, "maxTokens": 1048576 }, - "minimax/minimax-m2": { - "id": "minimax/minimax-m2", - "name": "MiniMax M2", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - }, - "contextWindow": 205000, - "maxTokens": 205000 - }, - "minimax/minimax-m2.1": { - "id": "minimax/minimax-m2.1", - "name": "MiniMax M2.1", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax/minimax-m2.1-lightning": { - "id": "minimax/minimax-m2.1-lightning", - "name": "MiniMax M2.1 Lightning", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 2.4, - "cacheRead": 0.03, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131072 - }, - "minimax/minimax-m2.5": { - "id": "minimax/minimax-m2.5", - "name": "MiniMax M2.5", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131000 - }, - "minimax/minimax-m2.5-highspeed": { - "id": "minimax/minimax-m2.5-highspeed", - "name": "MiniMax M2.5 High Speed", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.03, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131000 - }, - "minimax/minimax-m2.7": { - "id": "minimax/minimax-m2.7", - "name": "MiniMax M2.7", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131000 - }, - "minimax/minimax-m2.7-free": { - "id": "minimax/minimax-m2.7-free", - "name": "MiniMax M2.7 (Free)", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 196608, - "maxTokens": 196608 - }, - "minimax/minimax-m2.7-highspeed": { - "id": "minimax/minimax-m2.7-highspeed", - "name": "MiniMax M2.7 High Speed", - "api": "anthropic-messages", - "provider": "vercel-ai-gateway", - "baseUrl": "https://ai-gateway.vercel.sh", - "reasoning": true, - "input": [ - "text" - ], - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 - }, - "contextWindow": 204800, - "maxTokens": 131100 - }, "minimax/minimax-m3": { "id": "minimax/minimax-m3", "name": "MiniMax M3", diff --git a/crates/core/providers.json b/crates/core/providers.json index 480dc3437..8a1c0a9c7 100644 --- a/crates/core/providers.json +++ b/crates/core/providers.json @@ -4106,110 +4106,6 @@ "base_url": "https://router.huggingface.co/v1", "wire_api": "openai_chat_completions", "models": { - "MiniMaxAI/MiniMax-M2": { - "name": "MiniMax-M2", - "channel": "Hugging Face", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "MiniMaxAI/MiniMax-M2.1": { - "name": "MiniMax-M2.1", - "channel": "Hugging Face", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "MiniMaxAI/MiniMax-M2.5": { - "name": "MiniMax-M2.5", - "channel": "Hugging Face", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0 - } - }, - "MiniMaxAI/MiniMax-M2.7": { - "name": "MiniMax-M2.7", - "channel": "Hugging Face", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - } - }, "MiniMaxAI/MiniMax-M3": { "name": "MiniMax-M3", "channel": "Hugging Face", @@ -6038,57 +5934,70 @@ "base_url": "https://api.minimax.io/anthropic", "wire_api": "anthropic_messages", "models": { - "MiniMax-M2.7": { - "name": "MiniMax-M2.7", + "MiniMax-M3": { + "name": "MiniMax-M3", "channel": "MiniMax", "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] + "reasoning_capability": "toggle", + "thinking_level_map": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "adaptive", + "xhigh": null, + "max": null }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, + "context_window": 1000000, + "max_tokens": 524288, "input_modalities": [ - "text" + "text", + "image" ], "priority": 10, "cost": { "input": 0.3, "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } + "cacheRead": 0.06 + }, + "default_reasoning_selection": "off" }, - "MiniMax-M2.7-highspeed": { - "name": "MiniMax-M2.7-highspeed", + "MiniMax-M3.1-Flash": { + "name": "MiniMax-M3.1-Flash", "channel": "MiniMax", "reasoning": true, "reasoning_capability": { "levels": [ - "off", "low", "medium", - "high" + "high", + "xhigh", + "max" ] }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, + "default_reasoning_effort": "max", + "thinking_level_map": { + "off": null, + "minimal": null, + "low": "adaptive", + "medium": "adaptive", + "high": "adaptive", + "xhigh": "adaptive", + "max": "adaptive" + }, + "context_window": 1000000, + "max_tokens": 524288, "input_modalities": [ - "text" + "text", + "image" ], "priority": 10, "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 + }, + "default_reasoning_selection": "max" } } }, @@ -6098,57 +6007,70 @@ "base_url": "https://api.minimaxi.com/anthropic", "wire_api": "anthropic_messages", "models": { - "MiniMax-M2.7": { - "name": "MiniMax-M2.7", + "MiniMax-M3": { + "name": "MiniMax-M3", "channel": "MiniMax (CN)", "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] + "reasoning_capability": "toggle", + "thinking_level_map": { + "off": "disabled", + "minimal": null, + "low": null, + "medium": null, + "high": "adaptive", + "xhigh": null, + "max": null }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, + "context_window": 1000000, + "max_tokens": 524288, "input_modalities": [ - "text" + "text", + "image" ], "priority": 10, "cost": { "input": 0.3, "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } + "cacheRead": 0.06 + }, + "default_reasoning_selection": "off" }, - "MiniMax-M2.7-highspeed": { - "name": "MiniMax-M2.7-highspeed", + "MiniMax-M3.1-Flash": { + "name": "MiniMax-M3.1-Flash", "channel": "MiniMax (CN)", "reasoning": true, "reasoning_capability": { "levels": [ - "off", "low", "medium", - "high" + "high", + "xhigh", + "max" ] }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, + "default_reasoning_effort": "max", + "thinking_level_map": { + "off": null, + "minimal": null, + "low": "adaptive", + "medium": "adaptive", + "high": "adaptive", + "xhigh": "adaptive", + "max": "adaptive" + }, + "context_window": 1000000, + "max_tokens": 524288, "input_modalities": [ - "text" + "text", + "image" ], "priority": 10, "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } + "input": 0.15, + "output": 0.6, + "cacheRead": 0.015 + }, + "default_reasoning_selection": "max" } } }, @@ -10033,60 +9955,6 @@ }, "wire_api": "openai_chat_completions" }, - "minimax-m2.5": { - "name": "MiniMax-M2.5", - "channel": "OpenCode", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "wire_api": "openai_chat_completions" - }, - "minimax-m2.7": { - "name": "MiniMax-M2.7", - "channel": "OpenCode", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - }, - "wire_api": "openai_chat_completions" - }, "minimax-m3": { "name": "MiniMax-M3", "channel": "OpenCode", @@ -10827,32 +10695,6 @@ "cacheWrite": 0 } }, - "minimax-m2.7": { - "name": "MiniMax-M2.7", - "channel": "OpenCode Go", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - } - }, "minimax-m3": { "name": "MiniMax-M3", "channel": "OpenCode Go", @@ -13902,206 +13744,14 @@ "context_window": 1048576, "max_tokens": 943718, "input_modalities": [ - "text", - "image" - ], - "priority": 10, - "cost": { - "input": 0.09999999999999999, - "output": 0.19999999999999998, - "cacheRead": 0.002, - "cacheWrite": 0 - } - }, - "minimax/minimax-m1": { - "name": "MiniMax: MiniMax M1", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 1000000, - "max_tokens": 40000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.55, - "output": 2.2, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2": { - "name": "MiniMax: MiniMax M2", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.255, - "output": 1.02, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.1": { - "name": "MiniMax: MiniMax M2.1", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.5": { - "name": "MiniMax: MiniMax M2.5", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 128000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.27, - "output": 1.08, - "cacheRead": 0.027, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.7": { - "name": "MiniMax: MiniMax M2.7", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.7:free": { - "name": "MiniMax: MiniMax M2.7 (free)", - "channel": "OpenRouter", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 196608, - "max_tokens": 176947, - "input_modalities": [ - "text" + "text", + "image" ], "priority": 10, "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, + "input": 0.09999999999999999, + "output": 0.19999999999999998, + "cacheRead": 0.002, "cacheWrite": 0 } }, @@ -20961,70 +20611,6 @@ "cacheWrite": 0 } }, - "minimax/minimax-m2.5": { - "name": "Minimax M2.5", - "channel": "Hosted Inference", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 128000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.7": { - "name": "Minimax M2.7", - "channel": "Hosted Inference", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "high" - ] - }, - "default_reasoning_effort": "high", - "thinking_level_map": { - "off": null, - "minimal": null, - "low": null, - "medium": null, - "high": "high", - "xhigh": null, - "max": null - }, - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0, - "cacheWrite": 0 - } - }, "minimax/minimax-m3": { "name": "Minimax M3", "channel": "Hosted Inference", @@ -25932,214 +25518,6 @@ "cacheWrite": 0 } }, - "minimax/minimax-m2": { - "name": "MiniMax M2", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 205000, - "max_tokens": 205000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.1": { - "name": "MiniMax M2.1", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.1-lightning": { - "name": "MiniMax M2.1 Lightning", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131072, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 2.4, - "cacheRead": 0.03, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.5": { - "name": "MiniMax M2.5", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.03, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.5-highspeed": { - "name": "MiniMax M2.5 High Speed", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.03, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.7": { - "name": "MiniMax M2.7", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131000, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.3, - "output": 1.2, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } - }, - "minimax/minimax-m2.7-free": { - "name": "MiniMax M2.7 (Free)", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 196608, - "max_tokens": 196608, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - } - }, - "minimax/minimax-m2.7-highspeed": { - "name": "MiniMax M2.7 High Speed", - "channel": "Vercel AI Gateway", - "reasoning": true, - "reasoning_capability": { - "levels": [ - "off", - "low", - "medium", - "high" - ] - }, - "default_reasoning_effort": "high", - "context_window": 204800, - "max_tokens": 131100, - "input_modalities": [ - "text" - ], - "priority": 10, - "cost": { - "input": 0.6, - "output": 2.4, - "cacheRead": 0.06, - "cacheWrite": 0.375 - } - }, "minimax/minimax-m3": { "name": "MiniMax M3", "channel": "Vercel AI Gateway", @@ -30475,7 +29853,7 @@ "models": { "glm-5.3": { "name": "GLM 5.3", - "channel": "\u667a\u8c31 GLM", + "channel": "智谱 GLM", "reasoning_capability": { "levels": [ "off", @@ -30494,7 +29872,7 @@ }, "glm-5.3-flash": { "name": "GLM 5.3 Flash", - "channel": "\u667a\u8c31 GLM", + "channel": "智谱 GLM", "reasoning_capability": { "levels": [ "off", diff --git a/crates/core/src/model_catalog.rs b/crates/core/src/model_catalog.rs index c3aeeaae7..20778d802 100644 --- a/crates/core/src/model_catalog.rs +++ b/crates/core/src/model_catalog.rs @@ -524,6 +524,46 @@ mod tests { assert_eq!(catalog.list_visible()[0].slug, "openai/gpt-5.5"); } + #[test] + fn builtin_minimax_models_resolve_current_thinking_modes() { + let catalog = PresetModelCatalog::load().expect("load provider catalog"); + for provider in ["minimax", "minimax-cn"] { + let flash = catalog + .get(&format!("{provider}/MiniMax-M3.1-Flash")) + .expect("Flash model"); + assert_eq!(flash.context_window, 1_000_000); + assert_eq!(flash.max_tokens, Some(524_288)); + let default = flash.resolve_reasoning_effort_selection(None); + assert_eq!(default.request_thinking.as_deref(), Some("adaptive")); + assert_eq!( + default.request_reasoning_effort, + Some(crate::ReasoningEffort::Max) + ); + let off = flash.resolve_reasoning_effort_selection(Some("off")); + assert_eq!(off.request_thinking.as_deref(), Some("adaptive")); + + let m3 = catalog + .get(&format!("{provider}/MiniMax-M3")) + .expect("M3 model"); + assert_eq!( + m3.resolve_reasoning_effort_selection(None) + .request_thinking + .as_deref(), + Some("disabled") + ); + assert_eq!( + m3.resolve_reasoning_effort_selection(Some("high")) + .request_thinking + .as_deref(), + Some("adaptive") + ); + } + for model in catalog.list_visible() { + let id = model.slug.to_ascii_lowercase(); + assert!(!id.contains("minimax-m2") && !id.contains("minimax-m1")); + } + } + #[test] fn builtin_catalog_resolves_visible_defaults() { let catalog = PresetModelCatalog::load().expect("load catalog"); diff --git a/crates/protocol/src/reasoning_effort.rs b/crates/protocol/src/reasoning_effort.rs index 7ce071aa1..40a3bf994 100644 --- a/crates/protocol/src/reasoning_effort.rs +++ b/crates/protocol/src/reasoning_effort.rs @@ -386,6 +386,9 @@ pub(crate) fn nearest_effort( target: ReasoningEffort, supported: &[ReasoningEffort], ) -> ReasoningEffort { + if supported.contains(&target) { + return target; + } let target_rank = effort_rank(target); supported .iter() @@ -564,6 +567,18 @@ mod tests { ); } + #[test] + fn nearest_effort_preserves_distinct_max_and_xhigh_selections() { + let supported = [ReasoningEffort::XHigh, ReasoningEffort::Max]; + for effort in supported { + assert_eq!(super::nearest_effort(effort, &supported), effort); + } + assert_eq!( + super::nearest_effort(ReasoningEffort::Max, &[ReasoningEffort::XHigh]), + ReasoningEffort::XHigh + ); + } + #[test] fn reasoning_options_use_reasoning_effort_wire_values() { assert_eq!( diff --git a/crates/provider/src/anthropic/messages.rs b/crates/provider/src/anthropic/messages.rs index f1a7f2e31..008cacd03 100644 --- a/crates/provider/src/anthropic/messages.rs +++ b/crates/provider/src/anthropic/messages.rs @@ -773,6 +773,7 @@ fn build_thinking(level: &str) -> Option { match level.trim().to_ascii_lowercase().as_str() { "" | "default" => None, "disabled" => Some(AnthropicThinkingConfig { r#type: "disabled" }), + "adaptive" => Some(AnthropicThinkingConfig { r#type: "adaptive" }), "enabled" | "low" | "medium" | "high" | "xhigh" | "max" => { Some(AnthropicThinkingConfig { r#type: "enabled" }) } @@ -1122,6 +1123,33 @@ mod tests { assert_eq!(unknown.get("output_config"), None); } + #[test] + fn build_request_preserves_adaptive_thinking_and_flash_effort() { + let mut request = ModelRequest { + model_slug: devo_protocol::ModelProfileKey::Generic, + model: "MiniMax-M3.1-Flash".to_string(), + system: None, + messages: Vec::new(), + max_tokens: 1024, + tools: None, + hosted_tools: Vec::new(), + sampling: SamplingControls::default(), + request_thinking: Some("adaptive".to_string()), + reasoning_effort: Some(ReasoningEffort::Max), + extra_body: None, + }; + let flash = build_request(&request, true); + assert_eq!(flash["thinking"], json!({ "type": "adaptive" })); + assert_eq!(flash["output_config"], json!({ "effort": "max" })); + assert_eq!(flash.get("service_tier"), None); + + request.model = "MiniMax-M3".to_string(); + request.reasoning_effort = None; + let m3 = build_request(&request, true); + assert_eq!(m3["thinking"], json!({ "type": "adaptive" })); + assert_eq!(m3.get("output_config"), None); + } + #[test] fn build_request_maps_max_thinking_to_output_config_effort() { let request = ModelRequest { diff --git a/docs/usage.md b/docs/usage.md index 02f63996d..e41432e62 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -83,6 +83,24 @@ Its bundled catalog includes model definitions for providers such as DeepSeek, Qwen, Kimi, GLM, and MiniMax. You can configure a compatible endpoint or private gateway rather than relying on the bundled entries. +The built-in MiniMax endpoints offer `MiniMax-M3` and `MiniMax-M3.1-Flash`; +Flash is the TUI's MiniMax default. Both have a 1,000,000-token context window +and a documented 524,288-token output maximum. Devo exposes text and image +input; the API also accepts video, which Devo's input types do not yet represent. +Flash always uses adaptive thinking, with low/medium/high/xhigh/max effort +(default max). M3 supports adaptive thinking or disabled thinking; its Anthropic +API default is disabled and it ignores effort controls. MiniMax caching is +automatic; no manual cache-control markers or unpublished cache-write rate are +added. Cost totals omit cache writes when their price is unavailable. + +MiniMax catalog prices are USD per million tokens. Flash uses $0.15 input, +$0.60 output and $0.015 cache read. M3's flat catalog estimate uses Standard +prices for inputs up to 512k: $0.30/$1.20/$0.06; above 512k the published rates +are $0.60/$2.40/$0.12. Priority pricing is 1.5 times the corresponding M3 rate. +The flat estimator does not model those M3 tiers, and Flash has no service-tier +parameter. See the [MiniMax API reference](https://platform.minimax.io/docs/api-reference/text-chat-anthropic.md) +and [pricing](https://platform.minimax.io/docs/pricing/overview.md). + Desktop onboarding, Settings, and the composer share the server's provider and model catalog. Onboarding shows all available providers, supports searching by provider or model, and refreshes remote model metadata. Use **Refresh catalog** diff --git a/scripts/import-pi-ai-catalog.mjs b/scripts/import-pi-ai-catalog.mjs index b55afb4c8..9a8e3b07f 100644 --- a/scripts/import-pi-ai-catalog.mjs +++ b/scripts/import-pi-ai-catalog.mjs @@ -195,6 +195,10 @@ function convertModels(MODELS) { for (const [providerId, modelMap] of Object.entries(MODELS)) { for (const model of Object.values(modelMap)) { + if (/minimax[./-]m[12](?:\D|$)/i.test(model.id)) { + skipped += 1; + continue; + } const wireApi = API_MAP[model.api]; if (!wireApi) { skipped += 1; @@ -260,6 +264,20 @@ function convertModels(MODELS) { ...(model.featured ? { priority: 50 } : { priority: 10 }), ...(model.cost ? { cost: model.cost } : {}), }; + if (["minimax", "minimax-cn"].includes(providerId)) { + const flash = model.id === "MiniMax-M3.1-Flash"; + converted.reasoning_capability = flash + ? { levels: ["low", "medium", "high", "xhigh", "max"] } + : "toggle"; + converted.thinking_level_map = Object.fromEntries( + Object.entries(model.thinkingLevelMap).map(([level, value]) => [ + level, value === null || level === "off" ? value : "adaptive", + ]), + ); + converted.default_reasoning_selection = flash ? "max" : "off"; + if (flash) converted.default_reasoning_effort = "max"; + else delete converted.default_reasoning_effort; + } if (wireApi !== majorityApi) { converted.wire_api = wireApi; }