Skip to content

Commit

Permalink
added fast lora button to all workflows that support lora.
Browse files Browse the repository at this point in the history
  • Loading branch information
diStyApps committed Oct 28, 2024
1 parent 79a93ca commit 9584ce1
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 18 deletions.
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {}
APP_CONFIGS: List[AppConfig] = []
APP_NAME: str = "Flow"
APP_VERSION: str = "0.1.4"
APP_VERSION: str = "0.1.5"
PURPLE = "\033[38;5;129m"
RESET = "\033[0m"
FLOWMSG = f"{PURPLE}Flow{RESET}"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "comfyui-disty-flow"
description = "Flow is a custom node designed to provide a more user-friendly interface for ComfyUI by acting as an alternative user interface for running workflows. It is not a replacement for workflow creation.\nFlow is currently in the early stages of development, so expect bugs and ongoing feature enhancements. With your support and feedback, Flow will settle into a steady stream."
version = "0.1.4"
version = "0.1.5"
license = {file = "LICENSE"}

[project.urls]
Expand Down
15 changes: 15 additions & 0 deletions web/core/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -1350,3 +1350,18 @@ html:not(.css-loading) body {
max-height: 100%;
border: 1px dashed var(--color-border);
}

.add-lora-button {
background-color: var(--color-button-secondary);
color: var(--color-button-secondary-text);
/* padding: 10px; */
/* margin: 0 10px; */
border: none;
cursor: pointer;
transition: background-color 0.3s ease, color 0.3s ease;
width: 100%;
}
.add-lora-button:hover {
background-color: var(--color-button-secondary-hover);
color: var(--color-button-secondary-text-hover);
}
1 change: 1 addition & 0 deletions web/core/js/common/components/DropdownStepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class DropdownStepper {

this.createDropdown();
this.createSteppers();
// console.log('DropdownStepper initialized:', config);
}

createDropdown() {
Expand Down
214 changes: 198 additions & 16 deletions web/core/main.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@

import Seeder from "./js/common/components/Seeder.js"
import Stepper from "./js/common/components/Stepper.js"
import MultiStepper from "./js/common/components/MultiStepper.js"
import DropdownStepper from "./js/common/components/DropdownStepper.js"
import DropdownStepper from "./js/common/components/DropdownStepper.js"
import DimensionSelector from './js/common/components/DimSelector.js';
import Dropdown from './js/common/components/Dropdown.js';
import imageLoaderComp from './js/common/components/imageLoaderComp.js';
Expand All @@ -22,8 +21,8 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
(async (window, document, undefined) => {

const client_id = uuidv4();
const flowConfig = await fetchflowConfig();
const workflow = await fetchWorkflow();
const flowConfig = await fetchflowConfig();
let workflow = await fetchWorkflow();
const seeders = [];
let jobQueue = [];
let currentJobId = 0;
Expand All @@ -33,9 +32,8 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
injectStylesheet('/core/css/main.css', 'main');
injectStylesheet('/core/css/themes.css', 'themes-stylesheet');

console.log("flowConfig",flowConfig)
console.log("workflow",workflow)

console.log("flowConfig", flowConfig)
console.log("workflow", workflow)

const defaultPreferences = {
selectedCategories: [],
Expand All @@ -52,6 +50,183 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
const themeManager = new ThemeManager(preferencesManager);
themeManager.init();


class LoraWorkflowManager {

constructor(workflow, flowConfig) {
this.workflowManager = new WorkflowNodeAdder(workflow);
this.flowConfig = flowConfig;
this.container = document.getElementById('side-workflow-controls');
this.addButton = null;
this.initializeUI();
}

initializeUI() {
this.addButton = document.createElement('button');
this.addButton.textContent = 'Add LoRA';
this.addButton.classList.add('add-lora-button');
this.addButton.style.marginBottom = '10px';
this.container.appendChild(this.addButton);

this.addButton.addEventListener('click', () => this.handleAddLora());
// this.flowConfig.dropdownSteppers.forEach(dropdownStepperConfig => {
// new DropdownStepper(dropdownStepperConfig, workflow);
// });

// this.flowConfig.dropdownSteppers.forEach(dropdownStepper => {
// const div = document.createElement('div');
// div.id = dropdownStepper.id;
// div.classList.add('dropdown-stepper-container');
// this.container.appendChild(div);
// });
}

handleAddLora() {
try {
const newNodeId = this.workflowManager.addLora();

const dynamicConfig = this.createDynamicConfig(newNodeId);

const loraContainer = document.createElement('div');
loraContainer.id = dynamicConfig.id;
loraContainer.classList.add('dropdown-stepper-container');
this.container.appendChild(loraContainer);

new DropdownStepper(dynamicConfig, this.workflowManager.getWorkflow());

// console.log(`LoRA added with Node ID: ${newNodeId}`);
} catch (error) {
console.error('Error adding LoRA:', error);
}
}

createDynamicConfig(nodeId) {
return {
id: `LoraLoader_${nodeId}`,
label: "LoRA",
dropdown: {
url: "LoraLoaderModelOnly",
key: "lora_name",
nodePath: `${nodeId}.inputs.lora_name`
},
steppers: [
{
id: `lorastrength_${nodeId}`,
label: "Strength",
minValue: 0,
maxValue: 5,
step: 0.01,
defValue: 1,
precision: 2,
scaleFactor: 1,
container: `lorastrength_container_${nodeId}`,
nodePath: `${nodeId}.inputs.strength_model`
}
]
};
}

getWorkflow() {
return this.workflowManager.getWorkflow();
}
}

class WorkflowNodeAdder {

constructor(workflow) {
if (typeof workflow !== 'object' || workflow === null || Array.isArray(workflow)) {
throw new TypeError('Workflow must be a non-null object');
}
this.workflow = { ...workflow };
this.existingIds = new Set(Object.keys(this.workflow).map(id => parseInt(id, 10)));
this.highestId = this._getHighestNodeId();
this.loraCount = this._countExistingLoras();
}

addLora() {
const newLoraId = this._getNextNodeId();
const loraNode = this._createLoraNode(newLoraId);

const existingLoras = this._findLoraNodes();

if (existingLoras.length === 0) {
const modelLoaders = this._findModelLoaders();
if (modelLoaders.length === 0) {
throw new Error('No model loader found in the workflow to attach LoRA');
}

modelLoaders.forEach(loader => {
const originalModelInput = loader.inputs.model;
loader.inputs.model = [newLoraId.toString(), 0];
loraNode.inputs.model = originalModelInput;
});
} else {
const lastLora = existingLoras[existingLoras.length - 1];
const originalModelInput = lastLora.inputs.model;
lastLora.inputs.model = [newLoraId.toString(), 0];
loraNode.inputs.model = originalModelInput;
}

this.workflow[newLoraId.toString()] = loraNode;
this.existingIds.add(newLoraId);
this.highestId = newLoraId;
this.loraCount += 1;

return newLoraId;
}

getWorkflow() {
return this.workflow;
}

_createLoraNode(id) {
return {
inputs: {
lora_name: "lora.safetensors",
strength_model: 1,
model: []
},
class_type: "LoraLoaderModelOnly",
_meta: {
title: "LoraLoaderModelOnly"
}
};
}


_findLoraNodes() {
return Object.entries(this.workflow)
.filter(([_, node]) => node.class_type === "LoraLoaderModelOnly")
.map(([id, node]) => ({ id: parseInt(id, 10), ...node }));
}

_findModelLoaders() {
const modelLoaders = [];

Object.entries(this.workflow).forEach(([id, node]) => {
if (node.inputs && Array.isArray(node.inputs.model) && node.inputs.model.length === 2) {
modelLoaders.push({ id: parseInt(id, 10), ...node });
}
});

return modelLoaders;
}

_getNextNodeId() {
return this.highestId + 1;
}

_getHighestNodeId() {
return Math.max(...this.existingIds, 0);
}

_countExistingLoras() {
return this._findLoraNodes().length;
}
}



function generateWorkflowControls(config) {
const container = document.getElementById('side-workflow-controls');

Expand Down Expand Up @@ -121,27 +296,27 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
promptsContainer.appendChild(titleDiv);
});
}

function generateDynamicScriptDefault(index) {
const defaultsPrompts = [
'A cartoon happy goat with purple eyes and a black horn in the jungle',
'ugly, blur, jpeg artifacts, low quality, lowres, child',
];
return defaultsPrompts[index] || '';
}

generateWorkflowControls(flowConfig);
generateWorkflowInputs(flowConfig,true);

generateWorkflowControls(flowConfig);
generateWorkflowInputs(flowConfig, true);
const loraWorkflowManager = new LoraWorkflowManager(workflow, flowConfig);
workflow = loraWorkflowManager.getWorkflow();
processWorkflowNodes(workflow).then(({ nodeToCustomNodeMap, uniqueCustomNodesArray, missingNodes, missingCustomPackages }) => {
console.log("Node to Custom Node Mapping:", nodeToCustomNodeMap);
console.log("Unique Custom Nodes:", uniqueCustomNodesArray);
console.log("Missing Nodes:", missingNodes);
console.log("Missing Custom Packages:", missingCustomPackages);
checkAndShowMissingPackagesDialog(missingCustomPackages, missingNodes,flowConfig);
checkAndShowMissingPackagesDialog(missingCustomPackages, missingNodes, flowConfig);
});


flowConfig.dimensionSelectors.forEach(config => {
new DimensionSelector(config, workflow);
});
Expand All @@ -167,12 +342,12 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
new DropdownStepper(config, workflow);
});

imageLoaderComp(flowConfig,workflow);
imageLoaderComp(flowConfig, workflow);

async function queue() {
flowConfig.workflowInputs.forEach(pathConfig => {
const { id } = pathConfig;
const element = document.getElementById(id); // Removed .toLowerCase()
const element = document.getElementById(id);
if (element) {
const value = element.value.replace(/(\r\n|\n|\r)/gm, " ");
updateWorkflowValue(workflow, id, value, flowConfig);
Expand Down Expand Up @@ -224,6 +399,12 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
body: JSON.stringify(data)
});

if (!response.ok) {
throw new Error('Failed to process prompt.');
}

const result = await response.json();
// console.log('Prompt processed:', result);
} catch (error) {
console.error('Error processing prompt:', error);
throw error;
Expand Down Expand Up @@ -283,4 +464,5 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
overlay.style.display = 'none';
});
});
})(window, document, undefined);

})(window, document, undefined);

0 comments on commit 9584ce1

Please sign in to comment.