diff --git a/default/content/presets/openai/Default.json b/default/content/presets/openai/Default.json index 24115b9c1..db422d1bf 100644 --- a/default/content/presets/openai/Default.json +++ b/default/content/presets/openai/Default.json @@ -10,6 +10,8 @@ "mistralai_model": "mistral-large-latest", "chutes_model": "deepseek-ai/DeepSeek-V3-0324", "chutes_sort_models": "alphabetically", + "minimax_model": "MiniMax-M2.7", + "minimax_endpoint": "global", "electronhub_model": "gpt-4o-mini", "electronhub_sort_models": "alphabetically", "electronhub_group_models": false, diff --git a/public/img/minimax.svg b/public/img/minimax.svg new file mode 100644 index 000000000..1d32449ab --- /dev/null +++ b/public/img/minimax.svg @@ -0,0 +1 @@ +Minimax \ No newline at end of file diff --git a/public/index.html b/public/index.html index c4f05c214..875a1ef3b 100644 --- a/public/index.html +++ b/public/index.html @@ -697,7 +697,7 @@ -
+
Temperature
@@ -749,7 +749,7 @@
-
+
Top P
@@ -1997,7 +1997,7 @@
-
+
+
+

MiniMax API Key

+
+ + +
+
+ For privacy reasons, your API key will be hidden after you click 'Connect'. +
+

MiniMax Endpoint

+ +

MiniMax Model

+ +

Electron Hub API Key

diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index d056c13fe..186ccad0f 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -408,6 +408,7 @@ function RA_autoconnect(PrevApi) { || (secret_state[SECRET_KEYS.ZAI] && oai_settings.chat_completion_source == chat_completion_sources.ZAI) || (secret_state[SECRET_KEYS.POLLINATIONS] && oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS) || (secret_state[SECRET_KEYS.WORKERS_AI] && oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI) + || (secret_state[SECRET_KEYS.MINIMAX] && oai_settings.chat_completion_source == chat_completion_sources.MINIMAX) || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) || (secret_state[SECRET_KEYS.AZURE_OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) ) { diff --git a/public/scripts/custom-request.js b/public/scripts/custom-request.js index e7a2a5062..82fbce6db 100644 --- a/public/scripts/custom-request.js +++ b/public/scripts/custom-request.js @@ -591,7 +591,7 @@ export class ChatCompletionService { } // Ensure api-url is properly applied for all sources that accept it - ['custom_url', 'vertexai_region', 'zai_endpoint', 'siliconflow_endpoint'].forEach(field => { + ['custom_url', 'vertexai_region', 'zai_endpoint', 'siliconflow_endpoint', 'minimax_endpoint'].forEach(field => { // The order is: connection profile => CC preset => CC settings overridePayload[field] = overridePayload[field] || settings[field] || oai_settings[field]; }); diff --git a/public/scripts/extensions/shared.js b/public/scripts/extensions/shared.js index 0e11cca85..81224859e 100644 --- a/public/scripts/extensions/shared.js +++ b/public/scripts/extensions/shared.js @@ -448,6 +448,7 @@ export class ConnectionManagerRequestService { vertexai_region: profile['api-url'], zai_endpoint: profile['api-url'], siliconflow_endpoint: profile['api-url'], + minimax_endpoint: profile['api-url'], reverse_proxy: proxyPreset?.url, proxy_password: proxyPreset?.password, custom_prompt_post_processing: profile['prompt-post-processing'], diff --git a/public/scripts/openai.js b/public/scripts/openai.js index fdba275f3..104c25e8d 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -47,6 +47,7 @@ import { SECRET_KEYS, secret_state, writeSecret } from './secrets.js'; import { getEventSourceStream } from './sse-stream.js'; import { + clamp, createThumbnail, delay, download, @@ -197,6 +198,7 @@ export const chat_completion_sources = { ZAI: 'zai', SILICONFLOW: 'siliconflow', WORKERS_AI: 'workers_ai', + MINIMAX: 'minimax', }; const character_names_behavior = { @@ -270,6 +272,11 @@ export const SILICONFLOW_ENDPOINT = { CN: 'cn', }; +export const MINIMAX_ENDPOINT = { + GLOBAL: 'global', + CN: 'cn', +}; + const sensitiveFields = [ 'reverse_proxy', 'proxy_password', @@ -319,6 +326,8 @@ export const settingsToUpdate = { chutes_sort_models: ['#chutes_sort_models', 'chutes_sort_models', false, true], siliconflow_model: ['#model_siliconflow_select', 'siliconflow_model', false, true], siliconflow_endpoint: ['#siliconflow_endpoint', 'siliconflow_endpoint', false, true], + minimax_model: ['#model_minimax_select', 'minimax_model', false, true], + minimax_endpoint: ['#minimax_endpoint', 'minimax_endpoint', false, true], electronhub_model: ['#model_electronhub_select', 'electronhub_model', false, true], electronhub_sort_models: ['#electronhub_sort_models', 'electronhub_sort_models', false, true], electronhub_group_models: ['#electronhub_group_models', 'electronhub_group_models', false, true], @@ -431,6 +440,8 @@ const default_settings = { chutes_sort_models: 'alphabetically', siliconflow_model: 'deepseek-ai/DeepSeek-V3', siliconflow_endpoint: SILICONFLOW_ENDPOINT.GLOBAL, + minimax_model: 'MiniMax-M2.7', + minimax_endpoint: MINIMAX_ENDPOINT.GLOBAL, electronhub_model: 'gpt-4o-mini', electronhub_sort_models: 'alphabetically', electronhub_group_models: false, @@ -1712,6 +1723,8 @@ export function getChatCompletionModel(settings = null) { return settings.groq_model; case chat_completion_sources.SILICONFLOW: return settings.siliconflow_model; + case chat_completion_sources.MINIMAX: + return settings.minimax_model; case chat_completion_sources.ELECTRONHUB: return settings.electronhub_model; case chat_completion_sources.CHUTES: @@ -2845,6 +2858,14 @@ export async function createGenerationParameters(settings, model, type, messages generate_data.siliconflow_endpoint = settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL; } + if (settings.chat_completion_source === chat_completion_sources.MINIMAX) { + generate_data.minimax_endpoint = settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL; + // MiniMax requires temperature in (0.0, 1.0]; zero is rejected. + if (Number.isFinite(generate_data.temperature)) { + generate_data.temperature = clamp(generate_data.temperature, Number.EPSILON, 1.0); + } + } + if (settings.chat_completion_source === chat_completion_sources.WORKERS_AI) { generate_data.workers_ai_account_id = settings.workers_ai_account_id; generate_data.top_k = settings.top_k_openai > 0 ? Math.min(Number(settings.top_k_openai), 50) : undefined; @@ -4255,6 +4276,7 @@ async function getStatusOpen() { chat_completion_sources.VERTEXAI, chat_completion_sources.PERPLEXITY, chat_completion_sources.ZAI, + chat_completion_sources.MINIMAX, ]; if (noValidateSources.includes(oai_settings.chat_completion_source)) { let status = t`Key saved; press \"Test Message\" to verify.`; @@ -4312,6 +4334,10 @@ async function getStatusOpen() { data.siliconflow_endpoint = oai_settings.siliconflow_endpoint; } + if (oai_settings.chat_completion_source === chat_completion_sources.MINIMAX) { + data.minimax_endpoint = oai_settings.minimax_endpoint; + } + if (oai_settings.chat_completion_source === chat_completion_sources.WORKERS_AI) { data.workers_ai_account_id = oai_settings.workers_ai_account_id; } @@ -5266,6 +5292,15 @@ async function onModelChange() { oai_settings.siliconflow_model = value; } + if ($(this).is('#model_minimax_select')) { + if (!value) { + console.debug('Null MiniMax model selected. Ignoring.'); + return; + } + console.log('MiniMax model changed to', value); + oai_settings.minimax_model = value; + } + if ($(this).is('#model_electronhub_select')) { if (!value || !hasModelsLoaded) { console.debug('Null ElectronHub model selected. Ignoring.'); @@ -5707,6 +5742,15 @@ async function onModelChange() { $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); } + if (oai_settings.chat_completion_source === chat_completion_sources.MINIMAX) { + const maxContext = oai_settings.minimax_model === 'M2-her' ? 65536 : 204800; + $('#openai_max_context').attr('max', maxContext); + oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); + $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); + oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai); + $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input'); + } + if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) { const maxContext = getZaiMaxContext(oai_settings.zai_model, oai_settings.max_context_unlocked); $('#openai_max_context').attr('max', maxContext); @@ -5780,6 +5824,7 @@ async function onConnectButtonClick(e) { [chat_completion_sources.CHUTES]: { key: SECRET_KEYS.CHUTES, selector: '#api_key_chutes', proxy: false }, [chat_completion_sources.POLLINATIONS]: { key: SECRET_KEYS.POLLINATIONS, selector: '#api_key_pollinations', proxy: false }, [chat_completion_sources.WORKERS_AI]: { key: SECRET_KEYS.WORKERS_AI, selector: '#api_key_workers_ai', proxy: false }, + [chat_completion_sources.MINIMAX]: { key: SECRET_KEYS.MINIMAX, selector: '#api_key_minimax', proxy: false }, }; // Vertex AI Express version - use API key @@ -5845,6 +5890,8 @@ function toggleChatCompletionForms() { $('#model_chutes_select').trigger('change'); } else if (oai_settings.chat_completion_source == chat_completion_sources.SILICONFLOW) { $('#model_siliconflow_select').trigger('change'); + } else if (oai_settings.chat_completion_source == chat_completion_sources.MINIMAX) { + $('#model_minimax_select').trigger('change'); } else if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) { $('#model_electronhub_select').trigger('change'); } else if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) { @@ -7028,6 +7075,10 @@ export function initOpenAI() { oai_settings.siliconflow_endpoint = String($(this).val()); saveSettingsDebounced(); }); + $('#minimax_endpoint').on('input', function () { + oai_settings.minimax_endpoint = String($(this).val()); + saveSettingsDebounced(); + }); $('#workers_ai_account_id').on('input', function () { oai_settings.workers_ai_account_id = String($(this).val()); saveSettingsDebounced(); @@ -7048,6 +7099,7 @@ export function initOpenAI() { $('#model_groq_select').on('change', onModelChange); $('#model_chutes_select').on('change', onModelChange); $('#model_siliconflow_select').on('change', onModelChange); + $('#model_minimax_select').on('change', onModelChange); $('#model_electronhub_select').on('change', onModelChange); $('#model_nanogpt_select').on('change', onModelChange); $('#model_deepseek_select').on('change', onModelChange); diff --git a/public/scripts/secrets.js b/public/scripts/secrets.js index 01788393c..57c8fc410 100644 --- a/public/scripts/secrets.js +++ b/public/scripts/secrets.js @@ -130,7 +130,7 @@ const FRIENDLY_NAMES = { [SECRET_KEYS.LINGVA_URL]: 'Lingva Endpoint (e.g. https://lingva.ml/api/v1)', [SECRET_KEYS.ONERING_URL]: 'OneRingTranslator Endpoint (e.g. http://127.0.0.1:4990/translate)', [SECRET_KEYS.DEEPLX_URL]: 'DeepLX Endpoint (e.g. http://127.0.0.1:1188/translate)', - [SECRET_KEYS.MINIMAX]: 'MiniMax TTS', + [SECRET_KEYS.MINIMAX]: 'MiniMax', [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID', [SECRET_KEYS.MOONSHOT]: 'Moonshot AI', [SECRET_KEYS.COMETAPI]: 'CometAPI', @@ -184,6 +184,7 @@ const INPUT_MAP = { [SECRET_KEYS.AZURE_OPENAI]: '#api_key_azure_openai', [SECRET_KEYS.ZAI]: '#api_key_zai', [SECRET_KEYS.SILICONFLOW]: '#api_key_siliconflow', + [SECRET_KEYS.MINIMAX]: '#api_key_minimax', [SECRET_KEYS.POLLINATIONS]: '#api_key_pollinations', [SECRET_KEYS.WORKERS_AI]: '#api_key_workers_ai', }; diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 5dd24aeef..47252bf14 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -70,7 +70,7 @@ import { hideChatMessageRange } from './chats.js'; import { getContext, saveMetadataDebounced } from './extensions.js'; import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; import { findGroupMemberId, groups, is_group_generating, openGroupById, regenerateGroup, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js'; -import { chat_completion_sources, oai_settings, promptManager, SILICONFLOW_ENDPOINT, ZAI_ENDPOINT } from './openai.js'; +import { chat_completion_sources, MINIMAX_ENDPOINT, oai_settings, promptManager, SILICONFLOW_ENDPOINT, ZAI_ENDPOINT } from './openai.js'; import { user_avatar } from './personas.js'; import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, playMessageSound, power_user } from './power-user.js'; import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; @@ -3134,6 +3134,7 @@ export function initDefaultSlashCommands() { new SlashCommandEnumValue('zai', 'Z.AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'zai')), 'Z'), new SlashCommandEnumValue('vertexai', 'Google Vertex AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'vertexai')), 'V'), new SlashCommandEnumValue('siliconflow', 'SiliconFlow', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'siliconflow')), 'S'), + new SlashCommandEnumValue('minimax', 'MiniMax', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'minimax')), 'M'), new SlashCommandEnumValue('kobold', 'KoboldAI Classic', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'kobold')), 'K'), ...Object.values(textgen_types).filter(api => Object.keys(SERVER_INPUTS).includes(api)).map(api => new SlashCommandEnumValue(api, null, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'textgenerationwebui')), 'T')), ], @@ -3167,7 +3168,7 @@ export function initDefaultSlashCommands() { ${t`If a manual API is provided to set the URL, make sure to set connect=false, as auto-connect only works for the currently selected API, or consider switching to it with /api first.`}
- ${t`This slash command works for most of the Text Completion sources, KoboldAI Classic, and also Custom OpenAI compatible, Z.AI, SiliconFlow, and Google Vertex AI for the Chat Completion sources. If unsure which APIs are supported, check the auto-completion of the optional api argument of this command.`} + ${t`This slash command works for most of the Text Completion sources, KoboldAI Classic, and also Custom OpenAI compatible, Z.AI, SiliconFlow, MiniMax, and Google Vertex AI for the Chat Completion sources. If unsure which APIs are supported, check the auto-completion of the optional api argument of this command.`}
`, })); @@ -6261,6 +6262,7 @@ function getModelOptions(quiet) { { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ }, { id: 'model_chutes_select', api: 'openai', type: chat_completion_sources.CHUTES }, { id: 'model_siliconflow_select', api: 'openai', type: chat_completion_sources.SILICONFLOW }, + { id: 'model_minimax_select', api: 'openai', type: chat_completion_sources.MINIMAX }, { id: 'model_electronhub_select', api: 'openai', type: chat_completion_sources.ELECTRONHUB }, { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT }, { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK }, @@ -6628,6 +6630,32 @@ async function setApiUrlCallback({ api = null, connect = 'true', quiet = 'false' return oai_settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL; } + const isCurrentlyMinimax = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.MINIMAX; + if (api === chat_completion_sources.MINIMAX || (!api && isCurrentlyMinimax)) { + if (!url) { + return oai_settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL; + } + + const permittedValues = Object.values(MINIMAX_ENDPOINT); + if (!permittedValues.includes(url)) { + !isQuiet && toastr.warning(t`Valid options are: ${permittedValues.join(', ')}`, t`MiniMax endpoint '${url}' is not a valid option.`); + return ''; + } + + if (!isCurrentlyMinimax && autoConnect) { + toastr.warning(t`MiniMax is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.`); + return ''; + } + + $('#minimax_endpoint').val(url).trigger('input'); + + if (autoConnect) { + $('#api_button_openai').trigger('click'); + } + + return oai_settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL; + } + const isCurrentlyVertexAI = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.VERTEXAI; if (api === chat_completion_sources.VERTEXAI || (!api && isCurrentlyVertexAI)) { const defaultRegion = 'us-central1'; diff --git a/public/scripts/tokenizers.js b/public/scripts/tokenizers.js index 7f574e0eb..ce722b77f 100644 --- a/public/scripts/tokenizers.js +++ b/public/scripts/tokenizers.js @@ -694,6 +694,11 @@ export function getTokenizerModel() { } } + if (oai_settings.chat_completion_source == chat_completion_sources.MINIMAX) { + // MiniMax uses a proprietary tokenizer; fall back to a coarse OpenAI estimation. + return 'gpt-3.5-turbo'; + } + if (oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI && oai_settings.workers_ai_model) { const model = oai_settings.workers_ai_model.toLowerCase(); diff --git a/public/scripts/tool-calling.js b/public/scripts/tool-calling.js index 1cdbb4d2f..858908b6d 100644 --- a/public/scripts/tool-calling.js +++ b/public/scripts/tool-calling.js @@ -667,6 +667,7 @@ export class ToolManager { chat_completion_sources.SILICONFLOW, chat_completion_sources.NANOGPT, chat_completion_sources.WORKERS_AI, + chat_completion_sources.MINIMAX, ]; return supportedSources.includes(settings.chat_completion_source); } diff --git a/src/constants.js b/src/constants.js index 281228a2f..6cd43785d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -209,6 +209,7 @@ export const CHAT_COMPLETION_SOURCES = { AZURE_OPENAI: 'azure_openai', ZAI: 'zai', SILICONFLOW: 'siliconflow', + MINIMAX: 'minimax', WORKERS_AI: 'workers_ai', }; @@ -557,3 +558,8 @@ export const SILICONFLOW_ENDPOINT = { GLOBAL: 'global', CN: 'cn', }; + +export const MINIMAX_ENDPOINT = { + GLOBAL: 'global', + CN: 'cn', +}; diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index dd99bfb43..df911e65f 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -18,6 +18,7 @@ import { OPENROUTER_HEADERS, VERTEX_SAFETY, SILICONFLOW_ENDPOINT, + MINIMAX_ENDPOINT, ZAI_ENDPOINT, } from '../../constants.js'; import { @@ -89,6 +90,8 @@ const API_ZAI_COMMON = 'https://api.z.ai/api/paas/v4'; const API_ZAI_CODING = 'https://api.z.ai/api/coding/paas/v4'; const API_SILICONFLOW = 'https://api.siliconflow.com/v1'; const API_SILICONFLOW_CN = 'https://api.siliconflow.cn/v1'; +const API_MINIMAX = 'https://api.minimax.io/v1'; +const API_MINIMAX_CN = 'https://api.minimaxi.com/v1'; const API_OPENROUTER = 'https://openrouter.ai/api/v1'; const API_WORKERS_AI = 'https://api.cloudflare.com/client/v4/accounts'; @@ -1552,7 +1555,87 @@ async function sendChutesRequest(request, response) { } /** - * Sends a chat completion request to Azure OpenAI. + * Sends a request to MiniMax. + * @param {express.Request} request Express request + * @param {express.Response} response Express response + */ +async function sendMinimaxRequest(request, response) { + const apiUrl = request.body.minimax_endpoint === MINIMAX_ENDPOINT.CN + ? API_MINIMAX_CN : API_MINIMAX; + const apiKey = readSecret(request.user.directories, SECRET_KEYS.MINIMAX, request.body.secret_id); + + if (!apiKey) { + console.warn('MiniMax key is missing.'); + return response.status(400).send({ error: true }); + } + + const controller = new AbortController(); + request.socket.removeAllListeners('close'); + request.socket.on('close', function () { + controller.abort(); + }); + + try { + // MiniMax does not allow consecutive messages with the same role. + // Merge them into a single message to avoid "invalid chat setting (2013)". + const messages = postProcessPrompt(request.body.messages, PROMPT_PROCESSING_TYPE.MERGE_TOOLS, getPromptNames(request)); + + let bodyParams = {}; + + if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { + bodyParams['tools'] = request.body.tools; + bodyParams['tool_choice'] = request.body.tool_choice; + } + + const requestBody = { + 'messages': messages, + 'model': request.body.model, + 'temperature': request.body.temperature, + 'max_tokens': request.body.model === 'M2-her' ? Math.min(request.body.max_tokens, 2048) : request.body.max_tokens, + 'stream': request.body.stream, + 'top_p': request.body.top_p, + 'stop': request.body.stop, + ...bodyParams, + }; + + const config = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + apiKey, + }, + body: JSON.stringify(requestBody), + signal: controller.signal, + }; + + console.debug('MiniMax request:', requestBody); + + const generateResponse = await fetch(apiUrl + '/chat/completions', config); + + if (request.body.stream) { + await forwardFetchResponse(generateResponse, response); + } else { + if (!generateResponse.ok) { + const errorText = await generateResponse.text(); + console.warn('MiniMax returned error: ', errorText); + const errorJson = tryParse(errorText) ?? { error: true }; + return response.status(500).send(errorJson); + } + const generateResponseJson = await generateResponse.json(); + console.debug('MiniMax response:', generateResponseJson); + return response.send(generateResponseJson); + } + } catch (error) { + console.error('Error communicating with MiniMax: ', error); + if (!response.headersSent) { + response.send({ error: true }); + } else { + response.end(); + } + } +} + +/** * @param {express.Request} request Express request object (contains request.body with all generate_data) * @param {express.Response} response Express response object */ @@ -2096,6 +2179,7 @@ router.post('/generate', async function (request, response) { case CHAT_COMPLETION_SOURCES.AIMLAPI: return await sendAimlapiRequest(request, response); case CHAT_COMPLETION_SOURCES.XAI: return await sendXaiRequest(request, response); case CHAT_COMPLETION_SOURCES.CHUTES: return await sendChutesRequest(request, response); + case CHAT_COMPLETION_SOURCES.MINIMAX: return await sendMinimaxRequest(request, response); case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return await sendElectronHubRequest(request, response); case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return await sendAzureOpenAIRequest(request, response); }