Add GPT Image 2.5 edit models

This commit is contained in:
Alexander Tujicov
2026-09-09 11:36:50 +03:00
parent d488795a54
commit 87127bb2d6
2 changed files with 87 additions and 18 deletions
+23 -9
View File
@@ -21,6 +21,8 @@ A Custom Node that integrates [Fal.ai's](https://fal.ai) powerful image editing
| **Nano Banana Pro** | `nano_banana_pro` | Enhanced quality version of Nano Banana. |
| **Nano Banana 2** | `nano_banana_2` | New generation model with web search and thinking options. |
| **GPT Image 2 Edit** | `gpt_image_2_edit` | OpenAI image editing with quality and mask controls. |
| **GPT Image 2.5 Flare Edit** | `gpt_image_2_5_flare_edit` | Precise GPT Image 2.5 editing with high-resolution, background, quality, format, compression, and mask controls. |
| **GPT Image 2.5 Sunburst Edit** | `gpt_image_2_5_sunburst_edit` | Precision-focused GPT Image 2.5 editing with the same extended controls. |
| **Grok Imagine Edit** | `grok_imagine_edit` | xAI image edit model, up to 3 input images. |
| **Seedream 4.5** | `seedream_4.5` | ByteDance's model. Optimized for high-res editing. |
| **Seedream 5 Pro Edit** | `seedream_5` | ByteDance Seedream 5.0 Pro region-precise editing. |
@@ -58,55 +60,67 @@ You must have a **Fal.ai API Key** to use this node.
* **model**: Select the specific AI model to use.
* **fal_key**: Optional UI fallback for your API key when `FAL_KEY` is not set.
* **image1 - image10**: Connect the images you want to edit.
* **mask**: Optional GPT Image 2 edit mask. Other models ignore it.
* **mask**: Optional GPT Image 2 or GPT Image 2.5 edit mask. Other models ignore it.
### Settings
* **width / height**: Output resolution.
* *Note:* `nano_banana` models ignore this and use `aspect_ratio`.
* *Note:* `seedream_4.5` requires high resolutions (see constraints below).
* **num_images**: How many variations to generate (Max 6).
* **num_images**: How many variations to generate. GPT Image 2.5 supports up to 10; existing model-specific caps remain enforced.
* **num_inference_steps**: Number of inference steps for supported models.
* **seed**: Seed for reproducibility.
* **aspect_ratio**: Set target aspect ratio (e.g., 16:9, 1:1).
* **quality**: GPT quality. GPT Image 2 supports `auto` through `high`; GPT Image 2.5 also supports `xhigh` and `max`.
* **enable_web_search**: Enables web search context for supported models.
* **thinking_level**: `off`, `minimal`, or `high` for supported models.
* **concurrent_runs**: Number of independent fal.ai queue jobs to submit in parallel from this node. This is separate from workflow-level concurrency, which automatically overlaps independent NanoSeed nodes. Each run requests its own `num_images`; outputs are returned in run order. Models with an explicit seed increment it for each additional run.
* **queue_timeout**: Maximum wait per queue job in seconds. Timed-out jobs receive a best-effort cancellation request.
* **gpt_background**: `auto`, `transparent`, or `opaque` for GPT Image models.
* **gpt_image_size**: GPT output-size preset, including 1080p, 1440p, and 3840x2160. Non-zero `width` and `height` override this preset.
* **gpt_output_format**: `png`, `jpeg`, or `webp` for GPT Image models.
* **gpt_output_compression**: Optional GPT Image 2.5 JPEG/WebP compression from 0-100. Leave at `-1` to use the API default.
### ⚠️ Model Specific Constraints
Different models have different requirements implemented in the node:
1. **Flux 2 Edit (`flux_2_edit`)**:
1. **GPT Image 2.5 Flare / Sunburst**:
* Supports all 10 NanoSeed reference-image sockets and up to **10 output images**.
* Supports `auto`, `low`, `medium`, `high`, `xhigh`, and `max` quality. Higher levels increase cost and latency.
* Supports automatic, transparent, or opaque backgrounds and PNG, JPEG, or WebP output.
* Includes 1080p, 1440p, and **3840x2160** presets. Custom non-zero `width` and `height` take priority.
* Supports the shared GPT `mask` socket.
2. **Flux 2 Edit (`flux_2_edit`)**:
* **Single Image Only:** Do not connect `image2` through `image5`.
* **Resolution:** Width and Height must be between **512px and 2048px**.
2. **Flux 2 Klein 9B Edit (`flux_2_klein_9b_edit`)**:
3. **Flux 2 Klein 9B Edit (`flux_2_klein_9b_edit`)**:
* Supports up to **4 input images**.
* `num_inference_steps` is limited to **4-8**.
3. **Seedream 4.5 (`seedream_4.5`)**:
4. **Seedream 4.5 (`seedream_4.5`)**:
* **High Res Only:** Width and Height must be between **1920px and 4096px**.
* **Pixel Count:** Total pixels (W x H) must be between ~3.6MP and 16.7MP.
* **Batch Limit:** The sum of input images and output `num_images` cannot exceed 15.
4. **Seedream 5 Pro Edit (`seedream_5`)**:
5. **Seedream 5 Pro Edit (`seedream_5`)**:
* Supports up to **10 input images**.
* If `aspect_ratio` is not `auto`, the selected aspect ratio is prioritized and converted to Seedream's `image_size`.
* If `aspect_ratio` is `auto`, the selected `resolution` is mapped to Seedream's automatic size presets.
* Pro supports `auto_1K` and `auto_2K`; `4K` falls back to Pro's highest automatic preset.
5. **Seedream 5 Lite Edit (`seedream_5_lite`)**:
6. **Seedream 5 Lite Edit (`seedream_5_lite`)**:
* Supports up to **10 input images**.
* Uses the same aspect-ratio-first behavior as Seedream 5 Pro.
* Supports Lite automatic presets up to `auto_4K`.
6. **Nano Banana / Pro / 2**:
7. **Nano Banana / Pro / 2**:
* These models rely on `aspect_ratio` and `resolution` settings rather than custom width/height integers.
* `nano_banana_2` supports `0.5K`, `1K`, `2K`, and `4K` resolution presets.
* `nano_banana_2` also supports extended aspect ratios: `4:1`, `1:4`, `8:1`, and `1:8`.
7. **Grok Imagine Edit (`grok_imagine_edit`)**:
8. **Grok Imagine Edit (`grok_imagine_edit`)**:
* Supports up to **3 input images**.
* Uses up to **4 output images**.
+64 -9
View File
@@ -16,6 +16,30 @@ FAL_HTTP_TIMEOUT_SECONDS = 60
FAL_QUEUE_POLL_INTERVAL_SECONDS = 1.0
FAL_MAX_CONCURRENT_RUNS = 8
GPT_IMAGE_25_EDIT_URLS = {
"gpt_image_2_5_flare_edit": "https://fal.run/openai/gpt-image-2.5/flare/edit",
"gpt_image_2_5_sunburst_edit": "https://fal.run/openai/gpt-image-2.5/sunburst/edit",
}
GPT_IMAGE_SIZE_OPTIONS = [
"auto",
"square_hd",
"square",
"portrait_4_3",
"portrait_16_9",
"landscape_4_3",
"landscape_16_9",
"1920x1080",
"2560x1440",
"3840x2160",
]
GPT_IMAGE_SIZE_DIMENSIONS = {
"1920x1080": {"width": 1920, "height": 1080},
"2560x1440": {"width": 2560, "height": 1440},
"3840x2160": {"width": 3840, "height": 2160},
}
SEEDREAM_5_AUTO_IMAGE_SIZE_BY_RESOLUTION = {
"seedream_5": {
"0.5K": "auto_1K",
@@ -152,6 +176,12 @@ def _prepare_image_data_uris(images, resize_to=None):
return data_uris
def _resolve_gpt_image_size(width, height, image_size):
if width > 0 and height > 0:
return {"width": width, "height": height}
return GPT_IMAGE_SIZE_DIMENSIONS.get(image_size, image_size)
def _safe_error_detail(detail):
detail = re.sub(
r"data:[^,\s\"']+,[A-Za-z0-9+/=]+",
@@ -370,7 +400,7 @@ class NanoSeedEdit:
return {
"required": {
"prompt": ("STRING", {"default": "Edit the image according to this prompt.", "multiline": True}),
"model": (["nano_banana", "nano_banana_pro", "nano_banana_2", "gpt_image_2_edit", "grok_imagine_edit", "seedream_4.5", "seedream_5", "seedream_5_lite", "qwen_edit_plus", "flux_2_edit", "flux_2_pro", "flux_2_flex", "flux_2_klein_9b_edit"],),
"model": (["nano_banana", "nano_banana_pro", "nano_banana_2", "gpt_image_2_edit", "gpt_image_2_5_flare_edit", "gpt_image_2_5_sunburst_edit", "grok_imagine_edit", "seedream_4.5", "seedream_5", "seedream_5_lite", "qwen_edit_plus", "flux_2_edit", "flux_2_pro", "flux_2_flex", "flux_2_klein_9b_edit"],),
"fal_key": ("STRING", {"default": "", "multiline": False}),
},
"optional": {
@@ -387,16 +417,20 @@ class NanoSeedEdit:
"mask": ("IMAGE",),
"width": ("INT", {"default": 0, "min": 0, "max": 4096, "display": "number"}),
"height": ("INT", {"default": 0, "min": 0, "max": 4096, "display": "number"}),
"num_images": ("INT", {"default": 1, "min": 1, "max": 6}),
"num_images": ("INT", {"default": 1, "min": 1, "max": 10}),
"num_inference_steps": ("INT", {"default": 28, "min": 1, "max": 100}),
"seed": ("INT", {"default": 0, "min": 0, "max": 2**32 - 1}),
"aspect_ratio": (["auto", "21:9", "16:9", "3:2", "4:3", "5:4", "1:1", "4:5", "3:4", "2:3", "9:16", "4:1", "1:4", "8:1", "1:8"], {"default": "auto"}),
"resolution": (["0.5K", "1K", "2K", "4K"], {"default": "1K"}),
"quality": (["low", "medium", "high"], {"default": "high"}),
"quality": (["auto", "low", "medium", "high", "xhigh", "max"], {"default": "high"}),
"enable_web_search": ("BOOLEAN", {"default": False}),
"thinking_level": (["off", "minimal", "high"], {"default": "off"}),
"concurrent_runs": ("INT", {"default": 1, "min": 1, "max": 8, "step": 1}),
"queue_timeout": ("INT", {"default": 900, "min": 60, "max": 3600, "step": 30}),
"gpt_background": (["auto", "transparent", "opaque"], {"default": "auto"}),
"gpt_image_size": (GPT_IMAGE_SIZE_OPTIONS, {"default": "auto"}),
"gpt_output_format": (["png", "jpeg", "webp"], {"default": "png"}),
"gpt_output_compression": ("INT", {"default": -1, "min": -1, "max": 100, "step": 1}),
}
}
@@ -410,7 +444,8 @@ class NanoSeedEdit:
image6=None, image7=None, image8=None, image9=None, image10=None, mask=None,
width=0, height=0, num_images=1, num_inference_steps=28, seed=0, aspect_ratio="auto", resolution="1K",
quality="high", enable_web_search=False, thinking_level="off", acceleration="none",
concurrent_runs=1, queue_timeout=900): # Hardcoded to none, kept for compatibility
concurrent_runs=1, queue_timeout=900, gpt_background="auto", gpt_image_size="auto",
gpt_output_format="png", gpt_output_compression=-1): # Hardcoded to none, kept for compatibility
env_fal_key = (os.environ.get("FAL_KEY") or "").strip()
ui_fal_key = (fal_key or "").strip()
if ui_fal_key == "your_fal_key_here":
@@ -444,7 +479,7 @@ class NanoSeedEdit:
)
# Enforce limits (Updated: Removed Flux 2 single image limit)
if model in ["seedream_4.5", "seedream_5", "seedream_5_pro", "seedream_5_lite"] and len(img_data_uris) + num_images > 15:
if model in ["seedream_4.5", "seedream_5", "seedream_5_pro", "seedream_5_lite"] and len(img_data_uris) + min(num_images, 6) > 15:
raise ValueError("Seedream: Total inputs + outputs must <=15.")
# Model-specific payloads
@@ -487,17 +522,37 @@ class NanoSeedEdit:
if thinking_level != "off":
payload["thinking_level"] = thinking_level
elif model == "gpt_image_2_edit":
if quality in {"xhigh", "max"}:
raise ValueError("GPT Image 2 quality supports auto, low, medium, or high.")
url = "https://fal.run/openai/gpt-image-2/edit"
payload = {
"prompt": prompt,
"image_urls": img_data_uris,
"quality": quality,
"num_images": num_images,
"output_format": "png",
"background": gpt_background,
"image_size": _resolve_gpt_image_size(width, height, gpt_image_size),
"num_images": min(num_images, 4),
"output_format": gpt_output_format,
"sync_mode": False,
}
if custom_size:
payload["image_size"] = {"width": width, "height": height}
if mask is not None:
mask_data_uri = await asyncio.to_thread(tensor2data_uri, mask)
if mask_data_uri is not None:
payload["mask_url"] = mask_data_uri
elif model in GPT_IMAGE_25_EDIT_URLS:
url = GPT_IMAGE_25_EDIT_URLS[model]
payload = {
"prompt": prompt,
"image_urls": img_data_uris,
"quality": quality,
"background": gpt_background,
"image_size": _resolve_gpt_image_size(width, height, gpt_image_size),
"num_images": num_images,
"output_format": gpt_output_format,
"sync_mode": False,
}
if gpt_output_format in {"jpeg", "webp"} and gpt_output_compression >= 0:
payload["output_compression"] = gpt_output_compression
if mask is not None:
mask_data_uri = await asyncio.to_thread(tensor2data_uri, mask)
if mask_data_uri is not None: