Skip to content

Commit 36043a5

Browse files
committed
Add subgraph functionality to execution store
1 parent c4de069 commit 36043a5

File tree

2 files changed

+118
-11
lines changed

2 files changed

+118
-11
lines changed

src/stores/executionStore.ts

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { LGraph, Subgraph } from '@comfyorg/litegraph'
12
import { defineStore } from 'pinia'
23
import { computed, ref } from 'vue'
34

@@ -22,7 +23,7 @@ import type {
2223
import { api } from '@/scripts/api'
2324

2425
import { useCanvasStore } from './graphStore'
25-
import { ComfyWorkflow } from './workflowStore'
26+
import { ComfyWorkflow, useWorkflowStore } from './workflowStore'
2627

2728
export interface QueuedPrompt {
2829
/**
@@ -37,6 +38,7 @@ export interface QueuedPrompt {
3738
}
3839

3940
export const useExecutionStore = defineStore('execution', () => {
41+
const workflowStore = useWorkflowStore()
4042
const canvasStore = useCanvasStore()
4143

4244
const clientId = ref<string | null>(null)
@@ -61,6 +63,59 @@ export const useExecutionStore = defineStore('execution', () => {
6163
)
6264
})
6365

66+
const subgraphNodeIdToSubgraph = (id: string, graph: LGraph | Subgraph) => {
67+
const node = graph.getNodeById(id)
68+
if (node?.isSubgraphNode()) return node.subgraph
69+
}
70+
71+
/**
72+
* Recursively get the subgraph objects for the given subgraph instance IDs
73+
* @param currentGraph The current graph
74+
* @param subgraphNodeIds The instance IDs
75+
* @param subgraphs The subgraphs
76+
* @returns The subgraphs that correspond to each of the instance IDs.
77+
*/
78+
const getSubgraphsFromInstanceIds = (
79+
currentGraph: LGraph | Subgraph,
80+
subgraphNodeIds: string[],
81+
subgraphs: Subgraph[] = []
82+
): Subgraph[] => {
83+
// Last segment is the node portion; nothing to do.
84+
if (subgraphNodeIds.length === 1) return subgraphs
85+
86+
const currentPart = subgraphNodeIds.shift()
87+
if (currentPart === undefined) return subgraphs
88+
89+
const subgraph = subgraphNodeIdToSubgraph(currentPart, currentGraph)
90+
if (!subgraph) throw new Error(`Subgraph not found: ${currentPart}`)
91+
92+
subgraphs.push(subgraph)
93+
return getSubgraphsFromInstanceIds(subgraph, subgraphNodeIds, subgraphs)
94+
}
95+
96+
const executionIdToCurrentId = (id: string) => {
97+
const subgraph = workflowStore.activeSubgraph
98+
99+
// Short-circuit: ID belongs to the parent workflow / no active subgraph
100+
if (!id.includes(':')) {
101+
return !subgraph ? id : undefined
102+
} else if (!subgraph) {
103+
return
104+
}
105+
106+
// Parse the hierarchical ID (e.g., "123:456:789")
107+
const subgraphNodeIds = id.split(':')
108+
109+
// If the last subgraph is the active subgraph, return the node ID
110+
const subgraphs = getSubgraphsFromInstanceIds(
111+
subgraph.rootGraph,
112+
subgraphNodeIds
113+
)
114+
if (subgraphs.at(-1) === subgraph) {
115+
return subgraphNodeIds.at(-1)
116+
}
117+
}
118+
64119
// This is the progress of the currently executing node, if any
65120
const _executingNodeProgress = ref<ProgressWsMessage | null>(null)
66121
const executingNodeProgress = computed(() =>
@@ -167,12 +222,16 @@ export const useExecutionStore = defineStore('execution', () => {
167222
// Seems sometimes nodes that are cached fire executing but not executed
168223
activePrompt.value.nodes[executingNodeId.value] = true
169224
}
170-
executingNodeId.value = e.detail
171-
if (executingNodeId.value === null) {
172-
if (activePromptId.value) {
173-
delete queuedPrompts.value[activePromptId.value]
225+
if (typeof e.detail === 'string') {
226+
executingNodeId.value = executionIdToCurrentId(e.detail) ?? null
227+
} else {
228+
executingNodeId.value = e.detail
229+
if (executingNodeId.value === null) {
230+
if (activePromptId.value) {
231+
delete queuedPrompts.value[activePromptId.value]
232+
}
233+
activePromptId.value = null
174234
}
175-
activePromptId.value = null
176235
}
177236
}
178237

@@ -193,19 +252,31 @@ export const useExecutionStore = defineStore('execution', () => {
193252
lastExecutionError.value = e.detail
194253
}
195254

255+
function getNodeIdIfExecuting(nodeId: string | number) {
256+
const nodeIdStr = String(nodeId)
257+
return nodeIdStr.includes(':')
258+
? workflowStore.executionIdToCurrentId(nodeIdStr)
259+
: nodeIdStr
260+
}
261+
196262
function handleProgressText(e: CustomEvent<ProgressTextWsMessage>) {
197263
const { nodeId, text } = e.detail
198264
if (!text || !nodeId) return
199265

200-
const node = canvasStore.getCanvas().graph?.getNodeById(nodeId)
266+
// Handle hierarchical node IDs for subgraphs
267+
const currentId = getNodeIdIfExecuting(nodeId)
268+
const node = canvasStore.getCanvas().graph?.getNodeById(currentId)
201269
if (!node) return
202270

203271
useNodeProgressText().showTextPreview(node, text)
204272
}
205273

206274
function handleDisplayComponent(e: CustomEvent<DisplayComponentWsMessage>) {
207-
const { node_id, component, props = {} } = e.detail
208-
const node = canvasStore.getCanvas().graph?.getNodeById(node_id)
275+
const { node_id: nodeId, component, props = {} } = e.detail
276+
277+
// Handle hierarchical node IDs for subgraphs
278+
const currentId = getNodeIdIfExecuting(nodeId)
279+
const node = canvasStore.getCanvas().graph?.getNodeById(currentId)
209280
if (!node) return
210281

211282
if (component === 'ChatHistoryWidget') {

src/stores/workflowStore.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Subgraph } from '@comfyorg/litegraph'
1+
import type { LGraph, Subgraph } from '@comfyorg/litegraph'
22
import _ from 'lodash'
33
import { defineStore } from 'pinia'
44
import { type Raw, computed, markRaw, ref, shallowRef, watch } from 'vue'
@@ -162,6 +162,7 @@ export interface WorkflowStore {
162162
activeSubgraph: Subgraph | undefined
163163
/** Updates the {@link subgraphNamePath} and {@link isSubgraphActive} values. */
164164
updateActiveGraph: () => void
165+
executionIdToCurrentId: (id: string) => any
165166
}
166167

167168
export const useWorkflowStore = defineStore('workflow', () => {
@@ -442,11 +443,46 @@ export const useWorkflowStore = defineStore('workflow', () => {
442443
isSubgraphActive.value = isSubgraph(subgraph)
443444
}
444445

446+
const subgraphNodeIdToSubgraph = (id: string, graph: LGraph | Subgraph) => {
447+
const node = graph.getNodeById(id)
448+
if (node?.isSubgraphNode()) return node.subgraph
449+
}
450+
451+
const getSubgraphsFromInstanceIds = (
452+
currentGraph: LGraph | Subgraph,
453+
subgraphNodeIds: string[],
454+
subgraphs: Subgraph[] = []
455+
): Subgraph[] => {
456+
const currentPart = subgraphNodeIds.shift()
457+
if (currentPart === undefined) return subgraphs
458+
459+
const subgraph = subgraphNodeIdToSubgraph(currentPart, currentGraph)
460+
if (subgraph === undefined) throw new Error('Subgraph not found')
461+
462+
subgraphs.push(subgraph)
463+
return getSubgraphsFromInstanceIds(subgraph, subgraphNodeIds, subgraphs)
464+
}
465+
445466
const executionIdToCurrentId = (id: string) => {
446467
const subgraph = activeSubgraph.value
447468

469+
// Short-circuit: ID belongs to the parent workflow / no active subgraph
448470
if (!id.includes(':')) {
449-
return subgraph
471+
return !subgraph ? id : undefined
472+
} else if (!subgraph) {
473+
return
474+
}
475+
476+
// Parse the hierarchical ID (e.g., "123:456:789")
477+
const subgraphNodeIds = id.split(':')
478+
479+
// Start from the root graph
480+
const { graph } = comfyApp
481+
482+
// If the last subgraph is the active subgraph, return the node ID
483+
const subgraphs = getSubgraphsFromInstanceIds(graph, subgraphNodeIds)
484+
if (subgraphs.at(-1) === subgraph) {
485+
return subgraphNodeIds.at(-1)
450486
}
451487
}
452488

0 commit comments

Comments
 (0)