新增多图输入能力和gemini的seed参数,调整gemini系列使用比例作为输出尺寸参数

This commit is contained in:
TiyaAnlite
2025-11-24 20:47:17 +08:00
parent 9e3d4b0c24
commit 67ede1c9a4
2 changed files with 71 additions and 35 deletions
+45 -28
View File
@@ -28,10 +28,11 @@ class BaseImageNode:
}
}
EXTEND_INPUT = {} # replace it with your own input to extend
SUPPORTED_MODELS = [] # replace it with your own supported moldes
SUPPORTED_MODELS = [] # replace it with your own supported moldes
DELETED_INPUT = [] # replace it with your own deleted input
CATEGORY = "OneThingAI/image generation"
FUNCTION = "pre_input"
UA = "OneThingAI ComfyUI/1.0"
UA = "OneThingAI ComfyUI/1.1"
URL_GENERATIONS = "https://api-model.onethingai.com/v1/images/generations"
URL_EDIT = "https://api-model.onethingai.com/v1/images/edits"
RETURN_TYPES = ("IMAGE",)
@@ -43,9 +44,11 @@ class BaseImageNode:
@classmethod
def INPUT_TYPES(cls):
cls.BASE_INPUT["required"]["model"] = (cls.SUPPORTED_MODELS, {})
required = {**cls.BASE_INPUT.get("required", {}), **cls.EXTEND_INPUT.get("required", {})}
optional = {**cls.BASE_INPUT.get("optional", {}), **cls.EXTEND_INPUT.get("optional", {})}
return {
"required": {**cls.BASE_INPUT.get("required", {}), **cls.EXTEND_INPUT.get("required", {})},
"optional": {**cls.BASE_INPUT.get("optional", {}), **cls.EXTEND_INPUT.get("optional", {})}
"required": {k: v for k, v in required.items() if k not in cls.DELETED_INPUT},
"optional": {k: v for k, v in optional.items() if k not in cls.DELETED_INPUT}
}
@staticmethod
@@ -64,19 +67,23 @@ class BaseImageNode:
def read_reference_image(image):
# Convert the first image if it's a batch
if len(image.shape) == 4:
image_to_encode = image[0]
image_to_encode = [img for img in image]
else:
image_to_encode = image
image_to_encode = [image]
# Convert from torch tensor to PIL Image
image_to_encode = (image_to_encode.cpu().numpy() * 255).astype(np.uint8)
if len(image_to_encode.shape) == 3:
image_to_encode = Image.fromarray(image_to_encode)
buffered = []
for img in image_to_encode:
# Convert from torch tensor to PIL Image
img = (img.cpu().numpy() * 255).astype(np.uint8)
if len(img.shape) == 3:
img = Image.fromarray(img)
# write buffer
buffered = io.BytesIO()
image_to_encode.save(buffered, format="PNG")
buffered.seek(0)
# write buffer
buf = io.BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
buffered.append(buf)
print("buffered:", buffered)
return buffered
@staticmethod
@@ -105,11 +112,8 @@ class BaseImageNode:
final_image = final_image.unsqueeze(0)
return (final_image,)
def pre_input(self, api_key, model, prompt, image_size, custom_width, custom_height, retries, timeout,
reference_image=None, extra=None, **kwargs):
def pre_input(self, api_key, model, prompt, retries, timeout, reference_image=None, extra=None, **kwargs):
client = self.new_http_client(retries)
if image_size == "自定义":
image_size = f"{custom_width}x{custom_height}"
headers = {
"Authorization": f"Bearer {api_key}",
@@ -119,25 +123,30 @@ class BaseImageNode:
payload = {
"model": model,
"prompt": prompt,
"size": image_size
}
if "image_size" in kwargs:
if kwargs["image_size"] == "自定义":
payload["size"] = f"{kwargs['custom_width']}x{kwargs['custom_height']}"
else:
payload["size"] = kwargs["image_size"]
# filter
input_types = self.INPUT_TYPES()
for key, settings in input_types["required"].items():
if len(settings) > 1 and "enabled" in settings[1] and model not in settings[1]["enabled"]:
if key in kwargs:
del kwargs[key]
print(f"filtered input: {key} at models: {model}")
if key in kwargs:
del kwargs[key]
# print(f"filtered input: {key} at models: {model}")
for key, settings in input_types["optional"].items():
if len(settings) > 1 and "enabled" in settings[1] and model not in settings[1]["enabled"]:
if key == "reference_image":
reference_image = None
print("reference_image off")
# print("reference_image off")
continue
if key in kwargs:
del kwargs[key]
print(f"filtered input: {key} at models: {model}")
# print(f"filtered input: {key} at models: {model}")
extra_params = {}
@@ -147,11 +156,13 @@ class BaseImageNode:
except json.decoder.JSONDecodeError:
raise ValueError("extra not a json string")
payload, timeout, extra_params, reference_image, kwargs = self.post_input(payload, timeout, extra_params, reference_image, **kwargs)
payload, timeout, extra_params, reference_image, kwargs = self.post_input(payload, timeout, extra_params,
reference_image, **kwargs)
return self.generate(client, headers, payload, timeout, extra_params, reference_image, **kwargs)
def post_input(self, payload: dict, timeout: int, extra_params: dict, reference_image=None, **kwargs) -> tuple[dict, int, dict, list, dict]:
def post_input(self, payload: dict, timeout: int, extra_params: dict, reference_image=None, **kwargs) -> tuple[
dict, int, dict, list, dict]:
return payload, timeout, extra_params, reference_image, kwargs
def generate(self, client: requests.Session, headers: dict[str, str], payload: dict, timeout: int,
@@ -183,7 +194,10 @@ class OpenAICompatibleNode(BaseImageNode):
# edit mode, use form api
# headers["Content-Type"] = "multipart/form-data"
img = self.read_reference_image(reference_image)
files = {"image": img}
if len(img) == 1:
files = {"image": img[0]}
else:
files = [("image[]", i) for i in img]
response = client.post(
self.URL_EDIT,
headers=headers,
@@ -211,7 +225,10 @@ class VolcengineNode(BaseImageNode):
headers["Content-Type"] = "application/json"
if reference_image is not None:
img = self.read_reference_image(reference_image)
payload["image"] = "data:image/png;base64," + base64.b64encode(img.getvalue()).decode()
if len(img) == 1:
payload["image"] = "data:image/png;base64," + base64.b64encode(img[0].getvalue()).decode()
else:
payload["image"] = ["data:image/png;base64,"+base64.b64encode(b64_img.getvalue()).decode() for b64_img in img]
try:
response = client.post(
self.URL_GENERATIONS,
+26 -7
View File
@@ -3,9 +3,23 @@ from .base import OpenAICompatibleNode, VolcengineNode
class GeminiImage(OpenAICompatibleNode):
SUPPORTED_MODELS = [
"gemini-3-pro-image",
"gemini-2.5-flash-image",
"gemini-3-pro-image"
]
DELETED_INPUT = [
"image_size",
"custom_width",
"custom_height"
]
EXTEND_INPUT = {
"required": {
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
{"default": "16:9"}),
"seed": ("INT", {"default": "-1", "min": -1, "max": 2147483647,
"enabled": ["gemini-3-pro-image"]}),
}
}
class WanxiangImage(OpenAICompatibleNode):
SUPPORTED_MODELS = [
@@ -20,6 +34,7 @@ class WanxiangImage(OpenAICompatibleNode):
}
}
class FluxImage(OpenAICompatibleNode):
SUPPORTED_MODELS = [
"flux-dev"
@@ -30,6 +45,7 @@ class FluxImage(OpenAICompatibleNode):
}
}
class HunyuanImage(OpenAICompatibleNode):
SUPPORTED_MODELS = [
"hunyuan-image"
@@ -40,16 +56,18 @@ class HunyuanImage(OpenAICompatibleNode):
}
}
class OpenAIImage(OpenAICompatibleNode):
SUPPORTED_MODELS = [
"gpt-image-1"
]
EXTEND_INPUT = {
EXTEND_INPUT = {
"required": {
"quality": (["low", "medium", "high"], {"default": "medium"}),
}
}
class SeedreamImage(VolcengineNode):
SUPPORTED_MODELS = [
"doubao-seedream-4-0-250828",
@@ -58,12 +76,15 @@ class SeedreamImage(VolcengineNode):
]
EXTEND_INPUT = {
"required": {
"seed": ("INT", {"default": "-1", "min": -1, "max": 2147483647, "enabled": ["doubao-seedream-3-0-t2i-250415", "doubao-seededit-3-0-i2i-250628"]}),
"seed": ("INT", {"default": "-1", "min": -1, "max": 2147483647,
"enabled": ["doubao-seedream-3-0-t2i-250415", "doubao-seededit-3-0-i2i-250628"]}),
"watermark": ("BOOLEAN", {"default": True}),
},
"optional": {
"reference_image": ("IMAGE", {"enabled": ["doubao-seedream-4-0-250828", "doubao-seededit-3-0-i2i-250628"]}),
"guidance_scale": ("FLOAT", {"default":0, "min": 0, "max": 10, "enabled": ["doubao-seedream-3-0-t2i-250415", "doubao-seededit-3-0-i2i-250628"]})
"guidance_scale": ("FLOAT", {"default": 0, "min": 0, "max": 10,
"enabled": ["doubao-seedream-3-0-t2i-250415",
"doubao-seededit-3-0-i2i-250628"]})
}
}
@@ -72,8 +93,6 @@ class SeedreamImage(VolcengineNode):
payload["size"] = "adaptive"
if reference_image is None:
raise ValueError("doubao-seededit-3-0-i2i-250628需要参数:reference_image")
print("kwargs:", kwargs)
if "guidance_scale" in kwargs and kwargs["guidance_scale"] < 1.0:
del kwargs["guidance_scale"]
print("kwargs(updated):", kwargs)
return payload, timeout, extra_params, reference_image, kwargs
return payload, timeout, extra_params, reference_image, kwargs