Files
Claude 3adebb731f Rename FunPack to FunUI (5.0.3 Classy Chopper): /fun endpoint, wink logo, load-time migration of old names
Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
2026-10-25 13:11:03 +00:00

92 lines
4.7 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"
CATEGORY = "system"
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": "FunUIDiffusionModelLoader", "inputs": {
"weight_dtype": "default", "compute_dtype": "default", "attention": "default"}},
{"id": "clip", "group": "Loaders", "node": "FunUICLIPLoader", "inputs": {"type": "stable_diffusion"}},
{"id": "vae", "group": "Loaders", "node": "FunUIVAELoader", "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",
# A project-level field, not a per-run one: most FunUI pipelines run at
# CFG 1, where a negative prompt does nothing, so it does not belong beside
# the prompt every Generate reads.
"roles": [{"at": "project.negative", "input": "text", "label": "Negative prompt"}],
"inputs": {"clip": ["clip", 0], "text": ""}},
{"id": "latent", "group": "Preparation", "node": "FunUIEmptyLatent",
"roles": [{"at": "project.video", "input": "width", "label": "Width"},
{"at": "project.video", "input": "height", "label": "Height"},
{"at": "project.video", "input": "length", "label": "Length", "drives": "frames"}],
"inputs": {
"model": ["model", 0], "width": 512, "height": 512, "length": 1, "batch_size": 1}},
{"id": "settings", "group": "Preparation", "node": "FunUIModifierSettings", "inputs": {"settings": "{}"}},
{"id": "modifiers", "group": "Preparation", "node": "FunUILoadModifiers", "inputs": {
"model": ["model", 0], "settings": ["settings", 0]}},
{"id": "sampler", "group": "Sampling", "node": "FunUISampler",
# 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"},
{"at": "generation.seed", "input": "seed", "label": "Seed"}],
"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": "FunUIDecode", "inputs": {
"samples": ["sampler", 0], "vae": ["vae", 0], "model": ["model", 0]}},
{"id": "save", "group": "Render", "node": "SaveImage", "inputs": {
"images": ["decode", 0], "filename_prefix": "FunUI"}},
]
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}