feat: add MiniMax image-01 T2I model and document MiniMax provider support

- Add minimax-image-01 to t2iModels in src/lib/models.js and
  packages/studio/src/models.js (8 aspect ratios, up to 4 images per
  request, 1500-char prompt)
- Add minimax-image-01 entry to models_dump.json (t2i section)
- Update README: list MiniMax Image 01 in newly-added image models
  and call out existing MiniMax Hailuo 02/2.3 video model support
- Add scripts/test_minimax_provider.js: validates model registration
  and optionally runs a live API smoke test (MUAPI_KEY env var)

MiniMax Hailuo 02/2.3 T2V and I2V models were already present;
this commit brings MiniMax image generation to feature parity.
This commit is contained in:
octo-patch 2026-04-22 23:41:38 +08:00
commit bf32a393cc
5 changed files with 311 additions and 0 deletions

View file

@ -193,6 +193,7 @@ The Image Studio automatically switches between two model sets:
| **Nano Banana 2 Edit** | Image-to-Image | Up to **14 reference images** · Resolution 1K/2K/4K · Google Search enhancement |
| **Seedream 5.0** | Text-to-Image | ByteDance · Quality basic/high · 8 aspect ratios · up to 4K |
| **Seedream 5.0 Edit** | Image-to-Image | ByteDance · Natural language style transfer · Quality basic/high |
| **MiniMax Image 01** | Text-to-Image | MiniMax · 8 aspect ratios · up to 4 images per request · 1500 char prompt |
#### Multi-Image Input
@ -239,6 +240,7 @@ The Video Studio follows the same pattern:
| **Seedance 2.0 Extend** | Video Extension | ByteDance · Seamlessly continue any Seedance 2.0 generation · Preserves style, motion & audio · Optional continuation prompt · Duration 5 / 10 / 15s · Quality basic/high |
| **Grok Imagine T2V** | Text-to-Video | xAI · Duration 6 / 10 / **15s** · Modes: fun / normal / spicy · Aspect ratios 9:16 / 16:9 / 2:3 / 3:2 / 1:1 |
| **Grok Imagine I2V** | Image-to-Video | xAI · Duration 6 / 10 / **15s** · Modes: fun / normal / spicy · Cinematic motion from still images |
| **MiniMax Hailuo 02 / 2.3 Standard & Pro** | Text-to-Video / Image-to-Video | MiniMax · Full HD video · Multiple aspect ratios · Fast variant included |
### 🎙️ Lip Sync Studio

View file

@ -2001,6 +2001,48 @@
"step": 0.01
}
}
},
{
"id": "minimax-image-01",
"name": "MiniMax Image 01",
"inputs": {
"prompt": {
"type": "string",
"title": "Prompt",
"name": "prompt",
"description": "Text prompt describing the image to generate (max 1500 characters).",
"examples": [
"A serene mountain lake at sunset with golden reflections on the water, surrounded by pine forests and snow-capped peaks, photorealistic, 8k."
]
},
"aspect_ratio": {
"type": "string",
"title": "Aspect Ratio",
"name": "aspect_ratio",
"description": "Aspect ratio of the output image.",
"enum": [
"16:9",
"9:16",
"1:1",
"4:3",
"3:4",
"3:2",
"2:3",
"21:9"
],
"default": "1:1"
},
"num_images": {
"type": "int",
"title": "Number of images",
"name": "num_images",
"description": "Number of images to generate in a single request.",
"default": 1,
"minValue": 1,
"maxValue": 4,
"step": 1
}
}
}
]
}

View file

@ -2089,6 +2089,50 @@ export const t2iModels = [
"default": "basic"
}
}
},
{
"id": "minimax-image-01",
"name": "MiniMax Image 01",
"endpoint": "minimax-image-01",
"family": "minimax",
"inputs": {
"prompt": {
"type": "string",
"title": "Prompt",
"name": "prompt",
"description": "Text prompt describing the image to generate (max 1500 characters).",
"examples": [
"A serene mountain lake at sunset with golden reflections on the water, surrounded by pine forests and snow-capped peaks, photorealistic, 8k."
]
},
"aspect_ratio": {
"type": "string",
"title": "Aspect Ratio",
"name": "aspect_ratio",
"description": "Aspect ratio of the output image.",
"enum": [
"16:9",
"9:16",
"1:1",
"4:3",
"3:4",
"3:2",
"2:3",
"21:9"
],
"default": "1:1"
},
"num_images": {
"type": "int",
"title": "Number of images",
"name": "num_images",
"description": "Number of images to generate in a single request.",
"default": 1,
"minValue": 1,
"maxValue": 4,
"step": 1
}
}
}
];

View file

@ -0,0 +1,179 @@
/**
* Test script for MiniMax provider integration.
*
* Verifies that the MiniMax Image 01 model is correctly registered in models.js
* and that the model definition has the expected structure.
*
* Usage:
* node scripts/test_minimax_provider.js
*
* Set MUAPI_KEY env var to run the live API smoke test:
* MUAPI_KEY=your_key node scripts/test_minimax_provider.js
*/
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..");
// ── 1. Model registration check ──────────────────────────────────────────────
const modelsContent = readFileSync(
join(ROOT, "src", "lib", "models.js"),
"utf-8"
);
// Extract the t2iModels JSON array via a simple regex
const t2iMatch = modelsContent.match(/export const t2iModels = (\[[\s\S]*?\]);/);
if (!t2iMatch) {
console.error("FAIL: Could not parse t2iModels from src/lib/models.js");
process.exit(1);
}
let t2iModels;
try {
t2iModels = JSON.parse(t2iMatch[1]);
} catch (err) {
console.error("FAIL: t2iModels is not valid JSON:", err.message);
process.exit(1);
}
const minimaxModel = t2iModels.find((m) => m.id === "minimax-image-01");
if (!minimaxModel) {
console.error(
'FAIL: "minimax-image-01" not found in t2iModels.\n' +
"Expected it to be registered in src/lib/models.js."
);
process.exit(1);
}
// Validate required fields
const required = ["id", "name", "endpoint", "family", "inputs"];
for (const field of required) {
if (!minimaxModel[field]) {
console.error(`FAIL: minimax-image-01 is missing required field: ${field}`);
process.exit(1);
}
}
if (minimaxModel.family !== "minimax") {
console.error(
`FAIL: expected family "minimax", got "${minimaxModel.family}"`
);
process.exit(1);
}
if (!minimaxModel.inputs.prompt) {
console.error("FAIL: minimax-image-01 inputs missing 'prompt' field");
process.exit(1);
}
if (!minimaxModel.inputs.aspect_ratio?.enum?.includes("1:1")) {
console.error(
"FAIL: minimax-image-01 aspect_ratio enum does not include '1:1'"
);
process.exit(1);
}
console.log("PASS: minimax-image-01 is correctly registered in t2iModels");
console.log(
` endpoint=${minimaxModel.endpoint} family=${minimaxModel.family}`
);
console.log(
` aspect ratios: ${minimaxModel.inputs.aspect_ratio.enum.join(", ")}`
);
// ── 2. models_dump.json check ─────────────────────────────────────────────────
const dump = JSON.parse(
readFileSync(join(ROOT, "models_dump.json"), "utf-8")
);
const dumpEntry = dump.t2i?.find((m) => m.id === "minimax-image-01");
if (!dumpEntry) {
console.error(
'FAIL: "minimax-image-01" not found in models_dump.json t2i section'
);
process.exit(1);
}
console.log("PASS: minimax-image-01 found in models_dump.json");
// ── 3. Live API smoke test (optional) ────────────────────────────────────────
const apiKey = process.env.MUAPI_KEY;
if (!apiKey) {
console.log(
"\nINFO: Skipping live API test (set MUAPI_KEY env var to enable)."
);
console.log("\nAll checks passed.");
process.exit(0);
}
console.log("\nRunning live API smoke test against muapi.ai …");
const MUAPI_BASE = "https://api.muapi.ai";
async function testMiniMaxImageGeneration() {
const endpoint = minimaxModel.endpoint;
const url = `${MUAPI_BASE}/api/v1/${endpoint}`;
const payload = {
prompt: "A simple test: a red apple on a white background.",
aspect_ratio: "1:1",
num_images: 1,
};
console.log(`POST ${url}`);
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
body: JSON.stringify(payload),
});
if (!res.ok) {
const text = await res.text();
console.error(`FAIL: API returned ${res.status}: ${text.slice(0, 200)}`);
process.exit(1);
}
const data = await res.json();
const requestId = data.request_id || data.id;
if (!requestId) {
console.error("FAIL: No request_id in response:", JSON.stringify(data));
process.exit(1);
}
console.log(`PASS: Generation queued — request_id=${requestId}`);
// Poll for result (max 60 s)
const pollUrl = `${MUAPI_BASE}/api/v1/predictions/${requestId}/result`;
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 2000));
const poll = await fetch(pollUrl, {
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
});
if (!poll.ok) continue;
const result = await poll.json();
const status = result.status?.toLowerCase();
if (status === "completed" || status === "succeeded" || status === "success") {
const imageUrl =
result.outputs?.[0] || result.url || result.output?.url;
console.log(`PASS: Generation complete — image URL: ${imageUrl}`);
console.log("\nAll checks passed.");
return;
}
if (status === "failed" || status === "error") {
console.error("FAIL: Generation failed:", result.error);
process.exit(1);
}
console.log(` Polling … status=${status}`);
}
console.error("FAIL: Timed out waiting for generation result.");
process.exit(1);
}
testMiniMaxImageGeneration().catch((err) => {
console.error("FAIL: Unexpected error:", err.message);
process.exit(1);
});

View file

@ -2089,6 +2089,50 @@ export const t2iModels = [
"default": "basic"
}
}
},
{
"id": "minimax-image-01",
"name": "MiniMax Image 01",
"endpoint": "minimax-image-01",
"family": "minimax",
"inputs": {
"prompt": {
"type": "string",
"title": "Prompt",
"name": "prompt",
"description": "Text prompt describing the image to generate (max 1500 characters).",
"examples": [
"A serene mountain lake at sunset with golden reflections on the water, surrounded by pine forests and snow-capped peaks, photorealistic, 8k."
]
},
"aspect_ratio": {
"type": "string",
"title": "Aspect Ratio",
"name": "aspect_ratio",
"description": "Aspect ratio of the output image.",
"enum": [
"16:9",
"9:16",
"1:1",
"4:3",
"3:4",
"3:2",
"2:3",
"21:9"
],
"default": "1:1"
},
"num_images": {
"type": "int",
"title": "Number of images",
"name": "num_images",
"description": "Number of images to generate in a single request.",
"default": 1,
"minValue": 1,
"maxValue": 4,
"step": 1
}
}
}
];