Skip to content

Parameters and templates

A literal is compiled into the graph. A ParamRef is a hole filled later.

ts
seed: 42n              // literal
seed: paramRef("seed") // or g.param("seed", { type: "int" })

Topology stays put. Values arrive at instantiate / cwf run --param.

ts
import {
  instantiateTemplate,
  workflow,
  type Graph,
} from "@stepupgaming/comfy-workflows";
import { conditioning, image, latent, loaders, sampling } from "@stepupgaming/comfy-workflows/nodes";

export function buildTemplate(): Graph {
  const g = workflow("docs-t2i-template");
  const checkpoint = g.param("checkpoint", {
    type: "combo",
    description: "Checkpoint filename on the Comfy server.",
  });
  const prompt = g.param("prompt", {
    type: "string",
    description: "Positive prompt.",
  });
  const seed = g.param("seed", {
    type: "int",
    default: 42n,
    description: "Sampling seed. Use bigint for the full 64-bit range.",
  });
  const steps = g.param("steps", { type: "int", default: 20 });

  const ckpt = g.add(loaders.CheckpointLoaderSimple, { ckpt_name: checkpoint });
  const positive = g.add(conditioning.CLIPTextEncode, { text: prompt, clip: ckpt.CLIP });
  const negative = g.add(conditioning.CLIPTextEncode, { text: "", clip: ckpt.CLIP });
  const empty = g.add(latent.EmptyLatentImage, { width: 512, height: 512, batch_size: 1 });
  const sampled = g.add(sampling.KSampler, {
    model: ckpt.MODEL,
    positive: positive.CONDITIONING,
    negative: negative.CONDITIONING,
    latent_image: empty.LATENT,
    seed,
    steps,
    cfg: 7,
    sampler_name: "euler",
    scheduler: "normal",
    denoise: 1,
  });
  const decoded = g.add(latent.VAEDecode, { samples: sampled.LATENT, vae: ckpt.VAE });
  g.add(image.SaveImage, { images: decoded.IMAGE, filename_prefix: "docs-template" });
  g.output(decoded.IMAGE, { name: "image" });
  return g.toGraph();
}

export function bind(params: { checkpoint: string; prompt: string; seed?: bigint; steps?: number }): Graph {
  return instantiateTemplate(buildTemplate(), {
    params: {
      checkpoint: params.checkpoint,
      prompt: params.prompt,
      ...(params.seed !== undefined ? { seed: params.seed } : {}),
      ...(params.steps !== undefined ? { steps: params.steps } : {}),
    },
  });
}

g.param

ts
const seed = g.param("seed", {
  type: "int",          // int | float | string | boolean | combo
  default: 42n,
  description: "Sampling seed",
  // options: [...]    // combo
});

Duplicate names throw. The returned ParamRef is { $param: "seed" } and can sit in any widget slot.

paramRef("seed") builds the same placeholder without declaring metadata. Recipes use it; declare the param on the builder (or let the recipe declare it) so instantiate knows the type/default.

Binding

ts
import { instantiateTemplate } from "@stepupgaming/comfy-workflows";

const graph = instantiateTemplate(tpl, {
  params: { prompt: "a lighthouse", seed: 42n },
  // inputs: { image: someSlotRef },
});

Unbound param with no default → E_UNBOUND_PARAM. Unbound port → E_UNBOUND_PORT. Instantiation renumbers nodes n1..nN in topological order so the same template + bindings compile identically.

CLI:

sh
cwf run workflow.ts --url http://127.0.0.1:8188 --param prompt=hello --param seed=42

--param is repeatable (-p).

Integer / bigint

Use bigint (42n) for seeds. number is accepted but cannot hold values above 2^53 exactly. Lossless integers.

Files and paths

Local files that must be uploaded are AssetRef, not string paths in a published default. Machine-local paths fail cwf pack (E_PACK_LOCAL_PATH). Checkpoint names on the server are portable parameters. Absolute C:\Users\... paths are not. Assets.

Discovery

Package manifests list parameters. cwf inspect prints required vs optional. cwf suggest proposes expose candidates without mutating.

Topology vs parameter

If changing a value changes which nodes exist, it is topology. Put it in TypeScript.

If it only changes a node input, it is a runtime parameter.

That rule keeps a Python/Rust binder stupid in a good way: replace {$param}, do not grow graphs. No second compiler.

Released under the MIT License. Unofficial project. Not affiliated with or endorsed by Comfy Org.