LLMSkillHub
mirroredCopied from artokun/comfyui-mcp under MIT. artokun has not claimed this page, and is not affiliated with LLM SkillHub.

artokun / rgthree

Configure and author rgthree-comfy nodes — Fast Groups Bypasser/Muter (group toggles), Power Lora Loader, Context/Context Big, Seed, Any Switch. Use when a workflow contains rgthree nodes, when asked to add stage/section toggles or an A/B switch, when stacking LoRAs, or when an rgthree node needs configuring. Covers the frontend-only nodes that are absent from /object_info and the properties-not-widgets configuration model.

0
SKILL.md
12.9 KB

rgthree-comfy

rgthree-comfy is one of the most widely installed packs, so its nodes turn up in a
large share of community workflows. Three of its properties make it a recurring
agent-failure mode — all three fail quietly enough to look like success.

The three things that catch agents out

1. Some rgthree nodes are FRONTEND-ONLY. They are registered by the pack's JS
(registerCustomNodes()), have no Python class, and are therefore **absent from
/object_info by design**. Checking /object_info and concluding "this node doesn't
exist" is wrong. /object_info lists 24 rgthree backend types; the toggles are not
among them.

2. They are configured through PROPERTIES, not widgets. matchTitle,
toggleRestriction and sort live in node.properties (right-click → Properties),
not in widgets. Use panel_set_property. panel_set_widget does not silently
half-work — it refuses, with has no widget "matchTitle" (available: …) listing
the widgets that do exist. That refusal is the fastest confirmation you are on the
properties path; read it rather than retrying the write.

3. Fast Groups nodes take NO wiring and enumerate GROUPS by title. Leave the
OPT_CONNECTION output unconnected. The node renders one toggle per matching group,
so the groups must exist and be named before the node is useful.

Which rgthree nodes panel_add_node will actually add

The panel authorizes every add against fresh /object_info and fails closed on a
type it cannot find. Genuinely frontend-only types are exempt only via an explicit
allowlist (FRONTEND_ONLY_NODE_TYPES), so the exemption covers seven rgthree
types and no others:

Frontend-only, panel_add_node WORKSFrontend-only, panel_add_node REFUSES
Fast Groups Bypasser (rgthree)Bookmark (rgthree)
Fast Groups Muter (rgthree)Mute / Bypass Relay (rgthree)
Fast Bypasser (rgthree)Mute / Bypass Repeater (rgthree)
Fast Muter (rgthree)Fast Actions Button (rgthree)
Node Collector (rgthree)Random Unmuter (rgthree)
Label (rgthree)
Reroute (rgthree)

A refusal in the right-hand column is the guard working as designed, not a broken
pack and not something to work around — the node has no backend def and is not on the
allowlist. Say so and pick a different approach (the Bypasser/Muter cover almost every
real toggle need). Everything with a Python class — Power Lora Loader, Context*, Seed,
Any Switch, Power Prompt, Image Comparer — is a normal backend node and adds normally.

Fast Groups Bypasser / Muter

Fast Groups Bypasser (rgthree) sets the nodes of a group to bypass (mode 4 —
the node is skipped and its input passes through). Fast Groups Muter (rgthree) sets
them to mute (mode 2 — the node does not execute and everything downstream
dies). Prefer the Bypasser for toggling an optional stage inside a chain; reach
for the Muter only when you genuinely want to stop a branch.

All of the following are node properties — set them with panel_set_property:

PropertyValuesDefaultNotes
matchTitleregex, case-insensitive""Set this. Empty means every group in the workflow becomes a toggle. It is a real regex matched unanchored against the group title, so anchor it (^STAGE) or it matches mid-title.
matchColorscomma-separated colors""Alternative filter; pairs with a color convention.
toggleRestrictiondefault / max one / always onedefaultBoth non-default values enforce mutual exclusion (they are matched on the substring " one"). Do NOT set either if the user may ever want all stages on in one queue.
sortposition / alphanumeric / custom alphabetpositionThe default means moving a group on the canvas silently reorders the toggles. alphanumeric is stable — prefer it.
customSortAlphabetstring""Only read when sort is custom alphabet.
showNavbooltruePer-row jump-to-group arrow.
showAllGraphsbooltrueInclude groups that live inside subgraphs.

matchTitle does not rebuild the toggle list on the first write

panel_set_property stores matchTitle (and matchColors / sort / …) and
the reply from/to is truthful. Fast Groups nodes **do not implement
onPropertyChanged**. The toggle list is rebuilt by rgthree's
refreshWidgets() on a service tick (~8 ms after add, then every ~500 ms),
and leftover-row removal increments the index while splicing — a 22-group
list can stall at 13 with non-matching Enable … rows still present. A
never-drawn node can also come back as widgets:{} — that means the list
has not been built yet, not that there are no matching groups.
panel_query_graph also keys widgets by name, and every toggle is named
RGTHREE_TOGGLE_AND_NAV, so a built list collapses to one key.

Do this:

  1. Set matchTitle immediately after panel_add_node (before the first
    unfiltered refresh paints every group).

  2. Re-read with panel_query_graph {ids:[<id>], fields:'detail'}.
  3. If widgets is empty or the canvas still shows Enable rows that do
    not match the regex, set matchTitle again. Do not delete and
    re-add the node — that is slower and still needs a second set.

Recipe — make pipeline stages toggleable

  1. panel_create_group per stage, with a prefixed title (STAGE 1 — …) so one
    anchored regex selects exactly the intended set.

  2. Verify group membership before you trust it. Group membership is purely
    geometric: LiteGraph counts a node as a member when its centre falls inside
    the box, and the auto-fit box around your node_ids will happily swallow unrelated
    neighbours. When the live members differ from what you asked for, the result
    carries extra_node_ids, missing_node_ids and a warning alongside
    requested_node_ids — read them. (They appear only when you passed node_ids
    and something differs, so their absence is a real all-clear.)

    A stray node here is not cosmetic: toggling one stage will disable part of another.
    To fix it, move the nodes apart (panel_edit_node, or panel_auto_layout) so
    the regions are contiguous, or set an explicit bounds with panel_edit_group —
    then re-check. panel_move_group does not help: by default it drags the
    contained nodes along with the box, so the same nodes stay inside it.

  3. panel_add_node(class_type="Fast Groups Bypasser (rgthree)"). Leave its output
    unwired. Add nodes one at a time, not as a parallel batch.

  4. panel_set_property → matchTitle = ^STAGE, and sort = alphanumeric.
    Set them immediately after the add, then re-read the node. If widgets is
    empty or leftover Enable rows remain, set matchTitle again — do not
    delete and re-add.

  5. Toggle, then verify with panel_graph_outline — it tags nodes [bypass] / [mute].

Power Lora Loader (rgthree)

A backend node (present in /object_info) that stacks N LoRAs in one node. Each row
is a widget named lora_1, lora_2, … whose value is a composite object
{on: bool, lora: "subdir\\name.safetensors", strength: float, strengthTwo: float|null}
(strengthTwo is the separate CLIP strength, null in the simple view). Rows are
identified by the presence of a lora key, and the node's control widgets are
appended after them, so do not index positionally — address the row by name.

Rows CAN be created programmatically. A freshly added Power Lora Loader has
no lora_N widgets at all — the on-canvas "➕ Add Lora" button opens a chooser on a
mouse event that no panel tool can press, but panel_set_widget does not need it:
writing lora_1 (then lora_2, …) with a JSON object STRING creates the row and
the reply carries created_widget: "lora_1". Create rows in order, one call each:

panel_set_widget(node_id=<id>, widget="lora_1",
  value='{"on":true,"lora":"subdir/turbo.safetensors","strength":1,"strengthTwo":null}')

# A Windows subdir separator is a JSON escape — write it DOUBLED in the string:
#   "lora":"Anima\\Tools\\turbo.safetensors"

Re-read with panel_query_graph {ids:[<id>], fields:'detail'} to confirm the row
landed. If you need a stack from scratch, create lora_1, then lora_2, and so on;
do not assume a skipped number is the next row.

On an existing row, write ONE field with dotted sub-field addressing — it merges
onto the current object and preserves every other field:

panel_set_widget(node_id=<id>, widget="lora_1.strength", value=0.8)
panel_set_widget(node_id=<id>, widget="lora_2.on",       value=false)
panel_set_widget(node_id=<id>, widget="lora_1.lora",     value="style/foo.safetensors")

Writing a bare scalar to lora_1 itself is the trap: it would set one field and
null the rest, so it is refused. To change several fields at once, pass a **JSON
object STRING** (the value argument accepts only string/number/boolean, so a literal
object fails tool validation before any write) — it is parsed and merged:

panel_set_widget(node_id=<id>, widget="lora_1", value='{"on":false,"strength":0.6}')

Fields are schema-checked: on non-nullable boolean, strength non-nullable number,
lora nullable string, strengthTwo nullable number. An unknown field name (a typo like
lora_1.strenght) is refused, not silently created, and nested paths are unsupported.

Clearing a nullable field takes the JSON-string form, not a dotted write. The panel
accepts null for lora / strengthTwo, but value is typed `string | number |
boolean, so a barevalue=null` is rejected by tool-arg validation before any write
happens — the same schema limit as the whole-row case above. Clear it through the string:

panel_set_widget(node_id=<id>, widget="lora_1", value='{"lora":null}')

Turning a LoRA off (lora_N.on = false) is usually safer than clearing it anyway.

Other commonly-seen rgthree nodes

  • Context / Context Big / Context Switch / Context Merge — bundle
    MODEL/CLIP/VAE/conditioning into one RGTHREE_CONTEXT wire. **These are NOT virtual
    wiring.** Unlike Get/Set buses they are real executable backend nodes, so
    panel_strip_workflow and panel_flatten_workflow deliberately keep them — they
    run. Do not expect either tool to dissolve a Context chain. There is no hidden edge to
    resolve: every link is a real link, traceable with panel_query_graph. What a Context
    hides is which field a downstream node pulls out of the bundle, so read the chain
    node by node. (panel_slice_workflow still carves one pipeline out of a toggled
    monolith, and panel_strip_workflow still resolves any genuine Get/Set buses and
    Reroutes around it.)

  • Seed (rgthree) — it deletes the built-in control_after_generate widget on
    creation, so do not try to write it; the widget is not there. Control is by SPECIAL
    SEED VALUES written to the seed widget instead: **-1 randomize, -2 increment,
    -3 decrement. Any concrete seed is returned unchanged** — 42 stays 42 every
    run — so to pin a run, write the number; to re-randomize, write -1. The frontend
    resolves a special value into a real seed before queueing and shows the result in a
    read-only last_seed widget, so a seed re-read after a run may not be the special
    value the user set (a fixed seed, however, is stable).

  • Any Switch (rgthree) — the first non-null input wins; a common A/B toggle paired
    with bypassed branches. An empty Context counts as null, so an unfilled Context
    branch is skipped rather than selected.

Gotchas

  • A bypassed node is skipped and passes its input through; a muted node kills
    everything downstream. Choosing the Muter where the Bypasser was meant breaks the
    chain rather than shortening it.

  • Always panel_graph_outline before a run — a stale toggle is a top cause of a wrong
    render, and the outline marks [bypass] / [mute] explicitly.

  • rgthree's Bookmark nodes respond to keypresses and are inert to agents.

Sources

  • Official: https://github.com/rgthree/rgthree-comfy
  • Empirical: frontend-only allowlist and properties-not-widgets notes verified against the installed pack and the panel guard. #1808 matchTitle rebuild: Fast Groups have no onPropertyChanged; refreshWidgets() leftover removal is removeWidget(index++) in fast_groups_muter.ts; first unfiltered tick is scheduled from addFastGroupNode in fast_groups_service.ts.