diff --git a/src/backend/bisheng/database/data/t_gpts_tools.json b/src/backend/bisheng/database/data/t_gpts_tools.json
index 3d0934ea31..4cbdd0df25 100644
--- a/src/backend/bisheng/database/data/t_gpts_tools.json
+++ b/src/backend/bisheng/database/data/t_gpts_tools.json
@@ -1,4 +1,82 @@
[
+ {
+ "name": "MiniMax Image Generation",
+ "logo": null,
+ "desc": "Generate images from a text prompt with MiniMax",
+ "api_params": [
+ {
+ "in": "query",
+ "name": "prompt",
+ "schema": {"type": "string"},
+ "required": true,
+ "description": "Text description of the image"
+ },
+ {
+ "in": "query",
+ "name": "model",
+ "schema": {"type": "string", "enum": ["image-01", "image-01-live"], "default": "image-01"},
+ "required": false,
+ "description": "MiniMax image model"
+ },
+ {
+ "in": "query",
+ "name": "aspect_ratio",
+ "schema": {"type": "string"},
+ "required": false,
+ "description": "Generated image aspect ratio"
+ },
+ {
+ "in": "query",
+ "name": "width",
+ "schema": {"type": "integer", "minimum": 512, "maximum": 2048, "multipleOf": 8},
+ "required": false,
+ "description": "Generated image width"
+ },
+ {
+ "in": "query",
+ "name": "height",
+ "schema": {"type": "integer", "minimum": 512, "maximum": 2048, "multipleOf": 8},
+ "required": false,
+ "description": "Generated image height"
+ },
+ {
+ "in": "query",
+ "name": "response_format",
+ "schema": {"type": "string", "enum": ["url", "base64"], "default": "url"},
+ "required": false,
+ "description": "Image response format"
+ },
+ {
+ "in": "query",
+ "name": "seed",
+ "schema": {"type": "integer"},
+ "required": false,
+ "description": "Seed for deterministic generation"
+ },
+ {
+ "in": "query",
+ "name": "n",
+ "schema": {"type": "integer", "minimum": 1, "maximum": 9, "default": 1},
+ "required": false,
+ "description": "Number of images to generate"
+ },
+ {
+ "in": "query",
+ "name": "prompt_optimizer",
+ "schema": {"type": "boolean", "default": false},
+ "required": false,
+ "description": "Enable prompt optimization"
+ }
+ ],
+ "tool_key": "minimax_image_generation",
+ "type": 17,
+ "is_preset": 1,
+ "is_delete": 0,
+ "user_id": 1,
+ "create_time": "2026-08-12 00:00:00",
+ "update_time": "2026-08-12 00:00:00",
+ "id": 39
+ },
{
"name": "时间",
"logo": null,
@@ -1115,4 +1193,4 @@
"user_id": 1,
"id": 38
}
-]
\ No newline at end of file
+]
diff --git a/src/backend/bisheng/database/data/t_gpts_tools_type.json b/src/backend/bisheng/database/data/t_gpts_tools_type.json
index 692c3c56f5..40af921c13 100644
--- a/src/backend/bisheng/database/data/t_gpts_tools_type.json
+++ b/src/backend/bisheng/database/data/t_gpts_tools_type.json
@@ -1,4 +1,20 @@
[
+ {
+ "id": 17,
+ "name": "MiniMax",
+ "logo": "",
+ "description": "Generate images from text prompts with MiniMax image models",
+ "server_host": "",
+ "auth_method": 0,
+ "api_key": "",
+ "auth_type": "basic",
+ "is_preset": 1,
+ "user_id": null,
+ "is_delete": 0,
+ "create_time": "2026-08-12 00:00:00",
+ "update_time": "2026-08-12 00:00:00",
+ "openapi_schema": null
+ },
{
"id": 1,
"name": "时间",
diff --git a/src/backend/bisheng/utils/mask_data.py b/src/backend/bisheng/utils/mask_data.py
index 8a1dd4b705..174b9a06d2 100644
--- a/src/backend/bisheng/utils/mask_data.py
+++ b/src/backend/bisheng/utils/mask_data.py
@@ -23,6 +23,7 @@ def __init__(self):
'api_secret': self.mask_api_key,
'openai_api_key': self.mask_api_key,
'siliconflow_api_key': self.mask_api_key,
+ 'minimax_api_key': self.mask_api_key,
'email_password': self.mask_api_key,
'jina_api_key': self.mask_api_key,
'app_secret': self.mask_api_key,
@@ -42,6 +43,7 @@ def __init__(self):
'api_secret': r'^\*+$',
'openai_api_key': r'^\*+$',
'siliconflow_api_key': r'^\*+$',
+ 'minimax_api_key': r'^\*+$',
'email_password': r'^\*+$',
'jina_api_key': r'^\*+$',
'app_secret': r'^\*+$',
diff --git a/src/backend/bisheng_langchain/gpts/tools/api_tools/__init__.py b/src/backend/bisheng_langchain/gpts/tools/api_tools/__init__.py
index ae7d12ee6b..af3ca37766 100644
--- a/src/backend/bisheng_langchain/gpts/tools/api_tools/__init__.py
+++ b/src/backend/bisheng_langchain/gpts/tools/api_tools/__init__.py
@@ -3,6 +3,7 @@
# from .eastmoney import
from bisheng_langchain.gpts.tools.api_tools.firecrawl import FireCrawl
from bisheng_langchain.gpts.tools.api_tools.jina import JinaTool
+from bisheng_langchain.gpts.tools.api_tools.minimax_image import MiniMaxImage
from bisheng_langchain.gpts.tools.api_tools.silicon_flow import SiliconFlow
from bisheng_langchain.gpts.tools.message.dingding import DingdingMessageTool
from bisheng_langchain.gpts.tools.message.email import EmailMessageTool
@@ -74,6 +75,9 @@
'silicon_stable_diffusion': (SiliconFlow.get_api_tool, ['siliconflow_api_key']),
'silicon_flux': (SiliconFlow.get_api_tool, ['siliconflow_api_key'])
}
+_MINIMAX_IMAGE_TOOLS: Dict[str, Tuple[Callable[[KwArg(Any)], BaseTool], List[str]]] = {
+ "minimax_image_generation": (MiniMaxImage.get_api_tool, ["minimax_api_key", "minimax_base_url"])
+}
dingding_class_methods = [
method for method in DingdingMessageTool.__dict__
if isinstance(DingdingMessageTool.__dict__[method], classmethod)
@@ -120,6 +124,7 @@
ALL_API_TOOLS.update(_FIRE_TOOLS)
ALL_API_TOOLS.update(_JINA_TOOLS)
ALL_API_TOOLS.update(_SILICON_TOOLS)
+ALL_API_TOOLS.update(_MINIMAX_IMAGE_TOOLS)
ALL_API_TOOLS.update(_DING_TOOLS)
ALL_API_TOOLS.update(_EMAIL_TOOLS)
ALL_API_TOOLS.update(_FEISHU_TOOLS)
diff --git a/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image.py b/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image.py
new file mode 100644
index 0000000000..bca71849c6
--- /dev/null
+++ b/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image.py
@@ -0,0 +1,79 @@
+import json
+from typing import Any, Literal
+
+import requests
+from langchain_core.tools import BaseTool
+from pydantic import BaseModel, Field, model_validator
+
+from bisheng_langchain.gpts.tools.api_tools.base import APIToolBase, MultArgsSchemaTool
+from bisheng_langchain.gpts.tools.api_tools.minimax_image_core import (
+ build_image_generation_payload,
+ parse_image_generation_response,
+)
+
+
+class MiniMaxImageInput(BaseModel):
+ prompt: str = Field(description="Text description of the image", min_length=1, max_length=1500)
+ model: Literal["image-01", "image-01-live"] = "image-01"
+ aspect_ratio: Literal["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"] | None = None
+ width: int | None = Field(default=None, ge=512, le=2048, multiple_of=8)
+ height: int | None = Field(default=None, ge=512, le=2048, multiple_of=8)
+ response_format: Literal["url", "base64"] = "url"
+ seed: int | None = None
+ n: int = Field(default=1, ge=1, le=9)
+ prompt_optimizer: bool = False
+
+ @model_validator(mode="after")
+ def validate_dimensions(self) -> "MiniMaxImageInput":
+ if (self.width is None) != (self.height is None):
+ raise ValueError("width and height must be provided together")
+ return self
+
+
+class MiniMaxImage(APIToolBase):
+ minimax_api_key: str = Field(description="MiniMax API key")
+ minimax_base_url: str = Field(default="https://api.minimax.io", description="MiniMax API base URL")
+
+ def generate(
+ self,
+ prompt: str,
+ model: str = "image-01",
+ aspect_ratio: str | None = None,
+ width: int | None = None,
+ height: int | None = None,
+ response_format: str = "url",
+ seed: int | None = None,
+ n: int = 1,
+ prompt_optimizer: bool = False,
+ ) -> str:
+ """Generate images from a text prompt with MiniMax."""
+ payload = build_image_generation_payload(
+ prompt=prompt,
+ model=model,
+ aspect_ratio=aspect_ratio,
+ width=width,
+ height=height,
+ response_format=response_format,
+ seed=seed,
+ n=n,
+ prompt_optimizer=prompt_optimizer,
+ )
+ response = requests.post(
+ f"{self.minimax_base_url.rstrip('/')}/v1/image_generation",
+ json=payload,
+ headers={"Authorization": f"Bearer {self.minimax_api_key}", "Content-Type": "application/json"},
+ timeout=self.request_timeout,
+ )
+ response.raise_for_status()
+ result = parse_image_generation_response(response.json(), response_format)
+ return json.dumps(result, ensure_ascii=False)
+
+ @classmethod
+ def get_api_tool(cls, name: str, **kwargs: Any) -> BaseTool:
+ instance = cls(**kwargs)
+ return MultArgsSchemaTool(
+ name=name,
+ description=instance.generate.__doc__,
+ func=instance.generate,
+ args_schema=MiniMaxImageInput,
+ )
diff --git a/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image_core.py b/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image_core.py
new file mode 100644
index 0000000000..78a6a97e50
--- /dev/null
+++ b/src/backend/bisheng_langchain/gpts/tools/api_tools/minimax_image_core.py
@@ -0,0 +1,54 @@
+from typing import Any
+
+
+def build_image_generation_payload(
+ *,
+ prompt: str,
+ model: str,
+ aspect_ratio: str | None,
+ width: int | None,
+ height: int | None,
+ response_format: str,
+ seed: int | None,
+ n: int,
+ prompt_optimizer: bool,
+) -> dict[str, Any]:
+ payload: dict[str, Any] = {
+ "model": model,
+ "prompt": prompt,
+ "response_format": response_format,
+ "n": n,
+ "prompt_optimizer": prompt_optimizer,
+ }
+ optional_fields = {
+ "aspect_ratio": aspect_ratio,
+ "width": width,
+ "height": height,
+ "seed": seed,
+ }
+ payload.update({key: value for key, value in optional_fields.items() if value is not None})
+ return payload
+
+
+def parse_image_generation_response(payload: dict[str, Any], response_format: str) -> dict[str, Any]:
+ base_response = payload.get("base_resp")
+ if not isinstance(base_response, dict) or base_response.get("status_code") != 0:
+ message = base_response.get("status_msg") if isinstance(base_response, dict) else None
+ raise ValueError(message or "MiniMax image generation failed")
+
+ data = payload.get("data")
+ if not isinstance(data, dict):
+ raise ValueError("MiniMax image generation returned no image data")
+ response_key = "image_urls" if response_format == "url" else "image_base64"
+ images = data.get(response_key)
+ if not isinstance(images, list) or not images or not all(isinstance(image, str) for image in images):
+ raise ValueError("MiniMax image generation returned invalid image data")
+
+ metadata = payload.get("metadata")
+ metadata = metadata if isinstance(metadata, dict) else {}
+ return {
+ "response_format": response_format,
+ "images": images,
+ "success_count": metadata.get("success_count", len(images)),
+ "failed_count": metadata.get("failed_count", 0),
+ }
diff --git a/src/backend/test/tool/test_minimax_image.py b/src/backend/test/tool/test_minimax_image.py
new file mode 100644
index 0000000000..405348377a
--- /dev/null
+++ b/src/backend/test/tool/test_minimax_image.py
@@ -0,0 +1,76 @@
+import importlib.util
+import unittest
+from pathlib import Path
+
+MODULE_PATH = Path(__file__).parents[2] / "bisheng_langchain" / "gpts" / "tools" / "api_tools" / "minimax_image_core.py"
+SPEC = importlib.util.spec_from_file_location("minimax_image_core", MODULE_PATH)
+assert SPEC and SPEC.loader
+MODULE = importlib.util.module_from_spec(SPEC)
+SPEC.loader.exec_module(MODULE)
+build_image_generation_payload = MODULE.build_image_generation_payload
+parse_image_generation_response = MODULE.parse_image_generation_response
+
+
+class TestMiniMaxImagePayload(unittest.TestCase):
+ def test_builds_supported_request_fields(self):
+ payload = build_image_generation_payload(
+ prompt="A small robot reading a book",
+ model="image-01-live",
+ aspect_ratio=None,
+ width=1024,
+ height=768,
+ response_format="base64",
+ seed=7,
+ n=2,
+ prompt_optimizer=True,
+ )
+
+ self.assertEqual(
+ payload,
+ {
+ "model": "image-01-live",
+ "prompt": "A small robot reading a book",
+ "width": 1024,
+ "height": 768,
+ "response_format": "base64",
+ "seed": 7,
+ "n": 2,
+ "prompt_optimizer": True,
+ },
+ )
+
+ def test_parses_url_response_and_metadata(self):
+ result = parse_image_generation_response(
+ {
+ "data": {"image_urls": ["https://example.test/image.png"]},
+ "metadata": {"success_count": 1, "failed_count": 0},
+ "base_resp": {"status_code": 0, "status_msg": "success"},
+ },
+ "url",
+ )
+
+ self.assertEqual(result["images"], ["https://example.test/image.png"])
+ self.assertEqual(result["success_count"], 1)
+ self.assertEqual(result["failed_count"], 0)
+
+ def test_parses_base64_response(self):
+ result = parse_image_generation_response(
+ {
+ "data": {"image_base64": ["aW1hZ2U="]},
+ "base_resp": {"status_code": 0, "status_msg": "success"},
+ },
+ "base64",
+ )
+
+ self.assertEqual(result["images"], ["aW1hZ2U="])
+
+ def test_rejects_provider_error(self):
+ with self.assertRaisesRegex(ValueError, "invalid request"):
+ parse_image_generation_response(
+ {"base_resp": {"status_code": 2013, "status_msg": "invalid request"}},
+ "url",
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/frontend/platform/public/locales/en-US/tool.json b/src/frontend/platform/public/locales/en-US/tool.json
index 711023e01f..261b9b99a8 100644
--- a/src/frontend/platform/public/locales/en-US/tool.json
+++ b/src/frontend/platform/public/locales/en-US/tool.json
@@ -387,6 +387,8 @@
"fieldCannotBeEmpty": "{{label}} cannot be empty",
"jinaApiKeyLabel": "Jina API Key",
"siliconflowApiKeyLabel": "SiliconFlow API Key",
+ "minimaxApiKeyLabel": "MiniMax API Key",
+ "minimaxApiBaseUrlLabel": "MiniMax API Base URL",
"uploadFile": "Upload File",
"organizeKnowledgeBase": "Organize Knowledge Base",
"个人知识库": "Personal Knowledge Base",
@@ -406,4 +408,4 @@
"写入文件内容desc": "Append text to a file or create one if it does not exist.",
"替换文件指定行范围内容": "Replace File Line Range",
"替换文件指定行范围内容desc": "Replace a specified range of lines in a file."
-}
\ No newline at end of file
+}
diff --git a/src/frontend/platform/public/locales/ja/tool.json b/src/frontend/platform/public/locales/ja/tool.json
index 43208a1cb7..2df07fb733 100644
--- a/src/frontend/platform/public/locales/ja/tool.json
+++ b/src/frontend/platform/public/locales/ja/tool.json
@@ -387,6 +387,8 @@
"fieldCannotBeEmpty": "{{label}}は空にできません",
"jinaApiKeyLabel": "Jina API Key",
"siliconflowApiKeyLabel": "SiliconFlow API Key",
+ "minimaxApiKeyLabel": "MiniMax API Key",
+ "minimaxApiBaseUrlLabel": "MiniMax API Base URL",
"uploadFile": "ファイルをアップロード",
"organizeKnowledgeBase": "ナレッジベース整理",
"个人知识库": "個人ナレッジベース",
@@ -406,4 +408,4 @@
"写入文件内容desc": "テキストを追記し、存在しない場合は新規作成します。",
"替换文件指定行范围内容": "行範囲を置換",
"替换文件指定行范围内容desc": "ファイル内の指定行範囲を置き換えます。"
-}
\ No newline at end of file
+}
diff --git a/src/frontend/platform/public/locales/zh-Hans/tool.json b/src/frontend/platform/public/locales/zh-Hans/tool.json
index 4766671610..1dd04cc429 100644
--- a/src/frontend/platform/public/locales/zh-Hans/tool.json
+++ b/src/frontend/platform/public/locales/zh-Hans/tool.json
@@ -387,6 +387,8 @@
"fieldCannotBeEmpty": "{{label}} 不能为空",
"jinaApiKeyLabel": "Jina API 密钥",
"siliconflowApiKeyLabel": "SiliconFlow API 密钥",
+ "minimaxApiKeyLabel": "MiniMax API Key",
+ "minimaxApiBaseUrlLabel": "MiniMax API Base URL",
"uploadFile": "上传文件",
"organizeKnowledgeBase": "组织知识库",
"个人知识库": "个人知识库",
@@ -406,4 +408,4 @@
"写入文件内容desc": "将文本内容追加到文本文件,如果文件不存在,则创建文件",
"替换文件指定行范围内容": "替换文件指定行范围内容",
"替换文件指定行范围内容desc": "替换文件中的指定行范围。"
-}
\ No newline at end of file
+}
diff --git a/src/frontend/platform/src/pages/BuildPage/tools/ToolSet.tsx b/src/frontend/platform/src/pages/BuildPage/tools/ToolSet.tsx
index 5a018ab0c1..7247035719 100644
--- a/src/frontend/platform/src/pages/BuildPage/tools/ToolSet.tsx
+++ b/src/frontend/platform/src/pages/BuildPage/tools/ToolSet.tsx
@@ -10,6 +10,7 @@ import Dalle3ToolForm from "./builtInTool/Dalle3";
import EmailConfigForm from "./builtInTool/EmailConfig";
import FeishuConfigForm from "./builtInTool/FeishuConfig";
import JinaApiKeyForm from "./builtInTool/JinaConfig";
+import { MiniMaxConfig } from "./builtInTool/MiniMaxConfig";
import SiliconFlowApiKeyForm from "./builtInTool/SiliconFlowApiKey";
import TianyanchaToolForm from "./builtInTool/Tianyancha";
import WebSearchForm from "./builtInTool/WebSearchFrom";
@@ -102,6 +103,8 @@ const ToolSet = forwardRef(function ToolSet({ onChange }, ref) {
return ;
case 'SiliconFlow':
return ;
+ case 'MiniMax':
+ return ;
case '发送邮件':
return ;
case '飞书消息':
diff --git a/src/frontend/platform/src/pages/BuildPage/tools/builtInTool/MiniMaxConfig.tsx b/src/frontend/platform/src/pages/BuildPage/tools/builtInTool/MiniMaxConfig.tsx
new file mode 100644
index 0000000000..af77025889
--- /dev/null
+++ b/src/frontend/platform/src/pages/BuildPage/tools/builtInTool/MiniMaxConfig.tsx
@@ -0,0 +1,42 @@
+import { Button } from '@/components/bs-ui/button';
+import { DialogClose, DialogFooter } from '@/components/bs-ui/dialog';
+import { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { InputField } from './InputField';
+
+const defaultValues = {
+ minimax_api_key: '',
+ minimax_base_url: 'https://api.minimax.io',
+};
+
+export function MiniMaxConfig({ formData = {}, onSubmit }) {
+ const { t } = useTranslation('tool');
+ const [localFormData, setLocalFormData] = useState(() => ({ ...defaultValues, ...formData }));
+ const [errors, setErrors] = useState>({});
+
+ const handleChange = (event) => {
+ const { name, value } = event.target;
+ setLocalFormData((previous) => ({ ...previous, [name]: value }));
+ };
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ const nextErrors = {
+ minimax_api_key: !localFormData.minimax_api_key,
+ minimax_base_url: !localFormData.minimax_base_url,
+ };
+ setErrors(nextErrors);
+ if (!Object.values(nextErrors).some(Boolean)) onSubmit(localFormData);
+ };
+
+ return (
+
+ );
+}