| |
| """Generate the flux-redux workflow JSON drafts (ComfyUI graph format 0.4).""" |
| import json, os |
|
|
| OUT = "/workspace/workflows/wip" |
| os.makedirs(OUT, exist_ok=True) |
|
|
| CORE = {"cnr_id": "comfy-core", "ver": "0.27.0"} |
| AUX = {"cnr_id": "comfyui_controlnet_aux"} |
| REFLUX = {"aux_id": "kaibioinfo/ComfyUI_AdvancedRefluxControl"} |
|
|
| |
| DEFS = { |
| "MarkdownNote": ([], [], CORE, [520, 460]), |
| "UNETLoader": ([], [("MODEL", "MODEL")], CORE, [340, 82]), |
| "DualCLIPLoader": ([], [("CLIP", "CLIP")], CORE, [340, 130]), |
| "VAELoader": ([], [("VAE", "VAE")], CORE, [340, 58]), |
| "CLIPVisionLoader": ([], [("CLIP_VISION", "CLIP_VISION")], CORE, [340, 58]), |
| "LoadImage": ([], [("IMAGE", "IMAGE"), ("MASK", "MASK")], CORE, [274, 314]), |
| "CLIPVisionEncode": ([("clip_vision", "CLIP_VISION"), ("image", "IMAGE")], |
| [("CLIP_VISION_OUTPUT", "CLIP_VISION_OUTPUT")], CORE, [290, 78]), |
| "StyleModelLoader": ([], [("STYLE_MODEL", "STYLE_MODEL")], CORE, [340, 58]), |
| "StyleModelApply": ([("conditioning", "CONDITIONING"), ("style_model", "STYLE_MODEL"), |
| ("clip_vision_output", "CLIP_VISION_OUTPUT")], |
| [("CONDITIONING", "CONDITIONING")], CORE, [320, 102]), |
| "ReduxAdvanced": ([("conditioning", "CONDITIONING"), ("style_model", "STYLE_MODEL"), |
| ("clip_vision", "CLIP_VISION"), ("image", "IMAGE"), ("mask", "MASK")], |
| [("CONDITIONING", "CONDITIONING"), ("IMAGE", "IMAGE"), ("MASK", "MASK")], |
| REFLUX, [320, 190]), |
| "CLIPTextEncode": ([("clip", "CLIP")], [("CONDITIONING", "CONDITIONING")], CORE, [400, 160]), |
| "FluxGuidance": ([("conditioning", "CONDITIONING")], [("CONDITIONING", "CONDITIONING")], CORE, [290, 58]), |
| "ConditioningZeroOut": ([("conditioning", "CONDITIONING")], [("CONDITIONING", "CONDITIONING")], CORE, [290, 48]), |
| "ControlNetLoader": ([], [("CONTROL_NET", "CONTROL_NET")], CORE, [380, 58]), |
| "ControlNetApplySD3": ([("positive", "CONDITIONING"), ("negative", "CONDITIONING"), |
| ("control_net", "CONTROL_NET"), ("vae", "VAE"), ("image", "IMAGE")], |
| [("positive", "CONDITIONING"), ("negative", "CONDITIONING")], CORE, [320, 166]), |
| "Canny": ([("image", "IMAGE")], [("IMAGE", "IMAGE")], CORE, [290, 82]), |
| "DepthAnythingV2Preprocessor": ([("image", "IMAGE")], [("IMAGE", "IMAGE")], AUX, [330, 82]), |
| "EmptySD3LatentImage": ([], [("LATENT", "LATENT")], CORE, [290, 106]), |
| "ModelSamplingFlux": ([("model", "MODEL")], [("MODEL", "MODEL")], CORE, [300, 130]), |
| "LoraLoaderModelOnly": ([("model", "MODEL")], [("MODEL", "MODEL")], CORE, [340, 82]), |
| "KSampler": ([("model", "MODEL"), ("positive", "CONDITIONING"), ("negative", "CONDITIONING"), |
| ("latent_image", "LATENT")], [("LATENT", "LATENT")], CORE, [300, 262]), |
| "RandomNoise": ([], [("NOISE", "NOISE")], CORE, [290, 82]), |
| "KSamplerSelect": ([], [("SAMPLER", "SAMPLER")], CORE, [290, 58]), |
| "BasicScheduler": ([("model", "MODEL")], [("SIGMAS", "SIGMAS")], CORE, [290, 106]), |
| "BasicGuider": ([("model", "MODEL"), ("conditioning", "CONDITIONING")], |
| [("GUIDER", "GUIDER")], CORE, [240, 66]), |
| "SamplerCustomAdvanced": ([("noise", "NOISE"), ("guider", "GUIDER"), ("sampler", "SAMPLER"), |
| ("sigmas", "SIGMAS"), ("latent_image", "LATENT")], |
| [("output", "LATENT"), ("denoised_output", "LATENT")], CORE, [270, 126]), |
| "InstructPixToPixConditioning": ([("positive", "CONDITIONING"), ("negative", "CONDITIONING"), |
| ("vae", "VAE"), ("pixels", "IMAGE")], |
| [("positive", "CONDITIONING"), ("negative", "CONDITIONING"), |
| ("latent", "LATENT")], CORE, [300, 106]), |
| "VAEDecode": ([("samples", "LATENT"), ("vae", "VAE")], [("IMAGE", "IMAGE")], CORE, [210, 66]), |
| "SaveImage": ([("images", "IMAGE")], [], CORE, [400, 400]), |
| "PreviewImage": ([("images", "IMAGE")], [], CORE, [300, 300]), |
| } |
|
|
| BYPASS = 4 |
|
|
|
|
| class G: |
| def __init__(self): |
| self.nodes, self.links, self.groups = [], [], [] |
| self.nid, self.lid = 0, 0 |
|
|
| def add(self, type_, widgets=None, inputs=None, pos=(0, 0), size=None, mode=0, title=None): |
| """inputs: {slot_name: (src_node_dict, src_slot_index)}""" |
| link_ins, outs, pack, dsize = DEFS[type_] |
| self.nid += 1 |
| node = { |
| "id": self.nid, "type": type_, "pos": list(pos), |
| "size": list(size or dsize), "flags": {}, "order": len(self.nodes), |
| "mode": mode, |
| "inputs": [], "outputs": [], |
| "properties": {"Node name for S&R": type_, **pack}, |
| } |
| if title: |
| node["title"] = title |
| if widgets is not None: |
| node["widgets_values"] = list(widgets) |
| for name, typ in link_ins: |
| entry = {"name": name, "type": typ, "link": None} |
| src = (inputs or {}).get(name) |
| if src is not None: |
| src_node, src_slot = src |
| self.lid += 1 |
| entry["link"] = self.lid |
| self.links.append([self.lid, src_node["id"], src_slot, self.nid, |
| len(node["inputs"]), typ]) |
| src_node["outputs"][src_slot].setdefault("links", []).append(self.lid) |
| node["inputs"].append(entry) |
| for i, (name, typ) in enumerate(outs): |
| node["outputs"].append({"name": name, "type": typ, "links": [], "slot_index": i}) |
| self.nodes.append(node) |
| return node |
|
|
| def group(self, title, x, y, w, h, color="#3f789e"): |
| self.groups.append({"id": len(self.groups) + 1, "title": title, |
| "bounding": [x, y, w, h], "color": color, |
| "font_size": 24, "flags": {}}) |
|
|
| def dump(self, path): |
| doc = {"id": "00000000-0000-0000-0000-000000000000", "revision": 0, |
| "last_node_id": self.nid, "last_link_id": self.lid, |
| "nodes": self.nodes, "links": self.links, "groups": self.groups, |
| "config": {}, "extra": {}, "version": 0.4} |
| with open(path, "w") as f: |
| json.dump(doc, f, indent=2) |
| print("wrote", path, f"({len(self.nodes)} nodes, {len(self.links)} links)") |
|
|
|
|
| def flux_loaders(g, unet="flux1-dev.safetensors", x=-720): |
| unet_n = g.add("UNETLoader", [unet, "default"], pos=(x, -80)) |
| clip = g.add("DualCLIPLoader", ["t5xxl_fp16.safetensors", "clip_l.safetensors", "flux", "default"], pos=(x, 60)) |
| vae = g.add("VAELoader", ["ae.safetensors"], pos=(x, 250)) |
| return unet_n, clip, vae |
|
|
|
|
| def redux_branch(g, x, y, img_name="style_ref.png"): |
| img = g.add("LoadImage", [img_name, "image"], pos=(x, y), title="Style reference (Image A)") |
| cv_loader = g.add("CLIPVisionLoader", ["sigclip_vision_patch14_384.safetensors"], pos=(x, y + 360)) |
| enc = g.add("CLIPVisionEncode", ["center"], inputs={"clip_vision": (cv_loader, 0), "image": (img, 0)}, |
| pos=(x + 380, y + 60)) |
| sm = g.add("StyleModelLoader", ["flux1-redux-dev.safetensors"], pos=(x, y + 450)) |
| return img, cv_loader, enc, sm |
|
|
|
|
| |
| def build_style_composition(): |
| g = G() |
| g.add("MarkdownNote", ["""## FLUX Redux + ControlNet β style + composition (v1 draft) |
| |
| **Image A (style)** supplies aesthetic/palette/lighting via **FLUX.1 Redux**; **Image B |
| (composition)** supplies layout via **Depth** (Shakker-Labs ControlNet **Union-Pro-2.0** β mode |
| inferred from the control image, do NOT add SetUnionControlNetType). Optional text prompt. |
| |
| **Models** (HF `aleph65/ComfyUI`): |
| - `diffusion_models/flux1-dev.safetensors` (bf16) Β· `text_encoders/t5xxl_fp16.safetensors` + |
| `clip_l.safetensors` Β· `vae/ae.safetensors` |
| - `style_models/flux1-redux-dev.safetensors` Β· `clip_vision/sigclip_vision_patch14_384.safetensors` |
| - `controlnet/FLUX.1-dev-ControlNet-Union-Pro-2.0.safetensors` |
| - bypassed extras: `loras/flux1-turbo-alpha.safetensors` |
| - `depth_anything_v2_vitl.pth` auto-downloads into `custom_nodes/comfyui_controlnet_aux/ckpts/` |
| |
| **Knobs** |
| - *Apply Style Model* `strength` = Redux influence, `attn_bias` mode: 0.3β0.7 (start 0.5). |
| Raise if style too weak; lower (or use the ReduxAdvanced group) if Image A's layout leaks. |
| - *Depth ControlNet* strength 0.6β0.8, end_percent 0.8 (official 2.0 rec: 0.8/0.8). |
| - **Canny group (bypassed)**: enable to stack hard contour lock at LOW strength 0.25β0.4, or |
| use instead of depth (rewire). Union-Pro-2.0 handles both from the same loader. |
| - **ReduxAdvanced group (bypassed)**: alternative style isolation via token downsampling |
| (factor 3). To use: connect its CONDITIONING output to the depth Apply ControlNet `positive` |
| (replacing Apply Style Model) and enable the node. |
| - **Turbo group (bypassed)**: enable turbo-alpha LoRA + set steps to 8 for fast previews. |
| - Sampler: euler / simple / 28 steps / CFG 1.0 / FluxGuidance 3.5 / denoise 1.0. |
| |
| Prompt may be empty; a short scene description usually helps. Match latent aspect to Image B. |
| """], pos=(-1340, -120), size=[580, 700]) |
|
|
| unet, clip, vae = flux_loaders(g) |
|
|
| |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
|
|
| |
| txt = g.add("CLIPTextEncode", [""], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (optional β may stay empty)") |
| guid = g.add("FluxGuidance", [3.5], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| neg = g.add("ConditioningZeroOut", inputs={"conditioning": (guid, 0)}, pos=(140, 170)) |
|
|
| apply_style = g.add("StyleModelApply", [0.5, "attn_bias"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 300)) |
|
|
| |
| radv = g.add("ReduxAdvanced", [3, "area", "center crop (square)", 1.0, 0.1], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision": (cv_loader, 0), "image": (s_img, 0)}, |
| pos=(140, 520), mode=BYPASS, title="ALT: ReduxAdvanced (rewire to use)") |
|
|
| |
| c_img = g.add("LoadImage", ["composition_ref.png", "image"], pos=(-720, 1000), |
| title="Composition reference (Image B)") |
| depth = g.add("DepthAnythingV2Preprocessor", ["depth_anything_v2_vitl.pth", 1024], |
| inputs={"image": (c_img, 0)}, pos=(-300, 1030)) |
| depth_prev = g.add("PreviewImage", inputs={"images": (depth, 0)}, pos=(-300, 1180), |
| title="Depth map preview") |
| canny = g.add("Canny", [0.2, 0.5], inputs={"image": (c_img, 0)}, pos=(-300, 1540), |
| mode=BYPASS, title="OPTIONAL: canny edges") |
| cn_loader = g.add("ControlNetLoader", ["FLUX.1-dev-ControlNet-Union-Pro-2.0.safetensors"], |
| pos=(140, 950)) |
| cn_depth = g.add("ControlNetApplySD3", [0.7, 0.0, 0.8], |
| inputs={"positive": (apply_style, 0), "negative": (neg, 0), |
| "control_net": (cn_loader, 0), "vae": (vae, 0), "image": (depth, 0)}, |
| pos=(560, 300), title="Apply ControlNet β DEPTH") |
| cn_canny = g.add("ControlNetApplySD3", [0.35, 0.0, 0.6], |
| inputs={"positive": (cn_depth, 0), "negative": (cn_depth, 1), |
| "control_net": (cn_loader, 0), "vae": (vae, 0), "image": (canny, 0)}, |
| pos=(560, 560), mode=BYPASS, title="OPTIONAL: Apply ControlNet β CANNY (stack)") |
|
|
| |
| turbo = g.add("LoraLoaderModelOnly", ["flux1-turbo-alpha.safetensors", 1.0], |
| inputs={"model": (unet, 0)}, pos=(-300, -160), mode=BYPASS, |
| title="FAST PREVIEW: turbo LoRA (set steps 8)") |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, 1024, 1024], inputs={"model": (turbo, 0)}, |
| pos=(140, -160)) |
| latent = g.add("EmptySD3LatentImage", [1024, 1024, 1], pos=(560, 950)) |
| ks = g.add("KSampler", [0, "randomize", 28, 1.0, "euler", "simple", 1.0], |
| inputs={"model": (msf, 0), "positive": (cn_canny, 0), "negative": (cn_canny, 1), |
| "latent_image": (latent, 0)}, pos=(980, 300)) |
| dec = g.add("VAEDecode", inputs={"samples": (ks, 0), "vae": (vae, 0)}, pos=(1320, 300)) |
| g.add("SaveImage", ["flux-redux/style-comp"], inputs={"images": (dec, 0)}, pos=(1320, 420)) |
|
|
| g.group("STYLE REFERENCE β Redux", -740, 340, 1220, 700, "#3f789e") |
| g.group("COMPOSITION REFERENCE β Union-Pro-2.0", -740, 900, 1220, 800, "#8f5b34") |
| g.group("SAMPLING", 940, 200, 800, 700, "#444") |
| g.dump(f"{OUT}/flux-redux-style-composition.json") |
|
|
|
|
| |
| def build_fal_dev(): |
| g = G() |
| g.add("MarkdownNote", ["""## FLUX Redux β fal `flux/dev/redux` replica (v1 draft) |
| |
| Official ComfyUI Redux example graph with fal's exact defaults. One image in β variations out. |
| No safety checker (unlike fal). Doubles as the plain single-image Redux workflow. |
| |
| **Models** (HF `aleph65/ComfyUI`): `diffusion_models/flux1-dev.safetensors` (bf16, dtype |
| `default`) Β· `text_encoders/t5xxl_fp16.safetensors` + `clip_l.safetensors` Β· `vae/ae.safetensors` |
| Β· `style_models/flux1-redux-dev.safetensors` Β· `clip_vision/sigclip_vision_patch14_384.safetensors` |
| |
| **fal parity settings** β prompt empty (fal dev endpoint passes none), FluxGuidance 3.5 |
| (= guidance_scale), euler / simple / 28 steps / denoise 1.0, 768x1024 (= portrait_4_3), |
| Apply Style Model strength 1.0 / multiply. Seeds do NOT transfer from fal. |
| |
| **Extras fal doesn't expose**: type a prompt; lower strength (or switch to `attn_bias`) to let |
| the prompt steer; chain a second Apply Style Model to blend two references. |
| """], pos=(-1300, -80), size=[540, 460]) |
|
|
| unet, clip, vae = flux_loaders(g) |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", [""], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (empty = fal parity)") |
| guid = g.add("FluxGuidance", [3.5], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| apply_style = g.add("StyleModelApply", [1.0, "multiply"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 200)) |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, 768, 1024], inputs={"model": (unet, 0)}, |
| pos=(140, -160)) |
| noise = g.add("RandomNoise", [0, "randomize"], pos=(560, -160)) |
| guider = g.add("BasicGuider", inputs={"model": (msf, 0), "conditioning": (apply_style, 0)}, |
| pos=(560, 20)) |
| sampler = g.add("KSamplerSelect", ["euler"], pos=(560, 140)) |
| sched = g.add("BasicScheduler", ["simple", 28, 1.0], inputs={"model": (msf, 0)}, pos=(560, 250)) |
| latent = g.add("EmptySD3LatentImage", [768, 1024, 1], pos=(560, 420)) |
| samp = g.add("SamplerCustomAdvanced", |
| inputs={"noise": (noise, 0), "guider": (guider, 0), "sampler": (sampler, 0), |
| "sigmas": (sched, 0), "latent_image": (latent, 0)}, pos=(980, 60)) |
| dec = g.add("VAEDecode", inputs={"samples": (samp, 0), "vae": (vae, 0)}, pos=(1240, 60)) |
| g.add("SaveImage", ["flux-redux/fal-dev"], inputs={"images": (dec, 0)}, pos=(1240, 180)) |
| g.group("STYLE REFERENCE β Redux", -740, 340, 940, 700, "#3f789e") |
| g.group("SAMPLING (fal defaults)", 540, -220, 1100, 760, "#444") |
| g.dump(f"{OUT}/flux-redux-fal-dev.json") |
|
|
|
|
| |
| def build_prompt(): |
| g = G() |
| g.add("MarkdownNote", ["""## FLUX Redux + text prompt (v1 draft) |
| |
| One style image + a text prompt that keeps authority. Same graph as the fal replica, but Redux |
| is restrained with **attn_bias 0.5** so the prompt actually steers content β at 1.0/multiply |
| Redux drowns the prompt (that's the known Redux failure mode). |
| |
| **Models**: same as `flux-redux-fal-dev.json`. Optional fast preview: enable the bypassed |
| turbo LoRA group (`loras/flux1-turbo-alpha.safetensors`) and set steps 28 β 8. |
| |
| **Knobs**: strength 0.3β0.7 attn_bias (higher = more style, more subject leakage from the |
| reference); FluxGuidance 3.5; euler / simple / 28 steps; 1024x1024 (any flux-legal size). |
| If the reference's composition/subjects still leak: lower strength, or install |
| `ComfyUI_AdvancedRefluxControl` and swap in ReduxAdvanced (downsampling 3) β see the |
| style-composition workflow for the wiring. |
| """], pos=(-1300, -80), size=[540, 420]) |
|
|
| unet, clip, vae = flux_loaders(g) |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", ["describe the scene you want, in the reference's style"], |
| inputs={"clip": (clip, 0)}, pos=(-300, 60), title="Prompt") |
| guid = g.add("FluxGuidance", [3.5], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| apply_style = g.add("StyleModelApply", [0.5, "attn_bias"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 200)) |
| turbo = g.add("LoraLoaderModelOnly", ["flux1-turbo-alpha.safetensors", 1.0], |
| inputs={"model": (unet, 0)}, pos=(-300, -160), mode=BYPASS, |
| title="FAST PREVIEW: turbo LoRA (set steps 8)") |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, 1024, 1024], inputs={"model": (turbo, 0)}, |
| pos=(140, -160)) |
| noise = g.add("RandomNoise", [0, "randomize"], pos=(560, -160)) |
| guider = g.add("BasicGuider", inputs={"model": (msf, 0), "conditioning": (apply_style, 0)}, |
| pos=(560, 20)) |
| sampler = g.add("KSamplerSelect", ["euler"], pos=(560, 140)) |
| sched = g.add("BasicScheduler", ["simple", 28, 1.0], inputs={"model": (msf, 0)}, pos=(560, 250)) |
| latent = g.add("EmptySD3LatentImage", [1024, 1024, 1], pos=(560, 420)) |
| samp = g.add("SamplerCustomAdvanced", |
| inputs={"noise": (noise, 0), "guider": (guider, 0), "sampler": (sampler, 0), |
| "sigmas": (sched, 0), "latent_image": (latent, 0)}, pos=(980, 60)) |
| dec = g.add("VAEDecode", inputs={"samples": (samp, 0), "vae": (vae, 0)}, pos=(1240, 60)) |
| g.add("SaveImage", ["flux-redux/prompt"], inputs={"images": (dec, 0)}, pos=(1240, 180)) |
| g.group("STYLE REFERENCE β Redux", -740, 340, 940, 700, "#3f789e") |
| g.group("SAMPLING", 540, -220, 1100, 760, "#444") |
| g.dump(f"{OUT}/flux-redux-prompt.json") |
|
|
|
|
| |
| def build_schnell(): |
| g = G() |
| g.add("MarkdownNote", ["""## FLUX Redux schnell β Replicate `flux-redux-schnell` replica (v1 draft) |
| |
| Parity with Replicate's `black-forest-labs/flux-redux-schnell` endpoint: image variations in |
| **4 steps**, no prompt (their `redux_image` replaces it), no guidance (schnell ignores it β the |
| endpoint schema has none, hence no FluxGuidance node), no safety checker locally. |
| |
| **Models** (HF `aleph65/ComfyUI`): `diffusion_models/flux1-schnell.safetensors` (bf16, |
| Apache-2.0, HF repo not gated) Β· `text_encoders/t5xxl_fp16.safetensors` + `clip_l.safetensors` |
| Β· `vae/ae.safetensors` Β· `style_models/flux1-redux-dev.safetensors` Β· |
| `clip_vision/sigclip_vision_patch14_384.safetensors` |
| |
| **Endpoint mapping** β aspect_ratio @ ~1MP β EmptySD3LatentImage size (default 1:1 = |
| 1024x1024); num_outputs 1β4 β batch_size; num_inference_steps (max 4) β steps; seed β noise |
| seed (values do NOT reproduce Replicate's outputs). euler / simple / denoise 1.0 / |
| Apply Style Model 1.0 multiply. Lower steps = faster + worse, exactly like the endpoint. |
| """], pos=(-1300, -80), size=[540, 440]) |
|
|
| unet, clip, vae = flux_loaders(g, unet="flux1-schnell.safetensors") |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", [""], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (endpoint has none β leave empty)") |
| apply_style = g.add("StyleModelApply", [1.0, "multiply"], |
| inputs={"conditioning": (txt, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 100)) |
| noise = g.add("RandomNoise", [0, "randomize"], pos=(560, -160)) |
| guider = g.add("BasicGuider", inputs={"model": (unet, 0), "conditioning": (apply_style, 0)}, |
| pos=(560, 20)) |
| sampler = g.add("KSamplerSelect", ["euler"], pos=(560, 140)) |
| sched = g.add("BasicScheduler", ["simple", 4, 1.0], inputs={"model": (unet, 0)}, pos=(560, 250)) |
| latent = g.add("EmptySD3LatentImage", [1024, 1024, 1], pos=(560, 420), |
| title="size = aspect_ratio @ 1MP Β· batch = num_outputs") |
| samp = g.add("SamplerCustomAdvanced", |
| inputs={"noise": (noise, 0), "guider": (guider, 0), "sampler": (sampler, 0), |
| "sigmas": (sched, 0), "latent_image": (latent, 0)}, pos=(980, 60)) |
| dec = g.add("VAEDecode", inputs={"samples": (samp, 0), "vae": (vae, 0)}, pos=(1240, 60)) |
| g.add("SaveImage", ["flux-redux/schnell"], inputs={"images": (dec, 0)}, pos=(1240, 180)) |
| g.group("STYLE REFERENCE β Redux", -740, 340, 940, 700, "#3f789e") |
| g.group("SAMPLING (Replicate defaults, 4 steps)", 540, -220, 1100, 760, "#444") |
| g.dump(f"{OUT}/flux-redux-schnell.json") |
|
|
|
|
| |
| def build_bfl_lora(): |
| g = G() |
| g.add("MarkdownNote", ["""## FLUX Redux + official BFL Depth LoRA (v1 draft β A/B variant) |
| |
| Alternative to the Union-Pro-2.0 workflow for maximum structural adherence: BFL's |
| `flux1-depth-dev-lora` on stock flux1-dev, control image fed via latent-concat |
| (**InstructPixToPixConditioning**), Redux on the conditioning as usual. |
| |
| **Models**: dev base + Redux files as in the other workflows, plus |
| `loras/flux1-depth-dev-lora.safetensors` (HF `black-forest-labs/FLUX.1-Depth-dev-lora`, gated). |
| |
| **Differences vs the ControlNet version** β structure control has NO strength/start/end; |
| weaken it via the LoRA strength (1.0 = full adherence, ~0.7 looser). No canny stacking. |
| **FluxGuidance 10** (BFL's depth rec β NOT 3.5). Redux attn_bias 0.5 as usual. |
| Latent size comes from InstructPixToPixConditioning (= control image size) β feed a |
| flux-legal resolution composition image (~1MP). |
| """], pos=(-1300, -80), size=[540, 420]) |
|
|
| unet, clip, vae = flux_loaders(g) |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", [""], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (optional)") |
| guid = g.add("FluxGuidance", [10.0], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| neg = g.add("ConditioningZeroOut", inputs={"conditioning": (guid, 0)}, pos=(140, 170)) |
| apply_style = g.add("StyleModelApply", [0.5, "attn_bias"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 300)) |
| c_img = g.add("LoadImage", ["composition_ref.png", "image"], pos=(-720, 1000), |
| title="Composition reference (Image B)") |
| depth = g.add("DepthAnythingV2Preprocessor", ["depth_anything_v2_vitl.pth", 1024], |
| inputs={"image": (c_img, 0)}, pos=(-300, 1030)) |
| g.add("PreviewImage", inputs={"images": (depth, 0)}, pos=(-300, 1180), title="Depth map preview") |
| ip2p = g.add("InstructPixToPixConditioning", |
| inputs={"positive": (apply_style, 0), "negative": (neg, 0), "vae": (vae, 0), |
| "pixels": (depth, 0)}, pos=(560, 300)) |
| dlora = g.add("LoraLoaderModelOnly", ["flux1-depth-dev-lora.safetensors", 1.0], |
| inputs={"model": (unet, 0)}, pos=(-300, -160), title="BFL Depth LoRA") |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, 1024, 1024], inputs={"model": (dlora, 0)}, |
| pos=(140, -160)) |
| ks = g.add("KSampler", [0, "randomize", 28, 1.0, "euler", "simple", 1.0], |
| inputs={"model": (msf, 0), "positive": (ip2p, 0), "negative": (ip2p, 1), |
| "latent_image": (ip2p, 2)}, pos=(980, 300)) |
| dec = g.add("VAEDecode", inputs={"samples": (ks, 0), "vae": (vae, 0)}, pos=(1320, 300)) |
| g.add("SaveImage", ["flux-redux/style-comp-bfl"], inputs={"images": (dec, 0)}, pos=(1320, 420)) |
| g.group("STYLE REFERENCE β Redux", -740, 340, 940, 700, "#3f789e") |
| g.group("COMPOSITION β BFL Depth LoRA", -740, 900, 1220, 500, "#8f5b34") |
| g.dump(f"{OUT}/flux-redux-style-composition-bfl-lora.json") |
|
|
|
|
| build_style_composition() |
| build_fal_dev() |
| build_prompt() |
| build_schnell() |
| build_bfl_lora() |
|
|