feat: GPT Image节点改为9个独立参考图输入,异步节点优化轮询与响应兼容
- gpt_image: 移除单个图片输入,改为参考图1~参考图9独立输入,支持多批次合并 - async: 调试日志默认关闭,轮询间隔降至2s,兼容image_url响应格式 Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
+40
-24
@@ -33,24 +33,22 @@ class O1keyGPTImage:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
# 创建9个独立的参考图输入
|
||||||
"required": {
|
optional_inputs = {}
|
||||||
"prompt": ("STRING", {
|
for i in range(1, 10):
|
||||||
"default": "",
|
optional_inputs[f"参考图{i}"] = ("IMAGE", {
|
||||||
"multiline": True,
|
"tooltip": f"Optional reference image {i} for image editing.",
|
||||||
"tooltip": "Text prompt for GPT Image. Use --- on its own line to separate batch prompts.",
|
})
|
||||||
}),
|
|
||||||
},
|
optional_inputs["模型"] = ([
|
||||||
"optional": {
|
|
||||||
"模型": ([
|
|
||||||
"gpt-image-2",
|
"gpt-image-2",
|
||||||
"gpt-image-1.5",
|
"gpt-image-1.5",
|
||||||
"gpt-image-2-特价",
|
"gpt-image-2-特价",
|
||||||
"gpt-image-1.5-特价",
|
"gpt-image-1.5-特价",
|
||||||
], {
|
], {
|
||||||
"default": "gpt-image-2",
|
"default": "gpt-image-2",
|
||||||
}),
|
})
|
||||||
"分辨率": ([
|
optional_inputs["分辨率"] = ([
|
||||||
"auto(默认)",
|
"auto(默认)",
|
||||||
"1024x1024(正方形)",
|
"1024x1024(正方形)",
|
||||||
"1536x1024(景观)",
|
"1536x1024(景观)",
|
||||||
@@ -62,16 +60,16 @@ class O1keyGPTImage:
|
|||||||
], {
|
], {
|
||||||
"default": "auto(默认)",
|
"default": "auto(默认)",
|
||||||
"tooltip": "Image size (auto = API decides)",
|
"tooltip": "Image size (auto = API decides)",
|
||||||
}),
|
})
|
||||||
"生图数量": ("INT", {
|
optional_inputs["生图数量"] = ("INT", {
|
||||||
"default": 1,
|
"default": 1,
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 8,
|
"max": 8,
|
||||||
"step": 1,
|
"step": 1,
|
||||||
"display": "number",
|
"display": "number",
|
||||||
"tooltip": "How many images to generate per prompt",
|
"tooltip": "How many images to generate per prompt",
|
||||||
}),
|
})
|
||||||
"seed": ("INT", {
|
optional_inputs["seed"] = ("INT", {
|
||||||
"default": 0,
|
"default": 0,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 2**31 - 1,
|
"max": 2**31 - 1,
|
||||||
@@ -79,18 +77,24 @@ class O1keyGPTImage:
|
|||||||
"display": "number",
|
"display": "number",
|
||||||
"control_after_generate": True,
|
"control_after_generate": True,
|
||||||
"tooltip": "Random seed (0 = not specified)",
|
"tooltip": "Random seed (0 = not specified)",
|
||||||
}),
|
})
|
||||||
"质量": (["高", "中", "低", "自动"], {
|
optional_inputs["质量"] = (["高", "中", "低", "自动"], {
|
||||||
"default": "自动",
|
"default": "自动",
|
||||||
"tooltip": "Image quality: 高=high, 中=medium, 低=low, 自动=auto",
|
"tooltip": "Image quality: 高=high, 中=medium, 低=low, 自动=auto",
|
||||||
}),
|
})
|
||||||
"图片": ("IMAGE", {
|
optional_inputs["遮罩"] = ("MASK", {
|
||||||
"tooltip": "Optional reference image for image editing.",
|
|
||||||
}),
|
|
||||||
"遮罩": ("MASK", {
|
|
||||||
"tooltip": "Optional mask for inpainting (white areas will be replaced)",
|
"tooltip": "Optional mask for inpainting (white areas will be replaced)",
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"prompt": ("STRING", {
|
||||||
|
"default": "",
|
||||||
|
"multiline": True,
|
||||||
|
"tooltip": "Text prompt for GPT Image. Use --- on its own line to separate batch prompts.",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
"optional": optional_inputs,
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("IMAGE",)
|
RETURN_TYPES = ("IMAGE",)
|
||||||
@@ -107,8 +111,8 @@ class O1keyGPTImage:
|
|||||||
质量: str = "自动",
|
质量: str = "自动",
|
||||||
生图数量: int = 1,
|
生图数量: int = 1,
|
||||||
seed: int = 0,
|
seed: int = 0,
|
||||||
图片=None,
|
|
||||||
遮罩=None,
|
遮罩=None,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
生成图像(文生图 / 图生图 / 图像编辑 / 批量提示词)
|
生成图像(文生图 / 图生图 / 图像编辑 / 批量提示词)
|
||||||
@@ -121,6 +125,18 @@ class O1keyGPTImage:
|
|||||||
"""
|
"""
|
||||||
start_time = time.time()
|
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. 参数校验 ───────────────────────────────────────────────────────
|
# ── 1. 参数校验 ───────────────────────────────────────────────────────
|
||||||
if 遮罩 is not None and 图片 is None:
|
if 遮罩 is not None and 图片 is None:
|
||||||
raise ValueError("提供了遮罩但未提供图片,请同时提供图片和遮罩")
|
raise ValueError("提供了遮罩但未提供图片,请同时提供图片和遮罩")
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ except ImportError:
|
|||||||
MEMORY_MONITOR_AVAILABLE = False
|
MEMORY_MONITOR_AVAILABLE = False
|
||||||
print("⚠️ NanoBananaProAsync: psutil 不可用,内存监控功能禁用")
|
print("⚠️ NanoBananaProAsync: psutil 不可用,内存监控功能禁用")
|
||||||
|
|
||||||
DEBUG_LOG_ENABLED = True
|
DEBUG_LOG_ENABLED = False
|
||||||
REQUEST_LOG_ENABLED = True
|
REQUEST_LOG_ENABLED = False
|
||||||
|
|
||||||
_NODE = "Nano Banana Pro(异步)"
|
_NODE = "Nano Banana Pro(异步)"
|
||||||
_POLL_INTERVAL = 4 # 轮询间隔(秒)
|
_POLL_INTERVAL = 2 # 轮询间隔(秒)
|
||||||
_MAX_WAIT_TIME = 300 # 最大等待时间(秒)
|
_MAX_WAIT_TIME = 300 # 最大等待时间(秒)
|
||||||
|
|
||||||
|
|
||||||
@@ -310,6 +310,20 @@ class NanoBananaProAsync:
|
|||||||
|
|
||||||
response_data = await self._poll_task_async(session=session, task_id=task_id)
|
response_data = await self._poll_task_async(session=session, task_id=task_id)
|
||||||
|
|
||||||
|
# 兼容两种响应格式:
|
||||||
|
# 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)
|
images_list, _ = await self.client.parse_response_async(response_data, session=session)
|
||||||
|
|
||||||
if save_to_disk:
|
if save_to_disk:
|
||||||
|
|||||||
Reference in New Issue
Block a user