Audience: contributors and coding agents adding new MiniMax models or workflow nodes.
TL;DR
- Add a model ID to the relevant catalogue array in
minimax-provider.ts(for the generic provider) and to the matching constant inminimax-base.ts(for the node pack’s@propenum). - For a new node type, copy an existing node file in
packages/minimax-nodes/src/nodes/, register it insrc/index.ts, add a test undertests/. - Build:
npm run build:packages. - Verify:
npm run typecheck, thennpm run dev:nodetool -- node run minimax.<YourNodeType> --props '{...}'.
Where things live
| What | Path |
|---|---|
| Provider class (chat/image/video/TTS/music) | packages/runtime/src/providers/minimax-provider.ts |
| Shared node constants and helpers | packages/minimax-nodes/src/minimax-base.ts |
| Node implementations | packages/minimax-nodes/src/nodes/*.ts |
| Node pack entry point | packages/minimax-nodes/src/index.ts |
| Node pack tests | packages/minimax-nodes/tests/*.ts |
| Provider ID constant | packages/protocol/src/api-types.ts line 872 (MINIMAX: "minimax") |
| Provider registration | packages/runtime/src/providers/index.ts line 215 |
| API docs | https://platform.minimax.io/docs/api-reference/api-overview |
How MiniMax models and nodes are defined
Provider catalogue (static arrays)
MinimaxProvider in minimax-provider.ts overrides six getAvailable* methods. Each returns a hardcoded array — there is no live API discovery. The arrays are the source of truth for the generic nodes (chat, generic TTS, generic video).
// packages/runtime/src/providers/minimax-provider.ts
override async getAvailableVideoModels(): Promise<VideoModel[]> {
return [
{ id: "MiniMax-Hailuo-2.3", name: "MiniMax Hailuo 2.3", provider: "minimax",
supportedTasks: ["text_to_video", "image_to_video"] },
// ...
];
}
Node pack constants (in minimax-base.ts)
packages/minimax-nodes/src/minimax-base.ts duplicates the model IDs as plain string arrays. These feed the values field in each node’s @prop decorator, which drives UI drop-downs and validation.
// packages/minimax-nodes/src/minimax-base.ts
export const MINIMAX_T2V_MODELS: string[] = [
"MiniMax-Hailuo-2.3",
"MiniMax-Hailuo-2.3-Fast",
"MiniMax-Hailuo-02",
"T2V-01-Director"
];
Why two places? Nodes call MiniMax’s REST API directly (not through the provider) so they can expose MiniMax-specific fields (emotion, volume, pitch, camera direction, lyrics) that the generic BaseProvider interface does not carry.
Node registration
src/index.ts exports MINIMAX_NODES (the full array) and registerMinimaxNodes (the registry helper). Every new node class must be imported and added to both.
Add a new model or node
Case A: new model ID in an existing node type
-
Add the ID to
minimax-provider.tsin the matchinggetAvailable*method:{ id: "MiniMax-Hailuo-3.0", name: "MiniMax Hailuo 3.0", provider: "minimax", supportedTasks: ["text_to_video", "image_to_video"] }, -
Add the same ID to
minimax-base.tsin the matching constant:export const MINIMAX_T2V_MODELS: string[] = [ "MiniMax-Hailuo-3.0", // new "MiniMax-Hailuo-2.3", // ... ];Do the same for
MINIMAX_I2V_MODELSif the model supports image-to-video. -
The node’s
@propenum picks up the new entry automatically — no change to the node file needed.
Case B: new node type
Say MiniMax releases a new endpoint, e.g. POST /v1/text_to_3d.
-
Create
packages/minimax-nodes/src/nodes/text-to-3d.ts, modelled on an existing node:import { BaseNode, prop } from "@nodetool-ai/node-sdk"; import type { NodeClass } from "@nodetool-ai/node-sdk"; import { assertBaseResp, getMinimaxApiKey, MINIMAX_BASE_URL, minimaxHeaders } from "../minimax-base.js"; const TEXT_TO_3D_MODELS = ["3d-01"]; export class MinimaxTextTo3DNode extends BaseNode { static readonly nodeType = "minimax.TextTo3D"; static readonly body = "content_card"; static readonly title = "MiniMax Text to 3D"; static readonly description = "Generate a 3D model from a text prompt using MiniMax 3D-01.\n" + "3d, generation, text-to-3d, minimax\n\n" + "Use cases:\n" + "- Prototype 3D assets from descriptions\n" + "- Generate objects for scenes"; static readonly metadataOutputTypes = { output: "model3d" }; static readonly inlineFields: string[] = []; static readonly inputFields: string[] = ["prompt"]; static readonly requiredSettings = ["MINIMAX_API_KEY"]; static readonly autoSaveAsset = true; @prop({ type: "enum", default: "3d-01", title: "Model", description: "The MiniMax 3D model to use.", values: TEXT_TO_3D_MODELS }) declare model: any; @prop({ type: "str", default: "A ceramic vase with floral patterns", title: "Prompt", description: "Text prompt describing the 3D object." }) declare prompt: any; async process(): Promise<Record<string, unknown>> { const apiKey = getMinimaxApiKey(this._secrets); const prompt = String(this.prompt ?? ""); if (!prompt) throw new Error("Prompt is required"); const res = await fetch(`${MINIMAX_BASE_URL}/v1/text_to_3d`, { method: "POST", headers: minimaxHeaders(apiKey), body: JSON.stringify({ model: String(this.model ?? "3d-01"), prompt }) }); if (!res.ok) { throw new Error(`MiniMax text_to_3d failed: ${res.status} ${await res.text()}`); } const data = (await res.json()) as Record<string, unknown>; assertBaseResp(data, "text_to_3d"); // parse response and return output ref return { output: { type: "model3d", data: "" } }; } } export const TEXT_TO_3D_NODES: readonly NodeClass[] = [MinimaxTextTo3DNode]; -
Register in
src/index.ts:import { TEXT_TO_3D_NODES } from "./nodes/text-to-3d.js"; export { MinimaxTextTo3DNode } from "./nodes/text-to-3d.js"; export const MINIMAX_NODES: readonly NodeClass[] = [ ...VOICE_NODES, ...TEXT_TO_SPEECH_NODES, ...MUSIC_NODES, ...TEXT_TO_IMAGE_NODES, ...TEXT_TO_VIDEO_NODES, ...IMAGE_TO_VIDEO_NODES, ...TEXT_TO_3D_NODES // add here ]; -
Add a test in
packages/minimax-nodes/tests/text-to-3d.test.ts. Stubfetchwithvi.stubGlobal(see any existing test for the pattern). At minimum assert the node calls the right endpoint and returns an output. -
Update
tests/registration.test.ts— add"minimax.TextTo3D"to bothregistry.has(...)checks and thetypesarray.
Verify
Run these in order after any change:
# 1. Type check all packages
npm run typecheck
# 2. Build (minimax-nodes loads from dist/)
npm run build:packages
# 3. Run a single node in isolation (no full server needed)
npm run dev:nodetool -- node run minimax.TextToSpeech \
--props '{"text":"hello","voice_id":"English_Trustworth_Man","model":"speech-2.6-hd"}' \
--no-secrets
# 4. Run the minimax-nodes test suite
npm run test --workspace=packages/minimax-nodes
# 5. Validate a workflow that uses the new node type
npm run dev:nodetool -- validate workflow.json
The --no-secrets flag lets the node runner skip the database. The node will
fail at the network call (no real API key), but a type error or missing-field
panic surfaces before that.
How past commits did it
The minimax node pack and provider were introduced before the current git window, but the two most recent commits that touched these files are:
ca742050— version bump to0.7.0-rc.26(touchedpackages/minimax-nodes/package.jsonalong with all other packages)f900e7a6— version bump to0.7.0-rc.25
These are housekeeping commits. The substantive minimax work (node pack creation, provider implementation) predates the visible history on this branch, but the pattern matches the elevenlabs-nodes pack (same direct-fetch approach, same minimax-base.ts ↔ minimax-provider.ts split).
To find the original introduction commit across all branches:
git log --all --oneline -- packages/minimax-nodes/src/index.ts | tail -1
Contributing
Source: https://github.com/nodetool-ai/nodetool
Discord: https://discord.gg/WmQTWZRcYE
Before opening a PR:
npm run check # runs typecheck + lint + test for all packages
All three must pass. Do not commit if either npm run typecheck or npm run lint fails.