Serialize compact LoRA rows as a list
This commit is contained in:
+53
-15
@@ -159,6 +159,7 @@ class FunPackApplyLoraWeights:
|
||||
optional = FlexibleOptionalInputType(
|
||||
any_type,
|
||||
{
|
||||
"lora_list": ("STRING", {"default": "[]", "multiline": False}),
|
||||
"lora_0": (loras, {"default": "None"}),
|
||||
"lora_0_type": (LORA_TYPES, {"default": "general"}),
|
||||
"lora_0_base_weight": (
|
||||
@@ -194,6 +195,10 @@ class FunPackApplyLoraWeights:
|
||||
def IS_CHANGED(cls, **kwargs):
|
||||
return float("nan")
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, lora_list=None, lora_0=None):
|
||||
return True
|
||||
|
||||
def _load_suggestions(self, refinement_key, mode, prompt_key):
|
||||
path = refiner_state_path(refinement_key, mode)
|
||||
if not os.path.exists(path):
|
||||
@@ -215,7 +220,52 @@ class FunPackApplyLoraWeights:
|
||||
|
||||
return suggestions, "refiner suggestions applied"
|
||||
|
||||
def _entry_from_row(self, index, row):
|
||||
if not isinstance(row, dict):
|
||||
return None
|
||||
if not row.get("on", True):
|
||||
return None
|
||||
|
||||
name = row.get("lora", row.get("name", "None"))
|
||||
if not name or name == "None":
|
||||
return None
|
||||
|
||||
lora_type = row.get("type", row.get("lora_type", "general"))
|
||||
if lora_type not in LORA_TYPES:
|
||||
lora_type = "general"
|
||||
|
||||
return {
|
||||
"slot": index,
|
||||
"name": name,
|
||||
"type": lora_type,
|
||||
"id": lora_state_id(name, lora_type),
|
||||
"base_model_weight": safe_float(row.get("strength", row.get("base_weight", 1.0))),
|
||||
}
|
||||
|
||||
def _iter_lora_list(self, value):
|
||||
if value in (None, "", "[]"):
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
value = json.loads(value)
|
||||
except (json.JSONDecodeError, TypeError, ValueError):
|
||||
return None
|
||||
if not isinstance(value, list):
|
||||
return None
|
||||
|
||||
entries = []
|
||||
for index, row in enumerate(value):
|
||||
entry = self._entry_from_row(index, row)
|
||||
if entry:
|
||||
entries.append(entry)
|
||||
return entries
|
||||
|
||||
def _iter_slots(self, kwargs):
|
||||
listed_entries = self._iter_lora_list(kwargs.get("lora_list"))
|
||||
if listed_entries is not None:
|
||||
yield from listed_entries
|
||||
return
|
||||
|
||||
indexed_slots = set()
|
||||
for key in kwargs:
|
||||
match = re.fullmatch(r"lora_(\d+)", key)
|
||||
@@ -225,21 +275,9 @@ class FunPackApplyLoraWeights:
|
||||
for index in sorted(indexed_slots):
|
||||
lora_name = kwargs.get(f"lora_{index}", "None")
|
||||
if isinstance(lora_name, dict):
|
||||
if not lora_name.get("on", True):
|
||||
continue
|
||||
name = lora_name.get("lora", "None")
|
||||
if not name or name == "None":
|
||||
continue
|
||||
lora_type = lora_name.get("type", lora_name.get("lora_type", "general"))
|
||||
if lora_type not in LORA_TYPES:
|
||||
lora_type = "general"
|
||||
yield {
|
||||
"slot": index,
|
||||
"name": name,
|
||||
"type": lora_type,
|
||||
"id": lora_state_id(name, lora_type),
|
||||
"base_model_weight": safe_float(lora_name.get("strength", lora_name.get("base_weight", 1.0))),
|
||||
}
|
||||
entry = self._entry_from_row(index, lora_name)
|
||||
if entry:
|
||||
yield entry
|
||||
continue
|
||||
|
||||
shifted_base_weight = None
|
||||
|
||||
@@ -5,6 +5,7 @@ const NODE_NAME = "FunPackApplyLoraWeights";
|
||||
const LORA_TYPES = ["general", "concept", "style", "quality", "character"];
|
||||
const ADD_BUTTON_NAME = "+ Add LoRA";
|
||||
const HEADER_WIDGET_NAME = "funpack_lora_header";
|
||||
const LORA_LIST_WIDGET_NAME = "lora_list";
|
||||
const BASE_WIDGET_NAMES = new Set(["positive_prompt", "refinement_key", "mode", "per_block"]);
|
||||
|
||||
let cachedLoraValues = null;
|
||||
@@ -108,7 +109,11 @@ function inside(pos, bounds) {
|
||||
}
|
||||
|
||||
function getLoraValues(nodeData) {
|
||||
return cachedLoraValues || nodeData?.input?.optional?.lora_0?.[0] || ["None"];
|
||||
const inputType = nodeData?.input?.optional?.lora_0?.[0];
|
||||
const embeddedValues = Array.isArray(inputType)
|
||||
? inputType
|
||||
: nodeData?.input?.optional?.lora_0?.[1]?.available_loras;
|
||||
return cachedLoraValues || (Array.isArray(embeddedValues) ? embeddedValues : ["None"]);
|
||||
}
|
||||
|
||||
function rememberLoraValues(nodeData, values) {
|
||||
@@ -286,9 +291,28 @@ class FunPackLoraHeaderWidget extends FunPackBaseWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class FunPackLoraListWidget extends FunPackBaseWidget {
|
||||
constructor(node) {
|
||||
super(LORA_LIST_WIDGET_NAME);
|
||||
this.node = node;
|
||||
this.value = "[]";
|
||||
}
|
||||
|
||||
computeSize(width) {
|
||||
return [width, 0];
|
||||
}
|
||||
|
||||
draw() {}
|
||||
|
||||
serializeValue() {
|
||||
return JSON.stringify(getLoraRows(this.node).map((row) => row.serializeValue()));
|
||||
}
|
||||
}
|
||||
|
||||
class FunPackLoraRowWidget extends FunPackBaseWidget {
|
||||
constructor(name, value, nodeData) {
|
||||
super(name);
|
||||
this.options.serialize = false;
|
||||
this.value = normalizeRowValue(value);
|
||||
this.nodeData = nodeData;
|
||||
this.draggedWeight = false;
|
||||
@@ -494,6 +518,7 @@ function isDefaultLoraWidget(widget) {
|
||||
function isManagedWidget(widget) {
|
||||
return (
|
||||
isDefaultLoraWidget(widget) ||
|
||||
widget.name === LORA_LIST_WIDGET_NAME ||
|
||||
widget.name === HEADER_WIDGET_NAME ||
|
||||
widget.name === ADD_BUTTON_NAME ||
|
||||
widget.__funpackLoraRow === true
|
||||
@@ -611,6 +636,23 @@ function parseSerializedRows(values, baseCount) {
|
||||
}
|
||||
|
||||
const rowValues = values.slice(baseCount);
|
||||
for (const value of rowValues) {
|
||||
if (typeof value !== "string") {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
if (Array.isArray(parsed)) {
|
||||
const rows = parsed.map(normalizeRowValue);
|
||||
if (rows.length) {
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
} catch (_error) {
|
||||
// Older workflows stored rows as individual widget values.
|
||||
}
|
||||
}
|
||||
|
||||
const objectRows = rowValues
|
||||
.filter((value) => value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "lora"))
|
||||
.map(normalizeRowValue);
|
||||
@@ -637,6 +679,7 @@ function ensureCompactLoraUi(node, nodeData, info = null) {
|
||||
const rows = serializedRows.length ? serializedRows : defaultRows;
|
||||
|
||||
removeManagedWidgets(node);
|
||||
node.addCustomWidget(new FunPackLoraListWidget(node));
|
||||
node.addCustomWidget(new FunPackLoraHeaderWidget());
|
||||
if (rows.length) {
|
||||
for (const row of rows) {
|
||||
|
||||
Reference in New Issue
Block a user