Skip to content

Commit 302b8bc

Browse files
AustinMrozchristian-byrne
authored andcommitted
Fix cyclic prototype errors with subgraphNodes (#5637)
To accomplish this, it pulls WidgetLocator information from the node owning the widget. This `node` property does not exist on all IBaseWidget. `toConcrete` was used to instead have a BaseWidget which is guaranteed to have a node property. The issue that was missed, is that a widget which lacks this information (such as most implemented by custom nodes) sets the node value to the argument which was passed. Here that is the reference to the subgraph node. Sometimes, this `#setWidget` call is made multiple times, and when this occurs, the `input.widget` has itself set as the protoyep, throwing an error. This is resolved by instead taking an additional input which is unambiguous. For reference, this is a near minimal workflow using comfy_mtb that replicates the issue [cyclic.json](https://github.com/user-attachments/files/22412187/cyclic.json) Special thanks to @melMass for assistance discovering this issue. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5637-Fix-cyclic-prototype-errors-with-subgraphNodes-2726d73d365081fea356f5197e4c2b42) by [Unito](https://www.unito.io)
1 parent db80bea commit 302b8bc

File tree

1 file changed

+21
-16
lines changed

1 file changed

+21
-16
lines changed

src/lib/litegraph/src/subgraph/SubgraphNode.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
44
import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
55
import { LLink, type ResolvedConnection } from '@/lib/litegraph/src/LLink'
66
import { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'
7-
import type { ISubgraphInput } from '@/lib/litegraph/src/interfaces'
8-
import {
9-
type INodeInputSlot,
10-
type ISlotType,
11-
type NodeId
7+
import type {
8+
ISubgraphInput,
9+
IWidgetLocator
10+
} from '@/lib/litegraph/src/interfaces'
11+
import type {
12+
INodeInputSlot,
13+
ISlotType,
14+
NodeId
1215
} from '@/lib/litegraph/src/litegraph'
1316
import { NodeInputSlot } from '@/lib/litegraph/src/node/NodeInputSlot'
1417
import { NodeOutputSlot } from '@/lib/litegraph/src/node/NodeOutputSlot'
@@ -78,9 +81,10 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
7881
const existingInput = this.inputs.find((i) => i.name == name)
7982
if (existingInput) {
8083
const linkId = subgraphInput.linkIds[0]
81-
const { inputNode } = subgraph.links[linkId].resolve(subgraph)
84+
const { inputNode, input } = subgraph.links[linkId].resolve(subgraph)
8285
const widget = inputNode?.widgets?.find?.((w) => w.name == name)
83-
if (widget) this.#setWidget(subgraphInput, existingInput, widget)
86+
if (widget)
87+
this.#setWidget(subgraphInput, existingInput, widget, input?.widget)
8488
return
8589
}
8690
const input = this.addInput(name, type)
@@ -185,13 +189,14 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
185189

186190
subgraphInput.events.addEventListener(
187191
'input-connected',
188-
() => {
192+
(e) => {
189193
if (input._widget) return
190194

191195
const widget = subgraphInput._widget
192196
if (!widget) return
193197

194-
this.#setWidget(subgraphInput, input, widget)
198+
const widgetLocator = e.detail.input.widget
199+
this.#setWidget(subgraphInput, input, widget, widgetLocator)
195200
},
196201
{ signal }
197202
)
@@ -301,7 +306,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
301306
const widget = resolved.inputNode.getWidgetFromSlot(resolved.input)
302307
if (!widget) continue
303308

304-
this.#setWidget(subgraphInput, input, widget)
309+
this.#setWidget(subgraphInput, input, widget, resolved.input.widget)
305310
break
306311
}
307312
}
@@ -310,11 +315,13 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
310315
#setWidget(
311316
subgraphInput: Readonly<SubgraphInput>,
312317
input: INodeInputSlot,
313-
widget: Readonly<IBaseWidget>
318+
widget: Readonly<IBaseWidget>,
319+
inputWidget: IWidgetLocator | undefined
314320
) {
315321
// Use the first matching widget
316-
const targetWidget = toConcreteWidget(widget, this)
317-
const promotedWidget = targetWidget.createCopyForNode(this)
322+
const promotedWidget = toConcreteWidget(widget, this).createCopyForNode(
323+
this
324+
)
318325

319326
Object.assign(promotedWidget, {
320327
get name() {
@@ -372,11 +379,9 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
372379
// NOTE: This code creates linked chains of prototypes for passing across
373380
// multiple levels of subgraphs. As part of this, it intentionally avoids
374381
// creating new objects. Have care when making changes.
375-
const backingInput =
376-
targetWidget.node.findInputSlot(widget.name, true)?.widget ?? {}
377382
input.widget ??= { name: subgraphInput.name }
378383
input.widget.name = subgraphInput.name
379-
Object.setPrototypeOf(input.widget, backingInput)
384+
if (inputWidget) Object.setPrototypeOf(input.widget, inputWidget)
380385

381386
input._widget = promotedWidget
382387
}

0 commit comments

Comments
 (0)