feat: add MiniMax as a chat completion provider (#5452) * feat: add MiniMax as a chat completion provider Add MiniMax (https://www.minimax.io) as a first-class chat completion provider. MiniMax already has TTS integration in SillyTavern; this extends support to LLM chat completions via their OpenAI-compatible API. Supported models: - MiniMax-M2.5 (default) — 204K context - MiniMax-M2.5-highspeed — same capability, faster inference Key implementation details: - Reuses existing SECRET_KEYS.MINIMAX (shared with TTS) - API endpoint: https://api.minimax.io/v1 - Temperature clamped to (0.0, 1.0] as required by MiniMax API - Returns hardcoded model list since MiniMax doesn't expose /v1/models - Full UI integration: model selector, sampler parameters, streaming Co-Authored-By: octo-patch <octo-patch@users.noreply.github.com> * feat: upgrade MiniMax default model to M2.7 - Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list - Set MiniMax-M2.7 as default model - Keep all previous models as alternatives * feat: independent request function, vision support, temp clamping for MiniMax - Extract sendMinimaxRequest() following Chutes pattern (PR #4844) with function calling and JSON Schema structured output support - Clamp temperature to (0.01, 1.0] on backend; limit frontend UI max to 1.0 - Enable image inlining for MiniMax M2.7 model - Add MiniMax to slash-commands model selector and tokenizer mapping - Add minimax_model to default preset * feat: add VLM-based vision support for MiniMax M2.7 M2.7 does not natively accept image input. When images are detected in messages, pre-process them via the MiniMax VLM endpoint (/v1/coding_plan/vlm) to convert images to text descriptions before sending to the chat completions API. Uses the same API key. * feat: add M2-her model to MiniMax provider M2-her is MiniMax's dialogue/roleplay-optimized model with 64K context and 2048 max completion tokens. Text-only (no vision). * feat: add MiniMax China endpoint (minimaxi.com) support Add endpoint selector (Global/China) for MiniMax, mirroring the SiliconFlow pattern. Users can now choose between api.minimax.io (international) and api.minimaxi.com (China domestic). * fix: merge consecutive same-role messages for MiniMax MiniMax API rejects consecutive messages with the same role with error 'invalid chat setting (2013)'. Merge them before sending. * review: address PR feedback on MiniMax provider Backend (src/endpoints/backends/chat-completions.js): - Drop the entire MiniMax VLM image-preprocessing path; vision is no longer advertised for this provider, so M2.7 messages now go straight to /chat/completions without a separate VLM round-trip. - Drop the json_schema -> response_format mapping (MiniMax does not document structured-output support; relying on it was speculative). - Drop the backend temperature clamp; the same clamp now lives in the frontend so the wire payload matches what the user sees. - Drop the MINIMAX branch in /status that returned a hard-coded model list; the frontend hardcodes the same list and bypasses /status via noValidateSources, so the round-trip was wasted. - Add a streaming Transform + non-streaming helper that move <think>...</think> blocks from delta.content / message.content to reasoning_content. MiniMax M2.x emit chain-of-thought inline in content; without this transform the raw <think> tags leak into the rendered chat. Includes a state machine that holds back partial marker bytes so a marker split across SSE chunks is still detected. Frontend: - public/scripts/openai.js: add MINIMAX to noValidateSources so the key is accepted without a /models call; remove the dead saveModelList branch; clamp temperature to (0.0, 1.0] in createGenerationParameters. - public/scripts/reasoning.js: add MINIMAX to the non-streaming reasoning_content extraction case (the backend transform now produces this field for MiniMax responses). - public/scripts/slash-commands.js: add MINIMAX to the /api enum and add a MiniMax case to /api-url so users can switch endpoint by command. - public/scripts/custom-request.js: pass minimax_endpoint through the override-payload merge alongside the other per-source endpoint fields. - public/scripts/tokenizers.js: stop returning openai_model (which was always a MiniMax model id and thus an unknown tokenizer); fall back to gpt-3.5-turbo for a coarse but functional estimate. - public/scripts/tool-calling.js: add MINIMAX to supportedSources so function-calling settings are exposed. - public/index.html: drop the "-- Connect to the API --" placeholder option from the model select (the model list is hardcoded and always populated); remove minimax from the vision data-source attributes on the inline-media controls. - public/img/minimax.svg: replace the multicolor brand SVG with a single-color currentColor version that matches the other provider icons in the connect panel. * review: drop backend <think> parsing, defer to frontend Per reviewer feedback: SillyTavern's reasoningHandler / reasoning_auto_parse setting already extracts <think>...</think> blocks on the client side, so the backend doesn't need to rewrite MiniMax responses. Removes the SSE Transform, the non-streaming helper, and the corresponding case in reasoning.js. * fix: remove isImageInliningSupported declaration for MINIMAX * fix: remove MINIMAX from stream reasoning parsing * fix: add to autoconnect logic * fix: add missing MINIMAX models from docs * fix: freq. and pres. pen aren't supported for MINIMAX * fix: use clamp function for adjusting temperature * fix: pass minimax_endpoint from connection profile to ChatCompletionService * fix: update supported APIs in slash command documentation * fix: replace bespoke merge with standard MERGE_TOOLS processing * fix: add data-i18n attributes for headers --------- Co-authored-by: octo-patch <octo-patch@users.noreply.github.com> Co-authored-by: octo-patch <octo-patch@github.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

aecbb9a2ee643ff6e49bee46a651f4b440ce67a5

Octopus <liyuan851277048@icloud.com>

Signed
13 files changed, +217 -8Ignore whitespace
default/content/presets/openai/Default.json+2 -0
@@ -10,6 +10,8 @@
10 "mistralai_model": "mistral-large-latest",10 "mistralai_model": "mistral-large-latest",
11 "chutes_model": "deepseek-ai/DeepSeek-V3-0324",11 "chutes_model": "deepseek-ai/DeepSeek-V3-0324",
12 "chutes_sort_models": "alphabetically",12 "chutes_sort_models": "alphabetically",
13 "minimax_model": "MiniMax-M2.7",
14 "minimax_endpoint": "global",
13 "electronhub_model": "gpt-4o-mini",15 "electronhub_model": "gpt-4o-mini",
14 "electronhub_sort_models": "alphabetically",16 "electronhub_sort_models": "alphabetically",
15 "electronhub_group_models": false,17 "electronhub_group_models": false,
public/img/minimax.svg+1 -0
@@ -0,0 +1 @@
1<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Minimax</title><path d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z"></path></svg>
\ No newline at end of file1 \ No newline at end of file
public/index.html+30 -3
@@ -697,7 +697,7 @@
697 </span>697 </span>
698 </div>698 </div>
699 </div>699 </div>
700 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,siliconflow,electronhub,chutes,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai,workers_ai">700 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,siliconflow,minimax,electronhub,chutes,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai,workers_ai">
701 <div class="range-block-title" data-i18n="Temperature">701 <div class="range-block-title" data-i18n="Temperature">
702 Temperature702 Temperature
703 </div>703 </div>
@@ -749,7 +749,7 @@
749 </div>749 </div>
750 </div>750 </div>
751 </div>751 </div>
752 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,siliconflow,electronhub,chutes,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai,workers_ai">752 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,siliconflow,minimax,electronhub,chutes,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai,workers_ai">
753 <div class="range-block-title" data-i18n="Top P">753 <div class="range-block-title" data-i18n="Top P">
754 Top P754 Top P
755 </div>755 </div>
@@ -1997,7 +1997,7 @@
1997 </b>1997 </b>
1998 </div>1998 </div>
1999 </div>1999 </div>
2000 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,siliconflow,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks,cometapi,electronhub,chutes,azure_openai,zai,nanogpt,workers_ai">2000 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,siliconflow,minimax,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks,cometapi,electronhub,chutes,azure_openai,zai,nanogpt,workers_ai">
2001 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">2001 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
2002 <input id="openai_function_calling" type="checkbox" />2002 <input id="openai_function_calling" type="checkbox" />
2003 <span data-i18n="Enable function calling">Enable function calling</span>2003 <span data-i18n="Enable function calling">Enable function calling</span>
@@ -2907,6 +2907,7 @@
2907 <option value="makersuite">Google AI Studio</option>2907 <option value="makersuite">Google AI Studio</option>
2908 <option value="vertexai">Google Vertex AI</option>2908 <option value="vertexai">Google Vertex AI</option>
2909 <option value="mistralai">MistralAI</option>2909 <option value="mistralai">MistralAI</option>
2910 <option value="minimax">MiniMax</option>
2910 <option value="moonshot">Moonshot AI</option>2911 <option value="moonshot">Moonshot AI</option>
2911 <option value="nanogpt">NanoGPT</option>2912 <option value="nanogpt">NanoGPT</option>
2912 <option value="openrouter">OpenRouter</option>2913 <option value="openrouter">OpenRouter</option>
@@ -3571,6 +3572,32 @@
3571 <option value="" data-i18n="-- Connect to the API --">-- Connect to the API --</option>3572 <option value="" data-i18n="-- Connect to the API --">-- Connect to the API --</option>
3572 </select>3573 </select>
3573 </div>3574 </div>
3575 <div id="minimax_form" data-source="minimax">
3576 <h4 data-i18n="MiniMax API Key">MiniMax API Key</h4>
3577 <div class="flex-container">
3578 <input id="api_key_minimax" name="api_key_minimax" class="text_pole flex1" value="" type="text" autocomplete="off">
3579 <div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_minimax"></div>
3580 </div>
3581 <div data-for="api_key_minimax" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
3582 For privacy reasons, your API key will be hidden after you click 'Connect'.
3583 </div>
3584 <h4 data-i18n="MiniMax Endpoint">MiniMax Endpoint</h4>
3585 <select id="minimax_endpoint">
3586 <option value="global" data-i18n="Global (minimax.io)">Global (minimax.io)</option>
3587 <option value="cn" data-i18n="China (minimaxi.com)">China (minimaxi.com)</option>
3588 </select>
3589 <h4 data-i18n="MiniMax Model">MiniMax Model</h4>
3590 <select id="model_minimax_select">
3591 <option value="MiniMax-M2.7">MiniMax-M2.7</option>
3592 <option value="MiniMax-M2.7-highspeed">MiniMax-M2.7-highspeed</option>
3593 <option value="MiniMax-M2.5">MiniMax-M2.5</option>
3594 <option value="MiniMax-M2.5-highspeed">MiniMax-M2.5-highspeed</option>
3595 <option value="MiniMax-M2.1">MiniMax-M2.1</option>
3596 <option value="MiniMax-M2.1-highspeed">MiniMax-M2.1-highspeed</option>
3597 <option value="MiniMax-M2">MiniMax-M2</option>
3598 <option value="M2-her">M2-her</option>
3599 </select>
3600 </div>
3574 <div id="electronhub_form" data-source="electronhub">3601 <div id="electronhub_form" data-source="electronhub">
3575 <h4 data-i18n="Electron Hub API Key">Electron Hub API Key</h4>3602 <h4 data-i18n="Electron Hub API Key">Electron Hub API Key</h4>
3576 <div>3603 <div>
public/scripts/RossAscends-mods.js+1 -0
@@ -408,6 +408,7 @@ function RA_autoconnect(PrevApi) {
408 || (secret_state[SECRET_KEYS.ZAI] && oai_settings.chat_completion_source == chat_completion_sources.ZAI)408 || (secret_state[SECRET_KEYS.ZAI] && oai_settings.chat_completion_source == chat_completion_sources.ZAI)
409 || (secret_state[SECRET_KEYS.POLLINATIONS] && oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)409 || (secret_state[SECRET_KEYS.POLLINATIONS] && oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
410 || (secret_state[SECRET_KEYS.WORKERS_AI] && oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI)410 || (secret_state[SECRET_KEYS.WORKERS_AI] && oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI)
411 || (secret_state[SECRET_KEYS.MINIMAX] && oai_settings.chat_completion_source == chat_completion_sources.MINIMAX)
411 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)412 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
412 || (secret_state[SECRET_KEYS.AZURE_OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI)413 || (secret_state[SECRET_KEYS.AZURE_OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI)
413 ) {414 ) {
public/scripts/custom-request.js+1 -1
@@ -591,7 +591,7 @@ export class ChatCompletionService {
591 }591 }
592592
593 // Ensure api-url is properly applied for all sources that accept it593 // Ensure api-url is properly applied for all sources that accept it
594 ['custom_url', 'vertexai_region', 'zai_endpoint', 'siliconflow_endpoint'].forEach(field => {594 ['custom_url', 'vertexai_region', 'zai_endpoint', 'siliconflow_endpoint', 'minimax_endpoint'].forEach(field => {
595 // The order is: connection profile => CC preset => CC settings595 // The order is: connection profile => CC preset => CC settings
596 overridePayload[field] = overridePayload[field] || settings[field] || oai_settings[field];596 overridePayload[field] = overridePayload[field] || settings[field] || oai_settings[field];
597 });597 });
public/scripts/extensions/shared.js+1 -0
@@ -448,6 +448,7 @@ export class ConnectionManagerRequestService {
448 vertexai_region: profile['api-url'],448 vertexai_region: profile['api-url'],
449 zai_endpoint: profile['api-url'],449 zai_endpoint: profile['api-url'],
450 siliconflow_endpoint: profile['api-url'],450 siliconflow_endpoint: profile['api-url'],
451 minimax_endpoint: profile['api-url'],
451 reverse_proxy: proxyPreset?.url,452 reverse_proxy: proxyPreset?.url,
452 proxy_password: proxyPreset?.password,453 proxy_password: proxyPreset?.password,
453 custom_prompt_post_processing: profile['prompt-post-processing'],454 custom_prompt_post_processing: profile['prompt-post-processing'],
public/scripts/openai.js+52 -0
@@ -47,6 +47,7 @@ import { SECRET_KEYS, secret_state, writeSecret } from './secrets.js';
4747
48import { getEventSourceStream } from './sse-stream.js';48import { getEventSourceStream } from './sse-stream.js';
49import {49import {
50 clamp,
50 createThumbnail,51 createThumbnail,
51 delay,52 delay,
52 download,53 download,
@@ -197,6 +198,7 @@ export const chat_completion_sources = {
197 ZAI: 'zai',198 ZAI: 'zai',
198 SILICONFLOW: 'siliconflow',199 SILICONFLOW: 'siliconflow',
199 WORKERS_AI: 'workers_ai',200 WORKERS_AI: 'workers_ai',
201 MINIMAX: 'minimax',
200};202};
201203
202const character_names_behavior = {204const character_names_behavior = {
@@ -270,6 +272,11 @@ export const SILICONFLOW_ENDPOINT = {
270 CN: 'cn',272 CN: 'cn',
271};273};
272274
275export const MINIMAX_ENDPOINT = {
276 GLOBAL: 'global',
277 CN: 'cn',
278};
279
273const sensitiveFields = [280const sensitiveFields = [
274 'reverse_proxy',281 'reverse_proxy',
275 'proxy_password',282 'proxy_password',
@@ -319,6 +326,8 @@ export const settingsToUpdate = {
319 chutes_sort_models: ['#chutes_sort_models', 'chutes_sort_models', false, true],326 chutes_sort_models: ['#chutes_sort_models', 'chutes_sort_models', false, true],
320 siliconflow_model: ['#model_siliconflow_select', 'siliconflow_model', false, true],327 siliconflow_model: ['#model_siliconflow_select', 'siliconflow_model', false, true],
321 siliconflow_endpoint: ['#siliconflow_endpoint', 'siliconflow_endpoint', false, true],328 siliconflow_endpoint: ['#siliconflow_endpoint', 'siliconflow_endpoint', false, true],
329 minimax_model: ['#model_minimax_select', 'minimax_model', false, true],
330 minimax_endpoint: ['#minimax_endpoint', 'minimax_endpoint', false, true],
322 electronhub_model: ['#model_electronhub_select', 'electronhub_model', false, true],331 electronhub_model: ['#model_electronhub_select', 'electronhub_model', false, true],
323 electronhub_sort_models: ['#electronhub_sort_models', 'electronhub_sort_models', false, true],332 electronhub_sort_models: ['#electronhub_sort_models', 'electronhub_sort_models', false, true],
324 electronhub_group_models: ['#electronhub_group_models', 'electronhub_group_models', false, true],333 electronhub_group_models: ['#electronhub_group_models', 'electronhub_group_models', false, true],
@@ -431,6 +440,8 @@ const default_settings = {
431 chutes_sort_models: 'alphabetically',440 chutes_sort_models: 'alphabetically',
432 siliconflow_model: 'deepseek-ai/DeepSeek-V3',441 siliconflow_model: 'deepseek-ai/DeepSeek-V3',
433 siliconflow_endpoint: SILICONFLOW_ENDPOINT.GLOBAL,442 siliconflow_endpoint: SILICONFLOW_ENDPOINT.GLOBAL,
443 minimax_model: 'MiniMax-M2.7',
444 minimax_endpoint: MINIMAX_ENDPOINT.GLOBAL,
434 electronhub_model: 'gpt-4o-mini',445 electronhub_model: 'gpt-4o-mini',
435 electronhub_sort_models: 'alphabetically',446 electronhub_sort_models: 'alphabetically',
436 electronhub_group_models: false,447 electronhub_group_models: false,
@@ -1712,6 +1723,8 @@ export function getChatCompletionModel(settings = null) {
1712 return settings.groq_model;1723 return settings.groq_model;
1713 case chat_completion_sources.SILICONFLOW:1724 case chat_completion_sources.SILICONFLOW:
1714 return settings.siliconflow_model;1725 return settings.siliconflow_model;
1726 case chat_completion_sources.MINIMAX:
1727 return settings.minimax_model;
1715 case chat_completion_sources.ELECTRONHUB:1728 case chat_completion_sources.ELECTRONHUB:
1716 return settings.electronhub_model;1729 return settings.electronhub_model;
1717 case chat_completion_sources.CHUTES:1730 case chat_completion_sources.CHUTES:
@@ -2845,6 +2858,14 @@ export async function createGenerationParameters(settings, model, type, messages
2845 generate_data.siliconflow_endpoint = settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL;2858 generate_data.siliconflow_endpoint = settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL;
2846 }2859 }
28472860
2861 if (settings.chat_completion_source === chat_completion_sources.MINIMAX) {
2862 generate_data.minimax_endpoint = settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL;
2863 // MiniMax requires temperature in (0.0, 1.0]; zero is rejected.
2864 if (Number.isFinite(generate_data.temperature)) {
2865 generate_data.temperature = clamp(generate_data.temperature, Number.EPSILON, 1.0);
2866 }
2867 }
2868
2848 if (settings.chat_completion_source === chat_completion_sources.WORKERS_AI) {2869 if (settings.chat_completion_source === chat_completion_sources.WORKERS_AI) {
2849 generate_data.workers_ai_account_id = settings.workers_ai_account_id;2870 generate_data.workers_ai_account_id = settings.workers_ai_account_id;
2850 generate_data.top_k = settings.top_k_openai > 0 ? Math.min(Number(settings.top_k_openai), 50) : undefined;2871 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() {
4255 chat_completion_sources.VERTEXAI,4276 chat_completion_sources.VERTEXAI,
4256 chat_completion_sources.PERPLEXITY,4277 chat_completion_sources.PERPLEXITY,
4257 chat_completion_sources.ZAI,4278 chat_completion_sources.ZAI,
4279 chat_completion_sources.MINIMAX,
4258 ];4280 ];
4259 if (noValidateSources.includes(oai_settings.chat_completion_source)) {4281 if (noValidateSources.includes(oai_settings.chat_completion_source)) {
4260 let status = t`Key saved; press \"Test Message\" to verify.`;4282 let status = t`Key saved; press \"Test Message\" to verify.`;
@@ -4312,6 +4334,10 @@ async function getStatusOpen() {
4312 data.siliconflow_endpoint = oai_settings.siliconflow_endpoint;4334 data.siliconflow_endpoint = oai_settings.siliconflow_endpoint;
4313 }4335 }
43144336
4337 if (oai_settings.chat_completion_source === chat_completion_sources.MINIMAX) {
4338 data.minimax_endpoint = oai_settings.minimax_endpoint;
4339 }
4340
4315 if (oai_settings.chat_completion_source === chat_completion_sources.WORKERS_AI) {4341 if (oai_settings.chat_completion_source === chat_completion_sources.WORKERS_AI) {
4316 data.workers_ai_account_id = oai_settings.workers_ai_account_id;4342 data.workers_ai_account_id = oai_settings.workers_ai_account_id;
4317 }4343 }
@@ -5266,6 +5292,15 @@ async function onModelChange() {
5266 oai_settings.siliconflow_model = value;5292 oai_settings.siliconflow_model = value;
5267 }5293 }
52685294
5295 if ($(this).is('#model_minimax_select')) {
5296 if (!value) {
5297 console.debug('Null MiniMax model selected. Ignoring.');
5298 return;
5299 }
5300 console.log('MiniMax model changed to', value);
5301 oai_settings.minimax_model = value;
5302 }
5303
5269 if ($(this).is('#model_electronhub_select')) {5304 if ($(this).is('#model_electronhub_select')) {
5270 if (!value || !hasModelsLoaded) {5305 if (!value || !hasModelsLoaded) {
5271 console.debug('Null ElectronHub model selected. Ignoring.');5306 console.debug('Null ElectronHub model selected. Ignoring.');
@@ -5707,6 +5742,15 @@ async function onModelChange() {
5707 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');5742 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
5708 }5743 }
57095744
5745 if (oai_settings.chat_completion_source === chat_completion_sources.MINIMAX) {
5746 const maxContext = oai_settings.minimax_model === 'M2-her' ? 65536 : 204800;
5747 $('#openai_max_context').attr('max', maxContext);
5748 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
5749 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
5750 oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
5751 $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
5752 }
5753
5710 if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {5754 if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {
5711 const maxContext = getZaiMaxContext(oai_settings.zai_model, oai_settings.max_context_unlocked);5755 const maxContext = getZaiMaxContext(oai_settings.zai_model, oai_settings.max_context_unlocked);
5712 $('#openai_max_context').attr('max', maxContext);5756 $('#openai_max_context').attr('max', maxContext);
@@ -5780,6 +5824,7 @@ async function onConnectButtonClick(e) {
5780 [chat_completion_sources.CHUTES]: { key: SECRET_KEYS.CHUTES, selector: '#api_key_chutes', proxy: false },5824 [chat_completion_sources.CHUTES]: { key: SECRET_KEYS.CHUTES, selector: '#api_key_chutes', proxy: false },
5781 [chat_completion_sources.POLLINATIONS]: { key: SECRET_KEYS.POLLINATIONS, selector: '#api_key_pollinations', proxy: false },5825 [chat_completion_sources.POLLINATIONS]: { key: SECRET_KEYS.POLLINATIONS, selector: '#api_key_pollinations', proxy: false },
5782 [chat_completion_sources.WORKERS_AI]: { key: SECRET_KEYS.WORKERS_AI, selector: '#api_key_workers_ai', proxy: false },5826 [chat_completion_sources.WORKERS_AI]: { key: SECRET_KEYS.WORKERS_AI, selector: '#api_key_workers_ai', proxy: false },
5827 [chat_completion_sources.MINIMAX]: { key: SECRET_KEYS.MINIMAX, selector: '#api_key_minimax', proxy: false },
5783 };5828 };
57845829
5785 // Vertex AI Express version - use API key5830 // Vertex AI Express version - use API key
@@ -5845,6 +5890,8 @@ function toggleChatCompletionForms() {
5845 $('#model_chutes_select').trigger('change');5890 $('#model_chutes_select').trigger('change');
5846 } else if (oai_settings.chat_completion_source == chat_completion_sources.SILICONFLOW) {5891 } else if (oai_settings.chat_completion_source == chat_completion_sources.SILICONFLOW) {
5847 $('#model_siliconflow_select').trigger('change');5892 $('#model_siliconflow_select').trigger('change');
5893 } else if (oai_settings.chat_completion_source == chat_completion_sources.MINIMAX) {
5894 $('#model_minimax_select').trigger('change');
5848 } else if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {5895 } else if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
5849 $('#model_electronhub_select').trigger('change');5896 $('#model_electronhub_select').trigger('change');
5850 } else if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {5897 } else if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {
@@ -7028,6 +7075,10 @@ export function initOpenAI() {
7028 oai_settings.siliconflow_endpoint = String($(this).val());7075 oai_settings.siliconflow_endpoint = String($(this).val());
7029 saveSettingsDebounced();7076 saveSettingsDebounced();
7030 });7077 });
7078 $('#minimax_endpoint').on('input', function () {
7079 oai_settings.minimax_endpoint = String($(this).val());
7080 saveSettingsDebounced();
7081 });
7031 $('#workers_ai_account_id').on('input', function () {7082 $('#workers_ai_account_id').on('input', function () {
7032 oai_settings.workers_ai_account_id = String($(this).val());7083 oai_settings.workers_ai_account_id = String($(this).val());
7033 saveSettingsDebounced();7084 saveSettingsDebounced();
@@ -7048,6 +7099,7 @@ export function initOpenAI() {
7048 $('#model_groq_select').on('change', onModelChange);7099 $('#model_groq_select').on('change', onModelChange);
7049 $('#model_chutes_select').on('change', onModelChange);7100 $('#model_chutes_select').on('change', onModelChange);
7050 $('#model_siliconflow_select').on('change', onModelChange);7101 $('#model_siliconflow_select').on('change', onModelChange);
7102 $('#model_minimax_select').on('change', onModelChange);
7051 $('#model_electronhub_select').on('change', onModelChange);7103 $('#model_electronhub_select').on('change', onModelChange);
7052 $('#model_nanogpt_select').on('change', onModelChange);7104 $('#model_nanogpt_select').on('change', onModelChange);
7053 $('#model_deepseek_select').on('change', onModelChange);7105 $('#model_deepseek_select').on('change', onModelChange);
public/scripts/secrets.js+2 -1
@@ -130,7 +130,7 @@ const FRIENDLY_NAMES = {
130 [SECRET_KEYS.LINGVA_URL]: 'Lingva Endpoint (e.g. https://lingva.ml/api/v1)',130 [SECRET_KEYS.LINGVA_URL]: 'Lingva Endpoint (e.g. https://lingva.ml/api/v1)',
131 [SECRET_KEYS.ONERING_URL]: 'OneRingTranslator Endpoint (e.g. http://127.0.0.1:4990/translate)',131 [SECRET_KEYS.ONERING_URL]: 'OneRingTranslator Endpoint (e.g. http://127.0.0.1:4990/translate)',
132 [SECRET_KEYS.DEEPLX_URL]: 'DeepLX Endpoint (e.g. http://127.0.0.1:1188/translate)',132 [SECRET_KEYS.DEEPLX_URL]: 'DeepLX Endpoint (e.g. http://127.0.0.1:1188/translate)',
133 [SECRET_KEYS.MINIMAX]: 'MiniMax TTS',133 [SECRET_KEYS.MINIMAX]: 'MiniMax',
134 [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',134 [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',
135 [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',135 [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',
136 [SECRET_KEYS.COMETAPI]: 'CometAPI',136 [SECRET_KEYS.COMETAPI]: 'CometAPI',
@@ -184,6 +184,7 @@ const INPUT_MAP = {
184 [SECRET_KEYS.AZURE_OPENAI]: '#api_key_azure_openai',184 [SECRET_KEYS.AZURE_OPENAI]: '#api_key_azure_openai',
185 [SECRET_KEYS.ZAI]: '#api_key_zai',185 [SECRET_KEYS.ZAI]: '#api_key_zai',
186 [SECRET_KEYS.SILICONFLOW]: '#api_key_siliconflow',186 [SECRET_KEYS.SILICONFLOW]: '#api_key_siliconflow',
187 [SECRET_KEYS.MINIMAX]: '#api_key_minimax',
187 [SECRET_KEYS.POLLINATIONS]: '#api_key_pollinations',188 [SECRET_KEYS.POLLINATIONS]: '#api_key_pollinations',
188 [SECRET_KEYS.WORKERS_AI]: '#api_key_workers_ai',189 [SECRET_KEYS.WORKERS_AI]: '#api_key_workers_ai',
189};190};
public/scripts/slash-commands.js+30 -2
@@ -70,7 +70,7 @@ import { hideChatMessageRange } from './chats.js';
70import { getContext, saveMetadataDebounced } from './extensions.js';70import { getContext, saveMetadataDebounced } from './extensions.js';
71import { getRegexedString, regex_placement } from './extensions/regex/engine.js';71import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
72import { findGroupMemberId, groups, is_group_generating, openGroupById, regenerateGroup, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js';72import { findGroupMemberId, groups, is_group_generating, openGroupById, regenerateGroup, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js';
73import { chat_completion_sources, oai_settings, promptManager, SILICONFLOW_ENDPOINT, ZAI_ENDPOINT } from './openai.js';73import { chat_completion_sources, MINIMAX_ENDPOINT, oai_settings, promptManager, SILICONFLOW_ENDPOINT, ZAI_ENDPOINT } from './openai.js';
74import { user_avatar } from './personas.js';74import { user_avatar } from './personas.js';
75import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, playMessageSound, power_user } from './power-user.js';75import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, playMessageSound, power_user } from './power-user.js';
76import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';76import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
@@ -3134,6 +3134,7 @@ export function initDefaultSlashCommands() {
3134 new SlashCommandEnumValue('zai', 'Z.AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'zai')), 'Z'),3134 new SlashCommandEnumValue('zai', 'Z.AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'zai')), 'Z'),
3135 new SlashCommandEnumValue('vertexai', 'Google Vertex AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'vertexai')), 'V'),3135 new SlashCommandEnumValue('vertexai', 'Google Vertex AI', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'vertexai')), 'V'),
3136 new SlashCommandEnumValue('siliconflow', 'SiliconFlow', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'siliconflow')), 'S'),3136 new SlashCommandEnumValue('siliconflow', 'SiliconFlow', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'siliconflow')), 'S'),
3137 new SlashCommandEnumValue('minimax', 'MiniMax', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'minimax')), 'M'),
3137 new SlashCommandEnumValue('kobold', 'KoboldAI Classic', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'kobold')), 'K'),3138 new SlashCommandEnumValue('kobold', 'KoboldAI Classic', enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === 'kobold')), 'K'),
3138 ...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')),3139 ...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')),
3139 ],3140 ],
@@ -3167,7 +3168,7 @@ export function initDefaultSlashCommands() {
3167 ${t`If a manual API is provided to <b>set</b> the URL, make sure to set <code>connect=false</code>, as auto-connect only works for the currently selected API, or consider switching to it with <code>/api</code> first.`}3168 ${t`If a manual API is provided to <b>set</b> the URL, make sure to set <code>connect=false</code>, as auto-connect only works for the currently selected API, or consider switching to it with <code>/api</code> first.`}
3168 </div>3169 </div>
3169 <div>3170 <div>
3170 ${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 <code>api</code> argument of this command.`}3171 ${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 <code>api</code> argument of this command.`}
3171 </div>3172 </div>
3172 `,3173 `,
3173 }));3174 }));
@@ -6261,6 +6262,7 @@ function getModelOptions(quiet) {
6261 { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ },6262 { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ },
6262 { id: 'model_chutes_select', api: 'openai', type: chat_completion_sources.CHUTES },6263 { id: 'model_chutes_select', api: 'openai', type: chat_completion_sources.CHUTES },
6263 { id: 'model_siliconflow_select', api: 'openai', type: chat_completion_sources.SILICONFLOW },6264 { id: 'model_siliconflow_select', api: 'openai', type: chat_completion_sources.SILICONFLOW },
6265 { id: 'model_minimax_select', api: 'openai', type: chat_completion_sources.MINIMAX },
6264 { id: 'model_electronhub_select', api: 'openai', type: chat_completion_sources.ELECTRONHUB },6266 { id: 'model_electronhub_select', api: 'openai', type: chat_completion_sources.ELECTRONHUB },
6265 { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },6267 { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
6266 { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },6268 { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
@@ -6628,6 +6630,32 @@ async function setApiUrlCallback({ api = null, connect = 'true', quiet = 'false'
6628 return oai_settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL;6630 return oai_settings.siliconflow_endpoint || SILICONFLOW_ENDPOINT.GLOBAL;
6629 }6631 }
66306632
6633 const isCurrentlyMinimax = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.MINIMAX;
6634 if (api === chat_completion_sources.MINIMAX || (!api && isCurrentlyMinimax)) {
6635 if (!url) {
6636 return oai_settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL;
6637 }
6638
6639 const permittedValues = Object.values(MINIMAX_ENDPOINT);
6640 if (!permittedValues.includes(url)) {
6641 !isQuiet && toastr.warning(t`Valid options are: ${permittedValues.join(', ')}`, t`MiniMax endpoint '${url}' is not a valid option.`);
6642 return '';
6643 }
6644
6645 if (!isCurrentlyMinimax && autoConnect) {
6646 toastr.warning(t`MiniMax is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.`);
6647 return '';
6648 }
6649
6650 $('#minimax_endpoint').val(url).trigger('input');
6651
6652 if (autoConnect) {
6653 $('#api_button_openai').trigger('click');
6654 }
6655
6656 return oai_settings.minimax_endpoint || MINIMAX_ENDPOINT.GLOBAL;
6657 }
6658
6631 const isCurrentlyVertexAI = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.VERTEXAI;6659 const isCurrentlyVertexAI = main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.VERTEXAI;
6632 if (api === chat_completion_sources.VERTEXAI || (!api && isCurrentlyVertexAI)) {6660 if (api === chat_completion_sources.VERTEXAI || (!api && isCurrentlyVertexAI)) {
6633 const defaultRegion = 'us-central1';6661 const defaultRegion = 'us-central1';
public/scripts/tokenizers.js+5 -0
@@ -694,6 +694,11 @@ export function getTokenizerModel() {
694 }694 }
695 }695 }
696696
697 if (oai_settings.chat_completion_source == chat_completion_sources.MINIMAX) {
698 // MiniMax uses a proprietary tokenizer; fall back to a coarse OpenAI estimation.
699 return 'gpt-3.5-turbo';
700 }
701
697 if (oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI && oai_settings.workers_ai_model) {702 if (oai_settings.chat_completion_source == chat_completion_sources.WORKERS_AI && oai_settings.workers_ai_model) {
698 const model = oai_settings.workers_ai_model.toLowerCase();703 const model = oai_settings.workers_ai_model.toLowerCase();
699704
public/scripts/tool-calling.js+1 -0
@@ -667,6 +667,7 @@ export class ToolManager {
667 chat_completion_sources.SILICONFLOW,667 chat_completion_sources.SILICONFLOW,
668 chat_completion_sources.NANOGPT,668 chat_completion_sources.NANOGPT,
669 chat_completion_sources.WORKERS_AI,669 chat_completion_sources.WORKERS_AI,
670 chat_completion_sources.MINIMAX,
670 ];671 ];
671 return supportedSources.includes(settings.chat_completion_source);672 return supportedSources.includes(settings.chat_completion_source);
672 }673 }
src/constants.js+6 -0
@@ -209,6 +209,7 @@ export const CHAT_COMPLETION_SOURCES = {
209 AZURE_OPENAI: 'azure_openai',209 AZURE_OPENAI: 'azure_openai',
210 ZAI: 'zai',210 ZAI: 'zai',
211 SILICONFLOW: 'siliconflow',211 SILICONFLOW: 'siliconflow',
212 MINIMAX: 'minimax',
212 WORKERS_AI: 'workers_ai',213 WORKERS_AI: 'workers_ai',
213};214};
214215
@@ -557,3 +558,8 @@ export const SILICONFLOW_ENDPOINT = {
557 GLOBAL: 'global',558 GLOBAL: 'global',
558 CN: 'cn',559 CN: 'cn',
559};560};
561
562export const MINIMAX_ENDPOINT = {
563 GLOBAL: 'global',
564 CN: 'cn',
565};
src/endpoints/backends/chat-completions.js+85 -1
@@ -18,6 +18,7 @@ import {
18 OPENROUTER_HEADERS,18 OPENROUTER_HEADERS,
19 VERTEX_SAFETY,19 VERTEX_SAFETY,
20 SILICONFLOW_ENDPOINT,20 SILICONFLOW_ENDPOINT,
21 MINIMAX_ENDPOINT,
21 ZAI_ENDPOINT,22 ZAI_ENDPOINT,
22} from '../../constants.js';23} from '../../constants.js';
23import {24import {
@@ -89,6 +90,8 @@ const API_ZAI_COMMON = 'https://api.z.ai/api/paas/v4';
89const API_ZAI_CODING = 'https://api.z.ai/api/coding/paas/v4';90const API_ZAI_CODING = 'https://api.z.ai/api/coding/paas/v4';
90const API_SILICONFLOW = 'https://api.siliconflow.com/v1';91const API_SILICONFLOW = 'https://api.siliconflow.com/v1';
91const API_SILICONFLOW_CN = 'https://api.siliconflow.cn/v1';92const API_SILICONFLOW_CN = 'https://api.siliconflow.cn/v1';
93const API_MINIMAX = 'https://api.minimax.io/v1';
94const API_MINIMAX_CN = 'https://api.minimaxi.com/v1';
92const API_OPENROUTER = 'https://openrouter.ai/api/v1';95const API_OPENROUTER = 'https://openrouter.ai/api/v1';
93const API_WORKERS_AI = 'https://api.cloudflare.com/client/v4/accounts';96const API_WORKERS_AI = 'https://api.cloudflare.com/client/v4/accounts';
9497
@@ -1552,7 +1555,87 @@ async function sendChutesRequest(request, response) {
1552}1555}
15531556
1554/**1557/**
1555 * Sends a chat completion request to Azure OpenAI.1558 * Sends a request to MiniMax.
1559 * @param {express.Request} request Express request
1560 * @param {express.Response} response Express response
1561 */
1562async function sendMinimaxRequest(request, response) {
1563 const apiUrl = request.body.minimax_endpoint === MINIMAX_ENDPOINT.CN
1564 ? API_MINIMAX_CN : API_MINIMAX;
1565 const apiKey = readSecret(request.user.directories, SECRET_KEYS.MINIMAX, request.body.secret_id);
1566
1567 if (!apiKey) {
1568 console.warn('MiniMax key is missing.');
1569 return response.status(400).send({ error: true });
1570 }
1571
1572 const controller = new AbortController();
1573 request.socket.removeAllListeners('close');
1574 request.socket.on('close', function () {
1575 controller.abort();
1576 });
1577
1578 try {
1579 // MiniMax does not allow consecutive messages with the same role.
1580 // Merge them into a single message to avoid "invalid chat setting (2013)".
1581 const messages = postProcessPrompt(request.body.messages, PROMPT_PROCESSING_TYPE.MERGE_TOOLS, getPromptNames(request));
1582
1583 let bodyParams = {};
1584
1585 if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
1586 bodyParams['tools'] = request.body.tools;
1587 bodyParams['tool_choice'] = request.body.tool_choice;
1588 }
1589
1590 const requestBody = {
1591 'messages': messages,
1592 'model': request.body.model,
1593 'temperature': request.body.temperature,
1594 'max_tokens': request.body.model === 'M2-her' ? Math.min(request.body.max_tokens, 2048) : request.body.max_tokens,
1595 'stream': request.body.stream,
1596 'top_p': request.body.top_p,
1597 'stop': request.body.stop,
1598 ...bodyParams,
1599 };
1600
1601 const config = {
1602 method: 'POST',
1603 headers: {
1604 'Content-Type': 'application/json',
1605 'Authorization': 'Bearer ' + apiKey,
1606 },
1607 body: JSON.stringify(requestBody),
1608 signal: controller.signal,
1609 };
1610
1611 console.debug('MiniMax request:', requestBody);
1612
1613 const generateResponse = await fetch(apiUrl + '/chat/completions', config);
1614
1615 if (request.body.stream) {
1616 await forwardFetchResponse(generateResponse, response);
1617 } else {
1618 if (!generateResponse.ok) {
1619 const errorText = await generateResponse.text();
1620 console.warn('MiniMax returned error: ', errorText);
1621 const errorJson = tryParse(errorText) ?? { error: true };
1622 return response.status(500).send(errorJson);
1623 }
1624 const generateResponseJson = await generateResponse.json();
1625 console.debug('MiniMax response:', generateResponseJson);
1626 return response.send(generateResponseJson);
1627 }
1628 } catch (error) {
1629 console.error('Error communicating with MiniMax: ', error);
1630 if (!response.headersSent) {
1631 response.send({ error: true });
1632 } else {
1633 response.end();
1634 }
1635 }
1636}
1637
1638/**
1556 * @param {express.Request} request Express request object (contains request.body with all generate_data)1639 * @param {express.Request} request Express request object (contains request.body with all generate_data)
1557 * @param {express.Response} response Express response object1640 * @param {express.Response} response Express response object
1558 */1641 */
@@ -2096,6 +2179,7 @@ router.post('/generate', async function (request, response) {
2096 case CHAT_COMPLETION_SOURCES.AIMLAPI: return await sendAimlapiRequest(request, response);2179 case CHAT_COMPLETION_SOURCES.AIMLAPI: return await sendAimlapiRequest(request, response);
2097 case CHAT_COMPLETION_SOURCES.XAI: return await sendXaiRequest(request, response);2180 case CHAT_COMPLETION_SOURCES.XAI: return await sendXaiRequest(request, response);
2098 case CHAT_COMPLETION_SOURCES.CHUTES: return await sendChutesRequest(request, response);2181 case CHAT_COMPLETION_SOURCES.CHUTES: return await sendChutesRequest(request, response);
2182 case CHAT_COMPLETION_SOURCES.MINIMAX: return await sendMinimaxRequest(request, response);
2099 case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return await sendElectronHubRequest(request, response);2183 case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return await sendElectronHubRequest(request, response);
2100 case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return await sendAzureOpenAIRequest(request, response);2184 case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return await sendAzureOpenAIRequest(request, response);
2101 }2185 }