basic functionality working

This commit is contained in:
Sammy Franklin 2023-10-09 16:10:18 -07:00
parent e268692351
commit 5335249cd4
3 changed files with 93 additions and 23 deletions

View File

@ -8,10 +8,21 @@ app.registerExtension({
if (!node.widgets) return;
if (node.widgets[0].name !== "subflow_name") return;
const refreshPins = (subflowNodes) => {
let outputSlots = []; // (int (node), int (slot))
let inputSlots = [];
const refreshPins = async (subflowName) => {
const subflowData = await api.getSubflow(subflowName);
if (!subflowData.subflow) return;
inputSlots = [];
outputSlots = [];
const subflowNodes = subflowData.subflow.nodes
updateSubflowPrompt(subflowData.subflow);
// remove all existing pins
const numInputs = node.inputs.length;
const numOutputs = node.outputs.length;
const numInputs = node.inputs?.length ?? 0;
const numOutputs = node.outputs?.length ?? 0;
for(let i = numInputs-1; i > -1; i--) {
node.removeInput(i);
}
@ -22,33 +33,41 @@ app.registerExtension({
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;
node.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;
node.addOutput(output.name, output.type);
outputSlots.push([subflowNode.id, pinNum]);
pinNum++;
}
}
}
};
node.onConfigure = async function () {
const subflowData = await api.getSubflow(node.widgets[0].value);
if (subflowData.subflow) {
refreshPins(subflowData.subflow.nodes);
}
};
const updateSubflowPrompt = (subflow) => {
node.subflow = subflow;
};
node.widgets[0].callback = async function (subflowName) {
const subflowData = await api.getSubflow(subflowName);
if (subflowData.subflow) {
refreshPins(subflowData.subflow.nodes);
}
};
// node.onSerialize = () =>
node.onConfigure = () => refreshPins(node.widgets[0].value);
node.widgets[0].callback = (subflowName) => refreshPins(subflowName);
node.getExportedOutput = (slot) => {
return outputSlots[slot];
};
node.getExportedInput = (slot) => {
return inputSlots[slot];
};
}
});

View File

@ -2618,6 +2618,9 @@
if (this.shape) {
o.shape = this.shape;
}
if (this.subflow) {
o.subflow = this.subflow;
}
if (this.onSerialize) {
if (this.onSerialize(o)) {

View File

@ -1467,15 +1467,21 @@ export class ComfyApp {
}
}
/**
* Converts the current graph workflow for sending to the API
* @returns The workflow and node links
*/
async graphToPrompt() {
const workflow = this.graph.serialize();
async graphToPrompt(graph=this.graph) {
let subflowNodeIdOffset = graph.last_node_id;
const subflowIdMapping = {};
const workflow = graph.serialize();
const output = {};
// Process nodes in order of execution
for (const node of this.graph.computeExecutionOrder(false)) {
for (const node of graph.computeExecutionOrder(false)) {
const n = workflow.nodes.find((n) => n.id === node.id);
if (node.isVirtualNode) {
@ -1491,6 +1497,28 @@ export class ComfyApp {
continue;
}
if (node.subflow) {
const subgraph = new LGraph();
subgraph.configure(node.subflow);
const subgraphPrompt = (await this.graphToPrompt(subgraph)).output;
// replace ids to not conflict with existing ids
for ( const [key, value] of Object.entries(subgraphPrompt) ) {
for ( const [inputKey, inputValue] of Object.entries(value.inputs) ) {
if (Array.isArray(inputValue)) {
value.inputs[inputKey][0] = String(Number(value.inputs[inputKey][0]) + subflowNodeIdOffset);
}
}
output[String(Number(key) + subflowNodeIdOffset)] = {
...value,
for_subflow: String(node.id) // keep reference of parent node
};
}
subflowIdMapping[node.id] = subflowNodeIdOffset;
subflowNodeIdOffset += Object.keys(node.subflow).length;
}
const inputs = {};
const widgets = node.widgets;
@ -1504,6 +1532,17 @@ export class ComfyApp {
}
}
const getInputRef = (inputNode, inputSlot) => {
if (inputNode.type == "Subflow") {
// input should be mapped to inner node
const [ localOriginId, originSlot ] = inputNode.getExportedOutput(inputSlot);
const originId = subflowIdMapping[inputNode.id] + localOriginId;
return [String(originId), parseInt(originSlot)];
}
return [String(inputNode.id), parseInt(inputSlot)];
};
// Store all node links
for (let i in node.inputs) {
let parent = node.getInputNode(i);
@ -1543,15 +1582,24 @@ export class ComfyApp {
}
if (link) {
inputs[node.inputs[i].name] = [String(link.origin_id), parseInt(link.origin_slot)];
if (node.type == "Subflow") {
// inner node's input should be used
const [ localTargetId, targetSlot ] = node.getExportedInput(link.target_slot);
const targetId = subflowIdMapping[node.id] + localTargetId;
output[ String(targetId) ].inputs[ node.inputs[targetSlot].name ] = getInputRef(parent, link.origin_slot);
}
inputs[node.inputs[i].name] = getInputRef(parent, link.origin_slot);
}
}
}
output[String(node.id)] = {
inputs,
class_type: node.comfyClass,
};
if (node.type != "Subflow") {
output[String(node.id)] = {
inputs,
class_type: node.comfyClass,
};
}
}
// Remove inputs connected to removed nodes