feat: Disable all enhancements (project switch for plain-pipeline A/B runs; Settings ▸ Modules, chip beside Generate)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
7ffc06c424
commit
c4084d3db0
@@ -9,9 +9,14 @@ export default {
|
||||
const chip = c.button.sm({ label: "⚠ Nothing encodes your prompt", tone: "ghost", disabled: true,
|
||||
title: "This pipeline has no prompt input: the scene text is not sent." }).node;
|
||||
chip.hidden = true;
|
||||
host.append(chip);
|
||||
const plain = c.button.sm({ label: "Enhancements off", tone: "ghost", title: "Disable all enhancements is on: runs use the plain pipeline. Switch it off in Settings ▸ Modules.",
|
||||
onClick: () => app.openSettings?.("modules") }).node;
|
||||
plain.hidden = true;
|
||||
host.append(chip, plain);
|
||||
const draw = (slots) => { chip.hidden = !(slots && !slots.some((s) => (s.roles || []).some((r) => r.at === "generation.prompt"))); };
|
||||
const drawPlain = () => { plain.hidden = !app.pipeline.allOff(); };
|
||||
drawPlain();
|
||||
draw(app.pipeline.slots());
|
||||
return app.pipeline.subscribe ? app.pipeline.subscribe(draw) : undefined;
|
||||
return app.pipeline.subscribe ? app.pipeline.subscribe(() => { draw(app.pipeline.slots()); drawPlain(); }) : undefined;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ export const engine = (app) => function mount() {
|
||||
function draw() {
|
||||
if (ps.loading()) return page.set([c.hint.default({ text: "Loading…" })]);
|
||||
if (ps.loadError()) return page.set([c.banner.warn({ text: `Could not load Engine settings: ${ps.loadError()}` })]);
|
||||
if (ps.allOff()) return page.set([c.banner.info({ text: "All enhancements are disabled for this project, so there is nothing to set. Turn them back on in Settings ▸ Modules." })]);
|
||||
const withSettings = ps.activeModules().filter((m) => Object.keys(m.settings || {}).length);
|
||||
const cats = CATEGORIES.filter(([k]) => withSettings.some((m) => (m.category || "") === k));
|
||||
if (!cats.length) return page.set([c.emptyState.default({ icon: "⚡", title: "Nothing to set", hint: "No installed module exposes a setting for this pipeline." })]);
|
||||
|
||||
@@ -23,11 +23,13 @@ export const modules = (app) => function mount() {
|
||||
await ps.refreshControl(); draw();
|
||||
} }) });
|
||||
}
|
||||
return c.toggle.default({ label: name(m), hint: ps.isOff(m.id) ? "Off for this project" : undefined, checked: !ps.isOff(m.id),
|
||||
return c.toggle.default({ label: name(m), hint: ps.allOff() ? "Off: all enhancements are disabled" : ps.isOff(m.id) ? "Off for this project" : undefined, checked: !ps.allOff() && !ps.isOff(m.id), disabled: ps.allOff(),
|
||||
onChange: (on) => ps.setOff(m.id, !on).then(draw) });
|
||||
};
|
||||
const notes = ps.saveNotes();
|
||||
page.set([notes.length ? c.banner.warn({ text: notes.join(" ") }) : null, c.hint.default({ text: "A module you turn off here is off for this project only, and disappears from Engine settings. One that failed while generating is off for every project until its code is repaired or you turn it back on." }),
|
||||
const master = c.toggle.default({ label: "Disable all enhancements", hint: "Runs the plain pipeline: every module below sits out, and so do the rewrites of shot camera. Your choices are kept — switch it back off to restore them. Handy for an A/B test of whether a feature does anything.",
|
||||
checked: ps.allOff(), onChange: (on) => ps.setAllOff(on).then(draw) });
|
||||
page.set([master, notes.length ? c.banner.warn({ text: notes.join(" ") }) : null, c.hint.default({ text: "A module you turn off here is off for this project only, and disappears from Engine settings. One that failed while generating is off for every project until its code is repaired or you turn it back on." }),
|
||||
...CATEGORIES.flatMap(([key, label]) => { const list = mine.filter((m) => (m.category || "") === key); return list.length ? [c.label.section({ text: label }), ...list.map(row)] : []; })].filter(Boolean));
|
||||
}
|
||||
draw();
|
||||
|
||||
@@ -275,13 +275,17 @@ export function createPipelineState(API) {
|
||||
if (off) cur.add(id); else cur.delete(id);
|
||||
return setModuleValue("_off", "modules", [...cur]);
|
||||
}
|
||||
// "Disable all enhancements": one project switch that makes every run plain, for A/B tests. The per-module
|
||||
// choices are kept underneath, so switching it back restores exactly what was on.
|
||||
const allOff = () => Boolean((currentValues()._off || {}).all);
|
||||
const setAllOff = (on) => setModuleValue("_off", "all", Boolean(on));
|
||||
async function refreshControl() {
|
||||
try { controlState = (await API.modules()).control || {}; } catch (_) { /* keeps the last answer */ }
|
||||
return controlState;
|
||||
}
|
||||
function isQuarantined(id) { return !!(controlState[id] && controlState[id].quarantine); }
|
||||
function activeModules() {
|
||||
return Object.values(modulesById).filter((m) => !isOff(m.id) && !isQuarantined(m.id));
|
||||
return allOff() ? [] : Object.values(modulesById).filter((m) => !isOff(m.id) && !isQuarantined(m.id));
|
||||
}
|
||||
|
||||
// Patch ONE field and save the WHOLE tree. place() (core/graph.py) writes
|
||||
@@ -520,7 +524,7 @@ export function createPipelineState(API) {
|
||||
frozenInputs,
|
||||
ensureLoaded, save, edit, restore, snapshot, setGroup, adopt, subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); }, valuesAlreadyPlaced, currentValues, setModuleValue,
|
||||
modulesById: () => modulesById,
|
||||
activeModules, isOff, setOff, refreshControl, control: () => controlState,
|
||||
activeModules, isOff, setOff, allOff, setAllOff, refreshControl, control: () => controlState,
|
||||
removedIds: () => [...removed],
|
||||
unwiredMap: () => JSON.parse(JSON.stringify(unwired)),
|
||||
slots: () => slots,
|
||||
|
||||
+17
-6
@@ -37,11 +37,22 @@ def controllable(spec) -> bool:
|
||||
return bool(spec.provides.get("modifier") or spec.provides.get("sampler_modifier"))
|
||||
|
||||
|
||||
def all_off(settings) -> bool:
|
||||
"""The project's "disable all enhancements" switch: every module that modifies a run sits out, so a
|
||||
run shows what the plain pipeline does (an A/B test of whether an enhancement does anything)."""
|
||||
raw = settings.get("_off") if isinstance(settings, dict) else None
|
||||
return isinstance(raw, dict) and raw.get("all") is True
|
||||
|
||||
|
||||
def off_by_project(settings) -> set:
|
||||
"""Module ids the project turned off, read from the settings payload."""
|
||||
raw = settings.get("_off") if isinstance(settings, dict) else None
|
||||
ids = raw.get("modules") if isinstance(raw, dict) else None
|
||||
return {i for i in ids if isinstance(i, str)} if isinstance(ids, list) else set()
|
||||
off = {i for i in ids if isinstance(i, str)} if isinstance(ids, list) else set()
|
||||
if all_off(settings):
|
||||
from . import registry
|
||||
off |= {i for i, spec in registry.current().specs.items() if controllable(spec)}
|
||||
return off
|
||||
|
||||
|
||||
def bad_off(raw) -> str | None:
|
||||
@@ -49,8 +60,8 @@ def bad_off(raw) -> str | None:
|
||||
if raw is None:
|
||||
return None
|
||||
ids = raw.get("modules") if isinstance(raw, dict) else None
|
||||
if not isinstance(ids, list) or not all(isinstance(i, str) for i in ids):
|
||||
return '"_off" must be {"modules": ["module_id", ...]}'
|
||||
if not isinstance(ids, list) or not all(isinstance(i, str) for i in ids) or not isinstance(raw.get("all", False), bool):
|
||||
return '"_off" must be {"modules": ["module_id", ...], "all": true|false}'
|
||||
return None
|
||||
|
||||
|
||||
@@ -192,13 +203,13 @@ def start_failed(spec, exc: BaseException) -> None:
|
||||
def partition(specs: Iterable, settings) -> Tuple[List, List[str]]:
|
||||
"""(kept, notes): `specs` without the ones the project turned off or that are quarantined."""
|
||||
specs = list(specs)
|
||||
off = off_by_project(settings)
|
||||
off, everything = off_by_project(settings), all_off(settings)
|
||||
held = quarantined(specs)
|
||||
kept, notes = [], []
|
||||
for spec in specs:
|
||||
if controllable(spec):
|
||||
if spec.id in off:
|
||||
notes.append(f"{spec.id}: turned off for this project")
|
||||
if spec.id in off or everything:
|
||||
notes.append(f"{spec.id}: " + ("off -- all enhancements are disabled" if all_off(settings) else "turned off for this project"))
|
||||
continue
|
||||
if spec.id in held:
|
||||
notes.append(f"{spec.id}: OFF -- it failed on {held[spec.id].get('when', '?')} "
|
||||
|
||||
@@ -118,3 +118,10 @@ def test_a_module_that_cannot_run_here_is_said_but_not_quarantined():
|
||||
assert control.fingerprint() == ""
|
||||
control.start_failed(spec("a"), ValueError("a real bug"))
|
||||
assert control.fingerprint() == "a"
|
||||
|
||||
|
||||
def test_disable_all_enhancements_sits_out_every_modifier_and_nothing_else():
|
||||
kept, notes = control.partition([spec("a"), spec("loader", ("routes",))], {"_off": {"all": True}})
|
||||
assert [s.id for s in kept] == ["loader"] and "all enhancements" in notes[0]
|
||||
assert control.all_off({"_off": {"all": True}}) and not control.all_off({"_off": {"all": "yes"}}) and not control.all_off({})
|
||||
assert control.bad_off({"modules": [], "all": "yes"}) and control.bad_off({"modules": [], "all": True}) is None
|
||||
|
||||
@@ -4,7 +4,7 @@ import hashlib
|
||||
|
||||
from comfy_api.latest import io
|
||||
|
||||
from ..._core import log, registry as registry_mod, schema as schema_mod, shortcuts
|
||||
from ..._core import control as control_mod, log, registry as registry_mod, schema as schema_mod, shortcuts
|
||||
from . import engine, memory
|
||||
|
||||
ID = "conditioning_shot_camera"
|
||||
@@ -13,7 +13,7 @@ ID = "conditioning_shot_camera"
|
||||
def _values(settings):
|
||||
"""This module's settings from the pipeline's settings object, checked against its own declaration."""
|
||||
spec = registry_mod.current().specs.get(ID)
|
||||
if spec is None:
|
||||
if spec is None or control_mod.all_off(settings): # "disable all enhancements" is a switch for this too
|
||||
return {}
|
||||
clean, problems = schema_mod.check_values(spec, (settings or {}).get(ID))
|
||||
for problem in problems or ():
|
||||
|
||||
Reference in New Issue
Block a user