"""Generate the all-node smoke test from a running ComfyUI instance. Usage: python examples/generate_test_workflow.py http://127.0.0.1:8188 """ import json import sys import uuid from pathlib import Path from urllib.request import urlopen url = sys.argv[1].rstrip("/") if len(sys.argv) > 1 else "http://127.0.0.1:8188" schema = json.load(urlopen(f"{url}/object_info")) nodes = [] links = [] def add(kind, pos, values=None, title=None, size=None): info = schema[kind] values = values or {} inputs, widgets = [], [] for name, spec in {**info["input"].get("required", {}), **info["input"].get("optional", {})}.items(): raw_type = spec[0] input_type = "COMBO" if isinstance(raw_type, list) else raw_type options = spec[1] if len(spec) > 1 and isinstance(spec[1], dict) else {} entry = {"name": name, "type": input_type, "link": None} if input_type in ("COMBO", "STRING", "INT", "FLOAT", "BOOLEAN") and not options.get("forceInput"): entry["widget"] = {"name": name} default = options.get("default", raw_type[0] if isinstance(raw_type, list) and raw_type else "") widgets.append(values.get(name, default)) inputs.append(entry) if kind == "LoadImage": inputs.append({"name": "upload", "type": "IMAGEUPLOAD", "widget": {"name": "upload"}, "link": None}) widgets.append("image") node_id = len(nodes) + 1 node = { "id": node_id, "type": kind, "pos": pos, "size": size or [350, 180], "flags": {}, "order": node_id - 1, "mode": 0, "inputs": inputs, "outputs": [{"name": name, "type": typ, "links": []} for name, typ in zip(info.get("output_name", info["output"]), info["output"])], "properties": {"Node name for S&R": kind}, "widgets_values": widgets, } if title: node["title"] = title nodes.append(node) return node_id def connect(source, slot, target, input_name): origin = nodes[source - 1] dest = nodes[target - 1] dest_slot = next(i for i, item in enumerate(dest["inputs"]) if item["name"] == input_name) assert dest["inputs"][dest_slot]["link"] is None link_id = len(links) + 1 links.append([link_id, source, slot, target, dest_slot, origin["outputs"][slot]["type"]]) origin["outputs"][slot]["links"].append(link_id) dest["inputs"][dest_slot]["link"] = link_id image = add("LoadImage", [80, 100], {"image": "example.png"}, "Scegli la tua immagine", [380, 330]) questions = add("String Variable [n-suite]", [80, 500], {"string": "What is in this image?,What colors are in this image?"}, "Domande di prova") dynamic = add("DynamicPrompt [n-suite]", [520, 490], {"cached": "NO", "number_of_random_tag": "Fixed", "fixed_number_of_random_tag": 1}) caption_model = add("GPT Loader Simple [n-suite]", [520, 100], {"ckpt_name": "moondream"}) caption = add("GPT Sampler [n-suite]", [930, 100], {"max_tokens": 128, "cached": "NO", "print_output": "enable"}, size=[390, 700]) caption_preview = add("PreviewAny", [1400, 150], title="Risposta Moondream") noise = add("Float Variable [n-suite]", [80, 1040], {"value": 0.1}) pad = add("ImagePadForOutpaintAdvanced [n-suite]", [500, 930], {"left": 32, "right": 32, "top": 32, "bottom": 32}, size=[430, 590]) padded_preview = add("PreviewImage", [1030, 970], title="Immagine con bordo", size=[350, 300]) mask_to_image = add("MaskToImage", [1030, 1330]) mask_preview = add("PreviewImage", [1410, 1320], title="Maschera del bordo", size=[350, 300]) clip = add("CLIPLoader", [2030, 100], {"clip_name": "clip_l.safetensors", "type": "stable_diffusion"}) encode = add("CLIPTextEncodeAdvancedNSuite [n-suite]", [2460, 100], {"styles": "NAI", "positive_prompt": "a small test image", "negative_prompt": "blurry"}, size=[400, 350]) positive_preview = add("PreviewAny", [2940, 100], title="Condizionamento positivo") negative_preview = add("PreviewAny", [2940, 400], title="Condizionamento negativo") multiplier = add("Integer Variable [n-suite]", [80, 2060], {"value": 2}) video = add("LoadVideo [n-suite]", [430, 1950], {"video": "SELECT_VIDEO.mp4", "framerate": "original", "resize_by": "none", "images_limit": 0, "batch_size": 0, "starting_frame": 0, "autoplay": False, "use_ram": False}, size=[420, 570]) interpolator = add("FrameInterpolator [n-suite]", [940, 1990]) interpolated_video = add("SaveVideo [n-suite]", [1430, 1970], {"SaveVideo": True, "SaveFrames": False, "filename_prefix": "n_suite_test_interpolated"}) video_info = add("PreviewAny", [940, 2290], title="Metadati video originale") folder = add("String Variable [n-suite]", [80, 2990], {"string": "/workspace/ComfyUI/input/n-suite/test_frames"}, "Cartella frame: cambia qui", [500, 130]) image_folder = add("LoadImageFromFolder [n-suite]", [660, 2860]) image_folder_preview = add("PreviewImage", [1100, 2840], title="Immagini dalla cartella", size=[330, 260]) manual_metadata = add("SetMetadataForSaveVideo [n-suite]", [1100, 3210], {"fps": 24, "VideoName": "n_suite_folder"}) manual_video = add("SaveVideo [n-suite]", [1550, 2890], {"SaveVideo": True, "SaveFrames": False, "filename_prefix": "n_suite_test_manual_metadata"}) frame_folder = add("LoadFramesFromFolder [n-suite]", [660, 3530], {"fps": 24}) frame_folder_preview = add("PreviewImage", [1100, 3520], title="Frame numerati", size=[330, 260]) frame_video = add("SaveVideo [n-suite]", [1550, 3510], {"SaveVideo": True, "SaveFrames": False, "filename_prefix": "n_suite_test_folder_frames"}) for args in [ (questions, 0, dynamic, "variable_prompt"), (dynamic, 0, caption, "prompt"), (caption_model, 0, caption, "model"), (image, 0, caption, "image"), (caption, 0, caption_preview, "source"), (image, 0, pad, "image"), (noise, 0, pad, "noise"), (pad, 0, padded_preview, "images"), (pad, 1, mask_to_image, "mask"), (mask_to_image, 0, mask_preview, "images"), (clip, 0, encode, "clip"), (encode, 0, positive_preview, "source"), (encode, 1, negative_preview, "source"), (video, 0, interpolator, "images"), (video, 2, interpolator, "METADATA"), (multiplier, 0, interpolator, "multiplier"), (interpolator, 0, interpolated_video, "images"), (interpolator, 1, interpolated_video, "METADATA"), (video, 2, video_info, "source"), (folder, 0, image_folder, "folder"), (folder, 0, frame_folder, "folder"), (image_folder, 0, image_folder_preview, "images"), (image_folder, 0, manual_video, "images"), (image_folder, 3, manual_metadata, "number_of_frames"), (manual_metadata, 0, manual_video, "METADATA"), (frame_folder, 0, frame_folder_preview, "images"), (frame_folder, 0, frame_video, "images"), (frame_folder, 1, frame_video, "METADATA"), ]: connect(*args) groups = [ ("01 FOTO + MOONDREAM: scegli la foto in LoadImage", [40, 40, 1750, 790], "#3f789e"), ("02 IMAGE PAD: controlla immagine e maschera", [40, 870, 1760, 790], "#637c49"), ("03 CLIP: usa il modello clip_l presente", [1980, 40, 1400, 680], "#76578e"), ("04 VIDEO: copia un MP4 in input/n-suite, ricarica e selezionalo", [40, 1880, 1790, 690], "#896a3c"), ("05 CARTELLA: aggiungi 0001.png e 0002.png in test_frames", [40, 2780, 1920, 1050], "#3f789e"), ] used = {node["type"] for node in nodes if "[n-suite]" in node["type"].lower()} expected = {name for name in schema if "[n-suite]" in name.lower()} assert used == expected, f"Missing N-Suite nodes: {sorted(expected - used)}" workflow = { "id": str(uuid.uuid4()), "revision": 0, "last_node_id": len(nodes), "last_link_id": len(links), "nodes": nodes, "links": links, "groups": [{"id": i, "title": title, "bounding": bounds, "color": color, "flags": {}} for i, (title, bounds, color) in enumerate(groups, 1)], "config": {}, "extra": {"ds": {"scale": 0.55, "offset": [70, 70]}}, "version": 0.4, } destination = Path(__file__).with_name("N-Suite-all-nodes-test.json") destination.write_text(json.dumps(workflow, ensure_ascii=False, indent=2) + "\n") print(f"Saved {destination}: {len(nodes)} nodes, {len(links)} links, {len(used)} N-Suite types")