#!/usr/bin/env python3 """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"} # node type -> (link_inputs [(name,TYPE)...], outputs [(name,TYPE)...], pack_props, default_size) 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 # ---------------------------------------------------------------- #1 priority 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) # style branch s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) # text branch 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)) # ALT: ReduxAdvanced (bypassed, output unconnected) 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)") # composition branch 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)") # model chain + sampling 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") # ---------------------------------------------------------------- #2 fal replica 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") # ---------------------------------------------------------------- #3 image + prompt 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") # ---------------------------------------------------------------- #4 replicate schnell 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") # ---------------------------------------------------------------- #5 BFL depth lora A/B 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()