diff --git a/nodes/gpt_image.py b/nodes/gpt_image.py index c44b88f..4d2e4a6 100644 --- a/nodes/gpt_image.py +++ b/nodes/gpt_image.py @@ -33,6 +33,59 @@ class O1keyGPTImage: @classmethod def INPUT_TYPES(cls): + # 创建9个独立的参考图输入 + optional_inputs = {} + for i in range(1, 10): + optional_inputs[f"参考图{i}"] = ("IMAGE", { + "tooltip": f"Optional reference image {i} for image editing.", + }) + + optional_inputs["模型"] = ([ + "gpt-image-2", + "gpt-image-1.5", + "gpt-image-2-特价", + "gpt-image-1.5-特价", + ], { + "default": "gpt-image-2", + }) + optional_inputs["分辨率"] = ([ + "auto(默认)", + "1024x1024(正方形)", + "1536x1024(景观)", + "1024x1536(肖像)", + "2048x2048(2K 平方)", + "2048x1152(2K 横屏)", + "3840x2160(4K 横屏)", + "2160x3840(4K 竖屏)", + ], { + "default": "auto(默认)", + "tooltip": "Image size (auto = API decides)", + }) + optional_inputs["生图数量"] = ("INT", { + "default": 1, + "min": 1, + "max": 8, + "step": 1, + "display": "number", + "tooltip": "How many images to generate per prompt", + }) + optional_inputs["seed"] = ("INT", { + "default": 0, + "min": 0, + "max": 2**31 - 1, + "step": 1, + "display": "number", + "control_after_generate": True, + "tooltip": "Random seed (0 = not specified)", + }) + optional_inputs["质量"] = (["高", "中", "低", "自动"], { + "default": "自动", + "tooltip": "Image quality: 高=high, 中=medium, 低=low, 自动=auto", + }) + optional_inputs["遮罩"] = ("MASK", { + "tooltip": "Optional mask for inpainting (white areas will be replaced)", + }) + return { "required": { "prompt": ("STRING", { @@ -41,56 +94,7 @@ class O1keyGPTImage: "tooltip": "Text prompt for GPT Image. Use --- on its own line to separate batch prompts.", }), }, - "optional": { - "模型": ([ - "gpt-image-2", - "gpt-image-1.5", - "gpt-image-2-特价", - "gpt-image-1.5-特价", - ], { - "default": "gpt-image-2", - }), - "分辨率": ([ - "auto(默认)", - "1024x1024(正方形)", - "1536x1024(景观)", - "1024x1536(肖像)", - "2048x2048(2K 平方)", - "2048x1152(2K 横屏)", - "3840x2160(4K 横屏)", - "2160x3840(4K 竖屏)", - ], { - "default": "auto(默认)", - "tooltip": "Image size (auto = API decides)", - }), - "生图数量": ("INT", { - "default": 1, - "min": 1, - "max": 8, - "step": 1, - "display": "number", - "tooltip": "How many images to generate per prompt", - }), - "seed": ("INT", { - "default": 0, - "min": 0, - "max": 2**31 - 1, - "step": 1, - "display": "number", - "control_after_generate": True, - "tooltip": "Random seed (0 = not specified)", - }), - "质量": (["高", "中", "低", "自动"], { - "default": "自动", - "tooltip": "Image quality: 高=high, 中=medium, 低=low, 自动=auto", - }), - "图片": ("IMAGE", { - "tooltip": "Optional reference image for image editing.", - }), - "遮罩": ("MASK", { - "tooltip": "Optional mask for inpainting (white areas will be replaced)", - }), - }, + "optional": optional_inputs, } RETURN_TYPES = ("IMAGE",) @@ -107,8 +111,8 @@ class O1keyGPTImage: 质量: str = "自动", 生图数量: int = 1, seed: int = 0, - 图片=None, 遮罩=None, + **kwargs, ): """ 生成图像(文生图 / 图生图 / 图像编辑 / 批量提示词) @@ -121,6 +125,18 @@ class O1keyGPTImage: """ start_time = time.time() + # ── 0. 收集多参考图输入 ──────────────────────────────────────────────── + reference_tensors = [] + for i in range(1, 10): + key = f"参考图{i}" + if key in kwargs and kwargs[key] is not None: + reference_tensors.append(kwargs[key]) + + if reference_tensors: + 图片 = torch.cat(reference_tensors, dim=0) + else: + 图片 = None + # ── 1. 参数校验 ─────────────────────────────────────────────────────── if 遮罩 is not None and 图片 is None: raise ValueError("提供了遮罩但未提供图片,请同时提供图片和遮罩") diff --git a/nodes/nano_banana_pro_async.py b/nodes/nano_banana_pro_async.py index f6661d5..f43679a 100644 --- a/nodes/nano_banana_pro_async.py +++ b/nodes/nano_banana_pro_async.py @@ -45,11 +45,11 @@ except ImportError: MEMORY_MONITOR_AVAILABLE = False print("⚠️ NanoBananaProAsync: psutil 不可用,内存监控功能禁用") -DEBUG_LOG_ENABLED = True -REQUEST_LOG_ENABLED = True +DEBUG_LOG_ENABLED = False +REQUEST_LOG_ENABLED = False _NODE = "Nano Banana Pro(异步)" -_POLL_INTERVAL = 4 # 轮询间隔(秒) +_POLL_INTERVAL = 2 # 轮询间隔(秒) _MAX_WAIT_TIME = 300 # 最大等待时间(秒) @@ -310,7 +310,21 @@ class NanoBananaProAsync: response_data = await self._poll_task_async(session=session, task_id=task_id) - images_list, _ = await self.client.parse_response_async(response_data, session=session) + # 兼容两种响应格式: + # 1. 异步接口直接返回 image_url:{"image_url": "https://..."} + # 2. Gemini 标准格式:{"candidates": [...]} + image_url = response_data.get("image_url", "") if isinstance(response_data, dict) else "" + if image_url: + from io import BytesIO + async with session.get(image_url) as img_resp: + if img_resp.status == 200: + img_bytes = await img_resp.read() + img = Image.open(BytesIO(img_bytes)) + images_list = [img] + else: + raise RuntimeError(f"下载图片失败 ({img_resp.status}): {image_url}") + else: + images_list, _ = await self.client.parse_response_async(response_data, session=session) if save_to_disk: for gen_img in images_list: