ꘅ Choice to Value
One label in, one number out — the boring node that makes AI decisions useful
- value
What it's for
ꘅ Choice to Value does one small thing: it takes a label - the string a decision model picked - and returns the number you associated with that label. No model, no inference, no GPU. Roughly fifteen lines of Python.
It's also the node that decides whether the rest of this pack is interesting. A decision model hands you "blue". ComfyUI can't do anything with "blue". It can do something with 210, because 210 is a hue, a denoise strength, a CFG value, a control weight. This node is the seam between "the model made a judgement" and "the graph changed." Without it you've built an expensive random word generator.
The author's framing in the node description is exactly right: this authored rule translates a decision into a creative control; it does not run AI. That's the whole design philosophy of the pack in one sentence. You author the controls and the labels. The model picks among your labels. Nothing executes model output as code - a hard rule across every node here, and the reason the mapping is a JSON dict you write rather than an expression the model writes.
Inputs and the one output
Two required inputs, one output.
choice is a STRING, and it's a forced input - you can't type into a widget. It has to be wired, normally from ꘅ Local Decision's choice output or from ꘅ Decision Scores Validator.
values_json is a STRING, multiline, defaulting to:
{"blue":210,"orange":30}
Add your labels at the same time you add them to choices_json on the decision node. A label with no mapping is a dead end.
The output is value, a single FLOAT. Wire it into anything that takes a float: denoise/strength, CFG, a control weight, a numeric widget on an interactive sketch. Note the type - FLOAT. An INT-only input like steps won't accept it directly; put a Comfy core Float/Int converter in between rather than fighting the type checker.
The lookup is strict, on purpose. Two failure paths, both loud:
- The chosen label isn't a key in your JSON →
Add the selected choice to values_json. - The value isn't a finite number (a string,
true,NaN) →Choice values must be finite numbers. Booleans are explicitly rejected, because Python'sTrueis1and the author didn't want a silent 1.0 leaking into your sampler.
Installing it
Same pack, same three steps:
cd ComfyUI/custom_nodes
git clone https://github.com/languel/genereticore
Restart Comfy, or install via ComfyUI Manager by searching GeneretiCore. It shows up under Genereti / Inference as ꘅ Choice to Value.
Here's the useful part: this node works anywhere. The pack's base install declares zero dependencies, and this node is pure standard library - json and math. You don't need macOS, you don't need the Core ML helper, you don't need a model download. Linux and Windows users can wire this against decisions produced any other way, and it'll behave identically.
Where people get burned
The mismatch between "choices" and "values" as two separate JSON blobs is the classic one. They live on two different nodes, so nothing mechanically forces them to agree - add violet to choices_json and forget values_json, and the workflow runs fine right up until the model picks violet, at which point it dies with a ValueError that mentions values_json and nothing else. When you edit one, edit the other.
Second: don't shove meaning into the numbers that isn't there. {"blue":210,"orange":30} is a hue mapping - completely arbitrary, authored by you, and fine. If you catch yourself writing {"blue":0.8,"orange":0.2} because those were the scores, stop: this node maps the label, not the distribution. If you want the score, decision_json carries it and ꘅ Text / JSON Viewer will show you. Mixing the two concepts is how a graph starts behaving in ways you can't explain a week later.
Third, a caching note that applies to the whole pack: the inference nodes upstream of this one return NaN from IS_CHANGED, which forces them to re-run on every execution and invalidates the cache behind them. So a graph with a decision node in it never gets to skip work. That's deliberate - a decision you can't re-roll isn't much of a control - but it means "why is this slow" has an answer, and the answer is the model, not this lookup.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| choice | STRING | — | |
| values_json | STRING | {"blue":210,"orange":30} | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| value | FLOAT | — |