From 79f3244ea0ee642a0dbdfaf6a2fce0066fb55fa4 Mon Sep 17 00:00:00 2001 From: fkxianzhou <368684850@qq.com> Date: Thu, 4 Dec 2025 18:06:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96Seedream4=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84=E5=92=8C=E7=9B=B8=E5=85=B3=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 ++++++++-------- example_workflows/Jimeng Seedream 4.json | 2 +- locales/zh/nodeDefs.json | 10 ++--- nodes_image.py | 50 +++++++++++++++--------- nodes_shared.py | 2 + pyproject.toml | 2 +- web/docs/JimengSeedream4/zh.md | 35 ++++++++--------- web/js/jimeng_dynamic_widgets.js | 6 +-- 8 files changed, 77 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 8952b9e..58d9d0a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ - **智能统一节点**: - **图像生成**: - `即梦 Seedream 3` 节点会根据是否连接图像输入,**自动在后端智能切换**文生图 (`doubao-seedream-3-0-t2i-250415`) 和图生图编辑 (`doubao-seededit-3-0-i2i-250628`) 模型,无需用户手动配置。 - - `即梦 Seedream 4` 节点支持更高级的图生图(最多 10 张输入图像)和组图生成模式。 + - `即梦 Seedream 4` 节点支持更高级的图生图(最多 14 张输入图像)和组图生成模式。 - **视频生成**: `即梦视频生成` 节点统一了**文生视频**、**图生视频(首帧)** 和 **首尾帧生视频** 三种核心模式,极大简化了工作流。 - **优秀的用户体验**: - **流式进度条**: 支持视频生成等耗时任务的**流式进度条**,可在 ComfyUI 界面实时反馈任务状态。 @@ -87,23 +87,25 @@ - `response (STRING)`: API 的 JSON 响应。 ### `即梦 Seedream 4 (Jimeng Seedream 4)` +支持最新的即梦 Seedream 4.0 及 4.5 模型,提供更高质量的图像生成能力,并支持多图参考和组图生成功能。 -高级图像生成节点,支持组图生成和多图输入。 +* **Model Version (模型版本)**: + * `doubao-seedream-4.5`: 最新模型,效果更佳。 + * `doubao-seedream-4.0`: 标准模型。 +* **Enable Group Generation (启用组图生成)**: + * **`True` (开启/Auto)**: 激活**组图模式**。生成一组内容关联的图片。 + * **支持场景**: 文生组图、单图生组图、多图生组图。 + * **数量限制**: `输入参考图数量` + `生成图片数 (max_images)` ≤ **15**。 + * **`False` (关闭/Single)**: 激活**单图模式**。 + * **支持场景**: 文生图、单图生图、多图生图。 + * **注意**: 此模式下无论输入多少张参考图,最终仅生成 **1** 张图片。 +* **Prompt**: 文本提示词。 +* **Max Images**: (仅在组图模式下生效) 设置生成的图片数量。 +* **Images (Optional)**: 输入参考图像,支持单张或多张(最多 14 张)。 + * 连接 1 张图片 -> 单图参考。 + * 连接多张图片 (Batch) -> 多图参考。 -- **输入**: - - `client (JIMENG_CLIENT)`: 客户端。 - - `prompt (STRING)`: 提示词。 - - `generation_mode (STRING)`: 生成模式 (单图或组图)。 - - `max_images (INT)`: 最大图片数 (用于组图模式)。 - - `size (STRING)`: 尺寸。 - - `width (INT)`: 宽度。 - - `height (INT)`: 高度。 - - `seed (INT)`: 种子。 - - `watermark (BOOLEAN)`: 水印。 - - `images (IMAGE)`: (可选, 用于图生图, 最多 10 张)。 -- **输出**: - - `images (IMAGE)`: 生成的图像(批处理)。 - - `response (STRING)`: API 的 JSON 响应。 +> **注意**: 在组图模式下,请务必确保 `输入图片数 + 生成图片数` 不超过 15,否则会报错。 ### `即梦视频生成 (Jimeng Video Generation)` diff --git a/example_workflows/Jimeng Seedream 4.json b/example_workflows/Jimeng Seedream 4.json index 110c3b2..9f85a5a 100644 --- a/example_workflows/Jimeng Seedream 4.json +++ b/example_workflows/Jimeng Seedream 4.json @@ -1 +1 @@ -{"id":"9cdfd6e3-7426-4a3a-88b1-b5866725d7e0","revision":0,"last_node_id":48,"last_link_id":51,"nodes":[{"id":47,"type":"JimengAPIClient","pos":[659.3174882443304,464.525306048046],"size":[270,58],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"key_name","name":"key_name","type":"COMBO","widget":{"name":"key_name"},"link":null}],"outputs":[{"localized_name":"client","name":"client","type":"JIMENG_CLIENT","links":[50]}],"properties":{"cnr_id":"ComfyUI-Jimeng-API","ver":"bc7215f572cf093ccf7e4ce142267352a60fb428","Node name for S&R":"JimengAPIClient"},"widgets_values":["未找到密钥 (请配置 api_keys.json)"]},{"id":48,"type":"JimengSeedream4","pos":[949.1629520176931,463.793203747457],"size":[400,300],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"client","name":"client","type":"JIMENG_CLIENT","link":50},{"localized_name":"images","name":"images","shape":7,"type":"IMAGE","link":null},{"localized_name":"model_version","name":"model_version","type":"COMBO","widget":{"name":"model_version"},"link":null},{"localized_name":"prompt","name":"prompt","type":"STRING","widget":{"name":"prompt"},"link":null},{"localized_name":"generation_mode","name":"generation_mode","type":"COMBO","widget":{"name":"generation_mode"},"link":null},{"localized_name":"max_images","name":"max_images","type":"INT","widget":{"name":"max_images"},"link":null},{"localized_name":"size","name":"size","type":"COMBO","widget":{"name":"size"},"link":null},{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"watermark","name":"watermark","type":"BOOLEAN","widget":{"name":"watermark"},"link":null}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[51]},{"localized_name":"response","name":"response","type":"STRING","links":null}],"properties":{"cnr_id":"ComfyUI-Jimeng-API","ver":"c07c6b2ff122fcb0ebf9f345ef04b697d6e19b66","Node name for S&R":"JimengSeedream4"},"widgets_values":["doubao-seedream-4.0","","Single Image (disabled)",1,"Custom",2048,2048,-1,"randomize",false]},{"id":45,"type":"SaveImage","pos":[1366.2473373272921,464.2190469588465],"size":[270,58],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"图片","name":"images","type":"IMAGE","link":51},{"localized_name":"文件名前缀","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.65","Node name for S&R":"SaveImage"},"widgets_values":["Jimeng/Image/Seedream4"]}],"links":[[50,47,0,48,0,"JIMENG_CLIENT"],[51,48,0,45,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":2.11537812904981,"offset":[-565.3072584119454,-279.58656869694266]},"frontendVersion":"1.18.9","VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"workflowRendererVersion":"LG"},"version":0.4} \ No newline at end of file +{"id":"9cdfd6e3-7426-4a3a-88b1-b5866725d7e0","revision":0,"last_node_id":49,"last_link_id":53,"nodes":[{"id":45,"type":"SaveImage","pos":[1366.2473373272921,464.2190469588465],"size":[270,58],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"图片","name":"images","type":"IMAGE","link":53},{"localized_name":"文件名前缀","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.65","Node name for S&R":"SaveImage"},"widgets_values":["Jimeng/Image/Seedream4"]},{"id":47,"type":"JimengAPIClient","pos":[659.3174882443304,464.2190469588465],"size":[270,58],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"key_name","name":"key_name","type":"COMBO","widget":{"name":"key_name"},"link":null}],"outputs":[{"localized_name":"client","name":"client","type":"JIMENG_CLIENT","links":[52]}],"properties":{"cnr_id":"ComfyUI-Jimeng-API","ver":"bc7215f572cf093ccf7e4ce142267352a60fb428","Node name for S&R":"JimengAPIClient"},"widgets_values":["未找到密钥 (请配置 api_keys.json)"]},{"id":49,"type":"JimengSeedream4","pos":[947.7824127858113,464.2190469588465],"size":[400,300],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"client","name":"client","type":"JIMENG_CLIENT","link":52},{"localized_name":"images","name":"images","shape":7,"type":"IMAGE","link":null},{"localized_name":"model_version","name":"model_version","type":"COMBO","widget":{"name":"model_version"},"link":null},{"localized_name":"prompt","name":"prompt","type":"STRING","widget":{"name":"prompt"},"link":null},{"localized_name":"enable_group_generation","name":"enable_group_generation","type":"BOOLEAN","widget":{"name":"enable_group_generation"},"link":null},{"localized_name":"max_images","name":"max_images","type":"INT","widget":{"name":"max_images"},"link":null},{"localized_name":"size","name":"size","type":"COMBO","widget":{"name":"size"},"link":null},{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"watermark","name":"watermark","type":"BOOLEAN","widget":{"name":"watermark"},"link":null}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[53]},{"localized_name":"response","name":"response","type":"STRING","links":null}],"properties":{"cnr_id":"ComfyUI-Jimeng-API","ver":"0033eded3f183df3a3ff16d097602da3c8e985fc","Node name for S&R":"JimengSeedream4"},"widgets_values":["doubao-seedream-4.5","",false,1,"Custom",2048,2048,-1,"randomize",false]}],"links":[[52,47,0,49,0,"JIMENG_CLIENT"],[53,49,0,45,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.3134833866600077,"offset":[-330.0922977518734,-207.13823911048317]},"frontendVersion":"1.18.9","VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"workflowRendererVersion":"LG"},"version":0.4} \ No newline at end of file diff --git a/locales/zh/nodeDefs.json b/locales/zh/nodeDefs.json index 1a99028..e4620f7 100644 --- a/locales/zh/nodeDefs.json +++ b/locales/zh/nodeDefs.json @@ -87,13 +87,9 @@ "name": "提示词", "tooltip": "用于图像生成的文本提示词" }, - "generation_mode": { - "name": "生成模式", - "tooltip": "选择单图或组图生成", - "options": { - "Single Image (disabled)": "单图模式 (禁用组图)", - "Image Group (auto)": "组图模式 (自动)" - } + "enable_group_generation": { + "name": "启用组图生成", + "tooltip": "开启 (True) 则生成一组内容关联的图片;关闭 (False) 则生成单张图片。" }, "max_images": { "name": "最大图片数", diff --git a/nodes_image.py b/nodes_image.py index 2ce8f44..d417ee6 100644 --- a/nodes_image.py +++ b/nodes_image.py @@ -120,9 +120,9 @@ class JimengSeedream4: return { "required": { "client": ("JIMENG_CLIENT",), - "model_version": (["doubao-seedream-4.0", "doubao-seedream-4.5"],), + "model_version": (["doubao-seedream-4.5", "doubao-seedream-4.0"],), "prompt": ("STRING", {"multiline": True, "default": ""}), - "generation_mode": (["Single Image (disabled)", "Image Group (auto)"],), + "enable_group_generation": ("BOOLEAN", {"default": False, "label_on": "On (Group)", "label_off": "Off (Single)"}), "max_images": ("INT", {"default": 1, "min": 1, "max": 15, "step": 1}), "size": (s.RECOMMENDED_SIZES,), "width": ("INT", {"default": 2048, "min": 1, "max": 8192, "step": 1}), @@ -139,25 +139,32 @@ class JimengSeedream4: FUNCTION = "generate" CATEGORY = GLOBAL_CATEGORY - async def generate(self, client, model_version, prompt, generation_mode, max_images, size, width, height, seed, watermark, images=None): + async def generate(self, client, model_version, prompt, enable_group_generation, max_images, size, width, height, seed, watermark, images=None): # 节点的主要异步执行函数 - sequential_image_generation = "disabled" if "disabled" in generation_mode else "auto" - n_input_images = 0 - if images is not None: n_input_images = images.shape[0] - if n_input_images > 10: - raise ValueError(get_text("err_img_limit_10")) - if sequential_image_generation == "auto" and n_input_images + max_images > 15: - raise ValueError(get_text("err_img_limit_15").format(n=n_input_images, max=max_images)) - - actual_seed = random.randint(0, 2147483647) if seed == -1 else seed - # 映射模型版本到后端 ID model_id_map = { "doubao-seedream-4.0": "doubao-seedream-4-0-250828", "doubao-seedream-4.5": "doubao-seedream-4-5-251128" } model_id = model_id_map.get(model_version, "doubao-seedream-4-0-250828") + # 根据 enable_group_generation 布尔值设置 API 参数 + sequential_param = "auto" if enable_group_generation else "disabled" + + n_input_images = 0 + if images is not None: + n_input_images = images.shape[0] + + # 校验逻辑 + if sequential_param == "auto": + total_count = n_input_images + max_images + if total_count > 15: + raise ValueError(get_text("err_img_limit_group_15").format(n=n_input_images, max=max_images, total=total_count)) + else: + pass + + actual_seed = random.randint(0, 2147483647) if seed == -1 else seed + # 处理分辨率 if size == "Custom": total_pixels = width * height @@ -174,7 +181,7 @@ class JimengSeedream4: else: size_str = size.split(" ")[0] - # 处理输入的图像(可能为多个) + # 处理输入的图像 image_param = None if images is not None: image_b64_list = [_image_to_base64(images[i:i+1]) for i in range(n_input_images)] @@ -182,11 +189,18 @@ class JimengSeedream4: else: image_param = [f"data:image/jpeg;base64,{b64}" for b64 in image_b64_list] # 准备 API 请求体 - extra_body = {"seed": actual_seed, "watermark": watermark, "sequential_image_generation": sequential_image_generation} - if image_param: extra_body["image"] = image_param - if sequential_image_generation == "auto": - extra_body['sequential_image_generation_options'] = {"max_images": max_images} + extra_body = { + "seed": actual_seed, + "watermark": watermark, + "sequential_image_generation": sequential_param + } + if image_param: + extra_body["image"] = image_param + + if sequential_param == "auto": + extra_body['sequential_image_generation_options'] = {"max_images": max_images} + openai_client = client.openai async with aiohttp.ClientSession() as session: try: diff --git a/nodes_shared.py b/nodes_shared.py index bc5a525..8793772 100644 --- a/nodes_shared.py +++ b/nodes_shared.py @@ -70,6 +70,7 @@ LOG_TRANSLATIONS = { "err_gen_model": "[JimengAI] 模型 {model} 生成失败: {e}", "err_img_limit_10": "[JimengAI] 参数错误: 输入图像数量不能超过 10 张。", "err_img_limit_15": "[JimengAI] 参数错误: 输入图像数 ({n}) 与最大生成数 ({max}) 之和不能超过 15。", + "err_img_limit_group_15": "[JimengAI] 参数错误: 在组图模式下,输入参考图数量 ({n}) 加 生成图片数量 ({max}) 的总和 ({total}) 不能超过 15 张。", # API Key 相关 "combo_no_keys": "未找到密钥 (请配置 api_keys.json)", @@ -122,6 +123,7 @@ LOG_TRANSLATIONS = { "err_gen_model": "[JimengAI] Failed to generate image with model {model}: {e}", "err_img_limit_10": "[JimengAI] Parameter Error: The number of input images cannot exceed 10.", "err_img_limit_15": "[JimengAI] Parameter Error: The sum of input images ({n}) and max generated images ({max}) cannot exceed 15.", + "err_img_limit_group_15": "[JimengAI] Parameter Error: The sum of input images ({n}) and max generated images ({max}) cannot exceed 15 in group mode (Total: {total}).", # API Key "combo_no_keys": "No Keys Found (Check api_keys.json)", diff --git a/pyproject.toml b/pyproject.toml index e41c43f..4adffb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ComfyUI-Jimeng-API" description = "ComfyUI nodes for Volcano Ark (Jimeng) API" -version = "1.9.8" +version = "1.9.9" license = {file = "LICENSE"} dependencies = ["volcengine-python-sdk[ark]", "openai"] diff --git a/web/docs/JimengSeedream4/zh.md b/web/docs/JimengSeedream4/zh.md index d69ec6e..1ef4571 100644 --- a/web/docs/JimengSeedream4/zh.md +++ b/web/docs/JimengSeedream4/zh.md @@ -1,23 +1,22 @@ -# 即梦 Seedream 4 +# Jimeng Seedream 4 节点使用说明 -使用即梦 Seedream 4 模型生成图像,支持单图或组图(自动)模式。 +该节点使用即梦最新的 Seedream 4.0/4.5 模型生成图像。支持灵活的单图或组图生成策略。 -## 输入 (Inputs) +## 参数说明 -- **client (客户端)**: 即梦 API 客户端 -- **prompt (提示词)**: 用于图像生成的文本提示词 -- **generation_mode (生成模式)**: 选择单图或组图生成 - - `单图模式 (禁用组图)` - - `组图模式 (自动)` -- **max_images (最大图片数)**: 在组图模式下生成的最大图像数量 -- **size (尺寸)**: 预设的图像尺寸。如果选择 'Custom',将使用 '宽度' 和 '高度'。 -- **width (宽度)**: 自定义宽度(仅当尺寸为 'Custom' 时生效) -- **height (高度)**: 自定义高度(仅当尺寸为 'Custom' 时生效) -- **seed (种子)**: 随机种子,-1 表示随机 -- **watermark (水印)**: 是否在生成的图像上添加水印 -- **images (输入图像)**: 用于图生图的输入图像(最多 10 张) +### 核心参数 +- **client**: 连接 `JimengAPIClient` 节点。 +- **model_version**: 选择模型版本,推荐使用 `doubao-seedream-4.5`。 +- **enable_group_generation**: **关键开关**。 + - **开启 (True)**: 进入组图模式,根据提示词和参考图生成一组连贯的图片。 + - **关闭 (False)**: 进入单图模式,生成单张高质量图片。 +- **max_images**: 生成数量设置。仅在 `enable_group_generation` 开启时可见并生效。 -## 输出 (Outputs) +### 规则详解 -- **图像**: 生成的图像(批处理) -- **响应**: API 的 JSON 格式响应 +| 模式 | 开关状态 | 输入 | 输出数量 | 限制条件 | +| :--- | :--- | :--- | :--- | :--- | +| **文生图** | False | 仅提示词 | 1 张 | 无 | +| **图生图** | False | 提示词 + 参考图 (1-14张) | 1 张 | 无论输入几张参考图,均融合生成 1 张结果 | +| **文生组图** | True | 仅提示词 | N 张 | N ≤ 15 | +| **图生组图** | True | 提示词 + 参考图 (M张) | N 张 | M + N ≤ 15 | \ No newline at end of file diff --git a/web/js/jimeng_dynamic_widgets.js b/web/js/jimeng_dynamic_widgets.js index f85d868..60073e8 100644 --- a/web/js/jimeng_dynamic_widgets.js +++ b/web/js/jimeng_dynamic_widgets.js @@ -34,7 +34,7 @@ function updateNodeHeight(node) { app.graph.setDirtyCanvas(true, true); } -const TARGET_WIDGETS = ['size', 'generation_mode', 'generation_count']; +const TARGET_WIDGETS = ['size', 'enable_group_generation', 'generation_count']; function widgetLogic(node, widget) { if (node.comfyClass === "JimengSeedream3" || node.comfyClass === "JimengSeedream4") { @@ -49,8 +49,8 @@ function widgetLogic(node, widget) { } if (node.comfyClass === "JimengSeedream4") { - if (widget.name === 'generation_mode') { - const isGroupMode = widget.value === "Image Group (auto)"; + if (widget.name === 'enable_group_generation') { + const isGroupMode = widget.value === true; const maxImagesWidget = findWidgetByName(node, 'max_images'); if (maxImagesWidget) toggleWidget(node, maxImagesWidget, isGroupMode);