feat: GPT Image 节点新增质量参数、批量提示词及模型调整
- 新增「质量」下拉参数(高/中/低/自动),默认 auto,两个接口均传递 quality 字段 - 支持批量提示词模式:prompt 中用单独一行 --- 分隔多条提示词,逐条调用 API 并合并输出 - 模型列表更新:移除 gpt-image-1 / gpt-image-1-特价,新增 gpt-image-2,默认改为 gpt-image-2 - size 字段始终传递(auto 也上传),移除 seed 参数传递 Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
afa732b93a
commit
949f7bb180
@@ -33,7 +33,6 @@ _ENDPOINT_EDITS = "/v1/images/edits/"
|
||||
|
||||
# ── 模型名映射(UI 显示名 → API 实际参数名)─────────────────────────────────
|
||||
_MODEL_NAME_MAP = {
|
||||
"gpt-image-1-特价": "gpt-image-1-special",
|
||||
"gpt-image-1.5-特价": "gpt-image-1.5-special",
|
||||
"gpt-image-2-特价": "gpt-image-2-special",
|
||||
}
|
||||
@@ -215,13 +214,7 @@ class GptImageClient:
|
||||
"moderation": "low",
|
||||
}
|
||||
|
||||
# size = "auto" 时不传该字段,让 API 自行决定
|
||||
if size and size != "auto":
|
||||
body["size"] = size
|
||||
|
||||
# seed > 0 时才传递(0 视为不指定)
|
||||
if seed > 0:
|
||||
body["seed"] = seed
|
||||
body["size"] = size if size else "auto"
|
||||
|
||||
# 图生图:将 tensor 转成 data URI 内联
|
||||
if image_tensor is not None:
|
||||
@@ -298,15 +291,15 @@ class GptImageClient:
|
||||
image_tensor = image_tensor.unsqueeze(0) # [H,W,C] → [1,H,W,C]
|
||||
num_images = image_tensor.shape[0]
|
||||
|
||||
# o1key 中转服务的 edits 接口暂不支持 quality / background / moderation / seed,
|
||||
# o1key 中转服务的 edits 接口暂不支持 background / moderation / seed,
|
||||
# 待服务方更新后可重新加入。
|
||||
form = aiohttp.FormData()
|
||||
form.add_field("model", api_model)
|
||||
form.add_field("prompt", prompt)
|
||||
form.add_field("n", str(n))
|
||||
form.add_field("model", api_model)
|
||||
form.add_field("prompt", prompt)
|
||||
form.add_field("n", str(n))
|
||||
form.add_field("quality", quality)
|
||||
|
||||
if size and size != "auto":
|
||||
form.add_field("size", size)
|
||||
form.add_field("size", size if size else "auto")
|
||||
|
||||
# 多图:用 image[] 数组字段逐张附加,支持 gpt-image-1.5 最多 16 张
|
||||
for i in range(num_images):
|
||||
|
||||
+80
-33
@@ -7,6 +7,7 @@ import time
|
||||
import torch
|
||||
|
||||
from ..clients.gpt_image_client import GptImageClient
|
||||
from ..utils.image_utils import parse_batch_prompts
|
||||
|
||||
|
||||
class O1keyGPTImage:
|
||||
@@ -17,13 +18,15 @@ class O1keyGPTImage:
|
||||
- 文生图:仅提供 prompt
|
||||
- 图生图:提供 prompt + 图片(无遮罩)
|
||||
- 图像编辑:提供 prompt + 图片 + 遮罩(白色区域将被替换)
|
||||
- 批量模式:prompt 中用单独一行 --- 分隔多条提示词
|
||||
|
||||
参数:
|
||||
- prompt : 文本提示词(多行)
|
||||
- prompt : 文本提示词(多行;用 --- 独占一行分隔批量提示词)
|
||||
- 模型 : 模型选择
|
||||
- 分辨率 : 图像尺寸(auto 让 API 自动决定)
|
||||
- 生图数量 : 生成数量 1-8
|
||||
- 生图数量 : 每条提示词生成数量 1-8
|
||||
- seed : 随机种子(0 表示不指定)
|
||||
- 质量 : 生成质量
|
||||
- 图片 : 可选参考图(用于图生图或编辑)
|
||||
- 遮罩 : 可选蒙版(白色区域将被替换)
|
||||
"""
|
||||
@@ -35,18 +38,17 @@ class O1keyGPTImage:
|
||||
"prompt": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": True,
|
||||
"tooltip": "Text prompt for GPT Image",
|
||||
"tooltip": "Text prompt for GPT Image. Use --- on its own line to separate batch prompts.",
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"模型": ([
|
||||
"gpt-image-1",
|
||||
"gpt-image-2",
|
||||
"gpt-image-1.5",
|
||||
"gpt-image-2-特价",
|
||||
"gpt-image-1-特价",
|
||||
"gpt-image-1.5-特价",
|
||||
], {
|
||||
"default": "gpt-image-1.5",
|
||||
"default": "gpt-image-2",
|
||||
}),
|
||||
"分辨率": ([
|
||||
"auto(默认)",
|
||||
@@ -67,7 +69,7 @@ class O1keyGPTImage:
|
||||
"max": 8,
|
||||
"step": 1,
|
||||
"display": "number",
|
||||
"tooltip": "How many images to generate",
|
||||
"tooltip": "How many images to generate per prompt",
|
||||
}),
|
||||
"seed": ("INT", {
|
||||
"default": 0,
|
||||
@@ -78,6 +80,10 @@ class O1keyGPTImage:
|
||||
"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.",
|
||||
}),
|
||||
@@ -96,34 +102,37 @@ class O1keyGPTImage:
|
||||
def generate(
|
||||
self,
|
||||
prompt: str,
|
||||
模型: str = "gpt-image-1.5",
|
||||
模型: str = "gpt-image-2",
|
||||
分辨率: str = "auto",
|
||||
质量: str = "自动",
|
||||
生图数量: int = 1,
|
||||
seed: int = 0,
|
||||
图片=None,
|
||||
遮罩=None,
|
||||
):
|
||||
"""
|
||||
生成图像(文生图 / 图生图 / 图像编辑)
|
||||
生成图像(文生图 / 图生图 / 图像编辑 / 批量提示词)
|
||||
|
||||
路由逻辑:
|
||||
- 无图片 → generations 接口(文生图)
|
||||
- 有图片,无遮罩 → edits 接口(图生图)
|
||||
- 有图片,有遮罩 → edits 接口(图像编辑 + 蒙版)
|
||||
- prompt 含 --- → 批量模式,逐条调用上述接口
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
# ── 1. 参数校验 ───────────────────────────────────────────────────────
|
||||
if not prompt or not prompt.strip():
|
||||
raise ValueError("提示词不能为空")
|
||||
|
||||
if 遮罩 is not None and 图片 is None:
|
||||
raise ValueError("提供了遮罩但未提供图片,请同时提供图片和遮罩")
|
||||
|
||||
# ── 2. 解析分辨率显示值 → API 参数值 ──────────────────────────────────
|
||||
size = 分辨率.split("(")[0].strip()
|
||||
|
||||
# ── 2. 创建客户端 ─────────────────────────────────────────────────────
|
||||
# ── 2b. 解析质量显示值 → API 参数值 ───────────────────────────────────
|
||||
_quality_map = {"高": "high", "中": "medium", "低": "low", "自动": "auto"}
|
||||
quality = _quality_map.get(质量, "auto")
|
||||
|
||||
# ── 3. 创建客户端 ─────────────────────────────────────────────────────
|
||||
try:
|
||||
client = GptImageClient()
|
||||
except ValueError as e:
|
||||
@@ -132,28 +141,66 @@ class O1keyGPTImage:
|
||||
raise ValueError("未授权!") from None
|
||||
raise
|
||||
|
||||
# ── 3. 调用 API ───────────────────────────────────────────────────────
|
||||
try:
|
||||
pil_images = client.run_sync(
|
||||
prompt=prompt,
|
||||
model=模型,
|
||||
quality="low",
|
||||
background="auto",
|
||||
size=size,
|
||||
n=生图数量,
|
||||
seed=seed,
|
||||
image_tensor=图片,
|
||||
mask_tensor=遮罩,
|
||||
)
|
||||
except Exception as e:
|
||||
error_msg = str(e).split('\n')[0]
|
||||
print(f"[o1key GPT Image] ❌ {error_msg}")
|
||||
raise RuntimeError(error_msg) from None
|
||||
# ── 4. 解析批量提示词 ─────────────────────────────────────────────────
|
||||
batch_prompts = parse_batch_prompts(prompt)
|
||||
|
||||
# ── 4. PIL → tensor ───────────────────────────────────────────────────
|
||||
output_tensor = GptImageClient._pil_list_to_tensor(pil_images)
|
||||
# ── 5. 调用 API ───────────────────────────────────────────────────────
|
||||
all_pil_images = []
|
||||
|
||||
# ── 5. 完成日志 ───────────────────────────────────────────────────────
|
||||
if batch_prompts:
|
||||
# 批量模式:逐条提示词调用
|
||||
total = len(batch_prompts)
|
||||
print(f"[o1key GPT Image] 批量模式 | {total} 条提示词 | 每条生成 {生图数量} 张")
|
||||
for idx, p in enumerate(batch_prompts, 1):
|
||||
try:
|
||||
pil_images = client.run_sync(
|
||||
prompt=p,
|
||||
model=模型,
|
||||
quality=quality,
|
||||
background="auto",
|
||||
size=size,
|
||||
n=生图数量,
|
||||
seed=seed,
|
||||
image_tensor=图片,
|
||||
mask_tensor=遮罩,
|
||||
)
|
||||
all_pil_images.extend(pil_images)
|
||||
snippet = p[:30] + ("..." if len(p) >= 30 else "")
|
||||
print(f"[o1key GPT Image] [{idx}/{total}] ✓ {snippet}")
|
||||
except Exception as e:
|
||||
error_msg = str(e).split('\n')[0]
|
||||
snippet = p[:30] + ("..." if len(p) >= 30 else "")
|
||||
print(f"[o1key GPT Image] [{idx}/{total}] ❌ {snippet} → {error_msg}")
|
||||
else:
|
||||
# 单提示词模式
|
||||
if not prompt or not prompt.strip():
|
||||
raise ValueError("提示词不能为空")
|
||||
try:
|
||||
pil_images = client.run_sync(
|
||||
prompt=prompt,
|
||||
model=模型,
|
||||
quality=quality,
|
||||
background="auto",
|
||||
size=size,
|
||||
n=生图数量,
|
||||
seed=seed,
|
||||
image_tensor=图片,
|
||||
mask_tensor=遮罩,
|
||||
)
|
||||
all_pil_images.extend(pil_images)
|
||||
except Exception as e:
|
||||
error_msg = str(e).split('\n')[0]
|
||||
print(f"[o1key GPT Image] ❌ {error_msg}")
|
||||
raise RuntimeError(error_msg) from None
|
||||
|
||||
# ── 6. 检查是否有可用图像 ─────────────────────────────────────────────
|
||||
if not all_pil_images:
|
||||
raise RuntimeError("所有提示词均生成失败,无可用图像输出")
|
||||
|
||||
# ── 7. PIL → tensor ───────────────────────────────────────────────────
|
||||
output_tensor = GptImageClient._pil_list_to_tensor(all_pil_images)
|
||||
|
||||
# ── 8. 完成日志 ───────────────────────────────────────────────────────
|
||||
elapsed = time.time() - start_time
|
||||
print(
|
||||
f"[o1key GPT Image] 完成!耗时 {elapsed:.1f}s,"
|
||||
|
||||
Reference in New Issue
Block a user