Skip to content

Commit c4b8162

Browse files
committed
work around to handle exec behavior differences on Docker App in server vs host
1 parent 868b1f0 commit c4b8162

3 files changed

Lines changed: 441 additions & 37 deletions

File tree

MyApp/src/debug-graph-links.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env bun
2+
3+
import { LGraph, LGraphNode, LiteGraph } from '@comfyorg/litegraph'
4+
import { ComfyWorkflowJSON, validateComfyWorkflow } from './schemas/comfyWorkflowSchema'
5+
6+
// Mock node classes based on the object info
7+
function createMockNodeClass(nodeType: string, nodeInfo: any) {
8+
class MockNode extends LGraphNode {
9+
static override title = nodeInfo.display_name || nodeType
10+
static comfyClass = nodeType
11+
override comfyClass: string
12+
13+
constructor() {
14+
super(nodeType)
15+
this.comfyClass = nodeType
16+
this.title = nodeInfo.display_name || nodeType
17+
18+
// Add inputs in the correct order
19+
if (nodeInfo.input?.required) {
20+
const inputOrder = nodeInfo.input_order?.required || Object.keys(nodeInfo.input.required)
21+
inputOrder.forEach((inputName: string) => {
22+
const inputDef = nodeInfo.input.required[inputName]
23+
if (inputDef) {
24+
this.addInput(inputName, inputDef[0])
25+
}
26+
})
27+
}
28+
29+
// Add outputs
30+
if (nodeInfo.output) {
31+
nodeInfo.output.forEach((outputType: string, index: number) => {
32+
const outputName = nodeInfo.output_name?.[index] || outputType
33+
this.addOutput(outputName, outputType)
34+
})
35+
}
36+
}
37+
}
38+
return MockNode
39+
}
40+
41+
function registerObjectInfoNodeDefinitions(comfyObjectInfo: any) {
42+
Object.entries(comfyObjectInfo).forEach(([nodeType, nodeInfo]: [string, any]) => {
43+
const MockNodeClass = createMockNodeClass(nodeType, nodeInfo)
44+
LiteGraph.registerNodeType(nodeType, MockNodeClass)
45+
})
46+
}
47+
48+
function createNodeFromData(nodeData: any, graph: LGraph, nodeMap: Map<string | number, LGraphNode>, comfyObjectInfo: any) {
49+
const node = LiteGraph.createNode(nodeData.type)
50+
if (!node) {
51+
console.error(`Failed to create node of type: ${nodeData.type}`)
52+
return
53+
}
54+
55+
node.id = nodeData.id
56+
node.pos = [nodeData.pos[0], nodeData.pos[1]] as [number, number]
57+
58+
graph.add(node)
59+
nodeMap.set(nodeData.id, node)
60+
61+
console.log(`Created node ${nodeData.id} (${nodeData.type}) with ${node.inputs?.length || 0} inputs and ${node.outputs?.length || 0} outputs`)
62+
}
63+
64+
function createWorkflowGraph(comfyObjectInfo: any, workflow: ComfyWorkflowJSON) {
65+
const graph = new LGraph()
66+
const nodeMap = new Map<string | number, LGraphNode>()
67+
68+
// First pass: Create all nodes
69+
for (const nodeData of workflow.nodes) {
70+
createNodeFromData(nodeData, graph, nodeMap, comfyObjectInfo)
71+
}
72+
73+
console.log(`\nCreated ${nodeMap.size} nodes`)
74+
console.log(`Graph has ${workflow.links?.length || 0} links to process`)
75+
76+
// Second pass: Create connections based on links
77+
if (workflow.links && Array.isArray(workflow.links)) {
78+
const linkMap = new Map()
79+
for (const link of workflow.links) {
80+
if (Array.isArray(link) && link.length >= 6) {
81+
linkMap.set(link[0], link)
82+
console.log(`Link ${link[0]}: ${link[1]}:${link[2]} -> ${link[3]}:${link[4]} (${link[5]})`)
83+
}
84+
}
85+
86+
// Connect nodes based on the node inputs that reference links
87+
for (const nodeData of workflow.nodes) {
88+
const targetNode = nodeMap.get(nodeData.id)
89+
if (!targetNode || !nodeData.inputs) continue
90+
91+
console.log(`\nProcessing node ${nodeData.id} (${nodeData.type}) with ${nodeData.inputs?.length || 0} inputs`)
92+
93+
for (const input of nodeData.inputs) {
94+
console.log(` Input: ${input.name} (link: ${input.link})`)
95+
if (input.link && linkMap.has(input.link)) {
96+
const link = linkMap.get(input.link)
97+
const [, sourceNodeId, sourceSlot] = link
98+
const sourceNode = nodeMap.get(sourceNodeId)
99+
100+
const targetInputIndex = targetNode.inputs.findIndex(
101+
(nodeInput) => nodeInput.name === input.name
102+
)
103+
104+
if (sourceNode && targetInputIndex !== -1) {
105+
try {
106+
console.log(` Connecting ${sourceNodeId}:${sourceSlot} -> ${nodeData.id}:${targetInputIndex} (${input.name})`)
107+
sourceNode.connect(sourceSlot, targetNode, targetInputIndex)
108+
} catch (error) {
109+
console.error(` Connection failed: ${error.message ?? error}`)
110+
}
111+
} else {
112+
console.error(` Cannot connect - sourceNode: ${!!sourceNode}, targetInputIndex: ${targetInputIndex}`)
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
// Check final graph state
120+
console.log(`\nFinal graph state:`)
121+
console.log(`Graph.links type: ${typeof graph.links}`)
122+
console.log(`Graph.links instanceof Map: ${graph.links instanceof Map}`)
123+
console.log(`Graph.links size: ${graph.links?.size || 'no size property'}`)
124+
125+
if (graph.links instanceof Map) {
126+
console.log(`Graph.links keys: [${Array.from(graph.links.keys()).slice(0, 10).join(', ')}]`)
127+
}
128+
129+
return graph
130+
}
131+
132+
;(async () => {
133+
const cliArgs = Bun.argv.slice(2)
134+
135+
if (cliArgs.length != 2) {
136+
console.log(`USAGE ${import.meta.file} <path-to-object_info.json> <path-to-workflow.json>`)
137+
process.exit(1);
138+
}
139+
140+
try {
141+
const objectInfoFile = Bun.file(cliArgs[0])
142+
const objectInfoJson = await objectInfoFile.text()
143+
const objectInfo = JSON.parse(objectInfoJson)
144+
145+
const workflowFile = Bun.file(cliArgs[1])
146+
const workflowJson = await workflowFile.text()
147+
const workflowData = JSON.parse(workflowJson)
148+
149+
const workflow = await validateComfyWorkflow(workflowData)
150+
if (!workflow) {
151+
throw new Error(`${cliArgs[1]} is not a valid workflow`)
152+
}
153+
154+
registerObjectInfoNodeDefinitions(objectInfo)
155+
const graph = createWorkflowGraph(objectInfo, workflow)
156+
157+
console.log(`\nDebug complete.`)
158+
} catch (e) {
159+
console.error(`${e.message ?? e}`)
160+
process.exit(1);
161+
}
162+
})()
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env bun
2+
3+
import { LGraph, LGraphNode, LiteGraph } from '@comfyorg/litegraph'
4+
import { ComfyWorkflowJSON, validateComfyWorkflow } from './schemas/comfyWorkflowSchema'
5+
6+
// Mock node classes based on the object info
7+
function createMockNodeClass(nodeType: string, nodeInfo: any) {
8+
class MockNode extends LGraphNode {
9+
static override title = nodeInfo.display_name || nodeType
10+
static comfyClass = nodeType
11+
override comfyClass: string
12+
13+
constructor() {
14+
super(nodeType)
15+
this.comfyClass = nodeType
16+
this.title = nodeInfo.display_name || nodeType
17+
18+
// Add inputs in the correct order
19+
if (nodeInfo.input?.required) {
20+
const inputOrder = nodeInfo.input_order?.required || Object.keys(nodeInfo.input.required)
21+
inputOrder.forEach((inputName: string) => {
22+
const inputDef = nodeInfo.input.required[inputName]
23+
if (inputDef) {
24+
this.addInput(inputName, inputDef[0])
25+
}
26+
})
27+
}
28+
29+
// Add outputs
30+
if (nodeInfo.output) {
31+
nodeInfo.output.forEach((outputType: string, index: number) => {
32+
const outputName = nodeInfo.output_name?.[index] || outputType
33+
this.addOutput(outputName, outputType)
34+
})
35+
}
36+
}
37+
}
38+
return MockNode
39+
}
40+
41+
function registerObjectInfoNodeDefinitions(comfyObjectInfo: any) {
42+
Object.entries(comfyObjectInfo).forEach(([nodeType, nodeInfo]: [string, any]) => {
43+
const MockNodeClass = createMockNodeClass(nodeType, nodeInfo)
44+
LiteGraph.registerNodeType(nodeType, MockNodeClass)
45+
})
46+
}
47+
48+
function createNodeFromData(nodeData: any, graph: LGraph, nodeMap: Map<string | number, LGraphNode>, comfyObjectInfo: any) {
49+
const node = LiteGraph.createNode(nodeData.type)
50+
if (!node) {
51+
console.error(`Failed to create node of type: ${nodeData.type}`)
52+
return
53+
}
54+
55+
node.id = nodeData.id
56+
node.pos = [nodeData.pos[0], nodeData.pos[1]] as [number, number]
57+
58+
graph.add(node)
59+
nodeMap.set(nodeData.id, node)
60+
}
61+
62+
function resolveNodeInput(node: any, inputIndex: number, graph: LGraph) {
63+
console.log(` resolveNodeInput: node ${node.id}, inputIndex ${inputIndex}`)
64+
65+
const input = node.inputs[inputIndex]
66+
if (!input || !input.link) {
67+
console.log(` No input or link: input=${!!input}, link=${input?.link}`)
68+
return null
69+
}
70+
console.log(` Input name: ${input.name}, link: ${input.link}`)
71+
72+
// Find the link in the graph
73+
const link = graph.links?.get ? graph.links.get(input.link) : null
74+
if (!link) {
75+
console.log(` No link found in graph.links`)
76+
console.log(` graph.links type: ${typeof graph.links}`)
77+
console.log(` graph.links instanceof Map: ${graph.links instanceof Map}`)
78+
console.log(` graph.links size: ${graph.links?.size}`)
79+
if (graph.links instanceof Map) {
80+
console.log(` Available link IDs: [${Array.from(graph.links.keys()).slice(0, 10).join(', ')}]`)
81+
}
82+
return null
83+
}
84+
console.log(` Found link:`, link)
85+
86+
// Get the source node
87+
const sourceNode = graph._nodes_by_id[link.origin_id]
88+
if (!sourceNode) {
89+
console.log(` No source node found for origin_id ${link.origin_id}`)
90+
console.log(` Available node IDs: [${Object.keys(graph._nodes_by_id).slice(0, 10).join(', ')}]`)
91+
return null
92+
}
93+
console.log(` Source node found: ${sourceNode.id} (${sourceNode.type})`)
94+
95+
return {
96+
origin_id: link.origin_id,
97+
origin_slot: link.origin_slot
98+
}
99+
}
100+
101+
function createWorkflowGraph(comfyObjectInfo: any, workflow: ComfyWorkflowJSON) {
102+
const graph = new LGraph()
103+
const nodeMap = new Map<string | number, LGraphNode>()
104+
105+
// First pass: Create all nodes
106+
for (const nodeData of workflow.nodes) {
107+
createNodeFromData(nodeData, graph, nodeMap, comfyObjectInfo)
108+
}
109+
110+
// Second pass: Create connections based on links
111+
if (workflow.links && Array.isArray(workflow.links)) {
112+
const linkMap = new Map()
113+
for (const link of workflow.links) {
114+
if (Array.isArray(link) && link.length >= 6) {
115+
linkMap.set(link[0], link)
116+
}
117+
}
118+
119+
// Connect nodes based on the node inputs that reference links
120+
for (const nodeData of workflow.nodes) {
121+
const targetNode = nodeMap.get(nodeData.id)
122+
if (!targetNode || !nodeData.inputs) continue
123+
124+
for (const input of nodeData.inputs) {
125+
if (input.link && linkMap.has(input.link)) {
126+
const link = linkMap.get(input.link)
127+
const [, sourceNodeId, sourceSlot] = link
128+
const sourceNode = nodeMap.get(sourceNodeId)
129+
130+
const targetInputIndex = targetNode.inputs.findIndex(
131+
(nodeInput) => nodeInput.name === input.name
132+
)
133+
134+
if (sourceNode && targetInputIndex !== -1) {
135+
try {
136+
sourceNode.connect(sourceSlot, targetNode, targetInputIndex)
137+
} catch (error) {
138+
console.error(`Connection failed: ${error.message ?? error}`)
139+
}
140+
}
141+
}
142+
}
143+
}
144+
}
145+
146+
return graph
147+
}
148+
149+
async function testGraphToPrompt(graph: LGraph) {
150+
console.log(`\nTesting graph to prompt conversion...`)
151+
152+
// Find KSampler node
153+
const kSamplerNode = graph._nodes.find((n: any) => n.type === 'KSampler')
154+
if (!kSamplerNode) {
155+
console.log(`No KSampler node found in graph`)
156+
return
157+
}
158+
159+
console.log(`Found KSampler node ${kSamplerNode.id} with ${kSamplerNode.inputs?.length} inputs`)
160+
161+
// Test resolveNodeInput for each input
162+
for (let i = 0; i < kSamplerNode.inputs.length; i++) {
163+
console.log(`\nTesting input ${i}:`)
164+
const resolved = resolveNodeInput(kSamplerNode, i, graph)
165+
console.log(` Resolved: ${resolved ? `${resolved.origin_id}:${resolved.origin_slot}` : 'null'}`)
166+
}
167+
}
168+
169+
;(async () => {
170+
const cliArgs = Bun.argv.slice(2)
171+
172+
if (cliArgs.length != 2) {
173+
console.log(`USAGE ${import.meta.file} <path-to-object_info.json> <path-to-workflow.json>`)
174+
process.exit(1);
175+
}
176+
177+
try {
178+
const objectInfoFile = Bun.file(cliArgs[0])
179+
const objectInfoJson = await objectInfoFile.text()
180+
const objectInfo = JSON.parse(objectInfoJson)
181+
182+
const workflowFile = Bun.file(cliArgs[1])
183+
const workflowJson = await workflowFile.text()
184+
const workflowData = JSON.parse(workflowJson)
185+
186+
const workflow = await validateComfyWorkflow(workflowData)
187+
if (!workflow) {
188+
throw new Error(`${cliArgs[1]} is not a valid workflow`)
189+
}
190+
191+
registerObjectInfoNodeDefinitions(objectInfo)
192+
const graph = createWorkflowGraph(objectInfo, workflow)
193+
194+
await testGraphToPrompt(graph)
195+
196+
} catch (e) {
197+
console.error(`Error: ${e.message ?? e}`)
198+
console.error(`Stack: ${e.stack}`)
199+
process.exit(1);
200+
}
201+
})()

0 commit comments

Comments
 (0)