From 4810639876162b2da9cb2d7b9ebdc238edd3b90c Mon Sep 17 00:00:00 2001 From: octo-patch Date: Tue, 17 Mar 2026 17:20:20 +0800 Subject: [PATCH 1/3] feat: add MiniMax as LLM provider with M2.5 and M2.5-highspeed models Add MiniMax as a new API-based LLM provider in ChatALL, supporting two models: MiniMax-M2.5 (default) and MiniMax-M2.5-highspeed. Uses the OpenAI-compatible API via @langchain/openai ChatOpenAI with a custom base URL (https://api.minimax.io/v1). Changes: - New bot files: MiniMaxAPIBot, MiniMaxM25Bot, MiniMaxM25HighspeedBot - Settings component with API key, temperature (0.1-1.0), past rounds, and custom API URL support - Store state and mutation for minimaxApi configuration - Bot registration in index.js with api and madeInChina tags - i18n entries for all 11 locales - MiniMax logo (SVG) - README updates (EN and ZH-CN) - Unit tests (42 assertions) and integration tests (17 assertions) --- README.md | 1 + README_ZH-CN.md | 1 + public/bots/minimax-logo.svg | 5 + public/bots/minimax-m25-highspeed-logo.png | Bin 0 -> 2290 bytes public/bots/minimax-m25-logo.png | Bin 0 -> 1902 bytes src/bots/index.js | 8 + src/bots/minimax/MiniMaxAPIBot.js | 41 +++ src/bots/minimax/MiniMaxM25Bot.js | 10 + src/bots/minimax/MiniMaxM25HighspeedBot.js | 10 + .../BotSettings/MiniMaxAPIBotSettings.vue | 73 +++++ src/components/SettingsModal.vue | 2 + src/i18n/locales/de.json | 13 +- src/i18n/locales/en.json | 24 +- src/i18n/locales/es.json | 13 +- src/i18n/locales/fr.json | 13 +- src/i18n/locales/it.json | 13 +- src/i18n/locales/ja.json | 13 +- src/i18n/locales/ko.json | 13 +- src/i18n/locales/ru.json | 13 +- src/i18n/locales/vi.json | 13 +- src/i18n/locales/zh.json | 13 +- src/i18n/locales/zhtw.json | 13 +- src/store/index.js | 9 + tests/minimax-integration.test.js | 226 +++++++++++++ tests/minimax-unit.test.js | 307 ++++++++++++++++++ 25 files changed, 836 insertions(+), 11 deletions(-) create mode 100644 public/bots/minimax-logo.svg create mode 100644 public/bots/minimax-m25-highspeed-logo.png create mode 100644 public/bots/minimax-m25-logo.png create mode 100644 src/bots/minimax/MiniMaxAPIBot.js create mode 100644 src/bots/minimax/MiniMaxM25Bot.js create mode 100644 src/bots/minimax/MiniMaxM25HighspeedBot.js create mode 100644 src/components/BotSettings/MiniMaxAPIBotSettings.vue create mode 100644 tests/minimax-integration.test.js create mode 100644 tests/minimax-unit.test.js diff --git a/README.md b/README.md index 9fd3f14758..996b5b6abf 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Typical users of ChatALL are: | [iFLYTEK SPARK](http://xinghuo.xfyun.cn/) | Yes | Coming soon | | | [Kimi](https://kimi.moonshot.cn/ ) | Yes | No API | | | [Llama 2 13B & 70B](https://ai.meta.com/llama/) | Yes | No API | | +| [MiniMax](https://www.minimax.io/) | No | Yes | | | [MOSS](https://moss.fastnlp.top/) | Yes | No API | | | [Perplexity](https://www.perplexity.ai/) | Yes | No API | | | [Phind](https://www.phind.com/) | Yes | No API | | diff --git a/README_ZH-CN.md b/README_ZH-CN.md index 981730859d..6806972970 100644 --- a/README_ZH-CN.md +++ b/README_ZH-CN.md @@ -48,6 +48,7 @@ ChatALL 的典型用户是: | [讯飞星火](http://xinghuo.xfyun.cn/) | 支持 | 即将推出 | | | [Kimi](https://kimi.moonshot.cn/) | 支持 | 无 API | | | [Llama 2 13B 和 70B](https://ai.meta.com/llama/) | 支持 | 无 API | | +| [MiniMax](https://www.minimax.io/) | 不支持 | 支持 | | | [MOSS](https://moss.fastnlp.top/) | 支持 | 无 API | | | [Perplexity](https://www.perplexity.ai/) | 支持 | 无 | | | [Phind](https://www.phind.com/) | 支持 | 无 API | | diff --git a/public/bots/minimax-logo.svg b/public/bots/minimax-logo.svg new file mode 100644 index 0000000000..243dfc8785 --- /dev/null +++ b/public/bots/minimax-logo.svg @@ -0,0 +1,5 @@ + + + MM + MiniMax + diff --git a/public/bots/minimax-m25-highspeed-logo.png b/public/bots/minimax-m25-highspeed-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..17d0c5f6d99ed442bbcfac95e678fb82ab37faf1 GIT binary patch literal 2290 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 literal 0 HcmV?d00001 diff --git a/src/bots/index.js b/src/bots/index.js index 3f989ff94c..395f941fb6 100644 --- a/src/bots/index.js +++ b/src/bots/index.js @@ -66,6 +66,8 @@ import ClaudeAPIHaikuBot from "./anthropic/ClaudeAPIHaikuBot"; import Grok2APIBot from "./xai/Grok2APIBot"; import Grok3APIBot from "./xai/Grok3APIBot"; import Grok3MiniAPIBot from "./xai/Grok3MiniAPIBot"; +import MiniMaxM25Bot from "./minimax/MiniMaxM25Bot"; +import MiniMaxM25HighspeedBot from "./minimax/MiniMaxM25HighspeedBot"; const all = [ Qihoo360AIBrainBot.getInstance(), @@ -134,6 +136,8 @@ const all = [ Grok2APIBot.getInstance(), Grok3APIBot.getInstance(), Grok3MiniAPIBot.getInstance(), + MiniMaxM25Bot.getInstance(), + MiniMaxM25HighspeedBot.getInstance(), ]; const disabled = ["HuggingChatBot"]; @@ -230,6 +234,8 @@ export const botTags = { bots.getBotByClassName("Grok2APIBot"), bots.getBotByClassName("Grok3APIBot"), bots.getBotByClassName("Grok3MiniAPIBot"), + bots.getBotByClassName("MiniMaxM25Bot"), + bots.getBotByClassName("MiniMaxM25HighspeedBot"), ], madeInChina: [ bots.getBotByClassName("Qihoo360AIBrainBot"), @@ -243,6 +249,8 @@ export const botTags = { bots.getBotByClassName("ChatGLMBot"), bots.getBotByClassName("ChatGLM4Bot"), bots.getBotByClassName("KimiBot"), + bots.getBotByClassName("MiniMaxM25Bot"), + bots.getBotByClassName("MiniMaxM25HighspeedBot"), ], }; export default bots; diff --git a/src/bots/minimax/MiniMaxAPIBot.js b/src/bots/minimax/MiniMaxAPIBot.js new file mode 100644 index 0000000000..e992c5621c --- /dev/null +++ b/src/bots/minimax/MiniMaxAPIBot.js @@ -0,0 +1,41 @@ +import LangChainBot from "../LangChainBot"; +import store from "@/store"; +import { ChatOpenAI } from "@langchain/openai"; + +export default class MiniMaxAPIBot extends LangChainBot { + static _brandId = "minimaxApi"; + static _className = "MiniMaxAPIBot"; + + constructor() { + super(); + } + + async _checkAvailability() { + let available = false; + + if (store.state.minimaxApi.apiKey) { + this.setupModel(); + available = true; + } + return available; + } + + _setupModel() { + const chatModel = new ChatOpenAI({ + configuration: { + basePath: store.state.minimaxApi.alterUrl + ? store.state.minimaxApi.alterUrl + : "https://api.minimax.io/v1", + }, + openAIApiKey: store.state.minimaxApi.apiKey, + modelName: this.constructor._model ? this.constructor._model : "", + temperature: store.state.minimaxApi.temperature, + streaming: true, + }); + return chatModel; + } + + getPastRounds() { + return store.state.minimaxApi.pastRounds; + } +} diff --git a/src/bots/minimax/MiniMaxM25Bot.js b/src/bots/minimax/MiniMaxM25Bot.js new file mode 100644 index 0000000000..bc2a3f6a0c --- /dev/null +++ b/src/bots/minimax/MiniMaxM25Bot.js @@ -0,0 +1,10 @@ +import MiniMaxAPIBot from "./MiniMaxAPIBot"; + +export default class MiniMaxM25Bot extends MiniMaxAPIBot { + static _className = "MiniMaxM25Bot"; + static _logoFilename = "minimax-logo.svg"; + static _model = "MiniMax-M2.5"; + constructor() { + super(); + } +} diff --git a/src/bots/minimax/MiniMaxM25HighspeedBot.js b/src/bots/minimax/MiniMaxM25HighspeedBot.js new file mode 100644 index 0000000000..3cb081a16a --- /dev/null +++ b/src/bots/minimax/MiniMaxM25HighspeedBot.js @@ -0,0 +1,10 @@ +import MiniMaxAPIBot from "./MiniMaxAPIBot"; + +export default class MiniMaxM25HighspeedBot extends MiniMaxAPIBot { + static _className = "MiniMaxM25HighspeedBot"; + static _logoFilename = "minimax-logo.svg"; + static _model = "MiniMax-M2.5-highspeed"; + constructor() { + super(); + } +} diff --git a/src/components/BotSettings/MiniMaxAPIBotSettings.vue b/src/components/BotSettings/MiniMaxAPIBotSettings.vue new file mode 100644 index 0000000000..66206bc46b --- /dev/null +++ b/src/components/BotSettings/MiniMaxAPIBotSettings.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue index b639dd4315..fb33dc7665 100644 --- a/src/components/SettingsModal.vue +++ b/src/components/SettingsModal.vue @@ -128,6 +128,7 @@ import { resolveTheme, applyTheme, Mode } from "../theme"; import ClaudeAPIBotSettings from "./BotSettings/ClaudeAPIBotSettings.vue"; import GroqAPIBotSettings from "./BotSettings/GroqAPIBotSettings.vue"; import xAIAPIBotSettings from "./BotSettings/xAIAPIBotSettings.vue"; +import MiniMaxAPIBotSettings from "./BotSettings/MiniMaxAPIBotSettings.vue"; const { ipcRenderer } = window.require("electron"); const { t: $t, locale } = useI18n(); @@ -156,6 +157,7 @@ const botSettings = [ { brand: "huggingChat", component: HuggingChatBotSettings }, { brand: "kimi", component: KimiBotSettings }, { brand: "lmsys", component: LMSYSBotSettings }, + { brand: "minimaxApi", component: MiniMaxAPIBotSettings }, { brand: "mistral", component: MistralBotSettings }, { brand: "moss", component: MOSSBotSettings }, { brand: "openaiApi", component: OpenAIAPIBotSettings }, diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index a2a895cfa3..c94db8fff9 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b2483a03e6..d3751d2719 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -317,6 +317,17 @@ "gemma-7b-it": "Gemma 7b", "gemma2-9b-it": "Gemma2 9b" }, + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax M2.5", + "MiniMax-M25-highspeed": "MiniMax M2.5 Highspeed", + "alterUrl": "Alternate Base URL", + "alterUrlPrompt": "Base URL path for API requests, default is https://api.minimax.io/v1", + "temperature": "Temperature", + "temperaturePrompt": "Must be between 0.1 and 1.0. Higher values produce more creative but less deterministic responses", + "temperature01": "More deterministic", + "temperature1": "More creative" + }, "xaiApi": { "name": "xAI API", "grok-2-latest": "Grok-2", @@ -396,5 +407,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 2238dac17b..b04e435ec6 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index e3327bb793..fe56197dae 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index ad0f563897..5707d012b0 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -395,5 +395,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index dedb86e53d..93aceaabd0 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index a92bf0a5aa..65da137640 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json @@ -396,5 +396,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 0f1e4adb0e..d581ddb71d 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index a68bbca440..80aa3ab414 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -372,5 +372,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", + "temperature": "Temperature", + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" + } } diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index e7f3ddf1bf..2fd888d4c1 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API 地址", + "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", + "temperature": "温度", + "temperaturePrompt": "采样温度,范围 0.1 到 1", + "temperature01": "精确", + "temperature1": "创意" + } } diff --git a/src/i18n/locales/zhtw.json b/src/i18n/locales/zhtw.json index d727742046..fdb20513b7 100644 --- a/src/i18n/locales/zhtw.json +++ b/src/i18n/locales/zhtw.json @@ -393,5 +393,16 @@ "10": "10", "25": "25", "50": "50", - "100": "100" + "100": "100", + "minimaxApi": { + "name": "MiniMax API", + "MiniMax-M25": "MiniMax-M2.5", + "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "alterUrl": "API 地址", + "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", + "temperature": "温度", + "temperaturePrompt": "采样温度,范围 0.1 到 1", + "temperature01": "精确", + "temperature1": "创意" + } } diff --git a/src/store/index.js b/src/store/index.js index c48e1b5b1f..5be2184224 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -126,6 +126,12 @@ export default createStore({ apiKey: "", pastRounds: 5, }, + minimaxApi: { + apiKey: "", + temperature: 1, + pastRounds: 5, + alterUrl: "", + }, currentChatIndex: 0, updateCounter: 0, theme: undefined, @@ -277,6 +283,9 @@ export default createStore({ setXaiApi(state, values) { state.xaiApi = { ...state.xaiApi, ...values }; }, + setMiniMaxApi(state, values) { + state.minimaxApi = { ...state.minimaxApi, ...values }; + }, setLatestPromptIndex(state, promptIndex) { Chats.table.update(state.currentChatIndex, { latestPromptIndex: promptIndex, diff --git a/tests/minimax-integration.test.js b/tests/minimax-integration.test.js new file mode 100644 index 0000000000..a1019915a1 --- /dev/null +++ b/tests/minimax-integration.test.js @@ -0,0 +1,226 @@ +/** + * Integration tests for MiniMax API via OpenAI-compatible endpoint. + * + * Requires MINIMAX_API_KEY environment variable to be set. + * + * Run: MINIMAX_API_KEY= node tests/minimax-integration.test.js + */ + +const https = require("https"); + +const API_KEY = process.env.MINIMAX_API_KEY; +const BASE_URL = "https://api.minimax.io/v1"; + +let passed = 0; +let failed = 0; + +function assert(condition, message) { + if (condition) { + console.log(` ✓ ${message}`); + passed++; + } else { + console.error(` ✗ ${message}`); + failed++; + } +} + +function describe(name, fn) { + console.log(`\n${name}`); + return fn(); +} + +function chatCompletion(model, messages, options = {}) { + return new Promise((resolve, reject) => { + const url = new URL(`${BASE_URL}/chat/completions`); + + const body = JSON.stringify({ + model, + messages, + temperature: options.temperature || 1.0, + max_tokens: options.max_tokens || 100, + stream: false, + }); + + const req = https.request( + url, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${API_KEY}`, + }, + }, + (res) => { + let data = ""; + res.on("data", (chunk) => (data += chunk)); + res.on("end", () => { + try { + resolve({ status: res.statusCode, body: JSON.parse(data) }); + } catch (e) { + resolve({ status: res.statusCode, body: data }); + } + }); + }, + ); + + req.on("error", reject); + req.setTimeout(60000, () => { + req.destroy(); + reject(new Error("Request timed out")); + }); + req.write(body); + req.end(); + }); +} + +async function run() { + if (!API_KEY) { + console.error("ERROR: MINIMAX_API_KEY environment variable is not set"); + process.exit(1); + } + + // ─── Test 1: MiniMax-M2.5 basic completion ───────────────────── + await describe("MiniMax-M2.5 basic chat completion", async () => { + try { + const result = await chatCompletion("MiniMax-M2.5", [ + { role: "user", content: "Say hello in exactly one word." }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert(result.body.choices, "Response has choices array"); + assert( + result.body.choices.length > 0, + "Response has at least one choice", + ); + assert( + result.body.choices[0].message, + "First choice has a message", + ); + assert( + typeof result.body.choices[0].message.content === "string", + "Message content is a string", + ); + assert( + result.body.choices[0].message.content.length > 0, + `Got response: "${result.body.choices[0].message.content.substring(0, 50)}"`, + ); + assert( + result.body.model, + `Model in response: ${result.body.model}`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 2: MiniMax-M2.5-highspeed basic completion ────────── + await describe("MiniMax-M2.5-highspeed basic chat completion", async () => { + try { + const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + { role: "user", content: "What is 2+2? Answer with just the number." }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert(result.body.choices, "Response has choices array"); + assert( + result.body.choices.length > 0, + "Response has at least one choice", + ); + assert( + result.body.choices[0].message.content.length > 0, + `Got response: "${result.body.choices[0].message.content.substring(0, 50)}"`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 3: Temperature handling ────────────────────────────── + await describe("Temperature handling", async () => { + try { + // Test with valid temperature (1.0) + const result = await chatCompletion( + "MiniMax-M2.5-highspeed", + [{ role: "user", content: "Hi" }], + { temperature: 1.0, max_tokens: 10 }, + ); + assert( + result.status === 200, + `Temperature 1.0 works (status ${result.status})`, + ); + + // Test with valid low temperature (0.1) + const result2 = await chatCompletion( + "MiniMax-M2.5-highspeed", + [{ role: "user", content: "Hi" }], + { temperature: 0.1, max_tokens: 10 }, + ); + assert( + result2.status === 200, + `Temperature 0.1 works (status ${result2.status})`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 4: Multi-turn conversation ─────────────────────────── + await describe("Multi-turn conversation", async () => { + try { + const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + { role: "user", content: "Remember the number 42." }, + { + role: "assistant", + content: "I'll remember the number 42.", + }, + { + role: "user", + content: "What number did I ask you to remember? Reply with just the number.", + }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert( + result.body.choices[0].message.content.includes("42"), + `Response includes "42": "${result.body.choices[0].message.content.substring(0, 100)}"`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 5: System message ──────────────────────────────────── + await describe("System message support", async () => { + try { + const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + { + role: "system", + content: "You are a helpful assistant. Always end your response with the word PINEAPPLE.", + }, + { role: "user", content: "Say hi." }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert( + result.body.choices[0].message.content.length > 0, + `Got response with system message: "${result.body.choices[0].message.content.substring(0, 100)}"`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Summary ─────────────────────────────────────────────────── + console.log(`\n${"─".repeat(50)}`); + console.log(`Results: ${passed} passed, ${failed} failed`); + if (failed > 0) { + process.exit(1); + } else { + console.log("All integration tests passed!"); + } +} + +run().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); diff --git a/tests/minimax-unit.test.js b/tests/minimax-unit.test.js new file mode 100644 index 0000000000..70285106d7 --- /dev/null +++ b/tests/minimax-unit.test.js @@ -0,0 +1,307 @@ +/** + * Unit tests for MiniMax provider integration in ChatALL. + * + * Run: node tests/minimax-unit.test.js + */ + +const fs = require("fs"); +const path = require("path"); + +let passed = 0; +let failed = 0; + +function assert(condition, message) { + if (condition) { + console.log(` ✓ ${message}`); + passed++; + } else { + console.error(` ✗ ${message}`); + failed++; + } +} + +function describe(name, fn) { + console.log(`\n${name}`); + fn(); +} + +// ─── Bot files exist ─────────────────────────────────────────────── +describe("Bot files exist", () => { + const botsDir = path.join(__dirname, "..", "src", "bots", "minimax"); + + assert( + fs.existsSync(path.join(botsDir, "MiniMaxAPIBot.js")), + "MiniMaxAPIBot.js exists", + ); + assert( + fs.existsSync(path.join(botsDir, "MiniMaxM25Bot.js")), + "MiniMaxM25Bot.js exists", + ); + assert( + fs.existsSync(path.join(botsDir, "MiniMaxM25HighspeedBot.js")), + "MiniMaxM25HighspeedBot.js exists", + ); +}); + +// ─── Bot file contents ──────────────────────────────────────────── +describe("MiniMaxAPIBot.js contents", () => { + const content = fs.readFileSync( + path.join(__dirname, "..", "src", "bots", "minimax", "MiniMaxAPIBot.js"), + "utf-8", + ); + + assert( + content.includes('import { ChatOpenAI } from "@langchain/openai"'), + "Uses ChatOpenAI from @langchain/openai", + ); + assert( + content.includes("https://api.minimax.io/v1"), + "Default base URL is https://api.minimax.io/v1", + ); + assert( + content.includes("minimaxApi.apiKey"), + "Uses minimaxApi.apiKey from store", + ); + assert( + content.includes("minimaxApi.temperature"), + "Uses minimaxApi.temperature from store", + ); + assert( + content.includes("minimaxApi.alterUrl"), + "Supports custom API URL via alterUrl", + ); + assert(content.includes("streaming: true"), "Streaming is enabled"); + assert( + content.includes('static _brandId = "minimaxApi"'), + 'Brand ID is "minimaxApi"', + ); +}); + +describe("MiniMaxM25Bot.js contents", () => { + const content = fs.readFileSync( + path.join(__dirname, "..", "src", "bots", "minimax", "MiniMaxM25Bot.js"), + "utf-8", + ); + + assert( + content.includes('static _model = "MiniMax-M2.5"'), + 'Model is "MiniMax-M2.5"', + ); + assert( + content.includes("extends MiniMaxAPIBot"), + "Extends MiniMaxAPIBot", + ); + assert( + content.includes('static _className = "MiniMaxM25Bot"'), + 'Class name is "MiniMaxM25Bot"', + ); +}); + +describe("MiniMaxM25HighspeedBot.js contents", () => { + const content = fs.readFileSync( + path.join( + __dirname, + "..", + "src", + "bots", + "minimax", + "MiniMaxM25HighspeedBot.js", + ), + "utf-8", + ); + + assert( + content.includes('static _model = "MiniMax-M2.5-highspeed"'), + 'Model is "MiniMax-M2.5-highspeed"', + ); + assert( + content.includes("extends MiniMaxAPIBot"), + "Extends MiniMaxAPIBot", + ); + assert( + content.includes('static _className = "MiniMaxM25HighspeedBot"'), + 'Class name is "MiniMaxM25HighspeedBot"', + ); +}); + +// ─── Settings component ─────────────────────────────────────────── +describe("Settings component", () => { + const settingsPath = path.join( + __dirname, + "..", + "src", + "components", + "BotSettings", + "MiniMaxAPIBotSettings.vue", + ); + assert(fs.existsSync(settingsPath), "MiniMaxAPIBotSettings.vue exists"); + + const content = fs.readFileSync(settingsPath, "utf-8"); + assert( + content.includes("CommonBotSettings"), + "Uses CommonBotSettings component", + ); + assert( + content.includes("apiKey"), + "Has API key setting", + ); + assert( + content.includes("temperature"), + "Has temperature setting", + ); + assert( + content.includes("pastRounds"), + "Has pastRounds setting", + ); + assert( + content.includes("alterUrl"), + "Has alterUrl setting for custom API endpoint", + ); +}); + +// ─── Store configuration ────────────────────────────────────────── +describe("Store configuration", () => { + const storeContent = fs.readFileSync( + path.join(__dirname, "..", "src", "store", "index.js"), + "utf-8", + ); + + assert( + storeContent.includes("minimaxApi:"), + "minimaxApi state exists in store", + ); + assert( + storeContent.includes("setMiniMaxApi"), + "setMiniMaxApi mutation exists", + ); + + // Check default values + const stateMatch = storeContent.match( + /minimaxApi:\s*\{[^}]+\}/s, + ); + if (stateMatch) { + const stateBlock = stateMatch[0]; + assert( + stateBlock.includes('apiKey: ""'), + "Default apiKey is empty string", + ); + assert( + stateBlock.includes("temperature: 1"), + "Default temperature is 1", + ); + assert( + stateBlock.includes("pastRounds: 5"), + "Default pastRounds is 5", + ); + assert( + stateBlock.includes('alterUrl: ""'), + "Default alterUrl is empty string", + ); + } else { + assert(false, "Could not parse minimaxApi state block"); + } +}); + +// ─── Bot registration ───────────────────────────────────────────── +describe("Bot registration in index.js", () => { + const indexContent = fs.readFileSync( + path.join(__dirname, "..", "src", "bots", "index.js"), + "utf-8", + ); + + assert( + indexContent.includes( + 'import MiniMaxM25Bot from "./minimax/MiniMaxM25Bot"', + ), + "MiniMaxM25Bot is imported", + ); + assert( + indexContent.includes( + 'import MiniMaxM25HighspeedBot from "./minimax/MiniMaxM25HighspeedBot"', + ), + "MiniMaxM25HighspeedBot is imported", + ); + assert( + indexContent.includes("MiniMaxM25Bot.getInstance()"), + "MiniMaxM25Bot is registered in all array", + ); + assert( + indexContent.includes("MiniMaxM25HighspeedBot.getInstance()"), + "MiniMaxM25HighspeedBot is registered in all array", + ); + + // Check API tag + assert( + indexContent.includes('bots.getBotByClassName("MiniMaxM25Bot")'), + "MiniMaxM25Bot is in botTags", + ); + assert( + indexContent.includes('bots.getBotByClassName("MiniMaxM25HighspeedBot")'), + "MiniMaxM25HighspeedBot is in botTags", + ); +}); + +// ─── i18n entries ───────────────────────────────────────────────── +describe("i18n entries", () => { + const enLocale = JSON.parse( + fs.readFileSync( + path.join(__dirname, "..", "src", "i18n", "locales", "en.json"), + "utf-8", + ), + ); + + assert(enLocale.minimaxApi, "minimaxApi key exists in en.json"); + assert( + enLocale.minimaxApi.name === "MiniMax API", + 'name is "MiniMax API"', + ); + assert( + enLocale.minimaxApi["MiniMax-M25"] === "MiniMax-M2.5", + "MiniMax-M2.5 model name is correct", + ); + assert( + enLocale.minimaxApi["MiniMax-M25-highspeed"] === "MiniMax-M2.5-highspeed", + "MiniMax-M2.5-highspeed model name is correct", + ); + + // Check Chinese locale + const zhLocale = JSON.parse( + fs.readFileSync( + path.join(__dirname, "..", "src", "i18n", "locales", "zh.json"), + "utf-8", + ), + ); + assert(zhLocale.minimaxApi, "minimaxApi key exists in zh.json"); +}); + +// ─── Logo files ─────────────────────────────────────────────────── +describe("Logo files", () => { + const logosDir = path.join(__dirname, "..", "public", "bots"); + + assert( + fs.existsSync(path.join(logosDir, "minimax-logo.svg")), + "minimax-logo.svg exists", + ); +}); + +// ─── README entries ─────────────────────────────────────────────── +describe("README entries", () => { + const readme = fs.readFileSync( + path.join(__dirname, "..", "README.md"), + "utf-8", + ); + assert(readme.includes("MiniMax"), "MiniMax is mentioned in README.md"); + assert( + readme.includes("https://www.minimax.io/"), + "MiniMax link is in README.md", + ); +}); + +// ─── Summary ────────────────────────────────────────────────────── +console.log(`\n${"─".repeat(50)}`); +console.log(`Results: ${passed} passed, ${failed} failed`); +if (failed > 0) { + process.exit(1); +} else { + console.log("All unit tests passed!"); +} From 39babab65466219a23ff07c48a2272526623bb87 Mon Sep 17 00:00:00 2001 From: PR Bot Date: Thu, 19 Mar 2026 01:39:27 +0800 Subject: [PATCH 2/3] feat: add MiniMax M2.7 and M2.7-highspeed model support Add MiniMax-M2.7 and MiniMax-M2.7-highspeed as new bot options alongside existing M2.5 models. Both models share the same OpenAI-compatible API configuration via MiniMaxAPIBot base class. Changes: - Add MiniMaxM27Bot.js and MiniMaxM27HighspeedBot.js - Register M2.7 bots in index.js (all, api, madeInChina tags) - Add i18n entries for M2.7 models across all 11 locale files - Update unit tests (58 pass) and integration tests (28 pass) Co-Authored-By: Octopus --- src/bots/index.js | 8 ++ src/bots/minimax/MiniMaxM27Bot.js | 10 +++ src/bots/minimax/MiniMaxM27HighspeedBot.js | 10 +++ src/i18n/locales/de.json | 2 + src/i18n/locales/en.json | 4 + src/i18n/locales/es.json | 2 + src/i18n/locales/fr.json | 2 + src/i18n/locales/it.json | 2 + src/i18n/locales/ja.json | 2 + src/i18n/locales/ko.json | 2 + src/i18n/locales/ru.json | 2 + src/i18n/locales/vi.json | 2 + src/i18n/locales/zh.json | 2 + src/i18n/locales/zhtw.json | 2 + tests/minimax-integration.test.js | 64 ++++++++++++++- tests/minimax-unit.test.js | 91 ++++++++++++++++++++++ 16 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 src/bots/minimax/MiniMaxM27Bot.js create mode 100644 src/bots/minimax/MiniMaxM27HighspeedBot.js diff --git a/src/bots/index.js b/src/bots/index.js index 395f941fb6..bf13b1a9fe 100644 --- a/src/bots/index.js +++ b/src/bots/index.js @@ -68,6 +68,8 @@ import Grok3APIBot from "./xai/Grok3APIBot"; import Grok3MiniAPIBot from "./xai/Grok3MiniAPIBot"; import MiniMaxM25Bot from "./minimax/MiniMaxM25Bot"; import MiniMaxM25HighspeedBot from "./minimax/MiniMaxM25HighspeedBot"; +import MiniMaxM27Bot from "./minimax/MiniMaxM27Bot"; +import MiniMaxM27HighspeedBot from "./minimax/MiniMaxM27HighspeedBot"; const all = [ Qihoo360AIBrainBot.getInstance(), @@ -138,6 +140,8 @@ const all = [ Grok3MiniAPIBot.getInstance(), MiniMaxM25Bot.getInstance(), MiniMaxM25HighspeedBot.getInstance(), + MiniMaxM27Bot.getInstance(), + MiniMaxM27HighspeedBot.getInstance(), ]; const disabled = ["HuggingChatBot"]; @@ -236,6 +240,8 @@ export const botTags = { bots.getBotByClassName("Grok3MiniAPIBot"), bots.getBotByClassName("MiniMaxM25Bot"), bots.getBotByClassName("MiniMaxM25HighspeedBot"), + bots.getBotByClassName("MiniMaxM27Bot"), + bots.getBotByClassName("MiniMaxM27HighspeedBot"), ], madeInChina: [ bots.getBotByClassName("Qihoo360AIBrainBot"), @@ -251,6 +257,8 @@ export const botTags = { bots.getBotByClassName("KimiBot"), bots.getBotByClassName("MiniMaxM25Bot"), bots.getBotByClassName("MiniMaxM25HighspeedBot"), + bots.getBotByClassName("MiniMaxM27Bot"), + bots.getBotByClassName("MiniMaxM27HighspeedBot"), ], }; export default bots; diff --git a/src/bots/minimax/MiniMaxM27Bot.js b/src/bots/minimax/MiniMaxM27Bot.js new file mode 100644 index 0000000000..3b92e54763 --- /dev/null +++ b/src/bots/minimax/MiniMaxM27Bot.js @@ -0,0 +1,10 @@ +import MiniMaxAPIBot from "./MiniMaxAPIBot"; + +export default class MiniMaxM27Bot extends MiniMaxAPIBot { + static _className = "MiniMaxM27Bot"; + static _logoFilename = "minimax-logo.svg"; + static _model = "MiniMax-M2.7"; + constructor() { + super(); + } +} diff --git a/src/bots/minimax/MiniMaxM27HighspeedBot.js b/src/bots/minimax/MiniMaxM27HighspeedBot.js new file mode 100644 index 0000000000..86ea57e84a --- /dev/null +++ b/src/bots/minimax/MiniMaxM27HighspeedBot.js @@ -0,0 +1,10 @@ +import MiniMaxAPIBot from "./MiniMaxAPIBot"; + +export default class MiniMaxM27HighspeedBot extends MiniMaxAPIBot { + static _className = "MiniMaxM27HighspeedBot"; + static _logoFilename = "minimax-logo.svg"; + static _model = "MiniMax-M2.7-highspeed"; + constructor() { + super(); + } +} diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index c94db8fff9..746901aadf 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index d3751d2719..0f4117d174 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -321,6 +321,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax M2.5", "MiniMax-M25-highspeed": "MiniMax M2.5 Highspeed", + "MiniMax-M27": "MiniMax M2.7", + "MiniMax-M27-highspeed": "MiniMax M2.7 Highspeed", "alterUrl": "Alternate Base URL", "alterUrlPrompt": "Base URL path for API requests, default is https://api.minimax.io/v1", "temperature": "Temperature", @@ -412,6 +414,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index b04e435ec6..abcab0d2c7 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index fe56197dae..857f7aeccd 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 5707d012b0..74a883c31f 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -400,6 +400,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 93aceaabd0..baba6fe80a 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index 65da137640..14d6300125 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json @@ -401,6 +401,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index d581ddb71d..9ddf9809ae 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index 80aa3ab414..7f60475772 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -377,6 +377,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 2fd888d4c1..c08e67f1d3 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API 地址", "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", "temperature": "温度", diff --git a/src/i18n/locales/zhtw.json b/src/i18n/locales/zhtw.json index fdb20513b7..5cacdd5ea9 100644 --- a/src/i18n/locales/zhtw.json +++ b/src/i18n/locales/zhtw.json @@ -398,6 +398,8 @@ "name": "MiniMax API", "MiniMax-M25": "MiniMax-M2.5", "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", "alterUrl": "API 地址", "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", "temperature": "温度", diff --git a/tests/minimax-integration.test.js b/tests/minimax-integration.test.js index a1019915a1..f69e1bf1fa 100644 --- a/tests/minimax-integration.test.js +++ b/tests/minimax-integration.test.js @@ -113,7 +113,63 @@ async function run() { } }); - // ─── Test 2: MiniMax-M2.5-highspeed basic completion ────────── + // ─── Test 2: MiniMax-M2.7 basic completion ──────────────────── + await describe("MiniMax-M2.7 basic chat completion", async () => { + try { + const result = await chatCompletion("MiniMax-M2.7", [ + { role: "user", content: "Say hello in exactly one word." }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert(result.body.choices, "Response has choices array"); + assert( + result.body.choices.length > 0, + "Response has at least one choice", + ); + assert( + result.body.choices[0].message, + "First choice has a message", + ); + assert( + typeof result.body.choices[0].message.content === "string", + "Message content is a string", + ); + assert( + result.body.choices[0].message.content.length > 0, + `Got response: "${result.body.choices[0].message.content.substring(0, 50)}"`, + ); + assert( + result.body.model, + `Model in response: ${result.body.model}`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 3: MiniMax-M2.7-highspeed basic completion ───────── + await describe("MiniMax-M2.7-highspeed basic chat completion", async () => { + try { + const result = await chatCompletion("MiniMax-M2.7-highspeed", [ + { role: "user", content: "What is 3+3? Answer with just the number." }, + ]); + + assert(result.status === 200, `Status code is 200 (got ${result.status})`); + assert(result.body.choices, "Response has choices array"); + assert( + result.body.choices.length > 0, + "Response has at least one choice", + ); + assert( + result.body.choices[0].message.content.length > 0, + `Got response: "${result.body.choices[0].message.content.substring(0, 50)}"`, + ); + } catch (err) { + assert(false, `Request failed: ${err.message}`); + } + }); + + // ─── Test 4: MiniMax-M2.5-highspeed basic completion ────────── await describe("MiniMax-M2.5-highspeed basic chat completion", async () => { try { const result = await chatCompletion("MiniMax-M2.5-highspeed", [ @@ -135,7 +191,7 @@ async function run() { } }); - // ─── Test 3: Temperature handling ────────────────────────────── + // ─── Test 5: Temperature handling ────────────────────────────── await describe("Temperature handling", async () => { try { // Test with valid temperature (1.0) @@ -164,7 +220,7 @@ async function run() { } }); - // ─── Test 4: Multi-turn conversation ─────────────────────────── + // ─── Test 6: Multi-turn conversation ─────────────────────────── await describe("Multi-turn conversation", async () => { try { const result = await chatCompletion("MiniMax-M2.5-highspeed", [ @@ -189,7 +245,7 @@ async function run() { } }); - // ─── Test 5: System message ──────────────────────────────────── + // ─── Test 7: System message ──────────────────────────────────── await describe("System message support", async () => { try { const result = await chatCompletion("MiniMax-M2.5-highspeed", [ diff --git a/tests/minimax-unit.test.js b/tests/minimax-unit.test.js index 70285106d7..dd8650b303 100644 --- a/tests/minimax-unit.test.js +++ b/tests/minimax-unit.test.js @@ -41,6 +41,14 @@ describe("Bot files exist", () => { fs.existsSync(path.join(botsDir, "MiniMaxM25HighspeedBot.js")), "MiniMaxM25HighspeedBot.js exists", ); + assert( + fs.existsSync(path.join(botsDir, "MiniMaxM27Bot.js")), + "MiniMaxM27Bot.js exists", + ); + assert( + fs.existsSync(path.join(botsDir, "MiniMaxM27HighspeedBot.js")), + "MiniMaxM27HighspeedBot.js exists", + ); }); // ─── Bot file contents ──────────────────────────────────────────── @@ -124,6 +132,53 @@ describe("MiniMaxM25HighspeedBot.js contents", () => { ); }); +describe("MiniMaxM27Bot.js contents", () => { + const content = fs.readFileSync( + path.join(__dirname, "..", "src", "bots", "minimax", "MiniMaxM27Bot.js"), + "utf-8", + ); + + assert( + content.includes('static _model = "MiniMax-M2.7"'), + 'Model is "MiniMax-M2.7"', + ); + assert( + content.includes("extends MiniMaxAPIBot"), + "Extends MiniMaxAPIBot", + ); + assert( + content.includes('static _className = "MiniMaxM27Bot"'), + 'Class name is "MiniMaxM27Bot"', + ); +}); + +describe("MiniMaxM27HighspeedBot.js contents", () => { + const content = fs.readFileSync( + path.join( + __dirname, + "..", + "src", + "bots", + "minimax", + "MiniMaxM27HighspeedBot.js", + ), + "utf-8", + ); + + assert( + content.includes('static _model = "MiniMax-M2.7-highspeed"'), + 'Model is "MiniMax-M2.7-highspeed"', + ); + assert( + content.includes("extends MiniMaxAPIBot"), + "Extends MiniMaxAPIBot", + ); + assert( + content.includes('static _className = "MiniMaxM27HighspeedBot"'), + 'Class name is "MiniMaxM27HighspeedBot"', + ); +}); + // ─── Settings component ─────────────────────────────────────────── describe("Settings component", () => { const settingsPath = path.join( @@ -221,6 +276,18 @@ describe("Bot registration in index.js", () => { ), "MiniMaxM25HighspeedBot is imported", ); + assert( + indexContent.includes( + 'import MiniMaxM27Bot from "./minimax/MiniMaxM27Bot"', + ), + "MiniMaxM27Bot is imported", + ); + assert( + indexContent.includes( + 'import MiniMaxM27HighspeedBot from "./minimax/MiniMaxM27HighspeedBot"', + ), + "MiniMaxM27HighspeedBot is imported", + ); assert( indexContent.includes("MiniMaxM25Bot.getInstance()"), "MiniMaxM25Bot is registered in all array", @@ -229,6 +296,14 @@ describe("Bot registration in index.js", () => { indexContent.includes("MiniMaxM25HighspeedBot.getInstance()"), "MiniMaxM25HighspeedBot is registered in all array", ); + assert( + indexContent.includes("MiniMaxM27Bot.getInstance()"), + "MiniMaxM27Bot is registered in all array", + ); + assert( + indexContent.includes("MiniMaxM27HighspeedBot.getInstance()"), + "MiniMaxM27HighspeedBot is registered in all array", + ); // Check API tag assert( @@ -239,6 +314,14 @@ describe("Bot registration in index.js", () => { indexContent.includes('bots.getBotByClassName("MiniMaxM25HighspeedBot")'), "MiniMaxM25HighspeedBot is in botTags", ); + assert( + indexContent.includes('bots.getBotByClassName("MiniMaxM27Bot")'), + "MiniMaxM27Bot is in botTags", + ); + assert( + indexContent.includes('bots.getBotByClassName("MiniMaxM27HighspeedBot")'), + "MiniMaxM27HighspeedBot is in botTags", + ); }); // ─── i18n entries ───────────────────────────────────────────────── @@ -263,6 +346,14 @@ describe("i18n entries", () => { enLocale.minimaxApi["MiniMax-M25-highspeed"] === "MiniMax-M2.5-highspeed", "MiniMax-M2.5-highspeed model name is correct", ); + assert( + enLocale.minimaxApi["MiniMax-M27"] === "MiniMax-M2.7", + "MiniMax-M2.7 model name is correct", + ); + assert( + enLocale.minimaxApi["MiniMax-M27-highspeed"] === "MiniMax-M2.7-highspeed", + "MiniMax-M2.7-highspeed model name is correct", + ); // Check Chinese locale const zhLocale = JSON.parse( From 5c952abf7fd436baa4099e818edf28070dc0acb1 Mon Sep 17 00:00:00 2001 From: octo-patch Date: Wed, 3 Jun 2026 01:46:37 +0800 Subject: [PATCH 3/3] feat: upgrade MiniMax provider to M3 (default), keep M2.7 variants - Add MiniMax-M3 as new default model via MiniMaxM3Bot - Remove MiniMax-M2.5 and MiniMax-M2.5-highspeed models - Keep MiniMax-M2.7 and MiniMax-M2.7-highspeed - Update 11 i18n locale files to add M3 and drop M2.5 entries - Update unit and integration tests to reference M3 --- src/bots/index.js | 12 +-- src/bots/minimax/MiniMaxM25Bot.js | 10 -- src/bots/minimax/MiniMaxM25HighspeedBot.js | 10 -- src/bots/minimax/MiniMaxM3Bot.js | 10 ++ src/i18n/locales/de.json | 3 +- src/i18n/locales/en.json | 32 ++----- src/i18n/locales/es.json | 3 +- src/i18n/locales/fr.json | 3 +- src/i18n/locales/it.json | 3 +- src/i18n/locales/ja.json | 3 +- src/i18n/locales/ko.json | 3 +- src/i18n/locales/ru.json | 3 +- src/i18n/locales/vi.json | 3 +- src/i18n/locales/zh.json | 3 +- src/i18n/locales/zhtw.json | 3 +- tests/minimax-integration.test.js | 20 ++-- tests/minimax-unit.test.js | 101 ++++++--------------- 17 files changed, 69 insertions(+), 156 deletions(-) delete mode 100644 src/bots/minimax/MiniMaxM25Bot.js delete mode 100644 src/bots/minimax/MiniMaxM25HighspeedBot.js create mode 100644 src/bots/minimax/MiniMaxM3Bot.js diff --git a/src/bots/index.js b/src/bots/index.js index bf13b1a9fe..7bfdb34a23 100644 --- a/src/bots/index.js +++ b/src/bots/index.js @@ -66,10 +66,9 @@ import ClaudeAPIHaikuBot from "./anthropic/ClaudeAPIHaikuBot"; import Grok2APIBot from "./xai/Grok2APIBot"; import Grok3APIBot from "./xai/Grok3APIBot"; import Grok3MiniAPIBot from "./xai/Grok3MiniAPIBot"; -import MiniMaxM25Bot from "./minimax/MiniMaxM25Bot"; -import MiniMaxM25HighspeedBot from "./minimax/MiniMaxM25HighspeedBot"; import MiniMaxM27Bot from "./minimax/MiniMaxM27Bot"; import MiniMaxM27HighspeedBot from "./minimax/MiniMaxM27HighspeedBot"; +import MiniMaxM3Bot from "./minimax/MiniMaxM3Bot"; const all = [ Qihoo360AIBrainBot.getInstance(), @@ -138,10 +137,9 @@ const all = [ Grok2APIBot.getInstance(), Grok3APIBot.getInstance(), Grok3MiniAPIBot.getInstance(), - MiniMaxM25Bot.getInstance(), - MiniMaxM25HighspeedBot.getInstance(), MiniMaxM27Bot.getInstance(), MiniMaxM27HighspeedBot.getInstance(), + MiniMaxM3Bot.getInstance(), ]; const disabled = ["HuggingChatBot"]; @@ -238,10 +236,9 @@ export const botTags = { bots.getBotByClassName("Grok2APIBot"), bots.getBotByClassName("Grok3APIBot"), bots.getBotByClassName("Grok3MiniAPIBot"), - bots.getBotByClassName("MiniMaxM25Bot"), - bots.getBotByClassName("MiniMaxM25HighspeedBot"), bots.getBotByClassName("MiniMaxM27Bot"), bots.getBotByClassName("MiniMaxM27HighspeedBot"), + bots.getBotByClassName("MiniMaxM3Bot"), ], madeInChina: [ bots.getBotByClassName("Qihoo360AIBrainBot"), @@ -255,10 +252,9 @@ export const botTags = { bots.getBotByClassName("ChatGLMBot"), bots.getBotByClassName("ChatGLM4Bot"), bots.getBotByClassName("KimiBot"), - bots.getBotByClassName("MiniMaxM25Bot"), - bots.getBotByClassName("MiniMaxM25HighspeedBot"), bots.getBotByClassName("MiniMaxM27Bot"), bots.getBotByClassName("MiniMaxM27HighspeedBot"), + bots.getBotByClassName("MiniMaxM3Bot"), ], }; export default bots; diff --git a/src/bots/minimax/MiniMaxM25Bot.js b/src/bots/minimax/MiniMaxM25Bot.js deleted file mode 100644 index bc2a3f6a0c..0000000000 --- a/src/bots/minimax/MiniMaxM25Bot.js +++ /dev/null @@ -1,10 +0,0 @@ -import MiniMaxAPIBot from "./MiniMaxAPIBot"; - -export default class MiniMaxM25Bot extends MiniMaxAPIBot { - static _className = "MiniMaxM25Bot"; - static _logoFilename = "minimax-logo.svg"; - static _model = "MiniMax-M2.5"; - constructor() { - super(); - } -} diff --git a/src/bots/minimax/MiniMaxM25HighspeedBot.js b/src/bots/minimax/MiniMaxM25HighspeedBot.js deleted file mode 100644 index 3cb081a16a..0000000000 --- a/src/bots/minimax/MiniMaxM25HighspeedBot.js +++ /dev/null @@ -1,10 +0,0 @@ -import MiniMaxAPIBot from "./MiniMaxAPIBot"; - -export default class MiniMaxM25HighspeedBot extends MiniMaxAPIBot { - static _className = "MiniMaxM25HighspeedBot"; - static _logoFilename = "minimax-logo.svg"; - static _model = "MiniMax-M2.5-highspeed"; - constructor() { - super(); - } -} diff --git a/src/bots/minimax/MiniMaxM3Bot.js b/src/bots/minimax/MiniMaxM3Bot.js new file mode 100644 index 0000000000..d8cfe5b366 --- /dev/null +++ b/src/bots/minimax/MiniMaxM3Bot.js @@ -0,0 +1,10 @@ +import MiniMaxAPIBot from "./MiniMaxAPIBot"; + +export default class MiniMaxM3Bot extends MiniMaxAPIBot { + static _className = "MiniMaxM3Bot"; + static _logoFilename = "minimax-logo.svg"; + static _model = "MiniMax-M3"; + constructor() { + super(); + } +} diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index 746901aadf..c21d56f5ac 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 0f4117d174..4dd53915aa 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -319,16 +319,15 @@ }, "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax M2.5", - "MiniMax-M25-highspeed": "MiniMax M2.5 Highspeed", - "MiniMax-M27": "MiniMax M2.7", - "MiniMax-M27-highspeed": "MiniMax M2.7 Highspeed", - "alterUrl": "Alternate Base URL", - "alterUrlPrompt": "Base URL path for API requests, default is https://api.minimax.io/v1", + "MiniMax-M27": "MiniMax-M2.7", + "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", + "alterUrl": "API URL", + "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", - "temperaturePrompt": "Must be between 0.1 and 1.0. Higher values produce more creative but less deterministic responses", - "temperature01": "More deterministic", - "temperature1": "More creative" + "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", + "temperature01": "Precise", + "temperature1": "Creative" }, "xaiApi": { "name": "xAI API", @@ -409,18 +408,5 @@ "10": "10", "25": "25", "50": "50", - "100": "100", - "minimaxApi": { - "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", - "MiniMax-M27": "MiniMax-M2.7", - "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", - "alterUrl": "API URL", - "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", - "temperature": "Temperature", - "temperaturePrompt": "What sampling temperature to use, between 0.1 and 1", - "temperature01": "Precise", - "temperature1": "Creative" - } + "100": "100" } diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index abcab0d2c7..2ea8bdbe88 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 857f7aeccd..b98d42fc82 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 74a883c31f..32c58f8ab4 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -398,10 +398,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index baba6fe80a..87a9aa3742 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index 14d6300125..4d7a797491 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json @@ -399,10 +399,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 9ddf9809ae..8c38a3e86a 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index 7f60475772..79b8158e94 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -375,10 +375,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API URL", "alterUrlPrompt": "Custom API endpoint URL (default: https://api.minimax.io/v1)", "temperature": "Temperature", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index c08e67f1d3..ce0560c693 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API 地址", "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", "temperature": "温度", diff --git a/src/i18n/locales/zhtw.json b/src/i18n/locales/zhtw.json index 5cacdd5ea9..61344f8aa4 100644 --- a/src/i18n/locales/zhtw.json +++ b/src/i18n/locales/zhtw.json @@ -396,10 +396,9 @@ "100": "100", "minimaxApi": { "name": "MiniMax API", - "MiniMax-M25": "MiniMax-M2.5", - "MiniMax-M25-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M27": "MiniMax-M2.7", "MiniMax-M27-highspeed": "MiniMax-M2.7-highspeed", + "MiniMax-M3": "MiniMax-M3", "alterUrl": "API 地址", "alterUrlPrompt": "自定义 API 端点地址(默认:https://api.minimax.io/v1)", "temperature": "温度", diff --git a/tests/minimax-integration.test.js b/tests/minimax-integration.test.js index f69e1bf1fa..0ab032820d 100644 --- a/tests/minimax-integration.test.js +++ b/tests/minimax-integration.test.js @@ -79,10 +79,10 @@ async function run() { process.exit(1); } - // ─── Test 1: MiniMax-M2.5 basic completion ───────────────────── - await describe("MiniMax-M2.5 basic chat completion", async () => { + // ─── Test 1: MiniMax-M3 basic completion ───────────────────── + await describe("MiniMax-M3 basic chat completion", async () => { try { - const result = await chatCompletion("MiniMax-M2.5", [ + const result = await chatCompletion("MiniMax-M3", [ { role: "user", content: "Say hello in exactly one word." }, ]); @@ -169,10 +169,10 @@ async function run() { } }); - // ─── Test 4: MiniMax-M2.5-highspeed basic completion ────────── - await describe("MiniMax-M2.5-highspeed basic chat completion", async () => { + // ─── Test 4: MiniMax-M2.7-highspeed alternate completion ────────── + await describe("MiniMax-M2.7-highspeed alternate basic chat completion", async () => { try { - const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + const result = await chatCompletion("MiniMax-M2.7-highspeed", [ { role: "user", content: "What is 2+2? Answer with just the number." }, ]); @@ -196,7 +196,7 @@ async function run() { try { // Test with valid temperature (1.0) const result = await chatCompletion( - "MiniMax-M2.5-highspeed", + "MiniMax-M2.7-highspeed", [{ role: "user", content: "Hi" }], { temperature: 1.0, max_tokens: 10 }, ); @@ -207,7 +207,7 @@ async function run() { // Test with valid low temperature (0.1) const result2 = await chatCompletion( - "MiniMax-M2.5-highspeed", + "MiniMax-M2.7-highspeed", [{ role: "user", content: "Hi" }], { temperature: 0.1, max_tokens: 10 }, ); @@ -223,7 +223,7 @@ async function run() { // ─── Test 6: Multi-turn conversation ─────────────────────────── await describe("Multi-turn conversation", async () => { try { - const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + const result = await chatCompletion("MiniMax-M2.7-highspeed", [ { role: "user", content: "Remember the number 42." }, { role: "assistant", @@ -248,7 +248,7 @@ async function run() { // ─── Test 7: System message ──────────────────────────────────── await describe("System message support", async () => { try { - const result = await chatCompletion("MiniMax-M2.5-highspeed", [ + const result = await chatCompletion("MiniMax-M2.7-highspeed", [ { role: "system", content: "You are a helpful assistant. Always end your response with the word PINEAPPLE.", diff --git a/tests/minimax-unit.test.js b/tests/minimax-unit.test.js index dd8650b303..bd79776361 100644 --- a/tests/minimax-unit.test.js +++ b/tests/minimax-unit.test.js @@ -33,14 +33,6 @@ describe("Bot files exist", () => { fs.existsSync(path.join(botsDir, "MiniMaxAPIBot.js")), "MiniMaxAPIBot.js exists", ); - assert( - fs.existsSync(path.join(botsDir, "MiniMaxM25Bot.js")), - "MiniMaxM25Bot.js exists", - ); - assert( - fs.existsSync(path.join(botsDir, "MiniMaxM25HighspeedBot.js")), - "MiniMaxM25HighspeedBot.js exists", - ); assert( fs.existsSync(path.join(botsDir, "MiniMaxM27Bot.js")), "MiniMaxM27Bot.js exists", @@ -49,6 +41,10 @@ describe("Bot files exist", () => { fs.existsSync(path.join(botsDir, "MiniMaxM27HighspeedBot.js")), "MiniMaxM27HighspeedBot.js exists", ); + assert( + fs.existsSync(path.join(botsDir, "MiniMaxM3Bot.js")), + "MiniMaxM3Bot.js exists", + ); }); // ─── Bot file contents ──────────────────────────────────────────── @@ -85,50 +81,23 @@ describe("MiniMaxAPIBot.js contents", () => { ); }); -describe("MiniMaxM25Bot.js contents", () => { +describe("MiniMaxM3Bot.js contents", () => { const content = fs.readFileSync( - path.join(__dirname, "..", "src", "bots", "minimax", "MiniMaxM25Bot.js"), - "utf-8", - ); - - assert( - content.includes('static _model = "MiniMax-M2.5"'), - 'Model is "MiniMax-M2.5"', - ); - assert( - content.includes("extends MiniMaxAPIBot"), - "Extends MiniMaxAPIBot", - ); - assert( - content.includes('static _className = "MiniMaxM25Bot"'), - 'Class name is "MiniMaxM25Bot"', - ); -}); - -describe("MiniMaxM25HighspeedBot.js contents", () => { - const content = fs.readFileSync( - path.join( - __dirname, - "..", - "src", - "bots", - "minimax", - "MiniMaxM25HighspeedBot.js", - ), + path.join(__dirname, "..", "src", "bots", "minimax", "MiniMaxM3Bot.js"), "utf-8", ); assert( - content.includes('static _model = "MiniMax-M2.5-highspeed"'), - 'Model is "MiniMax-M2.5-highspeed"', + content.includes('static _model = "MiniMax-M3"'), + 'Model is "MiniMax-M3"', ); assert( content.includes("extends MiniMaxAPIBot"), "Extends MiniMaxAPIBot", ); assert( - content.includes('static _className = "MiniMaxM25HighspeedBot"'), - 'Class name is "MiniMaxM25HighspeedBot"', + content.includes('static _className = "MiniMaxM3Bot"'), + 'Class name is "MiniMaxM3Bot"', ); }); @@ -264,18 +233,6 @@ describe("Bot registration in index.js", () => { "utf-8", ); - assert( - indexContent.includes( - 'import MiniMaxM25Bot from "./minimax/MiniMaxM25Bot"', - ), - "MiniMaxM25Bot is imported", - ); - assert( - indexContent.includes( - 'import MiniMaxM25HighspeedBot from "./minimax/MiniMaxM25HighspeedBot"', - ), - "MiniMaxM25HighspeedBot is imported", - ); assert( indexContent.includes( 'import MiniMaxM27Bot from "./minimax/MiniMaxM27Bot"', @@ -289,12 +246,10 @@ describe("Bot registration in index.js", () => { "MiniMaxM27HighspeedBot is imported", ); assert( - indexContent.includes("MiniMaxM25Bot.getInstance()"), - "MiniMaxM25Bot is registered in all array", - ); - assert( - indexContent.includes("MiniMaxM25HighspeedBot.getInstance()"), - "MiniMaxM25HighspeedBot is registered in all array", + indexContent.includes( + 'import MiniMaxM3Bot from "./minimax/MiniMaxM3Bot"', + ), + "MiniMaxM3Bot is imported", ); assert( indexContent.includes("MiniMaxM27Bot.getInstance()"), @@ -304,16 +259,12 @@ describe("Bot registration in index.js", () => { indexContent.includes("MiniMaxM27HighspeedBot.getInstance()"), "MiniMaxM27HighspeedBot is registered in all array", ); - - // Check API tag - assert( - indexContent.includes('bots.getBotByClassName("MiniMaxM25Bot")'), - "MiniMaxM25Bot is in botTags", - ); assert( - indexContent.includes('bots.getBotByClassName("MiniMaxM25HighspeedBot")'), - "MiniMaxM25HighspeedBot is in botTags", + indexContent.includes("MiniMaxM3Bot.getInstance()"), + "MiniMaxM3Bot is registered in all array", ); + + // Check API tag assert( indexContent.includes('bots.getBotByClassName("MiniMaxM27Bot")'), "MiniMaxM27Bot is in botTags", @@ -322,6 +273,10 @@ describe("Bot registration in index.js", () => { indexContent.includes('bots.getBotByClassName("MiniMaxM27HighspeedBot")'), "MiniMaxM27HighspeedBot is in botTags", ); + assert( + indexContent.includes('bots.getBotByClassName("MiniMaxM3Bot")'), + "MiniMaxM3Bot is in botTags", + ); }); // ─── i18n entries ───────────────────────────────────────────────── @@ -338,14 +293,6 @@ describe("i18n entries", () => { enLocale.minimaxApi.name === "MiniMax API", 'name is "MiniMax API"', ); - assert( - enLocale.minimaxApi["MiniMax-M25"] === "MiniMax-M2.5", - "MiniMax-M2.5 model name is correct", - ); - assert( - enLocale.minimaxApi["MiniMax-M25-highspeed"] === "MiniMax-M2.5-highspeed", - "MiniMax-M2.5-highspeed model name is correct", - ); assert( enLocale.minimaxApi["MiniMax-M27"] === "MiniMax-M2.7", "MiniMax-M2.7 model name is correct", @@ -354,6 +301,10 @@ describe("i18n entries", () => { enLocale.minimaxApi["MiniMax-M27-highspeed"] === "MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed model name is correct", ); + assert( + enLocale.minimaxApi["MiniMax-M3"] === "MiniMax-M3", + "MiniMax-M3 model name is correct", + ); // Check Chinese locale const zhLocale = JSON.parse(