Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9370c82
Rebase GLM-Next support onto master, and migrate to llama-memory-hybr…
timkhronos Aug 27, 2026
d4ce825
Add initial MTP support
timkhronos Aug 27, 2026
d2fc716
Merge branch optimizations. Reduce allocated compute buffer size, spe…
timkhronos Aug 28, 2026
9855711
Review driven changes, remove env vars, protect tensors
timkhronos Aug 28, 2026
b93ed51
Strip MTP for initial PR
timkhronos Aug 28, 2026
ba436fd
Clean up after mtp strip
timkhronos Aug 28, 2026
3ff8d92
Clean up after mtp strip
timkhronos Aug 28, 2026
f176c8a
Update speculative.cpp
timkhronos Aug 28, 2026
c34db58
Update llama-context.h
timkhronos Aug 28, 2026
0aa327b
Clean up after mtp strip
timkhronos Aug 28, 2026
ec1cdbf
Fix tokenizer ignore merges
timkhronos Aug 29, 2026
7152e9b
Improve quantization protection selection
timkhronos Aug 29, 2026
fdc54be
Refactor mhc helpers, graph base
timkhronos Aug 31, 2026
8543941
Merge branch 'master' into GLM5.3-Flash
timkhronos Aug 31, 2026
74bb0e3
Lint Fixes
timkhronos Aug 31, 2026
a771613
Apply suggestions from code review
timkhronos Aug 31, 2026
5728a4b
Skip glm5-next in model saver, fix CRLF
timkhronos Aug 31, 2026
c35bddd
Skip glm5-next in sweep
timkhronos Aug 31, 2026
3bdb2d8
Remove T4 fallback
timkhronos Aug 31, 2026
81f95af
Review cleanup
timkhronos Sep 1, 2026
3498cc7
Merge branch 'master' into GLM5.3-Flash
timkhronos Sep 1, 2026
611e407
Merge branch 'ggml-org:master' into GLM5.3-Flash
timkhronos Sep 1, 2026
1eca274
Review suggestions
timkhronos Sep 1, 2026
7de5a8e
Defer separate MTP gguf handling to MTP PR, drop filter
timkhronos Sep 1, 2026
9fe9fd7
Repad n_head_kv
timkhronos Sep 1, 2026
a386cd7
kpool init apply
timkhronos Sep 1, 2026
5b05fcc
Merge branch 'ggml-org:master' into GLM5.3-Flash
timkhronos Sep 1, 2026
120eb9e
Order by descending score
timkhronos Sep 1, 2026
d0c5589
Drop guard
timkhronos Sep 2, 2026
8c28939
read kpool from hparams, clarify kpool cache flags, remove kpool_buil…
timkhronos Sep 2, 2026
a2f1d20
Add glm5-next support to model saver and add arch test fixture
timkhronos Sep 2, 2026
fe3187d
Review cleanup
timkhronos Sep 2, 2026
2b533e0
Kpool pooled caching clarify
timkhronos Sep 2, 2026
ff6be95
Add multi stream support
timkhronos Sep 3, 2026
ad74ec3
Merge branch 'ggml-org:master' into GLM5.3-Flash
timkhronos Sep 3, 2026
1b564d2
Finish Rebase
timkhronos Sep 3, 2026
99fdaab
Sparse FA fir DSA prefill
timkhronos Sep 3, 2026
db54719
Merge branch 'ggml-org:master' into GLM5.3-Flash
timkhronos Sep 3, 2026
5c4bd50
Const
timkhronos Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions conversion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"Glm4MoeLiteForCausalLM": "glm",
"Glm4vForConditionalGeneration": "glm",
"Glm4vMoeForConditionalGeneration": "glm",
"Glm5NextForConditionalGeneration": "glm",
"GlmForCausalLM": "chatglm",
"GlmMoeDsaForCausalLM": "glm",
"GlmOcrForConditionalGeneration": "glm",
Expand Down Expand Up @@ -298,6 +299,7 @@
"Gemma4UnifiedForConditionalGeneration": "gemma",
"Glm4vForConditionalGeneration": "qwen3vl",
"Glm4vMoeForConditionalGeneration": "qwen3vl",
"Glm5NextForConditionalGeneration": "qwen3vl",
"Glm5vForConditionalGeneration": "kimivl",
"GlmOcrForConditionalGeneration": "qwen3vl",
"GlmasrModel": "ultravox",
Expand Down
195 changes: 195 additions & 0 deletions conversion/glm.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,198 @@ def set_vocab(self):
special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<unk>"]) # ty: ignore[unresolved-attribute]
special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|startoftext|>"]) # ty: ignore[unresolved-attribute]
special_vocab.add_to_gguf(self.gguf_writer)


@ModelBase.register("Glm5NextForConditionalGeneration")
@ModelBase.example("zai-org/GLM-5.3-Flash")
class Glm5NextModel(TextModel):

model_arch = gguf.MODEL_ARCH.GLM5_NEXT
supports_mtp_export = True

_experts: list[dict[str, Tensor]] | None = None
_n_main_layers: int | None = None

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.n_nextn_layers = self.hparams.get("num_nextn_predict_layers", 0)
self.skip_mtp = self.no_mtp or self.n_nextn_layers == 0

if not self.skip_mtp:
self.block_count += self.n_nextn_layers
self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)

self.hparams.pop("head_dim", None)

def set_vocab(self):
# requires transformers >= 5, tokpre hash-resolves to glm4
return self._set_vocab_glm()

def index_tensors(self, remote_hf_model_id: str | None = None):
hp = self.hparams.get("text_config", self.hparams)
type(self)._n_main_layers = hp["num_hidden_layers"]
return super().index_tensors(remote_hf_model_id=remote_hf_model_id)

@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
if (titem := super().filter_tensors(item)) is None:
return None
name, gen = titem

assert cls._n_main_layers is not None
m = re.match(r"model\.layers\.(\d+)\.", name)
is_mtp = m is not None and int(m.group(1)) >= cls._n_main_layers

if is_mtp and cls.no_mtp:
return None
if cls.mtp_only and not is_mtp and name not in (
"model.embed_tokens.weight", "model.norm.weight", "lm_head.weight",
):
return None

return name, gen

def set_gguf_parameters(self):
super().set_gguf_parameters()
hp = self.hparams

layer_types = hp["layer_types"]
n_kv_heads = [0 if t == "linear_attention" else 1 for t in layer_types]
assert len(n_kv_heads) == hp["num_hidden_layers"]
# Pad to block_count
n_kv_heads += [1] * (self.block_count - len(n_kv_heads))
self.gguf_writer.add_head_count_kv(n_kv_heads)
self.gguf_writer.add_vocab_size(hp["vocab_size"])
self.gguf_writer.add_layer_norm_eps(1e-6)

if not self.skip_mtp:
self.gguf_writer.add_nextn_predict_layers(self.n_nextn_layers)

# KDA
lin = hp["linear_attn_config"]
assert lin["num_heads"] == hp["num_attention_heads"]
self.gguf_writer.add_ssm_conv_kernel(lin["short_conv_kernel_size"])
self.gguf_writer.add_kda_head_dim(lin["head_dim"])
if (lb := lin.get("gate_lower_bound")) is not None:
self.gguf_writer.add_kda_gate_lower_bound(lb)

# MLA (nope only)
assert hp.get("mla_use_nope") and hp["qk_rope_head_dim"] == 0, "expected nope-only MLA"
kv_lora_rank = hp["kv_lora_rank"]
qk_rope = hp["qk_rope_head_dim"]
self.gguf_writer.add_q_lora_rank(hp["q_lora_rank"])
self.gguf_writer.add_kv_lora_rank(kv_lora_rank)
self.gguf_writer.add_rope_dimension_count(qk_rope)
self.gguf_writer.add_key_length(kv_lora_rank + qk_rope)
self.gguf_writer.add_value_length(kv_lora_rank)
self.gguf_writer.add_key_length_mla(hp["qk_nope_head_dim"] + qk_rope)
self.gguf_writer.add_value_length_mla(hp["v_head_dim"])

# DSA indexer with k-pool compression
self.gguf_writer.add_indexer_head_count(hp["index_n_heads"])
self.gguf_writer.add_indexer_key_length(hp["index_head_dim"])
self.gguf_writer.add_indexer_top_k(hp["index_topk"])
self.gguf_writer.add_indexer_kpool(hp["index_kpool"])
self.gguf_writer.add_indexer_kpool_select_tail(hp.get("index_kpool_always_select_tail", True))
self.gguf_writer.add_indexer_index_share_mtp(hp.get("index_share_for_mtp_iteration", False))
if (indexer_types := hp.get("indexer_types")) is not None:
self.gguf_writer.add_indexer_types([t == "full" for t in indexer_types])

# mHC
assert hp.get("mhc", True)
self.gguf_writer.add_hyper_connection_count(hp["hc_mult"])
self.gguf_writer.add_hyper_connection_sinkhorn_iterations(hp["hc_sinkhorn_iters"])
self.gguf_writer.add_hyper_connection_epsilon(hp["hc_eps"])

# MoE
self.gguf_writer.add_leading_dense_block_count(hp["first_k_dense_replace"])
self.gguf_writer.add_expert_feed_forward_length(hp["moe_intermediate_size"])
self.gguf_writer.add_expert_shared_count(hp["n_shared_experts"])
self.gguf_writer.add_expert_weights_scale(hp["routed_scaling_factor"])
self.gguf_writer.add_expert_weights_norm(hp["norm_topk_prob"])
if (limit := hp.get("swiglu_limit")) is not None:
self.gguf_writer.add_swiglu_clamp_exp([limit] * self.block_count)
self.gguf_writer.add_swiglu_clamp_shexp([limit] * self.block_count)

def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
if name == "lm_head.weight" and self.hparams.get("tie_word_embeddings", False):
return

# routed experts
if ".mlp.experts." in name:
n_experts = self.hparams["n_routed_experts"]
assert bid is not None
if self._experts is None:
self._experts = [{} for _ in range(self.block_count)]
self._experts[bid][name] = data_torch
if len(self._experts[bid]) < n_experts * 3:
return
for w_name in ("down_proj", "gate_proj", "up_proj"):
datas: list[Tensor] = []
for xid in range(n_experts):
ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight"
datas.append(self._experts[bid].pop(ename))
merged = f"model.layers.{bid}.mlp.experts.{w_name}.weight"
yield from super().modify_tensors(torch.stack(datas, dim=0), merged, bid)
return

# MLA absorption
if name.endswith("kv_b_proj.weight"):
n_head = self.hparams["num_attention_heads"]
v_head_dim = self.hparams["v_head_dim"]
qk_nope_head_dim = self.hparams["qk_nope_head_dim"]
assert data_torch.shape[0] == n_head * (v_head_dim + qk_nope_head_dim)
kv_b = data_torch.view(n_head, v_head_dim + qk_nope_head_dim, data_torch.shape[-1])
k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1)
yield from super().modify_tensors(k_b.transpose(1, 2), name.replace("kv_b_proj", "k_b_proj"), bid)
yield from super().modify_tensors(v_b, name.replace("kv_b_proj", "v_b_proj"), bid)
return

# KDA conv1d
if name.endswith((".q_conv1d.weight", ".k_conv1d.weight", ".v_conv1d.weight")):
if data_torch.ndim == 3:
d_inner, _, d_conv = data_torch.shape
elif data_torch.ndim == 2:
d_inner, d_conv = data_torch.shape
else:
raise ValueError(f"unexpected conv1d rank {data_torch.ndim} for {name}")
data_torch = data_torch.reshape(1, d_inner, 1, d_conv)

if name.endswith(".A_log"):
n_head = self.hparams["num_attention_heads"]
data_torch = -torch.exp(data_torch.float().flatten()[:n_head])

if name.endswith(".dt_bias"):
name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias"

if re.search(r"\.(hc_(?:attn|ffn)_(?:fn|base|scale)|index_kpool_compress_(?:ape|gate))$", name):
yield self.map_tensor_name(name) + ".weight", data_torch
return

yield from super().modify_tensors(data_torch, name, bid)

def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool:
# keep the small mHC / gating parameters exact
exact_keys = ("hc_attn_", "hc_ffn_", "indexer_compressor_", "ssm_a", "ssm_dt", "exp_probs_b")
if new_name.startswith(("blk.", "output_hc")) and any(k in new_name for k in exact_keys):
return gguf.GGMLQuantizationType.F32
return super().tensor_force_quant(name, new_name, bid, n_dims)

def prepare_metadata(self, vocab_only: bool):
from_dir = self.fname_out.is_dir()
super().prepare_metadata(vocab_only=vocab_only)
if not self.mtp_only or not from_dir:
return
output_type: str = self.ftype.name.partition("_")[2]
fname_default: str = gguf.naming_convention(
self.metadata.name, self.metadata.basename, self.metadata.finetune,
self.metadata.version, size_label=None, output_type=output_type, model_type=None)
self.fname_out = self.fname_out.parent / f"mtp-{fname_default}.gguf"

def prepare_tensors(self):
super().prepare_tensors()
if self._experts is not None:
leftover = [k for d in self._experts for k in d.keys()]
if leftover:
raise ValueError(f"Unprocessed experts: {leftover}")
30 changes: 29 additions & 1 deletion conversion/qwen3vl.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,12 @@ class Qwen3ASRMmprojModel(Qwen3OmniMmprojModel):
@ModelBase.register("Glm4vForConditionalGeneration", "Glm4vMoeForConditionalGeneration", "GlmOcrForConditionalGeneration")
@ModelBase.example("zai-org/GLM-4.1V-9B-Thinking", "zai-org/GLM-4.5V")
class Glm4VVisionModel(Qwen3VLVisionModel):
projector_type = gguf.VisionProjectorType.GLM4V

def set_gguf_parameters(self):
MmprojModel.set_gguf_parameters(self) # skip Qwen3VLVisionModel parameters
assert self.hparams_vision is not None
self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GLM4V)
self.gguf_writer.add_clip_projector_type(self.projector_type)

hidden_act = str(self.hparams_vision.get("hidden_act", "")).lower()
if hidden_act == "gelu":
Expand All @@ -249,6 +251,32 @@ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iter
yield from super().modify_tensors(data_torch, name, bid)


@ModelBase.register("Glm5NextForConditionalGeneration")
@ModelBase.example("zai-org/GLM-5.3-Flash")
class Glm5NextVisionModel(Glm4VVisionModel):
# GLM-5.3-Flash vision tower. glm4v layout with per-head qk-norm, no post-conv norm and no learned position embeddings.
# Images are placed on a ceil aligned canvas with padding.

projector_type = gguf.VisionProjectorType.GLM5V

def set_gguf_parameters(self):
super().set_gguf_parameters()
assert self.hparams_vision is not None
self.gguf_writer.add_vision_spatial_merge_size(int(self.hparams_vision.get("spatial_merge_size", 2)))
if (limit := self.hparams_vision.get("swiglu_limit")) is not None:
self.gguf_writer.add_vision_swiglu_clamp(float(limit))

# image token budget from the processor, stored as single-frame pixel counts
pc = self.preprocessor_config
patch = int(pc.get("patch_size", 14))
merge = int(pc.get("merge_size", 2))
pixels_per_token = (patch * merge) ** 2
if (min_tok := pc.get("min_image_tokens")) is not None:
self.gguf_writer.add_vision_min_pixels(int(min_tok) * pixels_per_token)
if (max_tok := pc.get("max_image_tokens")) is not None:
self.gguf_writer.add_vision_max_pixels(int(max_tok) * pixels_per_token)


@ModelBase.register("Qwen3VLForConditionalGeneration")
@ModelBase.example("Qwen/Qwen3-VL-4B-Instruct")
class Qwen3VLTextModel(Qwen3Model):
Expand Down
75 changes: 75 additions & 0 deletions gguf-py/gguf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ class Indexer:
BLOCK_SIZE = "{arch}.attention.indexer.block_size" # MSA
LOCAL_BLOCKS = "{arch}.attention.indexer.local_blocks" # MSA
TYPES = "{arch}.attention.indexer.types"
KPOOL = "{arch}.attention.indexer.kpool" # GLM5-Next
INDEX_SHARE_MTP = "{arch}.attention.indexer.index_share_mtp" # GLM5-Next
KPOOL_SELECT_TAIL = "{arch}.attention.indexer.kpool_select_tail" # GLM5-Next

class HyperConnection:
COUNT = "{arch}.hyper_connection.count"
Expand Down Expand Up @@ -382,6 +385,7 @@ class ClipVision:
IMAGE_MEAN = "clip.vision.image_mean"
IMAGE_STD = "clip.vision.image_std"
SPATIAL_MERGE_SIZE = "clip.vision.spatial_merge_size"
SWIGLU_CLAMP = "clip.vision.swiglu_clamp"
EXPERT_COUNT_PER_LAYER = "clip.vision.expert_count_per_layer" # dots3note pyramid MoE, 0 = dense layer
EXPERT_USED_COUNT = "clip.vision.expert_used_count"
USE_GELU = "clip.use_gelu"
Expand Down Expand Up @@ -559,6 +563,7 @@ class MODEL_ARCH(IntEnum):
GLM4 = auto()
GLM4_MOE = auto()
GLM_DSA = auto()
GLM5_NEXT = auto()
BITNET = auto()
T5 = auto()
T5ENCODER = auto()
Expand Down Expand Up @@ -889,6 +894,8 @@ class MODEL_TENSOR(IntEnum):
INDEXER_COMPRESSOR_WGATE = auto()
INDEXER_COMPRESSOR_APE = auto()
INDEXER_COMPRESSOR_NORM = auto()
INDEXER_KPOOL_GATE = auto()
INDEXER_KPOOL_APE = auto()
# vision
V_MMPROJ = auto()
V_MMPROJ_FC = auto()
Expand Down Expand Up @@ -1311,6 +1318,7 @@ class MODEL_TENSOR(IntEnum):
MODEL_ARCH.GLM4: "glm4",
MODEL_ARCH.GLM4_MOE: "glm4moe",
MODEL_ARCH.GLM_DSA: "glm-dsa",
MODEL_ARCH.GLM5_NEXT: "glm5-next",
MODEL_ARCH.BITNET: "bitnet",
MODEL_ARCH.T5: "t5",
MODEL_ARCH.T5ENCODER: "t5encoder",
Expand Down Expand Up @@ -1640,6 +1648,8 @@ class MODEL_TENSOR(IntEnum):
MODEL_TENSOR.INDEXER_COMPRESSOR_WGATE: "blk.{bid}.indexer_compressor_gate",
MODEL_TENSOR.INDEXER_COMPRESSOR_APE: "blk.{bid}.indexer_compressor_ape",
MODEL_TENSOR.INDEXER_COMPRESSOR_NORM: "blk.{bid}.indexer_compressor_norm",
MODEL_TENSOR.INDEXER_KPOOL_GATE: "blk.{bid}.indexer_compressor_gate",
MODEL_TENSOR.INDEXER_KPOOL_APE: "blk.{bid}.indexer_compressor_ape",
# vision
MODEL_TENSOR.V_MMPROJ: "mm.{bid}",
MODEL_TENSOR.V_MMPROJ_FC: "mm.model.fc",
Expand Down Expand Up @@ -4003,6 +4013,70 @@ class MODEL_TENSOR(IntEnum):
MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
],
MODEL_ARCH.GLM5_NEXT: [
MODEL_TENSOR.TOKEN_EMBD,
MODEL_TENSOR.OUTPUT_NORM,
MODEL_TENSOR.OUTPUT,
MODEL_TENSOR.ATTN_NORM,
# mHC
MODEL_TENSOR.HC_ATTN_FN,
MODEL_TENSOR.HC_ATTN_BASE,
MODEL_TENSOR.HC_ATTN_SCALE,
MODEL_TENSOR.HC_FFN_FN,
MODEL_TENSOR.HC_FFN_BASE,
MODEL_TENSOR.HC_FFN_SCALE,
# KDA (linear attention) layers
MODEL_TENSOR.ATTN_Q,
MODEL_TENSOR.ATTN_K,
MODEL_TENSOR.ATTN_V,
MODEL_TENSOR.ATTN_OUT,
MODEL_TENSOR.SSM_CONV1D_Q,
MODEL_TENSOR.SSM_CONV1D_K,
MODEL_TENSOR.SSM_CONV1D_V,
MODEL_TENSOR.SSM_F_A,
MODEL_TENSOR.SSM_F_B,
MODEL_TENSOR.SSM_BETA,
MODEL_TENSOR.SSM_A,
MODEL_TENSOR.SSM_G_A,
MODEL_TENSOR.SSM_G_B,
MODEL_TENSOR.SSM_DT,
MODEL_TENSOR.SSM_NORM,
# MLA (nope) + DSA layers
MODEL_TENSOR.ATTN_Q_A,
MODEL_TENSOR.ATTN_Q_B,
MODEL_TENSOR.ATTN_KV_A_MQA,
MODEL_TENSOR.ATTN_KV_B,
MODEL_TENSOR.ATTN_K_B,
MODEL_TENSOR.ATTN_V_B,
MODEL_TENSOR.ATTN_Q_A_NORM,
MODEL_TENSOR.ATTN_KV_A_NORM,
MODEL_TENSOR.INDEXER_K_NORM,
MODEL_TENSOR.INDEXER_PROJ,
MODEL_TENSOR.INDEXER_ATTN_K,
MODEL_TENSOR.INDEXER_ATTN_Q_B,
MODEL_TENSOR.INDEXER_KPOOL_GATE,
MODEL_TENSOR.INDEXER_KPOOL_APE,
# FFN
MODEL_TENSOR.FFN_NORM,
MODEL_TENSOR.FFN_GATE,
MODEL_TENSOR.FFN_DOWN,
MODEL_TENSOR.FFN_UP,
MODEL_TENSOR.FFN_GATE_INP,
MODEL_TENSOR.FFN_GATE_EXP,
MODEL_TENSOR.FFN_DOWN_EXP,
MODEL_TENSOR.FFN_UP_EXP,
MODEL_TENSOR.FFN_GATE_SHEXP,
MODEL_TENSOR.FFN_DOWN_SHEXP,
MODEL_TENSOR.FFN_UP_SHEXP,
MODEL_TENSOR.FFN_EXP_PROBS_B,
# NextN/MTP tensors - preserved but unused
MODEL_TENSOR.NEXTN_EH_PROJ,
MODEL_TENSOR.NEXTN_EMBED_TOKENS,
MODEL_TENSOR.NEXTN_ENORM,
MODEL_TENSOR.NEXTN_HNORM,
MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
],
MODEL_ARCH.BITNET: [
MODEL_TENSOR.ATTN_Q,
MODEL_TENSOR.ATTN_K,
Expand Down Expand Up @@ -5661,6 +5735,7 @@ class VisionProjectorType:
LFM2A = "lfm2a" # audio
MUSIC_FLAMINGO = "musicflamingo" # audio
GLM4V = "glm4v"
GLM5V = "glm5v"
YOUTUVL = "youtuvl"
NEMOTRON_V2_VL = "nemotron_v2_vl"
QWEN3TTS_SPKENC = "qwen3tts_spkenc" # audio: ECAPA-TDNN speaker encoder
Expand Down
Loading