ComfyUI Node

LM Config

Change one sampling field without throwing away the model's own defaults

By 0xBeycan·Created about a month ago·Updated 21 days ago· 2
LM Config
    • config
    ◄do_sampletrue►
    ◄temperature0.70►
    ◄top_k20►
    ◄top_p0.80►
    ◄min_p0.00►
    ◄repetition_penalty1.00►
    ◄presence_penalty1.50►
    ◄mtpauto►
    ◄edited►

    The failure mode LM Config exists to prevent is mundane and infuriating. An LM node starts from the model's official defaults - temperature 0.7, top_p 0.8, top_k 20, presence penalty 1.5 for Qwen3.5 with thinking off. You decide the presence penalty is hurting you, so you override it. If the override node hands over a complete sampling block, you've also just thrown away every other default you never looked at, and you're now quietly running settings nobody validated.

    So LM Config only sends what you touched.

    How the "only what you touched" part works

    Widgets you change get recorded into edited - a comma-separated string the node keeps, hidden in the UI. The config output is a dict of exactly those fields, plus any field whose input is fed by a link. Everything else is absent, and the LM node applies its own model's defaults to whatever's missing.

    That carries two consequences worth knowing:

    1. The defaults shown on the node are just a display. They track the first LM node the output is linked to, for that node's model and thinking mode, so you can see what you'd be overruling. On the classic canvas they're drawn greyed but still accept input; the Vue renderer has no greyed look that still takes input, so there they just look edited. Neither changes what gets applied.
    2. A linked field is always applied, even with edited empty and even after Reset to model defaults. That's deliberate - feeding a value by wire is an explicit statement of intent, so it can't get lost. It also means a workflow that pipes something into temperature from elsewhere will ignore your reset. That's not a bug; it's the one case where the "only what I touched" rule has a tiebreaker.

    Reset to model defaults is in the node menu (both renderers). It clears edited and, if an LM node is linked, the display snaps back to that node's defaults.

    Sending this over the API? Set edited yourself - "temperature, top_p" - since there's no frontend to record what you changed. Names get trimmed, empties skipped, repeats counted once, and an unknown name is an error listing the valid fields.

    The fields that matter

    do_sample - on by default. Turn it off and you get greedy decoding: the most likely token at each step, and core then applies no temperature, no filter and no penalty at all. The other widgets become decoration. If you want reproducibility in a captioning pass, this is the switch, though PreFlight Observe already hardcodes greedy for its own reasons and a wrapper has its own opinion about penalties.

    temperature, top_p, top_k, min_p - the familiar nucleus/truncation knobs. Worth knowing: top_p in core keeps a slightly smaller set than the official Hugging Face implementation, because core drops the token whose cumulative probability crosses the threshold rather than including it. It's core's sampler and isn't patched here. As the author puts it, that's a known limit, not a bug report worth filing.

    repetition_penalty, presence_penalty - and this is the paragraph to actually read. Both only ever look at tokens this run generated: never the prompt, never a prefill. repetition_penalty divides a positive logit (multiplies a negative one); presence_penalty is subtracted, and its default here is 1.5 because that's what Qwen's card recommends for general tasks. But a caption legitimately repeats tokens - commas between every tag, the pronoun after the class word - and a penalty of 1.5 pushes it straight off its format. That's precisely why Captioner and Prompter ship their own sampling with no presence penalty and ignore this node's 1.5 unless you specifically edit it. If you connect an LM Config and change nothing, you get the model's own defaults back, penalty included. Connect it and edit one field, and you get the model's defaults with your one change over them.

    mtp - multi-token prediction, i.e. core's speculative decoding with the model file's MTP head. auto lets core adapt the draft depth, 2–5 pins it, off disables it. It only runs on files that have the head (Qwen3.5-4B, Qwen3.5-9B, Qwen3.8-27B) and only for text-only prompts - connect an image and it's skipped silently. Measured on a 3090 with Qwen3.5-9B INT8: 35.5 vs 21.6 tokens/s, about 1.65×. Sampled output with MTP differs from output without it for the same seed, and even greedy text can drift apart after a couple hundred tokens, because core verifies several drafted positions in one pass. Nobody's gaslighting you if the text shifts.

    An edited field the model can't use gets one console warning and the run continues - e.g. asking for mtp on Qwen3-VL-8B, which has no MTP head.

    Install

    Nothing special. It's a plain node in the pack:

    cd ComfyUI/custom_nodes
    git clone https://github.com/0xBeycan/ComfyUI-BCNodes
    pip install -r ComfyUI-BCNodes/requirements.txt
    

    Restart and search qwen, llm or caption. LM Config pulls in no dependencies of its own, and its single output is config (BC_LM_CONFIG), which wires into the config input of Qwen LM, Captioner or Prompter.

    Small traps

    The node's id is part of ComfyUI's cache key, so a new LM Config with identical values will still make the LM node run again. Not a problem, just don't be surprised when swapping two identically-configured nodes costs you a model load.

    Linked only to a wrapper - Captioner or Prompter, whose model lives on an LM Loader - the displayed defaults keep whatever values they were showing, since there's no LM node to read from. The wrapper still applies its own model's thinking-off defaults to the fields you didn't touch, which is the behaviour that matters.

    CategoryBCNodes/lm

    Inputs (9)

    NameTypeDefaultDescription
    do_sampleBOOLEANtrueOn: sample with the settings below and the LM node's seed. Off: greedy, the most likely token each step; core then applies no temperature, filter or penalty.
    temperatureFLOAT0.700.01–2Divides the logits: lower is more focused, higher more varied.
    top_kINT200–1000Keep only the k most likely tokens; 0 = off.
    top_pFLOAT0.800–1Keep the most likely tokens up to this total probability; 1.0 = off. Core leaves out the token that crosses it, a slightly smaller set than the official implementation keeps.
    min_pFLOAT0.000–1Drop the tokens less likely than min_p x the most likely one; 0 = off.
    repetition_penaltyFLOAT1.000–5Tokens already generated (never the prompt or the prefill) have their logits divided by it (multiplied when negative); 1.0 = off.
    presence_penaltyFLOAT1.500–5Subtracted from the logits of tokens already generated (never the prompt or the prefill); 0 = off. Official advice: 0 to 2 against endless repetition; a higher value can mix languages and cost some quality.
    mtpCOMBOautoMulti-token prediction: faster decoding with the file's MTP head. auto adapts the draft depth, 2-5 pins it, off disables it. Runs only for text-only prompts (no image) on files with an MTP head (Qwen3.5-4B / 9B, Qwen3.8-27B); elsewhere the run goes without it. Sampled output differs from non-MTP output for the same seed.
    editedSTRINGThe fields changed on this node, comma-separated (kept by the node, hidden there). Only these, and the fields fed by a link, are applied; the others keep the LM node's model defaults.

    Outputs (1)

    NameTypeDescription
    configBC_LM_CONFIG—