feat: 将 47 个 Operit TypeScript 脚本全面翻译为 Python

- 新增 operit_runtime.py: 跨平台 Python 运行时
  - 实现 Tools.System/Files/Net/UI/Memory/FFmpeg/Workflow/Chat/SoftwareSettings/Tasker
  - 实现 OkHttp 链式 HTTP 客户端兼容层
  - 实现 complete()/exports/console 全局对象
  - 实现 OperitScriptLoader 脚本加载器
  - 支持 Windows/macOS/Linux 三平台

- 新增 operit_py/ 目录: 47 个 Python 翻译脚本
  - 搜索类: duckduckgo, google_search, tavily, zhipu_search, various_search, crossref
  - 绘图类: openai_draw, qwen_draw, xai_draw, minimax_draw, siliconflow_draw, zhipu_draw, nanobanana_draw
  - 媒体类: ffmpeg, file_converter, douyin_download, bilibili_tools, jmcomic
  - 文件类: extended_file_tools, hex_editor, reader, beeimg_image_uploader
  - 网络类: extended_http_tools, network_test
  - 自动化类: browser, automatic_ui_base, automatic_ui_subagent, automatic_bilibili_assistant, automatic_xiaohongshu_assistant, automatic_baidu_map_assistant
  - 系统类: system_tools, super_admin, daily_life, operit_editor
  - 对话类: ai_chat, extended_chat, qq_intelligent
  - 工作流类: workflow, vflow_trigger, tasker
  - 工具类: time, code_runner, quick_start, extended_memory_tools, baidu_map, 12306, pdf_vision_parser

- 测试结果: 47/47 语法检查通过, 38 脚本加载成功, 280 个工具注册
- Android 专属功能已降级处理(保留接口,返回不支持提示)
This commit is contained in:
shuiyu1123 committed 2026-06-23 12:27:18 +00:00
1 parent 6fa759665d
commit dc77dfd304
48 files changed
+22273

No files matched your search

+597
View File
@@ -0,0 +1,597 @@
# METADATA
# {
# "name": "minimax_draw",
# "display_name": {
# "zh": "MiniMax 绘图",
# "en": "MiniMax Draw"
# },
# "description": {
# "zh": "使用 MiniMax 官方图像生成接口 (/v1/image_generation) 生成图片,支持文生图和带参考图的生图;结果保存到本地 /sdcard/Download/Operit/plugins/draw/minimax_draw/draws/ 目录,并返回 Markdown 图片提示。",
# "en": "Generate images with the official MiniMax image generation API (/v1/image_generation). Supports text-to-image and reference-image generation. Saves results to /sdcard/Download/Operit/plugins/draw/minimax_draw/draws/ and returns Markdown image hints."
# },
# "category": "Draw",
# "env": [
# { "name": "MINIMAX_API_KEY", "description": { "zh": "MiniMax API Key(必填)", "en": "MiniMax API key (required)" }, "required": true },
# { "name": "MINIMAX_API_BASE_URL", "description": { "zh": "MiniMax API Base URL(可选;默认 https://api.minimaxi.com,国际站可改为 https://api.minimax.io)", "en": "MiniMax API base URL (optional; defaults to https://api.minimaxi.com, international users can use https://api.minimax.io)" }, "required": false },
# { "name": "MINIMAX_IMAGE_MODEL", "description": { "zh": "默认图片模型(可选;未传 model 时使用,默认 image-01)", "en": "Default image model (optional; used when model is omitted, default image-01)" }, "required": false },
# { "name": "BEEIMG_API_KEY", "description": { "zh": "BeeIMG API Key(可选;仅当 image_paths 传本地参考图路径时需要,用于先上传到公网图床)", "en": "BeeIMG API key (optional; only needed when image_paths is used so local reference images can be uploaded first)" }, "required": false }
# ],
# "tools": [
# {
# "name": "draw_image",
# "description": { "zh": "根据提示词调用 MiniMax 官方绘图接口生成图片,支持文生图和参考图生图,保存到本地并返回 Markdown 图片提示。", "en": "Generate images with the official MiniMax image API using a prompt. Supports text-to-image and reference-image generation, saves locally, and returns Markdown image hints." },
# "parameters": [
# { "name": "prompt", "description": { "zh": "绘图提示词(中文或英文)", "en": "Image prompt (Chinese or English)" }, "type": "string", "required": true },
# { "name": "model", "description": { "zh": "模型名称(可选;不传则使用环境变量 MINIMAX_IMAGE_MODEL,否则默认 image-01)", "en": "Model name (optional; falls back to MINIMAX_IMAGE_MODEL, then image-01)" }, "type": "string", "required": false },
# { "name": "aspect_ratio", "description": { "zh": "输出比例,可选 1:1、16:9、4:3、3:2、2:3、3:4、9:16、21:9;21:9 仅 image-01 支持", "en": "Aspect ratio. Supported: 1:1, 16:9, 4:3, 3:2, 2:3, 3:4, 9:16, 21:9. 21:9 is only supported by image-01." }, "type": "string", "required": false },
# { "name": "width", "description": { "zh": "输出宽度(可选;仅 image-01 支持;需与 height 一起传,范围 512-2048 且为 8 的倍数)", "en": "Output width (optional; image-01 only; must be provided together with height, range 512-2048 and multiple of 8)" }, "type": "number", "required": false },
# { "name": "height", "description": { "zh": "输出高度(可选;仅 image-01 支持;需与 width 一起传,范围 512-2048 且为 8 的倍数)", "en": "Output height (optional; image-01 only; must be provided together with width, range 512-2048 and multiple of 8)" }, "type": "number", "required": false },
# { "name": "n", "description": { "zh": "生成图片数量,范围 1-9,默认 1", "en": "Number of images to generate, range 1-9, default 1" }, "type": "number", "required": false },
# { "name": "response_format", "description": { "zh": "返回格式,可选 url 或 base64;默认 base64(更适合直接保存到本地)", "en": "Response format: url or base64. Defaults to base64 for direct local saving." }, "type": "string", "required": false },
# { "name": "prompt_optimizer", "description": { "zh": "是否启用提示词优化(可选)", "en": "Enable prompt optimizer (optional)" }, "type": "boolean", "required": false },
# { "name": "aigc_watermark", "description": { "zh": "是否添加 AIGC 水印(可选)", "en": "Add AIGC watermark (optional)" }, "type": "boolean", "required": false },
# { "name": "seed", "description": { "zh": "随机种子(可选)", "en": "Seed (optional)" }, "type": "number", "required": false },
# { "name": "image_urls", "description": { "zh": "参考图公网 URL 数组(可选;图生图用)。支持字符串数组、JSON 字符串或逗号分隔字符串", "en": "Reference image public URL list (optional; for reference-image generation). Accepts string array, JSON string, or comma-separated string." }, "type": "array", "required": false },
# { "name": "image_paths", "description": { "zh": "参考图本地路径数组(可选;会先上传到图床再提交给 MiniMax)。支持字符串数组、JSON 字符串或逗号分隔字符串", "en": "Reference local image path list (optional; local images are uploaded first before calling MiniMax). Accepts string array, JSON string, or comma-separated string." }, "type": "array", "required": false },
# { "name": "file_name", "description": { "zh": "自定义本地文件名(不含扩展名)", "en": "Custom local output file name without extension" }, "type": "string", "required": false },
# { "name": "api_base_url", "description": { "zh": "自定义 MiniMax API Base URL(可选)", "en": "Custom MiniMax API base URL (optional)" }, "type": "string", "required": false }
# ]
# }
# ]
# }
import json
import math
import re
import time
import traceback
HTTP_TIMEOUT_MS = 600000
client = OkHttp.newBuilder().connectTimeout(HTTP_TIMEOUT_MS).readTimeout(HTTP_TIMEOUT_MS).writeTimeout(HTTP_TIMEOUT_MS).build()
DEFAULT_API_BASE_URL = "https://api.minimaxi.com"
DEFAULT_MODEL = "image-01"
DEFAULT_RESPONSE_FORMAT = "base64"
DEFAULT_IMAGE_COUNT = 1
DEFAULT_BASE64_EXTENSION = "jpeg"
SUPPORTED_ASPECT_RATIOS = ["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]
BEEIMG_UPLOAD_ENDPOINT = "https://beeimg.com/api/upload/file/json/"
DRAW_ROOT_DIR = getPluginConfigDir("draw")
STORAGE_DIR = f"{DRAW_ROOT_DIR}/minimax_draw"
DRAWS_DIR = f"{STORAGE_DIR}/draws"
def _is_finite(n):
return isinstance(n, (int, float)) and not isinstance(n, bool) and math.isfinite(n)
def _is_integer(n):
return _is_finite(n) and n == int(n)
def isRecord(value):
return isinstance(value, dict)
def getErrorMessage(error):
if isinstance(error, Exception):
return str(error)
return str(error)
def getApiKey():
apiKey = str(getEnv("MINIMAX_API_KEY") or "").strip()
if not apiKey:
raise Exception("MINIMAX_API_KEY 未配置,请先在环境变量中设置 MiniMax API Key。")
return apiKey
def joinUrl(baseUrl, path):
baseStr = str(baseUrl or "")
normalizedBase = baseStr if baseStr.endswith("/") else f"{baseStr}/"
pathStr = str(path or "")
normalizedPath = pathStr[1:] if pathStr.startswith("/") else pathStr
return f"{normalizedBase}{normalizedPath}"
def getApiBaseUrl(customBaseUrl=None):
fromParam = str(customBaseUrl or "").strip()
if fromParam:
return fromParam
fromEnv = str(getEnv("MINIMAX_API_BASE_URL") or "").strip()
if fromEnv:
return fromEnv
return DEFAULT_API_BASE_URL
def getImageEndpoint(baseUrl):
trimmed = str(baseUrl or "").strip()
if not trimmed:
return joinUrl(DEFAULT_API_BASE_URL, "v1/image_generation")
if "/v1/image_generation" in trimmed:
return trimmed
if trimmed.endswith("/v1"):
return joinUrl(trimmed, "image_generation")
if trimmed.endswith("/v1/"):
return joinUrl(trimmed, "image_generation")
return joinUrl(trimmed, "v1/image_generation")
def resolveModel(model):
fromParam = str(model or "").strip()
if fromParam:
return fromParam
fromEnv = str(getEnv("MINIMAX_IMAGE_MODEL") or "").strip()
if fromEnv:
return fromEnv
return DEFAULT_MODEL
def sanitizeFileName(name, fallbackPrefix):
safe = re.sub(r'[\\/:*?"<>|]', "_", str(name or "")).strip()
if not safe:
return f"{fallbackPrefix}_{int(time.time() * 1000)}"
return safe[:80]
def buildFileBaseName(prompt, customName, fallbackPrefix):
fromCustom = str(customName or "").strip()
if fromCustom:
return sanitizeFileName(fromCustom, fallbackPrefix)
rawPrompt = str(prompt or "").strip()
shortPrompt = f"{rawPrompt[:40]}..." if len(rawPrompt) > 40 else rawPrompt
return f"{sanitizeFileName(shortPrompt or fallbackPrefix, fallbackPrefix)}_{int(time.time() * 1000)}"
async def ensureDirectories():
dirs = [DRAW_ROOT_DIR, STORAGE_DIR, DRAWS_DIR]
for dir_ in dirs:
try:
result = await Tools.Files.mkdir(dir_)
if not result.get("successful"):
console.warn(f"创建目录失败(可能已存在): {dir_} -> {result.get('details')}")
except Exception as error:
console.warn(f"创建目录异常: {dir_} -> {getErrorMessage(error)}")
def parsePositiveInteger(value, fieldName, options=None):
hasDefault = options is not None and "defaultValue" in options
defaultValue = options["defaultValue"] if hasDefault else None
if value is None or value == "":
return defaultValue
numberValue = value if isinstance(value, (int, float)) and not isinstance(value, bool) else float(str(value))
if not _is_finite(numberValue) or numberValue <= 0 or not _is_integer(numberValue):
raise Exception(f"{fieldName} 必须是正整数。")
numberValue = int(numberValue)
if options and options.get("min") is not None and numberValue < options["min"]:
raise Exception(f"{fieldName} 不能小于 {options['min']}。")
if options and options.get("max") is not None and numberValue > options["max"]:
raise Exception(f"{fieldName} 不能大于 {options['max']}。")
return numberValue
def parseInteger(value, fieldName):
if value is None or value == "":
return None
numberValue = value if isinstance(value, (int, float)) and not isinstance(value, bool) else float(str(value))
if not _is_finite(numberValue) or not _is_integer(numberValue):
raise Exception(f"{fieldName} 必须是整数。")
return int(numberValue)
def parseBoolean(value, fieldName):
if value is None or value == "":
return None
if isinstance(value, bool):
return value
normalized = str(value).strip().lower()
if normalized in ("true", "1", "yes"):
return True
if normalized in ("false", "0", "no"):
return False
raise Exception(f"{fieldName} 必须是布尔值。")
def normalizeResponseFormat(value):
normalized = str(value or DEFAULT_RESPONSE_FORMAT).strip().lower()
if normalized != "url" and normalized != "base64":
raise Exception("response_format 仅支持 url 或 base64。")
return normalized
def normalizeAspectRatio(model, value):
normalized = str(value or "").strip()
if not normalized:
return None
if normalized not in SUPPORTED_ASPECT_RATIOS:
raise Exception(f"aspect_ratio 仅支持 {'、'.join(SUPPORTED_ASPECT_RATIOS)}。")
if normalized == "21:9" and str(model or "").strip() != "image-01":
raise Exception("aspect_ratio=21:9 仅支持 model=image-01。")
return normalized
def normalizeDimensions(model, widthValue, heightValue, aspectRatio):
if aspectRatio:
return {"width": None, "height": None}
width = parsePositiveInteger(widthValue, "width")
height = parsePositiveInteger(heightValue, "height")
if width is None and height is None:
return {"width": None, "height": None}
if width is None or height is None:
raise Exception("width 和 height 需要同时传入。")
if str(model or "").strip() != "image-01":
raise Exception("width 和 height 仅支持 model=image-01。")
if width < 512 or width > 2048 or width % 8 != 0:
raise Exception("width 需要在 512-2048 之间,且必须为 8 的倍数。")
if height < 512 or height > 2048 or height % 8 != 0:
raise Exception("height 需要在 512-2048 之间,且必须为 8 的倍数。")
return {"width": width, "height": height}
def parseStringList(value, fieldName):
if value is None or value == "":
return []
if isinstance(value, list):
return [str(item or "").strip() for item in value if str(item or "").strip()]
if isinstance(value, str):
trimmed = value.strip()
if not trimmed:
return []
try:
parsed = json.loads(trimmed)
if isinstance(parsed, list):
return [str(item or "").strip() for item in parsed if str(item or "").strip()]
except Exception:
# ignore and try comma-separated parsing
pass
return [item.strip() for item in trimmed.split(",") if item.strip()]
raise Exception(f"{fieldName} 必须是字符串数组、JSON 字符串或逗号分隔字符串。")
def isProbablyUrl(value):
return bool(re.match(r"^https?://", str(value or "").strip(), re.IGNORECASE))
def getBeeimgApiKey():
return str(getEnv("BEEIMG_API_KEY") or "").strip()
def guessMimeTypeFromPath(filePath):
lower = str(filePath or "").lower()
if lower.endswith(".png"):
return "image/png"
if lower.endswith(".jpg") or lower.endswith(".jpeg"):
return "image/jpeg"
if lower.endswith(".webp"):
return "image/webp"
if lower.endswith(".gif"):
return "image/gif"
return "application/octet-stream"
async def uploadImageToBeeimg(filePath):
exists = await Tools.Files.exists(filePath)
if not exists.get("exists"):
raise Exception(f"参考图文件不存在: {filePath}")
apiKey = getBeeimgApiKey()
if not apiKey:
raise Exception("使用 image_paths 需要配置 BEEIMG_API_KEY,用于先把本地参考图上传到公网图床。")
response = await Tools.Net.uploadFile({
"url": BEEIMG_UPLOAD_ENDPOINT,
"method": "POST",
"form_data": {
"apikey": apiKey
},
"files": [
{
"field_name": "file",
"file_path": filePath,
"content_type": guessMimeTypeFromPath(filePath)
}
]
})
if response.statusCode < 200 or response.statusCode >= 300:
raise Exception(f"BeeIMG 上传失败: HTTP {response.statusCode} - {response.content}")
try:
parsed = json.loads(response.content)
except Exception as error:
raise Exception(f"解析 BeeIMG 响应失败: {getErrorMessage(error)}")
files = parsed.get("files") if (isRecord(parsed) and isRecord(parsed.get("files"))) else None
fileUrl = str(files.get("url")).strip() if (files and files.get("url")) else ""
code = str(files.get("code")) if (files and files.get("code") is not None) else ""
status = str(files.get("status")) if (files and files.get("status")) else ""
if not fileUrl or (code != "200" and code != "" and status != "Success"):
raise Exception(f"BeeIMG 上传失败: {response.content}")
return fileUrl
async def resolveSubjectReference(imageUrls, imagePaths):
resolvedUrls = parseStringList(imageUrls, "image_urls")
resolvedPaths = parseStringList(imagePaths, "image_paths")
for url in resolvedUrls:
if not isProbablyUrl(url):
raise Exception(f"image_urls 中包含无效链接: {url}")
for filePath in resolvedPaths:
uploadedUrl = await uploadImageToBeeimg(filePath)
resolvedUrls.append(uploadedUrl)
if len(resolvedUrls) == 0:
return None
return [{"type": "character", "image_file": url} for url in resolvedUrls]
def normalizeBase64(base64):
raw = str(base64 or "").strip()
if not raw:
return raw
prefixIndex = raw.find("base64,")
if raw.startswith("data:") and prefixIndex >= 0:
return raw[prefixIndex + len("base64,"):].strip()
return raw
def guessExtensionFromUrl(url, fallbackExtension):
match = re.search(r"\.([a-z0-9]{2,5})(?:[?#].*)?$", str(url or ""), re.IGNORECASE)
if match and match.group(1):
return match.group(1).lower()
return fallbackExtension
def parseMiniMaxError(parsed, fallbackMessage):
baseResp = parsed.get("base_resp") if (isRecord(parsed) and isRecord(parsed.get("base_resp"))) else None
if not baseResp:
return
statusCode = float(baseResp.get("status_code")) if baseResp.get("status_code") is not None else 0
if _is_finite(statusCode) and statusCode != 0:
statusMsg = str(baseResp.get("status_msg")) if baseResp.get("status_msg") else fallbackMessage
raise Exception(f"MiniMax 图片接口返回错误: {statusMsg} (status_code={int(statusCode) if _is_integer(statusCode) else statusCode})")
async def callMiniMaxImageApi(params):
apiBaseUrl = getApiBaseUrl(params.get("api_base_url"))
endpoint = getImageEndpoint(apiBaseUrl)
effectiveModel = resolveModel(params.get("model"))
responseFormat = normalizeResponseFormat(params.get("response_format"))
aspectRatio = normalizeAspectRatio(effectiveModel, params.get("aspect_ratio"))
dimensions = normalizeDimensions(effectiveModel, params.get("width"), params.get("height"), aspectRatio)
imageCount = parsePositiveInteger(params.get("n"), "n", {"defaultValue": DEFAULT_IMAGE_COUNT, "min": 1, "max": 9})
promptOptimizer = parseBoolean(params.get("prompt_optimizer"), "prompt_optimizer")
aigcWatermark = parseBoolean(params.get("aigc_watermark"), "aigc_watermark")
seed = parseInteger(params.get("seed"), "seed")
subjectReference = await resolveSubjectReference(params.get("image_urls"), params.get("image_paths"))
body = {
"model": effectiveModel,
"prompt": str(params.get("prompt") or "").strip(),
"response_format": responseFormat,
"n": imageCount
}
if aspectRatio:
body["aspect_ratio"] = aspectRatio
if dimensions.get("width") is not None and dimensions.get("height") is not None:
body["width"] = dimensions["width"]
body["height"] = dimensions["height"]
if promptOptimizer is not None:
body["prompt_optimizer"] = promptOptimizer
if aigcWatermark is not None:
body["aigc_watermark"] = aigcWatermark
if seed is not None:
body["seed"] = seed
if subjectReference:
body["subject_reference"] = subjectReference
request = client.newRequest().url(endpoint).method("POST").headers({
"accept": "application/json",
"content-type": "application/json",
"Authorization": f"Bearer {getApiKey()}"
}).body(json.dumps(body), "json")
response = await request.build().execute()
if not response.isSuccessful():
raise Exception(f"MiniMax 图片 API 调用失败: {response.statusCode} - {response.content}")
try:
parsed = json.loads(response.content)
except Exception as error:
raise Exception(f"解析 MiniMax 响应失败: {getErrorMessage(error)}")
parseMiniMaxError(parsed, "MiniMax 接口返回错误")
data = parsed.get("data") if (isRecord(parsed) and isRecord(parsed.get("data"))) else None
if not data:
raise Exception("MiniMax 响应中未找到 data 对象。")
metadata = data.get("metadata") if isRecord(data.get("metadata")) else None
successCount = float(metadata.get("success_count")) if (metadata and metadata.get("success_count") is not None) else None
failedCount = float(metadata.get("failed_count")) if (metadata and metadata.get("failed_count") is not None) else None
requestId = str(parsed.get("id")) if (isRecord(parsed) and parsed.get("id")) else ""
if responseFormat == "base64":
imageBase64 = [str(item or "").strip() for item in data.get("image_base64")] if isinstance(data.get("image_base64"), list) else []
imageBase64 = [item for item in imageBase64 if len(item) > 0]
if len(imageBase64) == 0:
raise Exception("MiniMax 响应中未找到 data.image_base64。")
return {
"request_id": requestId,
"effective_model": effectiveModel,
"response_format": responseFormat,
"image_base64": imageBase64,
"image_urls": [],
"success_count": int(successCount) if _is_finite(successCount) else len(imageBase64),
"failed_count": int(failedCount) if _is_finite(failedCount) else 0,
"reference_count": len(subjectReference) if subjectReference else 0
}
imageUrls = [str(item or "").strip() for item in data.get("image_urls")] if isinstance(data.get("image_urls"), list) else []
imageUrls = [item for item in imageUrls if len(item) > 0]
if len(imageUrls) == 0:
raise Exception("MiniMax 响应中未找到 data.image_urls。")
return {
"request_id": requestId,
"effective_model": effectiveModel,
"response_format": responseFormat,
"image_base64": [],
"image_urls": imageUrls,
"success_count": int(successCount) if _is_finite(successCount) else len(imageUrls),
"failed_count": int(failedCount) if _is_finite(failedCount) else 0,
"reference_count": len(subjectReference) if subjectReference else 0
}
async def draw_image(params):
prompt = str(params.get("prompt") if params and params.get("prompt") else "").strip()
if not prompt:
raise Exception("参数 prompt 不能为空。")
await ensureDirectories()
apiResult = await callMiniMaxImageApi({
"prompt": prompt,
"model": params.get("model"),
"aspect_ratio": params.get("aspect_ratio"),
"width": params.get("width"),
"height": params.get("height"),
"n": params.get("n"),
"response_format": params.get("response_format"),
"prompt_optimizer": params.get("prompt_optimizer"),
"aigc_watermark": params.get("aigc_watermark"),
"seed": params.get("seed"),
"image_urls": params.get("image_urls"),
"image_paths": params.get("image_paths"),
"api_base_url": params.get("api_base_url")
})
baseName = buildFileBaseName(prompt, params.get("file_name"), "minimax_draw")
files = []
if apiResult["response_format"] == "base64":
for index in range(len(apiResult["image_base64"])):
suffix = f"_{index + 1}" if len(apiResult["image_base64"]) > 1 else ""
filePath = f"{DRAWS_DIR}/{baseName}{suffix}.{DEFAULT_BASE64_EXTENSION}"
writeResult = await Tools.Files.writeBinary(filePath, normalizeBase64(apiResult["image_base64"][index]))
if not writeResult.get("successful"):
raise Exception(f"保存图片失败: {writeResult.get('details')}")
fileUri = f"file://{filePath}"
files.append({
"file_path": filePath,
"file_uri": fileUri,
"markdown": f"![MiniMax 生成的图片{f' {index + 1}' if len(apiResult['image_base64']) > 1 else ''}]({fileUri})",
"remote_image_url": None
})
else:
for index in range(len(apiResult["image_urls"])):
imageUrl = apiResult["image_urls"][index]
extension = guessExtensionFromUrl(imageUrl, "png")
suffix = f"_{index + 1}" if len(apiResult["image_urls"]) > 1 else ""
filePath = f"{DRAWS_DIR}/{baseName}{suffix}.{extension}"
downloadResult = await Tools.Files.download(imageUrl, filePath)
if not downloadResult.get("successful"):
raise Exception(f"下载图片失败: {downloadResult.get('details')}")
fileUri = f"file://{filePath}"
files.append({
"file_path": filePath,
"file_uri": fileUri,
"markdown": f"![MiniMax 生成的图片{f' {index + 1}' if len(apiResult['image_urls']) > 1 else ''}]({fileUri})",
"remote_image_url": imageUrl
})
hintLines = []
hintLines.append(f"图片已生成并保存在本地 {DRAWS_DIR}。")
hintLines.append(f"共生成 {len(files)} 张。")
if apiResult["reference_count"] > 0:
hintLines.append(f"本次使用了 {apiResult['reference_count']} 张参考图。")
hintLines.append("")
hintLines.append("后续回答如果需要展示图片,请直接输出下面这些 Markdown:")
hintLines.append("")
for fileItem in files:
hintLines.append(fileItem["markdown"])
return {
"prompt": prompt,
"model": apiResult["effective_model"],
"request_id": apiResult["request_id"] or None,
"response_format": apiResult["response_format"],
"success_count": apiResult["success_count"],
"failed_count": apiResult["failed_count"],
"reference_count": apiResult["reference_count"],
"file_path": files[0]["file_path"],
"file_uri": files[0]["file_uri"],
"markdown": files[0]["markdown"],
"files": files,
"hint": "\n".join(hintLines)
}
async def draw_image_wrapper(params=None):
if params is None:
params = {}
try:
result = await draw_image(params)
complete({
"success": True,
"message": "MiniMax 图片生成成功,已保存到本地。",
"data": result
})
except Exception as error:
console.error("draw_image 执行失败:", error)
complete({
"success": False,
"message": f"MiniMax 图片生成失败: {getErrorMessage(error)}",
"error_stack": traceback.format_exc()
})
exports.draw_image = draw_image_wrapper