Files
olivv-cs-ComfyUI-FunPack/modules/system/pipeline/__init__.py
T
digital-garbage 33eeff08e3 app: the dials on the main window, and a way to have two projects
Steps, Sampler and Scheduler are on the main window -- the three v4 buried
several clicks deep in Engine Settings. No new UI: the sampler slot declares
roles the way the prompt and the size already do.

What made that nearly free is that the controls now come from the NODE's own
description, through the settingFor/rendererFor pair the pipeline window has
always used. So a combo is a dropdown of the real choices and a number obeys
the real bounds, and prompt.js lost both of its hand-rolled branches: it no
longer decides what a control looks like, it asks.

File: New project, and every project listed with the open one ticked. The
store has been able to do this since the day it arrived and there was no way
in -- 20 lines of menu.

Three faults found on the way, all in the shared input helper:

* ENTER COULD NOT BE TYPED IN A TEXTAREA. `wire()` bound Enter to commit for
  every text control, textareas included, so a multi-line value was
  impossible -- the prompt above all, which is the longest multi-line thing
  in the app. It has been that way in the pipeline window since it was
  written; nobody had typed a second line into one.
* A control committed on blur alone, so autofill, a password manager and any
  script setting a value went unheard. It listens to `change` as well now,
  and commit() only acts on a real difference so the pair costs nothing.
* A control commits on blur, which means text still being typed is not in
  the project yet -- and a tab closing is exactly when that is lost. boot
  blurs before it saves.
2026-09-06 12:18:33 +03:00

87 lines
4.4 KiB
Python

"""The default pipeline, as data.
Every slot here can be pointed at a different node or taken out entirely, and
that is only true because this is a list rather than a function. v4's equivalent
was a `CORE` dict inside the builder, so "remove this node" had nothing to
delete and "use a different node" had nothing to change -- the pipeline was code,
and code is not a thing a user can edit.
The rule for what belongs here: enough to generate with nothing else wired. A
run with no overrides at all has to produce a picture, because that is what makes
the UI an override layer rather than a requirement.
Nothing about these slots is privileged. Core does not know this list exists; it
is handed one and builds it.
`roles` is how a slot puts one of its inputs on the app's own surface: the
prompt is a slot input like any other, and typing it into a box beside the
picture rather than digging for the node it belongs to is the whole difference
between a pipeline and an app. The name is a mount point, so a role naming a
place this shell does not offer is simply not shown.
`group` is the pipeline's own idea of how it reads to a person -- the cards the
app shows are these names, in the order they first appear. It travels on the
slot rather than living in the app, so a pipeline that replaced every slot keeps
its own arrangement, and a group is created by a slot claiming a name nobody
used yet rather than by registering it anywhere.
"""
ID = "pipeline"
TITLE = "Pipeline"
STAGE = "load"
STATUS = "proven"
# Slot ids are stable: they are how an override says which slot it means, and
# they end up as node ids in the queued prompt.
DEFAULT = [
{"id": "model", "group": "Loaders", "node": "FunPackDiffusionModelLoader", "inputs": {
"weight_dtype": "default", "compute_dtype": "default", "attention": "default"}},
{"id": "clip", "group": "Loaders", "node": "FunPackCLIPLoader", "inputs": {"type": "stable_diffusion"}},
{"id": "vae", "group": "Loaders", "node": "FunPackVAELoader", "inputs": {"dtype": "bfloat16"}},
{"id": "positive", "group": "Preparation", "node": "CLIPTextEncode",
"roles": [{"at": "generation.prompt", "input": "text", "label": "Prompt"}],
"inputs": {"clip": ["clip", 0], "text": ""}},
{"id": "negative", "group": "Preparation", "node": "CLIPTextEncode",
"roles": [{"at": "generation.prompt", "input": "text", "label": "Negative"}],
"inputs": {"clip": ["clip", 0], "text": ""}},
{"id": "latent", "group": "Preparation", "node": "FunPackEmptyLatent",
"roles": [{"at": "project.video", "input": "width", "label": "Width"},
{"at": "project.video", "input": "height", "label": "Height"},
{"at": "project.video", "input": "length", "label": "Length"}],
"inputs": {
"model": ["model", 0], "width": 512, "height": 512, "length": 1, "batch_size": 1}},
{"id": "settings", "group": "Preparation", "node": "FunPackModifierSettings", "inputs": {"settings": "{}"}},
{"id": "modifiers", "group": "Preparation", "node": "FunPackLoadModifiers", "inputs": {
"model": ["model", 0], "settings": ["settings", 0]}},
{"id": "sampler", "group": "Sampling", "node": "FunPackSampler",
# The three dials that get reached for constantly. v4 buried them several
# clicks deep in Engine Settings; here they are on the main window because
# the slot says they belong there.
"roles": [{"at": "generation.sampling", "input": "steps", "label": "Steps"},
{"at": "generation.sampling", "input": "sampler_name", "label": "Sampler"},
{"at": "generation.sampling", "input": "scheduler", "label": "Scheduler"}],
"inputs": {
"model": ["modifiers", 0], "positive": ["positive", 0], "negative": ["negative", 0],
"latent": ["latent", 0], "settings": ["settings", 0],
"seed": 0, "steps": 20, "cfg": 7.0,
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0}},
{"id": "decode", "group": "Render", "node": "FunPackDecode", "inputs": {
"samples": ["sampler", 0], "vae": ["vae", 0], "model": ["model", 0]}},
{"id": "save", "group": "Render", "node": "SaveImage", "inputs": {
"images": ["decode", 0], "filename_prefix": "FunPack"}},
]
def default():
"""A fresh copy: a caller that edits the pipeline must not edit everyone's."""
return [dict(slot, inputs=dict(slot.get("inputs") or {})) for slot in DEFAULT]
PROVIDES = {"default_pipeline": default}