Path 1 architecture: one comfyui adapter, workflows as data.
- workflow_template.go: embed.FS + token substitution with type-preserving
whole-value placeholders. ${prompt} → string, ${seed} → int64,
${cfg} → float64 — no JSON round-tripping. Partial matches ignored.
- comfyui.go: refactored to load workflow from embedded FS or filesystem
path. Back-compat preserved: workflow: defaults to flux1-schnell.
- workflows/{flux1-schnell,flux2-klein,sd35-medium}.json — bundled
templates. flux1-schnell migrated from hardcoded with identical node IDs.
- compare.go: new `imagen compare` subcommand. Sequential N-backend run
(one GPU on mRock — parallel would OOM), per-backend PNG, sidecar JSON
with per-model metadata + errors, composite contact sheet via Go image
package (no ImageMagick dep).
- Sample config gains flux2-klein-local + sd35-medium-local instances.
- docs/backends.md: architecture rationale + per-model HF download paths
+ how to add a new bundled workflow + compare-harness reference.
Live smoke verified: compare mock + flux-schnell-local at 768×768 →
both PNGs written, sidecar JSON has workflow="flux1-schnell" + full
metadata, contact sheet renders. Worker contract (Request → Generate)
unchanged, so flexsiebels /imagine UI API surface preserved.
Tests: 11 existing comfyui + 6 new workflow_template + 5 new compare
tests, all green.
Adding a new model is now yaml + JSON, never Go.
67 lines
1.2 KiB
JSON
67 lines
1.2 KiB
JSON
{
|
|
"4": {
|
|
"class_type": "CheckpointLoaderSimple",
|
|
"inputs": {
|
|
"ckpt_name": "${model}"
|
|
}
|
|
},
|
|
"6": {
|
|
"class_type": "CLIPTextEncode",
|
|
"inputs": {
|
|
"text": "${prompt}",
|
|
"clip": ["4", 1]
|
|
}
|
|
},
|
|
"7": {
|
|
"class_type": "CLIPTextEncode",
|
|
"inputs": {
|
|
"text": "${negative}",
|
|
"clip": ["4", 1]
|
|
}
|
|
},
|
|
"8": {
|
|
"class_type": "VAEDecode",
|
|
"inputs": {
|
|
"samples": ["31", 0],
|
|
"vae": ["4", 2]
|
|
}
|
|
},
|
|
"9": {
|
|
"class_type": "SaveImage",
|
|
"inputs": {
|
|
"filename_prefix": "imagen",
|
|
"images": ["8", 0]
|
|
}
|
|
},
|
|
"13": {
|
|
"class_type": "ModelSamplingSD3",
|
|
"inputs": {
|
|
"model": ["4", 0],
|
|
"shift": "${shift}"
|
|
}
|
|
},
|
|
"27": {
|
|
"class_type": "EmptySD3LatentImage",
|
|
"inputs": {
|
|
"width": "${width}",
|
|
"height": "${height}",
|
|
"batch_size": 1
|
|
}
|
|
},
|
|
"31": {
|
|
"class_type": "KSampler",
|
|
"inputs": {
|
|
"model": ["13", 0],
|
|
"seed": "${seed}",
|
|
"steps": "${steps}",
|
|
"cfg": "${cfg}",
|
|
"sampler_name": "${sampler}",
|
|
"scheduler": "${scheduler}",
|
|
"denoise": 1.0,
|
|
"positive": ["6", 0],
|
|
"negative": ["7", 0],
|
|
"latent_image": ["27", 0]
|
|
}
|
|
}
|
|
}
|