145 lines
8.1 KiB
Python
145 lines
8.1 KiB
Python
"""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")
|