fix(models): a project's graph is no longer the template for new projects
Saving a pipeline mirrored it into the global default, so every later project adopted an imported workflow. Only FunPack's own seeded loaders travel now; the family is recorded on its own. Also: creating a linked input no longer self-aborts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
33aaa1b883
commit
7ae308f261
@@ -680,6 +680,32 @@ def reseed_for_family(models: dict, object_info: Optional[dict] = None) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def global_template_update(current: Optional[dict], saved: dict) -> Optional[dict]:
|
||||
"""What a project's save should leave in the global default that seeds NEW projects.
|
||||
|
||||
Only FunPack's own seeded loaders travel: those are the file picks anyone's next
|
||||
project wants, and the reason the global exists. An imported workflow or a hand-wired
|
||||
graph is that ONE project's business — mirroring it whole is how every later project
|
||||
came to adopt it, loaders and all. The family is reusable either way, so it is recorded
|
||||
on its own.
|
||||
|
||||
Returns the global to write, or None when the save must not touch it.
|
||||
"""
|
||||
current = current if isinstance(current, dict) else {}
|
||||
saved = saved if isinstance(saved, dict) else {}
|
||||
if is_seeded_pipeline(saved) and not is_workflow_import(saved):
|
||||
out = dict(saved)
|
||||
# A save that simply omits the family must not erase the one already recorded:
|
||||
# a caller changing it sends it, one rearranging loaders does not.
|
||||
if not out.get("model_family") and current.get("model_family"):
|
||||
out["model_family"] = current["model_family"]
|
||||
return out
|
||||
family = saved.get("model_family")
|
||||
if family and current.get("model_family") != family:
|
||||
return {**current, "model_family": family}
|
||||
return None
|
||||
|
||||
|
||||
def new_project_models(glob: Optional[dict], object_info: Optional[dict] = None) -> dict:
|
||||
"""The pipeline a NEW project starts with.
|
||||
|
||||
|
||||
@@ -1679,7 +1679,14 @@
|
||||
}
|
||||
ensureLinks().push(link);
|
||||
applyLinkValue(link, val);
|
||||
linkMode = false; linkSel = []; await persist(); setView("links");
|
||||
linkMode = false; linkSel = [];
|
||||
// Members are picked ON node pages, where saving is deferred until the node's Save
|
||||
// button. Going through that buffer meant "Save link" only marked the page dirty, and
|
||||
// the jump to the links view then offered to DISCARD the link it had just made — so a
|
||||
// link could never be created at all. Pressing Save link is the explicit save.
|
||||
if (deferSave) { await persistNow(); deferDirty = false; }
|
||||
else await persist();
|
||||
setView("links");
|
||||
}
|
||||
|
||||
// Persistent bar while picking link members — stays visible as the user moves
|
||||
|
||||
+5
-14
@@ -2614,22 +2614,13 @@ if web is not None and PromptServer is not None:
|
||||
seeded_now = True
|
||||
p.models = body
|
||||
projects.save(p)
|
||||
# Keep the global default in sync (it seeds new projects) — but never let a save that
|
||||
# simply omits `model_family` erase the one already recorded there. A caller that
|
||||
# means to change the family sends it; one that is only rearranging loaders does not,
|
||||
# and silently clearing it is how every later project ends up with no family at all.
|
||||
#
|
||||
# A SEED is not a user edit, and must not reach the global at all: opening a new
|
||||
# project would otherwise replace the loaders someone configured — the very thing
|
||||
# the global exists to hand on — with four empty ones.
|
||||
# A SEED is not a user edit and must not reach the global default at all: opening a
|
||||
# new project would otherwise replace the loaders someone configured with empty ones.
|
||||
if seeded_now:
|
||||
return web.json_response(body)
|
||||
glob = dict(body)
|
||||
if not glob.get("model_family"):
|
||||
inherited = nodes.load_models().get("model_family")
|
||||
if inherited:
|
||||
glob["model_family"] = inherited
|
||||
nodes.save_models(glob)
|
||||
glob = pipeline_wiring.global_template_update(nodes.load_models(), body)
|
||||
if glob is not None:
|
||||
nodes.save_models(glob)
|
||||
return web.json_response(body)
|
||||
|
||||
# --- UI: legacy entry points ---
|
||||
|
||||
@@ -522,3 +522,38 @@ def test_the_samplers_own_positive_stays_core_owned():
|
||||
offered = {p for ports in pipeline_wiring._type_fallback_ports(family).values()
|
||||
for p in ports}
|
||||
assert "FunPackLTXAVSceneChainSampler.positive" not in offered
|
||||
|
||||
|
||||
# ── what a project's save leaves in the global default ────────────────────────
|
||||
|
||||
def test_an_imported_workflow_never_becomes_the_global_template():
|
||||
"""Mirroring one project's graph is how every later project came to adopt it."""
|
||||
current = {"model_family": "ltxav", "slots": []}
|
||||
saved = dict(IMPORTED_GLOBAL)
|
||||
assert pipeline_wiring.global_template_update(current, saved) is None
|
||||
|
||||
|
||||
def test_a_hand_wired_graph_stays_that_projects_business():
|
||||
saved = {"model_family": "ltxav",
|
||||
"slots": [{"id": "mine", "role": "custom", "node_class": "VAELoader"}]}
|
||||
assert pipeline_wiring.global_template_update({"model_family": "ltxav"}, saved) is None
|
||||
|
||||
|
||||
def test_seeded_loaders_do_travel_because_that_is_what_the_global_is_for():
|
||||
"""Picking the same model files in every project is the thing it prevents."""
|
||||
saved = {"model_family": "ltxav",
|
||||
"slots": pipeline_wiring.default_pipeline_slots("ltxav", OI_FILES)}
|
||||
assert pipeline_wiring.global_template_update({}, saved) == saved
|
||||
|
||||
|
||||
def test_the_family_is_recorded_even_when_the_graph_is_not():
|
||||
saved = dict(IMPORTED_GLOBAL, model_family="minimax_h3")
|
||||
out = pipeline_wiring.global_template_update({"model_family": "ltxav", "slots": []}, saved)
|
||||
assert out["model_family"] == "minimax_h3"
|
||||
assert out["slots"] == [] # the old global's own slots, not the import's
|
||||
|
||||
|
||||
def test_a_save_that_omits_the_family_does_not_erase_it():
|
||||
saved = {"slots": pipeline_wiring.default_pipeline_slots("ltxav", OI_FILES)}
|
||||
out = pipeline_wiring.global_template_update({"model_family": "minimax_h3"}, saved)
|
||||
assert out["model_family"] == "minimax_h3"
|
||||
|
||||
Reference in New Issue
Block a user