Nodes/TrainyThing/TrainyThing Filter
ComfyUI Node

TrainyThing Filter

The one node in this pack that works offline

By ryanrinkel·Created 21 days ago·Updated 21 days ago· 0
TrainyThing Filter
  • images
  • latent
  • images_b
  • images
  • latent
  • indices
  • count
◄labels—►
◄ruleaccepted►
◄option_id►

What it is, and why you'd actually want it

You generated twelve, kept four on your phone, and now those four need to be the batch going into the upscale pass. That's the whole job - and it's easy to get wrong by hand, because what records which four you kept is a text string, not another batch.

TrainyThing Filter is the node that turns that string into a slice of the batch you already have. Feed it the pictures, the labels that describe them, and a rule; out comes the subset, its latent, the indices and a count.

Here's the part worth knowing before you install anything: Filter never talks to the TrainyThing daemon. No Node.js, no localhost URL, no token, no network. It's a pure function over a batch and a text blob - the only node in the pack that needn't know a daemon exists. An on-canvas chooser like chrisgoringe's cg-image-picker is simpler when you're at the desk clicking keepers yourself; Filter is for when the verdicts were made somewhere else - a phone, another person, an hour ago - and arrive as text.

How it works

The labels string is the daemon's labels.jsonl: one JSON object per line, one line per picture, carrying ref, label, verdict, the vote counts, a contested flag and any notes. Filter reads those lines positionally against the batch - line 0 is image 0 - then walks them into one of five sets: accepted, rejected, contested, unjudged, or the option buckets.

Two of those sets are why this node exists instead of just wiring accepted through. contested means the reviewers tied (needs min_reviews above 1); unjudged means nobody looked - a timed-out run, a skipped card. Neither lands in accepted or rejected, so without a rule they silently vanish from your pipeline.

If the subset is empty, Filter doesn't error: it puts an execution blocker on images, latent and indices so the downstream branch simply doesn't run, and leaves count at 0. Deliberate - an empty list output would reach something like Image From Batch as one call with None and blow up, and "nothing was rejected" isn't an error.

The inputs and outputs that matter

  • images - the batch the labels describe, in the same order.
  • labels - a socket, not a text box. You can't type into it; wire it from TrainyThing Review, Wait or Load, or from a string node if you're replaying a saved labels.jsonl.
  • rule - accepted (default), rejected, contested, unjudged, or option. With option, fill option_id with the option's id, e.g. hair.
  • latent (optional) - cut the same way, so the keepers' latents survive to a low-denoise KSampler.
  • images_b (optional) - only for pair labels: with it wired, accepted and rejected return the winning and losing image of each pair rather than always the A side.

Outputs: images (the subset, in batch order), latent, indices (INT list), count. The usual wiring is images → VAE Encode or an upscaler, latent → KSampler at a low denoise, and indices → core's Latent From Batch when you want to reproduce one keeper exactly.

The latent slicing is Latent From Batch generalised from a contiguous range to an arbitrary index list, with noise_mask and batch_index handled rather than quietly dropped.

Installing it

Same pack, same three commands, even though Filter alone doesn't need the daemon:

cd ComfyUI/custom_nodes
git clone https://github.com/ryanrinkel/comfyui-trainything
cd comfyui-trainything
python -m pip install -r requirements.txt   # use ComfyUI's own python / python_embeded\python.exe

Restart, and all five nodes are under TrainyThing; or search "TrainyThing" in ComfyUI Manager (comfy node install trainything). Deps are segno (the QR codes the other nodes draw), pillow, numpy. No model downloads. One constraint: the pack is built on ComfyUI's V3 node API and pins 0.39.0 or newer - older builds get a warning line and quite possibly no nodes.

Where people get burned

The line count has to match the batch. Filter counts lines against the batch size and refuses to guess. This bites when you wire labels from one place and images from another - a Load whose queue has more cards than the batch you passed, or a batch that's already been through another Filter. Same source, same order, every time.

latent gets blocked on pair labels with images_b. If the picks can come from both sides, there is no single latent to slice, so Filter blocks it with a console line rather than handing you a misaligned one. VAE Encode the winners instead.

An empty rule isn't a bug. When nothing matched, a quiet branch is the design. Check count. And images_b is ignored outside accepted/rejected, with a console note.

One last thing: the pack's console strings name the offending line or entry. Read them before you start debugging.

CategoryTrainyThing

Inputs (6)

NameTypeDefaultDescription
imagesIMAGEThe pictures the labels describe, in the same order.
labelsSTRINGThe labels output of Review, Wait or Load: one line per picture with its verdict.
ruleCOMBOacceptedWhich pictures to pass on: accepted, rejected, contested (the reviewers disagreed), unjudged (nobody looked), or option (together with option_id).
option_idSTRINGWith rule option: the option's id, e.g. hair. choose: the pictures where that option won. select: the pictures where it was ticked.
latentoptLATENTOptional: their latents, cut the same way.
images_boptIMAGEpair mode only: the B pictures, so accepted and rejected pick the winning and losing picture of each pair. Not wired: the A picture is used.

Outputs (4)

NameTypeDescription
imagesIMAGE—
latentLATENT—
indicesINT—
countINT—