@@ -11,12 +11,14 @@ import { LGraphEventMode } from "@/litegraph"
1111
1212import { Subgraph } from "./Subgraph"
1313
14+ export type ExecutionId = string
15+
1416/**
1517 * Interface describing the data transfer objects used when compiling a graph for execution.
1618 */
17- export type ExecutableLGraphNode = Omit < ExecutableNodeDTO , "graph" | "node" | "subgraphNodePath" | " subgraphNode">
19+ export type ExecutableLGraphNode = Omit < ExecutableNodeDTO , "graph" | "node" | "subgraphNode" >
1820
19- type NodeAndInput = {
21+ type ResolvedInput = {
2022 node : ExecutableLGraphNode
2123 origin_id : NodeId
2224 origin_slot : number
@@ -35,7 +37,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
3537 inputs : { linkId : number | null , name : string , type : ISlotType } [ ]
3638
3739 /** Backing field for {@link id}. */
38- #id: NodeId
40+ #id: ExecutionId
3941
4042 /**
4143 * The path to the acutal node through subgraph instances, represented as a list of all subgraph node IDs (instances),
@@ -79,6 +81,8 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
7981 readonly node : LGraphNode | SubgraphNode ,
8082 /** A list of subgraph instance node IDs from the root graph to the containing instance. @see {@link id} */
8183 readonly subgraphNodePath : readonly NodeId [ ] ,
84+ /** A flattened map of all DTOs in this node network. Subgraph instances have been expanded into their inner nodes. */
85+ readonly nodesByExecutionId : Map < ExecutionId , ExecutableLGraphNode > ,
8286 /** The actual subgraph instance that contains this node, otherise undefined. */
8387 readonly subgraphNode ?: SubgraphNode ,
8488 ) {
@@ -101,7 +105,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
101105
102106 /** Returns either the DTO itself, or the DTOs of the inner nodes of the subgraph. */
103107 getInnerNodes ( ) : ExecutableLGraphNode [ ] {
104- return this . subgraphNode ? this . subgraphNode . getInnerNodes ( ) : [ this ]
108+ return this . subgraphNode ? this . subgraphNode . getInnerNodes ( this . nodesByExecutionId , this . subgraphNodePath ) : [ this ]
105109 }
106110
107111 /**
@@ -112,7 +116,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
112116 * If overriding, ensure that the set is passed on all recursive calls.
113117 * @returns The node and the origin ID / slot index of the output.
114118 */
115- resolveInput ( slot : number , visited = new Set < string > ( ) ) : NodeAndInput | undefined {
119+ resolveInput ( slot : number , visited = new Set < string > ( ) ) : ResolvedInput | undefined {
116120 const uniqueId = `${ this . subgraphNode ?. subgraph . id } :${ this . node . id } [I]${ slot } `
117121 if ( visited . has ( uniqueId ) ) throw new RecursionError ( `While resolving subgraph input [${ uniqueId } ]` )
118122 visited . add ( uniqueId )
@@ -140,18 +144,20 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
140144 const outerLink = subgraphNode . graph . getLink ( linkId )
141145 if ( ! outerLink ) throw new InvalidLinkError ( `No outer link found for slot [${ link . origin_slot } ] ${ input . name } ` )
142146
143- // Translate subgraph node IDs to instances (not worth optimising yet)
144- const subgraphNodes = this . graph . rootGraph . resolveSubgraphIdPath ( this . subgraphNodePath )
147+ const subgraphNodeExecutionId = this . subgraphNodePath . join ( ":" )
148+ const subgraphNodeDto = this . nodesByExecutionId . get ( subgraphNodeExecutionId )
149+ if ( ! subgraphNodeDto ) throw new Error ( `No subgraph node DTO found for id [${ subgraphNodeExecutionId } ]` )
145150
146- const subgraphNodeDto = new ExecutableNodeDTO ( subgraphNode , this . subgraphNodePath . slice ( 0 , - 1 ) , subgraphNodes . at ( - 2 ) )
147151 return subgraphNodeDto . resolveInput ( outerLink . target_slot , visited )
148152 }
149153
150154 // Not part of a subgraph; use the original link
151155 const outputNode = this . graph . getNodeById ( link . origin_id )
152156 if ( ! outputNode ) throw new InvalidLinkError ( `No input node found for id [${ this . id } ] slot [${ slot } ] ${ input . name } ` )
153157
154- const outputNodeDto = new ExecutableNodeDTO ( outputNode , this . subgraphNodePath , subgraphNode )
158+ const outputNodeExecutionId = [ ...this . subgraphNodePath , outputNode . id ] . join ( ":" )
159+ const outputNodeDto = this . nodesByExecutionId . get ( outputNodeExecutionId )
160+ if ( ! outputNodeDto ) throw new Error ( `No output node DTO found for id [${ outputNodeExecutionId } ]` )
155161
156162 return outputNodeDto . resolveOutput ( link . origin_slot , input . type , visited )
157163 }
@@ -163,7 +169,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
163169 * @param visited A set of unique IDs to guard against infinite recursion. See {@link resolveInput}.
164170 * @returns The node and the origin ID / slot index of the output.
165171 */
166- resolveOutput ( slot : number , type : ISlotType , visited : Set < string > ) : NodeAndInput | undefined {
172+ resolveOutput ( slot : number , type : ISlotType , visited : Set < string > ) : ResolvedInput | undefined {
167173 const uniqueId = `${ this . subgraphNode ?. subgraph . id } :${ this . node . id } [O]${ slot } `
168174 if ( visited . has ( uniqueId ) ) throw new RecursionError ( `While resolving subgraph output [${ uniqueId } ]` )
169175 visited . add ( uniqueId )
@@ -200,7 +206,9 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
200206 const outputNode = this . graph . getNodeById ( virtualLink . origin_id )
201207 if ( ! outputNode ) throw new InvalidLinkError ( `Virtual node failed to resolve parent [${ this . id } ] slot [${ slot } ]` )
202208
203- const outputNodeDto = new ExecutableNodeDTO ( outputNode , this . subgraphNodePath , this . subgraphNode )
209+ const outputNodeExecutionId = [ ...this . subgraphNodePath , outputNode . id ] . join ( ":" )
210+ const outputNodeDto = this . nodesByExecutionId . get ( outputNodeExecutionId )
211+ if ( ! outputNodeDto ) throw new Error ( `No output node DTO found for id [${ outputNode . id } ]` )
204212
205213 return outputNodeDto . resolveOutput ( virtualLink . origin_slot , type , visited )
206214 }
@@ -222,7 +230,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
222230 * @param visited A set of unique IDs to guard against infinite recursion. See {@link resolveInput}.
223231 * @returns A DTO for the node, and the origin ID / slot index of the output.
224232 */
225- #resolveSubgraphOutput( slot : number , type : ISlotType , visited : Set < string > ) : NodeAndInput | undefined {
233+ #resolveSubgraphOutput( slot : number , type : ISlotType , visited : Set < string > ) : ResolvedInput | undefined {
226234 const { node } = this
227235 const output = node . outputs . at ( slot )
228236
@@ -237,7 +245,10 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
237245 if ( ! innerNode ) throw new Error ( `No output node found for id [${ this . id } ] slot [${ slot } ] ${ output . name } ` )
238246
239247 // Recurse into the subgraph
240- const innerNodeDto = new ExecutableNodeDTO ( innerNode , [ ...this . subgraphNodePath , node . id ] , node )
248+ const innerNodeExecutionId = [ ...this . subgraphNodePath , node . id , innerNode . id ] . join ( ":" )
249+ const innerNodeDto = this . nodesByExecutionId . get ( innerNodeExecutionId )
250+ if ( ! innerNodeDto ) throw new Error ( `No inner node DTO found for id [${ innerNodeExecutionId } ]` )
251+
241252 return innerNodeDto . resolveOutput ( innerResolved . link . origin_slot , type , visited )
242253 }
243254}
0 commit comments