Files
agarzon-ComfyUI-Imagent/imagent/node_edit.py
T
Alexander Garzon 6d3d2fca46 feat: initial public release of ComfyUI-Imagent
BYOK OpenAI gpt-image generation and editing nodes for ComfyUI — calls go
directly to the OpenAI API with your own key (no proxy, no credit markup).

- Two nodes: "Imagent: OpenAI Image" (text-to-image) and "Imagent: OpenAI
  Image Edit" (edit / inpaint / multi-reference compose).
- Models gpt-image-2 / gpt-image-1.5 / gpt-image-1 with a per-model dynamic UI
  (comfy_api DynamicCombo): size, background, custom WxH, and input_fidelity
  are revealed only for the models that support them.
- Edit node accepts an auto-growing set of reference images (up to 16) plus an
  optional inpaint mask; generate supports batches up to 8.
- API key via OPENAI_API_KEY or a local config.json.

Requires ComfyUI >= 0.23.0.
2026-06-08 09:39:21 -04:00

89 lines
4.5 KiB
Python

"""ComfyUI node: image edit / inpaint via OpenAI images.edit.
Like node_generate, `model` is a DynamicCombo with a nested size combo. Reference
images use an auto-growing input (up to 16). Behavior lives in build.run_edit().
"""
from __future__ import annotations
from comfy_api.latest import IO
from . import build
from . import params as params_mod
from .node_generate import _gpt_image_2_inputs, unpack_size
_MAX_REFS = 16
def _legacy_edit_inputs():
"""gpt-image-1 / gpt-image-1.5: base sizes, transparent allowed, input_fidelity."""
return [
IO.Combo.Input("size", options=params_mod.SIZES_BASE, default="auto",
tooltip="Output size (base sizes supported by gpt-image-1.x)."),
IO.Combo.Input("background", options=params_mod.BACKGROUNDS, default="auto",
tooltip="Background handling. 'transparent' needs png or webp output."),
IO.Combo.Input("input_fidelity", options=params_mod.INPUT_FIDELITIES, default="high",
tooltip="How closely to preserve the reference image: 'high' "
"(default) or 'low'."),
]
class OpenAIImageEdit(IO.ComfyNode):
@classmethod
def define_schema(cls):
return IO.Schema(
node_id="ImagentOpenAIImageEdit",
display_name="🤖 Imagent: OpenAI Image Edit",
category="Imagent",
description="BYOK image edit / inpaint / multi-reference compose via OpenAI "
"gpt-image models (direct API, no proxy).",
inputs=[
IO.String.Input("prompt", multiline=True, default="",
placeholder="Describe the edit to apply…",
tooltip="Describe the edit to apply to the reference image(s)."),
IO.DynamicCombo.Input(
"model",
tooltip="OpenAI gpt-image model. Switching this changes the options below.",
options=[
IO.DynamicCombo.Option("gpt-image-2", _gpt_image_2_inputs()),
IO.DynamicCombo.Option("gpt-image-1.5", _legacy_edit_inputs()),
IO.DynamicCombo.Option("gpt-image-1", _legacy_edit_inputs()),
],
),
IO.Autogrow.Input(
"images",
template=IO.Autogrow.TemplateNames(
IO.Image.Input("image"),
names=[f"image_{i}" for i in range(1, _MAX_REFS + 1)],
min=1),
tooltip=f"Reference image(s) to edit; grows up to {_MAX_REFS}. A mask "
"(if used) applies to the first image and requires exactly one image."),
IO.Combo.Input("quality", options=params_mod.QUALITIES, default="auto",
tooltip="Rendering quality. 'auto' lets the model decide; "
"higher quality costs more."),
IO.Combo.Input("output_format", options=params_mod.FORMATS, default="png",
tooltip="Image file format returned by the API."),
IO.Combo.Input("moderation", options=params_mod.MODERATIONS, default="auto",
tooltip="Content moderation strictness. 'auto' (default) or "
"'low'. Sent only when not 'auto'."),
IO.Int.Input("n", default=1, min=params_mod.N_MIN, max=params_mod.N_MAX, step=1,
tooltip="How many images to generate (1-8)."),
IO.Mask.Input("mask", optional=True,
tooltip="Inpaint mask: white marks the region to edit. Requires a "
"single reference image."),
],
outputs=[IO.Image.Output(display_name="image")],
)
@classmethod
def execute(cls, prompt, model, images, quality, output_format, moderation, n,
mask=None) -> IO.NodeOutput:
size, custom_width, custom_height = unpack_size(model)
ref_tensors = [t for t in (images or {}).values() if t is not None]
img, _info = build.run_edit(
prompt=prompt, model=model["model"], size=size, quality=quality,
background=model["background"], output_format=output_format, n=n,
images=ref_tensors, mask=mask,
input_fidelity=model.get("input_fidelity", "high"), moderation=moderation,
custom_width=custom_width, custom_height=custom_height)
return IO.NodeOutput(img)