FunPack Prompt Markup
Attention weights that mean what you thought they meant
- clean
- markup
- status
Everyone who came from A1111 types (word:1.5) out of muscle memory. On SDXL it mostly did something. On an LLM-encoded model with a packed attention stream - which is what MiniMax H3 is - it's worse than useless, because those prompt rows are contextual hidden states: there is no per-word magnitude to turn up. Scaling them is a no-op or a lie. FunPack Prompt Markup takes the three markups people actually use, strips them out of the text the encoder sees, and hands the intent to something that can apply it properly.
The three syntaxes
Weights. (phrase:1.5) or [phrase:1.5]. The mechanism isn't embedding scaling, it's a logit bias of log(w) added to that phrase's key positions in attention. Softmax over logits + log w multiplies that key's share by w - which is exactly what "this word matters 1.5×" is supposed to mean. Nice.
Timed phrases. [[email protected]], optionally [phrase:[email protected]]. Only the video rows inside that window get the phrase, weighted; outside the window it's masked away from video. Text and audio rows keep the untimed bias. This is a genuine video-model feature - "she turns away from 2 to 3.5 seconds" is something no image-era weighting syntax could express.
Blends. [a|b], or [a|b:0.8] to set the mix. Per row: (1-s)·A + s·B, with the second option encoded separately. The default strength is 0.5.
All three are stripped in one left-to-right pass, which is subtler than it sounds: stripping them sequentially left earlier spans pointing into a string that no longer existed. One pass keeps every span indexed against the same clean text.
Inputs and outputs
One required input: text. Three outputs:
clean- the prompt with all markup removed. This is what goes into your CLIP Text Encode. It is clean, so the encoder never sees syntax it would otherwise tokenize literally.markup- a typed payload holding the parsed spans, the alternates, the windows.status- a human summary like2 weighted, 1 blended, orno markupif you wrote a perfectly ordinary prompt.
clean to the encoder, markup into FunPack Apply Prompt Markup along with the model, clip, positive conditioning and latent. That node puts the weights and timed windows on the model and applies the blends to the conditioning. FunPack's own H3 pipeline wires all of this for you; if you're hand-building, that's the pair.
Where it doesn't help
Three limits are worth knowing before you go wild.
The bias is clamped. Past ±6, a phrase wins every query regardless of content and the prompt visibly collapses onto that one word - the source sets the ceiling deliberately rather than letting you find it yourself. A weight at or below 0.001 isn't a tiny weight, it's a mask; that's an explicit "remove this".
Escaped delimiters stay literal, so \(not a weight\) survives if you genuinely need the punctuation.
And the honest one: applied weights only mean something where the text lives inside the attention sequence. That's H3's packed stream. The pack has an SD 1.5 preset but marks it experimental, so don't build a workflow around this syntax on SDXL and be surprised when the status says the encoder "is not one a model module can place words in" - when that happens the apply node refuses by name and the prompt still goes in, just without the markup.
Blending, for what it's worth, is flagged in the source as running end-to-end but visually unjudged. It's the one of the three I'd treat as a toy until you've done your own comparison.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/olivv-cs/ComfyUI-FunPack
pip install -r ComfyUI-FunPack/requirements.txt
Restart, and confirm the version in Settings ▸ About FunPack if you installed via ComfyUI Manager (search ComfyUI-FunPack) - the Registry copy can trail the repo. No models to download for this node, but the H3 pipeline it's designed around needs MiniMax H3 weights, which carry a licence that excludes the US, EU, UK and South Korea. That's the real gate on this feature for a lot of readers.
Troubleshooting
The markup shows up in my image as literal brackets. You fed the raw text to the encoder instead of clean. That's the whole bug.
Status says "no markup". Your regex didn't match - usually a missing colon ((word 1.5)), or parentheses that are actually escaped.
Nothing changes visually. Check you wired markup into an apply node. The parser is pure text handling; it doesn't touch the model on its own.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| clean | STRING | — |
| markup | FUNPACK_MARKUP | — |
| status | STRING | — |