The Six Profiles
v3.1 organizes comfy-headless around six workflow profiles. Each profile is a set of
verified graph builders plus a high-level client method; all six share one retrieval path
(/history + /view) and one dependency story (check_workflow_dependencies names any
missing pack).
| Profile | What it does | Client surface |
|---|---|---|
| Image | txt2img (SDXL, Qwen-Image-2512), instruction edit, ControlNet | generate_image, edit_image, compile_workflow, build_controlnet_workflow |
| Video | 26 presets, 9 families, t2v + i2v | generate_video |
| 3D | image → GLB mesh (Hunyuan3D-2) | generate_3d |
| Inference | caption, tag, detect, segment, OCR | run_inference |
| Metadata | provenance round-trip on output PNGs | rerun_from_png, read_workflow_metadata |
| Audio | text-to-music, stem separation | generate_audio, separate_audio |
The layer underneath
Section titled “The layer underneath”All six profiles sit on a shared addressing/typing layer
(comfy_headless.addressing) that speaks ComfyUI’s own validation rules:
- Union type matching, transcribed from the server. ComfyUI accepts an edge when the
source and declared type sets intersect — so
VoxelToMesh’sMESHoutput legitimately feedsSaveGLB’s 14-memberFILE_3D_*union input.validate_node_input()is a line-for-line transcription of the server’s validator, andclient.check_workflow_types()applies it per edge against the live/object_info. A client stricter than the server manufactures false rejections; a corpus test asserts zero rejections across every graph the builders emit. - Dotted dynamic-combo fields.
SaveVideo.codecandSaveAudioAdvanced.formatare dynamic combos whose sub-fields serialize as flattened dotted keys (codec.encoding.crf,format.quality). Sent flat, they fail withrequired_input_missing. - Presence-aware conditionals.
format.qualityexists only undermp3/opus; writing it underflacis a bug even when tolerated.DynamicCombo.build()refuses inactive-branch fields at construction time — a stale value cannot survive a tag change. - The output-node guard. A result reaches
/historyonly through a node withOUTPUT_NODE=True; a graph without one runs green and returns nothing.require_output_node()makes that failure impossible to ship.
result = client.generate_3d("character.png", preset="detail")glb = client.get_file(**result["meshes"][0])Hunyuan3D-2 image-to-mesh, entirely core nodes — the chain is
ImageOnlyCheckpointLoader → Hunyuan3Dv2Conditioning → KSampler → VAEDecodeHunyuan3D → VoxelToMesh → SaveGLB. It is pure image conditioning: there is no
text prompt in the graph. The GLB registers in /history under the 3d key and
downloads through /view like any image — no new routes.
Presets: standard (steps 30, cfg 5.5, octree 256), draft (faster, coarser),
detail (steps 50, octree 384). Wrapper-pack models (TRELLIS, TripoSG, …) are
deliberately not emitted; ComfyUI-3D-Pack’s native dependencies are the least stable in
the ecosystem.
result = client.generate_audio( tags="orchestral, epic, battle theme", lyrics="", # empty = instrumental preset="music", # music / music_long / jingle / music_mp3 / draft seconds=30,)ACE-Step 1.5 — MIT-licensed code and weights, native core, zero packs. The turbo AIO checkpoint runs at 8 steps / cfg 1. Two invariants are enforced by the builder rather than left as user knobs:
- The encoder’s
durationand the latent’ssecondsare one logical parameter split across two nodes; the runtime does not cross-validate them, and a mismatch completes “successfully” with silently drifted output. Both come fromseconds. - Output terminates in
SaveAudioAdvanced— the only non-deprecated audio save node.flacemits no quality field at all;mp3/opusemit the dottedformat.quality.
Stem separation (separate_audio) uses the audio-separation-nodes-comfyui pack and
wires the four outputs by the documented order — bass, drums, other, vocals — never by
slot guess. Audio uploads go through POST /upload/image (upload_audio): ComfyUI has
no audio-specific upload route, and that route is the server’s general input uploader.
Inference
Section titled “Inference”r = client.run_inference("photo.png", task="caption")print(r["text"]) # read straight from /history — no second round-tripTasks: caption, detailed_caption, more_detailed_caption, tag, detect,
segment, ocr. All run on Florence-2 (pack comfyui-florence2); detect and
segment need text_input (what to find).
Choices you should know about, because they were made against the live catalog:
- Tagging rides Florence-2 PromptGen (
task="prompt_gen_tags", checkpointMiaoshouAI/Florence-2-base-PromptGen-v1.5). No WD14 tagger class exists in the live catalog, and this library does not emit classes it cannot verify. - Detect bridges through
Florence2toCoordinates(packcomfyui-segment-anything-2): Florence2Run’s detection data leaves on aJSONoutput, and JSON → STRING is not a valid edge, so it cannot feedSaveTextdirectly. - Segment returns the mask as a PNG (
MaskToImage → SaveImage) for the consumer to threshold.
Every inference graph terminates in an output node by construction — SaveText reports
the text inline (text) plus the written file (files).
Metadata
Section titled “Metadata”from comfy_headless import read_workflow_metadata, extract_prompt_graph
graph = extract_prompt_graph("output.png") # the exact /prompt payloadresult = client.rerun_from_png("output.png") # re-POST it verbatimclient.queue_prompt(workflow, extra_pnginfo={"myapp:run_id": "r-77"})ComfyUI’s save nodes embed the API-format graph in every output PNG (prompt text
chunk) alongside the GUI graph (workflow chunk). The reader is pure stdlib and parses
all three PNG text chunk flavors. Custom keys passed via extra_pnginfo become extra
chunks — no custom node needed.
Documented limits: WebP/JPEG carry the same data in EXIF (different reader path, not implemented); video outputs don’t embed the graph; hardened deployments may strip unknown keys; GUI→API conversion has no server route — use ComfyUI’s “Workflow → Export (API)”.
Image additions in 3.1
Section titled “Image additions in 3.1”qwen_txt2imgtemplate — Qwen-Image-2512 with the recipe it actually wants:UNETLoaderpath, 16-channelEmptySD3LatentImage, steps 20, cfg 2.5, shift 3.1, 1328×1328.edit_image/build_qwen_edit_workflow— Qwen-Image-Edit-2511; 1–3 reference images as discreteimage1..image3inputs, no VAEEncode.build_controlnet_workflow— union ControlNet, one code path for Qwen (Qwen-Image-2512-Fun-Controlnet-Union-2602) and SDXL (controlnet-union-sdxl-1.0); the type enum is verbatim, and only the coreCannypreprocessor is emitted.
Video repairs in 3.1
Section titled “Video repairs in 3.1”- Real Hunyuan 1.5 i2v — presets
hunyuan15_i2v/hunyuan15_i2v_fastbuild on coreHunyuanVideo15ImageToVideoand requireinit_image(v3.0 silently built t2v). - Core terminator option —
output="core"swapsVHS_VideoCombineforCreateVideo → SaveVideo, dropping the Video Helper Suite pack.
The knowledge base
Section titled “The knowledge base”The repo carries an LLM-first knowledge base at
kb/:
index.json (the dispatch table), per-profile fact pages, runnable reference graphs
generated from these builders, and node provenance. If you are pointing an LLM at this
library, point it at kb/index.json first.