Skip to content

feat: upgrade MiniMax default model to M3#6468

Open
octo-patch wants to merge 1 commit into
FlowiseAI:mainfrom
octo-patch:feature/upgrade-minimax-m3
Open

feat: upgrade MiniMax default model to M3#6468
octo-patch wants to merge 1 commit into
FlowiseAI:mainfrom
octo-patch:feature/upgrade-minimax-m3

Conversation

@octo-patch
Copy link
Copy Markdown

Summary

Add MiniMax as a chat model provider in Flowise, with the latest M3 model as default.

Changes

  • Add ChatMiniMax node wrapping ChatAnthropic with MiniMax Anthropic-compatible base URL
  • Add MiniMaxApi credential type for API key management
  • Register MiniMax models in models.json: M3, M2.7, M2.7-highspeed
  • Set MiniMax-M3 as the default model
  • Include topP/topK stripping for MiniMax API constraints
  • Add MiniMax TTS support in textToSpeech.ts (TTS model unchanged, no chat-model impact)

Why

MiniMax-M3 is the latest flagship model with a 512K context window, up to 128K output, and image input support. Upgrading from M2.5 to M3 gives Flowise users access to the new generation model.

Testing

  • Verified models.json is valid JSON with correct model ordering
  • M3 is first in the list and set as default
  • M2.5/M2.5-highspeed have been removed; M2.7 and M2.7-highspeed are retained as alternatives
  • ChatMiniMax node uses MiniMax-M3 as both the form default and the configured model fallback

- Add ChatMiniMax node with FlowiseChatMiniMax integration
- Add MiniMaxApi credential definition
- Update models.json with MiniMax model list (M3, M2.7, M2.7-highspeed)
- Set MiniMax-M3 as the default model
- Add MiniMax TTS support in textToSpeech.ts
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for MiniMax services, including a new ChatMiniMax model node, MiniMax API credentials, and MiniMax Text-to-Speech (TTS) integration. Feedback on the changes highlights a critical bug in the TTS stream processing where the loop does not fully terminate and the stream reader is not cancelled on error or abort, leading to potential resource leaks. Additionally, the falsy checks for temperature and topP in the ChatMiniMax node should be updated to safe nullish checks to allow valid values of 0.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +159 to +211
const processMinimaxStream = async () => {
for (;;) {
if (abortController.signal.aborted) {
reader.cancel()
streamDestroyed = true
reject(new Error('TTS generation aborted'))
return
}

const { done, value } = await reader.read()
if (done) break

sseBuffer += decoder.decode(value, { stream: true })
const lines = sseBuffer.split('\n')
sseBuffer = lines.pop() || ''

for (const line of lines) {
const trimmedLine = line.trim()
if (!trimmedLine || trimmedLine.startsWith(':')) {
continue
}

if (trimmedLine.startsWith('data:')) {
const jsonStr = trimmedLine.slice(5).trim()
if (!jsonStr) continue

try {
const eventData = JSON.parse(jsonStr)

if (eventData.base_resp?.status_code !== 0) {
const errorMsg = eventData.base_resp?.status_msg || 'Unknown error'
reject(new Error(`MiniMax TTS error: ${errorMsg}`))
return
}

if (eventData.data?.audio) {
const audioChunk = Buffer.from(eventData.data.audio, 'hex')
onChunk(audioChunk)
}

if (eventData.data?.status === 2) {
break
}
} catch {
// Skip malformed JSON
}
}
}
}

onEnd()
resolve()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There are two critical issues in processMinimaxStream:

  1. The break statement on line 200 only exits the inner for (const line of lines) loop. The outer for (;;) loop continues to run and call reader.read(), causing unnecessary processing and potential hangs.
  2. If an error occurs (status_code !== 0) or the stream is aborted, the stream reader is not properly cancelled, leading to resource/connection leaks.

Using a labeled loop (outerLoop) and ensuring reader.cancel() is called on all exit paths resolves these issues. Additionally, throwing an error when the API returns an invalid status code promotes fail-fast behavior.

const processMinimaxStream = async () => {
    try {
        outerLoop: for (;;) {
            if (abortController.signal.aborted) {
                await reader.cancel();
                streamDestroyed = true;
                reject(new Error("TTS generation aborted"));
                return;
            }

            const { done, value } = await reader.read();
            if (done) break;

            sseBuffer += decoder.decode(value, { stream: true });
            const lines = sseBuffer.split("\n");
            sseBuffer = lines.pop() || "";

            for (const line of lines) {
                const trimmedLine = line.trim();
                if (!trimmedLine || trimmedLine.startsWith(":")) {
                    continue;
                }

                if (trimmedLine.startsWith("data:")) {
                    const jsonStr = trimmedLine.slice(5).trim();
                    if (!jsonStr) continue;

                    try {
                        const eventData = JSON.parse(jsonStr);

                        if (eventData.base_resp?.status_code !== 0) {
                            const errorMsg = eventData.base_resp?.status_msg || "Unknown error";
                            await reader.cancel();
                            reject(new Error("MiniMax TTS error: " + errorMsg));
                            return;
                        }

                        if (eventData.data?.audio) {
                            const audioChunk = Buffer.from(eventData.data.audio, "hex");
                            onChunk(audioChunk);
                        }

                        if (eventData.data?.status === 2) {
                            await reader.cancel();
                            break outerLoop;
                        }
                    } catch {
                        // Skip malformed JSON
                    }
                }
            }
        }

        onEnd();
        resolve();
    } catch (err) {
        await reader.cancel().catch(() => {});
        reject(err);
    }
}
References
  1. When handling potentially invalid data from external sources (like an API response), prefer throwing an error for invalid input types rather than silently returning a default or empty value. This promotes fail-fast behavior.

Comment on lines +108 to +111
if (temperature) obj.temperature = parseFloat(temperature)
if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10)
if (topP) obj.topP = parseFloat(topP)
if (cache) obj.cache = cache
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking if (temperature) can fail when temperature is resolved as the number 0 (which is falsy in JavaScript), preventing users from setting a deterministic temperature of 0. It is safer to check temperature != null && temperature !== '' to allow 0 as a valid value. Similarly, topP should be checked safely. Use loose equality (!= null) as a standard idiom for nullish checks.

Suggested change
if (temperature) obj.temperature = parseFloat(temperature)
if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10)
if (topP) obj.topP = parseFloat(topP)
if (cache) obj.cache = cache
if (temperature != null && temperature !== '') obj.temperature = parseFloat(temperature)
if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10)
if (topP != null && topP !== '') obj.topP = parseFloat(topP)
if (cache) obj.cache = cache
References
  1. In JavaScript/TypeScript, use loose equality (== null) as a standard idiom for a 'nullish' check that covers both null and undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant