186 lines
6.8 KiB
Python
186 lines
6.8 KiB
Python
"""
|
|
PD: OpenAI GPT Image 1 & 1.5 Node (ComfyUI Auth Token)
|
|
"""
|
|
|
|
import time
|
|
|
|
import requests
|
|
import torch
|
|
|
|
from ._image_gpt_common import (
|
|
calculate_token_price_1_or_1_5,
|
|
decode_response_images,
|
|
empty_image,
|
|
estimate_price_range_1_or_1_5,
|
|
format_rmb_range,
|
|
format_rmb_value,
|
|
mask_to_png_bytes,
|
|
tensor_to_bytes,
|
|
)
|
|
|
|
|
|
class PDOpenAIGPTImageAuthToken:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"auth_token": ("STRING", {
|
|
"default": "",
|
|
"multiline": False,
|
|
"placeholder": "Paste your Auth Token here"
|
|
}),
|
|
"prompt": ("STRING", {
|
|
"multiline": True,
|
|
"default": "",
|
|
"tooltip": "Describe what you want to generate or edit"
|
|
}),
|
|
"model": (["gpt-image-1", "gpt-image-1.5"], {
|
|
"default": "gpt-image-1.5",
|
|
"tooltip": "GPT Image model version"
|
|
}),
|
|
"quality": (["low", "medium", "high"], {
|
|
"default": "low",
|
|
"tooltip": "Image quality, affects cost and generation time"
|
|
}),
|
|
"background": (["auto", "opaque", "transparent"], {
|
|
"default": "auto",
|
|
"tooltip": "Background transparency"
|
|
}),
|
|
"size": (["auto", "1024x1024", "1024x1536", "1536x1024"], {
|
|
"default": "auto",
|
|
"tooltip": "Image size"
|
|
}),
|
|
"n": ("INT", {
|
|
"default": 1,
|
|
"min": 1,
|
|
"max": 8,
|
|
"step": 1,
|
|
"tooltip": "Number of images to generate"
|
|
}),
|
|
"seed": ("INT", {
|
|
"default": 0,
|
|
"min": 0,
|
|
"max": 0xFFFFFFFFFFFFFFFF
|
|
}),
|
|
},
|
|
"optional": {
|
|
"image": ("IMAGE", {"default": None, "tooltip": "Input image for editing"}),
|
|
"mask": ("MASK", {"default": None, "tooltip": "Mask for inpainting"}),
|
|
},
|
|
"hidden": {
|
|
"unique_id": "UNIQUE_ID",
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE", "STRING")
|
|
RETURN_NAMES = ("image", "info")
|
|
FUNCTION = "generate_image"
|
|
CATEGORY = "PD_Tools/Image_Generation"
|
|
|
|
def generate_image(self, auth_token, prompt, model, quality, background, size, n, seed, unique_id, image=None, mask=None):
|
|
if not auth_token or not auth_token.strip():
|
|
error_msg = "Error: Auth Token is required"
|
|
print(f"[IMAGE_GPT_AUTHTOKEN] {error_msg}")
|
|
return (empty_image(), error_msg)
|
|
|
|
auth_token = auth_token.strip()
|
|
if auth_token.lower().startswith("bearer "):
|
|
auth_token = auth_token[7:].strip()
|
|
|
|
base_url = "https://api.comfy.org"
|
|
is_edit_mode = image is not None
|
|
start_time = time.time()
|
|
|
|
try:
|
|
if is_edit_mode:
|
|
url = f"{base_url}/proxy/openai/images/edits"
|
|
files = {
|
|
"image": ("image.png", tensor_to_bytes(image), "image/png")
|
|
}
|
|
if mask is not None:
|
|
files["mask"] = ("mask.png", mask_to_png_bytes(mask), "image/png")
|
|
|
|
data = {
|
|
"model": model,
|
|
"prompt": prompt,
|
|
"n": str(n),
|
|
"size": size,
|
|
"quality": quality,
|
|
"background": background,
|
|
"moderation": "low"
|
|
}
|
|
headers = {
|
|
"Authorization": f"Bearer {auth_token}",
|
|
"User-Agent": "ComfyUI-PD-Node/2.1"
|
|
}
|
|
response = requests.post(url, headers=headers, files=files, data=data, timeout=300)
|
|
else:
|
|
url = f"{base_url}/proxy/openai/images/generations"
|
|
payload = {
|
|
"model": model,
|
|
"prompt": prompt,
|
|
"n": n,
|
|
"size": size,
|
|
"quality": quality,
|
|
"background": background,
|
|
"moderation": "low"
|
|
}
|
|
headers = {
|
|
"Authorization": f"Bearer {auth_token}",
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "ComfyUI-PD-Node/2.1"
|
|
}
|
|
response = requests.post(url, headers=headers, json=payload, timeout=300)
|
|
|
|
if response.status_code != 200:
|
|
error_msg = f"API Error {response.status_code}: {response.text[:200]}"
|
|
print(f"[IMAGE_GPT_AUTHTOKEN] {error_msg}")
|
|
return (empty_image(), error_msg)
|
|
|
|
result = response.json()
|
|
image_tensors = decode_response_images(result)
|
|
if not image_tensors:
|
|
error_msg = "Error: Failed to process output images"
|
|
print(f"[IMAGE_GPT_AUTHTOKEN] {error_msg}")
|
|
return (empty_image(), error_msg)
|
|
|
|
final_image = torch.cat(image_tensors, dim=0)
|
|
duration = time.time() - start_time
|
|
min_cost, max_cost = estimate_price_range_1_or_1_5(model, quality, n)
|
|
token_cost = calculate_token_price_1_or_1_5(model, n)
|
|
|
|
info_str = (
|
|
f"模型: {model}\n"
|
|
f"显示名称: OpenAI GPT Image 1 & 1.5\n"
|
|
f"模式: {'编辑' if is_edit_mode else '生成'}\n"
|
|
f"状态: 成功\n"
|
|
f"耗时: {duration:.2f} 秒\n"
|
|
f"图片数量: {len(image_tensors)}\n"
|
|
f"尺寸: {size}\n"
|
|
f"质量: {quality}\n"
|
|
f"背景: {background}\n"
|
|
f"预估价格区间(USD): ${min_cost:.3f}-${max_cost:.3f}\n"
|
|
f"预估价格区间(RMB): {format_rmb_range(min_cost, max_cost)}\n"
|
|
f"Token 公式估算(USD): ${token_cost:.4f}\n"
|
|
f"Token 公式估算(RMB): {format_rmb_value(token_cost)}"
|
|
)
|
|
return (final_image, info_str)
|
|
|
|
except requests.exceptions.Timeout:
|
|
return (empty_image(), "Error: Request timeout (300s)")
|
|
except requests.exceptions.ConnectionError as e:
|
|
return (empty_image(), f"Error: Connection failed - {str(e)}")
|
|
except Exception as e:
|
|
import traceback
|
|
traceback.print_exc()
|
|
return (empty_image(), f"Error: {str(e)}")
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"PDOpenAIGPTImageAuthToken": PDOpenAIGPTImageAuthToken
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"PDOpenAIGPTImageAuthToken": "PD: GPT Image 1 & 1.5 (comfyui_AuthToken)"
|
|
}
|