feat(api): add flux 3 image klein node
BFL released FLUX 3 Image Klein (POST /v1/flux-3-image-klein) on 2026-10-15. It takes the same prompt, references, resolution and aspect ratio as Flux 3 Image, so the node subclasses Flux3Image and adds what Klein accepts on top: seed, steps, output_format, exact width x height and a 0-7 safety_tolerance, without grounding. The Flux 3 Image example workflow gets two bypassed Klein groups (text to image, edit by URL); existing groups are untouched.
This commit is contained in:
@@ -42,7 +42,7 @@ Every BFL request is asynchronous: the node submits the job, then polls until th
|
||||
|
||||
- The submit response contains a `polling_url`, and the node polls **that URL**. BFL requires this because it points at the region that holds your task (Flux 3 Image, for example, returns a regional one).
|
||||
- Only when a response has no `polling_url` does the node fall back to building `<base_url>get_result?id=<task_id>` itself, with the base URL from `config.ini` or the **Flux Config (BFL)** node.
|
||||
- Polls run every 5 s: up to 40 attempts (~200 s) for image nodes, 120 for **Flux 3 Image** (`4k` can take several minutes) and 240 for the video nodes. A terminal status (`Error`, `Request Moderated`, `Content Moderated`) stops polling at once, even when BFL reports it as HTTP 503, and the node returns a black placeholder so the workflow keeps running.
|
||||
- Polls run every 5 s: up to 40 attempts (~200 s) for image nodes, 120 for **Flux 3 Image** and **Flux 3 Image Klein** (`4k` can take several minutes) and 240 for the video nodes. A terminal status (`Error`, `Request Moderated`, `Content Moderated`) stops polling at once, even when BFL reports it as HTTP 503, and the node returns a black placeholder so the workflow keeps running.
|
||||
- Every poll is logged in the ComfyUI console as `[BFL] Poll attempt … | GET <url>`, so you can see which URL was used.
|
||||
|
||||
## Nodes
|
||||
@@ -51,6 +51,7 @@ Every BFL request is asynchronous: the node submits the job, then polls until th
|
||||
| Node | Description |
|
||||
|---|---|
|
||||
| Flux 3 Image (BFL) | Text-to-image and editing with FLUX 3 in one node — up to 10 references (`image_1`…`image_10` are "Image 1"…"Image 10" in the prompt), bounding boxes as a JSON list at the end of the prompt, `768sq` to `4k` output (`flux-3-image`) |
|
||||
| Flux 3 Image Klein (BFL) | Open-weights 5.6B FLUX 3 — same prompt, 10 references and bounding boxes as Flux 3 Image, plus `seed`, `steps` (1–16), `output_format` and exact `width` × `height`; no grounding (`flux-3-image-klein`) |
|
||||
| Flux 3 Video T2V (BFL) | Text-to-video with FLUX 3 Video (up to 20 s, with audio) |
|
||||
| Flux 3 Video I2V (BFL) | Image-to-video — feed keyframes from the Flux 3 Keyframes node or a single image |
|
||||
| Flux 3 Video V2V (BFL) | Video continuation from an existing MP4 (URL or base64), up to 15 s |
|
||||
|
||||
@@ -909,6 +909,63 @@ class Flux3Image(BaseFlux):
|
||||
return self.create_blank_image()
|
||||
|
||||
|
||||
class Flux3ImageKlein(Flux3Image):
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
types = super().INPUT_TYPES()
|
||||
del types["required"]["grounding"]
|
||||
types["required"]["safety_tolerance"] = ("INT", {"default": 2, "min": 0, "max": 7})
|
||||
types["required"]["output_format"] = (["jpeg", "png"], {"default": "jpeg"})
|
||||
types["required"]["steps"] = ("INT", {"default": 16, "min": 1, "max": 16})
|
||||
size = ("INT", {
|
||||
"default": 0, "min": 0, "max": 6256,
|
||||
"tooltip": "0 = not sent. Set width and height together (64-6256) to override resolution and aspect_ratio"
|
||||
})
|
||||
types["optional"] = {
|
||||
"width": size,
|
||||
"height": size,
|
||||
"seed": ("INT", {"default": -1}),
|
||||
**types["optional"]
|
||||
}
|
||||
return types
|
||||
|
||||
def generate_image(self, prompt, resolution, aspect_ratio, safety_tolerance, output_format, steps,
|
||||
width=0, height=0, seed=-1, config=None, **images):
|
||||
arguments = {"prompt": prompt}
|
||||
connected = [name for name in self.IMAGE_SLOTS if images.get(name)]
|
||||
if connected:
|
||||
arguments["images"] = [images[name] for name in connected]
|
||||
if connected != self.IMAGE_SLOTS[:len(connected)]:
|
||||
print(f"[BFL] Warning: references {connected} skip a socket — BFL numbers them Image 1.."
|
||||
f"{len(connected)} in this order, so 'Image N' in the prompt shifts")
|
||||
if width > 0 and height > 0:
|
||||
arguments["width"] = width
|
||||
arguments["height"] = height
|
||||
elif width > 0 or height > 0:
|
||||
print("[BFL] Warning: width and height must be set together — sending neither")
|
||||
if resolution != "1k":
|
||||
arguments["resolution"] = resolution
|
||||
if aspect_ratio != "auto":
|
||||
arguments["aspect_ratio"] = aspect_ratio
|
||||
if safety_tolerance != 2:
|
||||
arguments["safety_tolerance"] = safety_tolerance
|
||||
if output_format != "jpeg":
|
||||
arguments["output_format"] = output_format
|
||||
if steps != 16:
|
||||
arguments["steps"] = steps
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
try:
|
||||
task_id = self.post_request("flux-3-image-klein", arguments, config)
|
||||
if task_id:
|
||||
return self.get_result(task_id, output_format=output_format, max_attempts=self.MAX_ATTEMPTS,
|
||||
config_override=config)
|
||||
return self.create_blank_image()
|
||||
except Exception as e:
|
||||
print(f"[BFL] Error generating image: {str(e)}")
|
||||
return self.create_blank_image()
|
||||
|
||||
|
||||
class FluxCredits:
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "get_credits"
|
||||
@@ -947,6 +1004,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Flux2Klein9bPreview_BFL": Flux2Klein9bPreview,
|
||||
"Flux2Klein4b_BFL": Flux2Klein4b,
|
||||
"Flux3Image_BFL": Flux3Image,
|
||||
"Flux3ImageKlein_BFL": Flux3ImageKlein,
|
||||
"FluxCredits_BFL": FluxCredits
|
||||
}
|
||||
|
||||
@@ -966,5 +1024,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Flux2Klein9bPreview_BFL": "Flux 2 Klein 9B Preview (BFL)",
|
||||
"Flux2Klein4b_BFL": "Flux 2 Klein 4B (BFL)",
|
||||
"Flux3Image_BFL": "Flux 3 Image (BFL)",
|
||||
"Flux3ImageKlein_BFL": "Flux 3 Image Klein (BFL)",
|
||||
"FluxCredits_BFL": "Flux Credits (BFL)"
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user