feat: add LoRA weight arrays UI for comparing strengths
New "+ Compare Strengths" button next to each LoRA's Model Strength slider. Enter comma-separated values (e.g., 0.5, 0.8, 1.0) to grid search different LoRA strengths. Outputs bracket notation in the config JSON (e.g., lora.safetensors:[0.5, 0.8, 1.0]:1.0) which the existing Python expansion code handles as Cartesian products. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
36719ade3f
commit
c2fadff0e1
@@ -1583,6 +1583,14 @@ export function createLoraElement(node, loraStr, arrayIdx, loraIdx, availableLor
|
||||
);
|
||||
contentDiv.appendChild(nameSearchable);
|
||||
|
||||
// Check if this LoRA has weight arrays stored
|
||||
if (!node.state.config_arrays[arrayIdx].lora_weight_arrays) {
|
||||
node.state.config_arrays[arrayIdx].lora_weight_arrays = {};
|
||||
}
|
||||
const weightArrays = node.state.config_arrays[arrayIdx].lora_weight_arrays;
|
||||
const hasModelArray = weightArrays[parsed.name + "_model"] && weightArrays[parsed.name + "_model"].length > 1;
|
||||
const hasClipArray = weightArrays[parsed.name + "_clip"] && weightArrays[parsed.name + "_clip"].length > 1;
|
||||
|
||||
const modelSlider = createSlider("Model Strength", currentModelStr, 0, 2, 0.05, (val) => {
|
||||
currentModelStr = val;
|
||||
const currentName = isCombined ? cleanName + "*" : parsed.name;
|
||||
@@ -1605,6 +1613,53 @@ export function createLoraElement(node, loraStr, arrayIdx, loraIdx, availableLor
|
||||
});
|
||||
contentDiv.appendChild(modelSlider);
|
||||
|
||||
// Weight Array "+" button and editor for Model Strength
|
||||
const modelArrayDiv = document.createElement("div");
|
||||
modelArrayDiv.style.cssText = "margin: -4px 0 6px 0; display: flex; align-items: center; gap: 4px;";
|
||||
const modelArrayBtn = document.createElement("button");
|
||||
modelArrayBtn.textContent = hasModelArray ? "✕ Array" : "+ Compare Strengths";
|
||||
modelArrayBtn.className = "cb-btn";
|
||||
modelArrayBtn.style.cssText = "font-size: 9px; padding: 1px 6px; color: " + (hasModelArray ? "#f80" : "#0af") + ";";
|
||||
|
||||
const modelArrayInput = document.createElement("input");
|
||||
modelArrayInput.type = "text";
|
||||
modelArrayInput.placeholder = "e.g. 0.5, 0.8, 1.0";
|
||||
modelArrayInput.style.cssText = "flex: 1; background: #1a1a1a; color: #ccc; border: 1px solid #444; border-radius: 4px; padding: 2px 6px; font-size: 10px; display: " + (hasModelArray ? "block" : "none") + ";";
|
||||
modelArrayInput.value = hasModelArray ? weightArrays[parsed.name + "_model"].join(", ") : "";
|
||||
|
||||
modelArrayBtn.onclick = () => {
|
||||
if (hasModelArray || modelArrayInput.style.display !== "none") {
|
||||
// Remove array
|
||||
delete weightArrays[parsed.name + "_model"];
|
||||
if (isStrengthLocked) delete weightArrays[parsed.name + "_clip"];
|
||||
node.saveState();
|
||||
updatePreview(node);
|
||||
debouncedRenderUI(node);
|
||||
} else {
|
||||
// Show array input
|
||||
modelArrayInput.style.display = "block";
|
||||
modelArrayInput.value = currentModelStr.toFixed(2);
|
||||
modelArrayInput.focus();
|
||||
}
|
||||
};
|
||||
|
||||
modelArrayInput.onchange = () => {
|
||||
const vals = modelArrayInput.value.split(",").map(v => parseFloat(v.trim())).filter(v => !isNaN(v));
|
||||
if (vals.length > 1) {
|
||||
weightArrays[parsed.name + "_model"] = vals;
|
||||
if (isStrengthLocked) weightArrays[parsed.name + "_clip"] = vals;
|
||||
} else {
|
||||
delete weightArrays[parsed.name + "_model"];
|
||||
if (isStrengthLocked) delete weightArrays[parsed.name + "_clip"];
|
||||
}
|
||||
node.saveState();
|
||||
updatePreview(node);
|
||||
};
|
||||
|
||||
modelArrayDiv.appendChild(modelArrayBtn);
|
||||
modelArrayDiv.appendChild(modelArrayInput);
|
||||
contentDiv.appendChild(modelArrayDiv);
|
||||
|
||||
// CLIP Slider - conditionally visible based on lock state
|
||||
let clipSliderContainer = null;
|
||||
if (!isStrengthLocked) {
|
||||
|
||||
@@ -513,6 +513,23 @@ export function convertStateToConfigs(state) {
|
||||
return !configArray.lora_bypass_states?.[parsed.name];
|
||||
});
|
||||
|
||||
// Apply weight arrays (bracket notation) for LoRAs with multiple strengths
|
||||
const wa = configArray.lora_weight_arrays || {};
|
||||
loras = loras.map(l => {
|
||||
const parsed = parseLoraString(l);
|
||||
const modelArr = wa[parsed.name + "_model"];
|
||||
const clipArr = wa[parsed.name + "_clip"];
|
||||
if (modelArr && modelArr.length > 1) {
|
||||
const modelPart = "[" + modelArr.join(", ") + "]";
|
||||
const clipPart = (clipArr && clipArr.length > 1) ? "[" + clipArr.join(", ") + "]" : parsed.clip_str.toFixed(2);
|
||||
return parsed.name + ":" + modelPart + ":" + clipPart;
|
||||
}
|
||||
if (clipArr && clipArr.length > 1) {
|
||||
return parsed.name + ":" + parsed.model_str.toFixed(2) + ":[" + clipArr.join(", ") + "]";
|
||||
}
|
||||
return l;
|
||||
});
|
||||
|
||||
// Convert loras array to proper format
|
||||
let loraValue;
|
||||
if (loras.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user