Files
smthemex-ComfyUI_Dif_GGUF/tokenizer_map.py
T
2026-08-04 20:01:35 +08:00

149 lines
5.5 KiB
Python

# Key remapping for text-encoder GGUF files.
#
# Unet GGUF files (city96 style) keep the original ComfyUI tensor names, so the
# unet loader can consume them directly. Text-encoder GGUF files produced by
# llama.cpp's convert scripts instead use llama.cpp naming ("blk.0.attn_q.weight",
# "token_embd.weight", ...). This module converts those back to the names that
# ComfyUI's T5 / llama / gemma implementations expect.
#
# Nothing here touches ComfyUI source; it only rewrites a state dict in memory.
import logging
def sd_prefix_from_state_dict(sd):
"""Guess the architecture of a text encoder state dict."""
keys = sd.keys()
if any(k.startswith("enc.") or k.startswith("dec.") for k in keys):
return "t5"
if "token_embd.weight" in sd and any(k.endswith("attn_norm.weight") for k in keys):
return "llama"
if any(k.startswith("text_model.") for k in keys):
return "clip"
if any(k.startswith("encoder.block.") for k in keys):
return "t5_native"
return None
def _t5_block_map(n_block, prefix="encoder"):
"""llama.cpp T5 block names -> ComfyUI T5 block names."""
src = f"enc.blk.{n_block}"
dst = f"{prefix}.block.{n_block}"
return {
f"{src}.attn_q.weight": f"{dst}.layer.0.SelfAttention.q.weight",
f"{src}.attn_k.weight": f"{dst}.layer.0.SelfAttention.k.weight",
f"{src}.attn_v.weight": f"{dst}.layer.0.SelfAttention.v.weight",
f"{src}.attn_o.weight": f"{dst}.layer.0.SelfAttention.o.weight",
f"{src}.attn_norm.weight": f"{dst}.layer.0.layer_norm.weight",
f"{src}.attn_rel_b.weight": f"{dst}.layer.0.SelfAttention.relative_attention_bias.weight",
f"{src}.ffn_up.weight": f"{dst}.layer.1.DenseReluDense.wi_1.weight",
f"{src}.ffn_gate.weight": f"{dst}.layer.1.DenseReluDense.wi_0.weight",
f"{src}.ffn_down.weight": f"{dst}.layer.1.DenseReluDense.wo.weight",
f"{src}.ffn_norm.weight": f"{dst}.layer.1.layer_norm.weight",
}
def _llama_block_map(n_block):
"""llama.cpp block names -> ComfyUI llama/gemma block names."""
src = f"blk.{n_block}"
dst = f"model.layers.{n_block}"
return {
f"{src}.attn_q.weight": f"{dst}.self_attn.q_proj.weight",
f"{src}.attn_k.weight": f"{dst}.self_attn.k_proj.weight",
f"{src}.attn_v.weight": f"{dst}.self_attn.v_proj.weight",
f"{src}.attn_output.weight": f"{dst}.self_attn.o_proj.weight",
f"{src}.attn_norm.weight": f"{dst}.input_layernorm.weight",
f"{src}.ffn_up.weight": f"{dst}.mlp.up_proj.weight",
f"{src}.ffn_gate.weight": f"{dst}.mlp.gate_proj.weight",
f"{src}.ffn_down.weight": f"{dst}.mlp.down_proj.weight",
f"{src}.ffn_norm.weight": f"{dst}.post_attention_layernorm.weight",
}
def _count_blocks(sd, pattern):
n = 0
while True:
if not any(k.startswith(pattern.format(n)) for k in sd):
break
n += 1
return n
def convert_t5_state_dict(sd):
"""llama.cpp T5 GGUF -> ComfyUI T5 naming."""
n_blocks = _count_blocks(sd, "enc.blk.{}.")
key_map = {}
for i in range(n_blocks):
key_map.update(_t5_block_map(i))
key_map.update({
"token_embd.weight": "shared.weight",
"enc.output_norm.weight": "encoder.final_layer_norm.weight",
"output.weight": "lm_head.weight",
})
new_sd = {}
for k, v in sd.items():
# Drop the decoder: ComfyUI only runs the T5 encoder.
if k.startswith("dec."):
continue
dst = key_map.get(k)
if dst is None:
if k.startswith("enc.") or k == "output.weight":
logging.debug(f"GGUF TE: dropping unmapped key {k}")
continue
dst = k
new_sd[dst] = v
# ComfyUI's T5 keeps the relative attention bias only on block 0.
rel_b = "encoder.block.0.layer.0.SelfAttention.relative_attention_bias.weight"
for i in range(1, n_blocks):
new_sd.pop(
f"encoder.block.{i}.layer.0.SelfAttention.relative_attention_bias.weight",
None,
)
if rel_b not in new_sd:
logging.warning("GGUF TE: T5 relative_attention_bias missing")
return new_sd
def convert_llama_state_dict(sd):
"""llama.cpp LLaMA/Gemma GGUF -> ComfyUI naming."""
n_blocks = _count_blocks(sd, "blk.{}.")
key_map = {}
for i in range(n_blocks):
key_map.update(_llama_block_map(i))
key_map.update({
"token_embd.weight": "model.embed_tokens.weight",
"output_norm.weight": "model.norm.weight",
"output.weight": "lm_head.weight",
})
new_sd = {}
for k, v in sd.items():
dst = key_map.get(k)
if dst is None:
if k.startswith("blk.") or k.startswith("output"):
logging.debug(f"GGUF TE: dropping unmapped key {k}")
continue
dst = k
new_sd[dst] = v
return new_sd
def convert_text_encoder_state_dict(sd):
"""
Detect the text encoder architecture and normalize its key names.
Returns (state_dict, arch). If the file already uses ComfyUI naming it is
returned unchanged.
"""
arch = sd_prefix_from_state_dict(sd)
if arch == "t5":
logging.info("GGUF TE: detected T5 (llama.cpp naming), converting keys")
return convert_t5_state_dict(sd), "t5"
if arch == "llama":
logging.info("GGUF TE: detected LLaMA/Gemma (llama.cpp naming), converting keys")
return convert_llama_state_dict(sd), "llama"
logging.info(f"GGUF TE: using keys as-is (arch={arch})")
return sd, arch