inmemorysubflows

This commit is contained in:
Sammy Franklin 2023-10-10 21:40:23 -07:00
parent a5df9e8c12
commit 4b50c20ccb
4 changed files with 153 additions and 22 deletions

View File

@ -1,24 +1,13 @@
import folder_paths
import json
import os.path as osp
class Subflow:
@classmethod
def INPUT_TYPES(s):
return {"required": { "subflow_name": (folder_paths.get_filename_list("subflows"), ), }}
RETURN_TYPES = ()
FUNCTION = "exec_subflow"
FUNCTION = ""
CATEGORY = "loaders"
def exec_subflow(self, subflow_name):
subflow_path = folder_paths.get_full_path("subflows", subflow_name)
with open(subflow_path) as f:
if osp.splitext(subflow_path)[1] == ".json":
subflow_data = json.load(f)
return subflow_data
return None
NODE_CLASS_MAPPINGS = {
"Subflow": Subflow,

View File

@ -1,10 +1,80 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
class InMemorySubflow {
constructor() {}
onConfigure() {
console.log(this);
}
getExportedOutput(slot) {
return this.subflow.extras.outputSlots[slot];
};
getExportedInput(slot) {
return this.subflow.extras.inputSlots[slot];
};
async refreshNode(subflow) {
if (!subflow) return;
this.updateSubflowPrompt(subflow);
this.refreshPins(subflow);
}
refreshPins(subflow) {
if(!subflow)
return;
subflow.extras = { inputSlots: [], outputSlots: [] };
const { inputSlots } = subflow.extras;
const { outputSlots } = subflow.extras;
// remove all existing pins
const numInputs = this.inputs?.length ?? 0;
const numOutputs = this.outputs?.length ?? 0;
for(let i = numInputs-1; i > -1; i--) {
this.removeInput(i);
}
for(let i = numOutputs-1; i > -1; i--) {
this.removeOutput(i);
}
const subflowNodes = subflow.nodes;
// add the new pins and keep track of where the exported vars go to within the inner nodes
for (const subflowNode of subflowNodes) {
const exports = subflowNode.properties?.exports;
if (exports) {
let pinNum = 0;
for (const inputRef of exports.inputs) {
const input = subflowNode.inputs.find(q => q.name === inputRef);
if (!input) continue;
this.addInput(input.name, input.type);
inputSlots.push([subflowNode.id, pinNum]);
pinNum++;
}
pinNum = 0;
for (const outputRef of exports.outputs) {
const output = subflowNode.outputs.find(q => q.name === outputRef);
if (!output) continue;
this.addOutput(output.name, output.type);
outputSlots.push([subflowNode.id, pinNum]);
pinNum++;
}
}
}
this.size[0] = 180;
};
updateSubflowPrompt(subflow) {
this.subflow = subflow;
};
}
app.registerExtension({
name: "Comfy.Subflow",
async nodeCreated(node) {
if (!node.widgets) return;
if (node.widgets[0].name !== "subflow_name") return;
@ -68,8 +138,6 @@ app.registerExtension({
return Math.max(100, LiteGraph.NODE_TEXT_SIZE * subflowName.length * 0.45 + 160);
};
node.onAdded = () => updateSubflowPrompt(node.widgets[0].value);
node.onConfigure = () => updateSubflowPrompt(node.widgets[0].value);
node.widgets[0].callback = (subflowName) => refreshNode(subflowName);
node.getExportedOutput = (slot) => {
@ -79,7 +147,13 @@ app.registerExtension({
node.getExportedInput = (slot) => {
return inputSlots[slot];
};
refreshNode(node.widgets[0].value);
}
},
registerCustomNodes() {
LiteGraph.registerNodeType(
"InMemorySubflow",
Object.assign(InMemorySubflow, {
title: "InMemorySubflow",
})
);
},
});

View File

@ -13023,6 +13023,63 @@ LGraphNode.prototype.executeAction = function(action)
return false;
};
// LGraphCanvas.onDecoupleSubflow = function(value, options, e, menu, node) {
// const { subflow } = node;
// if (!subflow) return;
// const subgraph = new LGraph( subflow );
// for (const node of subgraph._nodes) {
// console.log(node);
// graph.add(node);
// }
// };
LGraphCanvas.onCollapseToSubflow = function(value, options, e, menu, node) {
const calculatePosition = (nodes) => {
const sum = [0, 0];
const N = nodes.length;
for (const node of nodes) {
sum[0] += node.pos[0];
sum[1] += node.pos[1];
}
return [Math.round(sum[0])/N, Math.round(sum[1]/N)];
};
var graphcanvas = LGraphCanvas.active_canvas;
if (graphcanvas.selected_nodes && Object.keys(graphcanvas.selected_nodes).length > 1){
const nodes = Object.values(graphcanvas.selected_nodes);
// New subflow is the current graph - non included nodes
const subgraph = new LGraph(graph.serialize());
const subgraphNodes = subgraph._nodes.map(n => n.id);
const nodesToKeep = nodes.map(n => n.id);
for (const n of subgraphNodes) {
if(!nodesToKeep.includes(n)) {
subgraph.remove(subgraph.getNodeById(n));
}
}
const subflow = subgraph.serialize();
const subflowNode = LiteGraph.createNode("InMemorySubflow");
subflowNode.updateSubflowPrompt(subflow);
subflowNode.pos = calculatePosition(subflow.nodes);
subflowNode.size[0] = 180;
subflowNode.refreshNode(subflow);
graph.add(subflowNode);
// remove selected nodes
for (const node of nodes) {
graph.remove(node);
}
}
console.log(graph._nodes);
return false;
};
LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) {
if (!node) {
throw "no node passed";
@ -13281,9 +13338,20 @@ LGraphNode.prototype.executeAction = function(action)
content: "Align Selected To",
has_submenu: true,
callback: LGraphCanvas.onNodeAlign,
})
});
options.push({
content: "Collapse to Subflow",
callback: LGraphCanvas.onCollapseToSubflow
});
}
// if (Object.keys(this.selected_nodes).length == 1 && node.subflow) {
// options.push({
// content: "Decouple Subflow",
// callback: LGraphCanvas.onDecoupleSubflow,
// });
// }
options.push(null, {
content: "Remove",
disabled: !(node.removable !== false && !node.block_delete ),

View File

@ -1533,7 +1533,7 @@ export class ComfyApp {
}
const getInputRef = (inputNode, inputSlot) => {
if (inputNode.type == "Subflow") {
if (inputNode.subflow) {
// input should be mapped to inner node
const [ localOriginId, originSlot ] = inputNode.getExportedOutput(inputSlot);
const originId = subflowIdMapping[inputNode.id] + localOriginId;
@ -1582,7 +1582,7 @@ export class ComfyApp {
}
if (link) {
if (node.type == "Subflow") {
if (node.subflow) {
// inner node's input should be used
const [ localTargetId, targetSlot ] = node.getExportedInput(link.target_slot);
const targetId = subflowIdMapping[node.id] + localTargetId;
@ -1594,7 +1594,7 @@ export class ComfyApp {
}
}
if (node.type != "Subflow") {
if (!node.subflow) {
output[String(node.id)] = {
inputs,
class_type: node.comfyClass,