Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 8 additions & 8 deletions docs/api/build_from_gguf.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/mobius/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ def _resolve_static_cache_task(model_type: str) -> ModelTask:
"--static-cache cannot represent Falcon-H1's per-layer K, V, "
"convolution, and SSM states"
)
if model_type == "glm_moe_dsa":
raise ValueError(
"--static-cache cannot represent GLM-DSA's per-layer-varying packed DSA cache"
)
if model_type == "mistral4_gguf":
raise ValueError(
"--static-cache is not implemented for Mistral4's latent K-only cache"
)
if model_type == "gemma4":
from mobius.tasks._gemma4 import Gemma4Task

Expand Down
13 changes: 13 additions & 0 deletions src/mobius/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@
Llama4CausalLMModel,
MageVLForConditionalGeneration,
MaincoderCausalLMModel,
MiniMaxM2GGUFCausalLMModel,
Mistral4GGUFCausalLMModel,
MoECausalLMModel,
MoonshineForConditionalGeneration,
NanoChatCausalLMModel,
Expand Down Expand Up @@ -492,6 +494,17 @@ def _detect_fallback_registration(hf_config) -> ModelRegistration | None:
test_model_id="Maincode/Maincoder-1B",
test_revision="088ec98640bdeb105f46a9ef6a1370ed5d0d2ea5",
),
"minimax_m2_gguf": ModelRegistration(
MiniMaxM2GGUFCausalLMModel,
family="minimax",
variant="gguf",
),
"mistral4_gguf": ModelRegistration(
Mistral4GGUFCausalLMModel,
task="mistral4-gguf-text-generation",
family="mistral",
variant="gguf",
),
"bloom": ModelRegistration(BloomCausalLMModel),
"orion": ModelRegistration(LayerNormCausalLMModel),
"chatglm": ModelRegistration(ChatGLMCausalLMModel),
Expand Down
60 changes: 42 additions & 18 deletions src/mobius/components/_deepseek_mla.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(
config: ArchitectureConfig,
scale: float | None = None,
linear_class: type | None = None,
split_kv_b: bool = False,
):
super().__init__()
if linear_class is None:
Expand Down Expand Up @@ -82,12 +83,25 @@ def __init__(
bias=False,
)
self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
# Decompresses latent KV into per-head k_nope + v
self.kv_b_proj = linear_class(
self.kv_lora_rank,
self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
bias=False,
)
self._split_kv_b = split_kv_b
if split_kv_b:
self.k_b_proj = linear_class(
self.kv_lora_rank,
self.num_heads * self.qk_nope_head_dim,
bias=False,
)
self.v_b_proj = linear_class(
self.kv_lora_rank,
self.num_heads * self.v_head_dim,
bias=False,
)
else:
# Decompresses latent KV into per-head k_nope + v
self.kv_b_proj = linear_class(
self.kv_lora_rank,
self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
bias=False,
)

self.o_proj = linear_class(
self.num_heads * self.v_head_dim,
Expand Down Expand Up @@ -154,18 +168,28 @@ def forward(

# Decompress latent KV → per-head k_nope + v
k_pass = self.kv_a_layernorm(op, k_pass)
kv_decompressed = self.kv_b_proj(op, k_pass)
# (B, S, num_heads * (nope + v_dim)) → (B, S, num_heads, nope + v_dim)
kv_decompressed = op.Reshape(
kv_decompressed,
[0, 0, self.num_heads, self.qk_nope_head_dim + self.v_head_dim],
)
k_nope, value_states = op.Split(
kv_decompressed,
[self.qk_nope_head_dim, self.v_head_dim],
axis=-1,
_outputs=2,
)
if self._split_kv_b:
k_nope = op.Reshape(
self.k_b_proj(op, k_pass),
[0, 0, self.num_heads, self.qk_nope_head_dim],
)
value_states = op.Reshape(
self.v_b_proj(op, k_pass),
[0, 0, self.num_heads, self.v_head_dim],
)
else:
kv_decompressed = self.kv_b_proj(op, k_pass)
# (B, S, num_heads * (nope + v_dim)) → (B, S, num_heads, nope + v_dim)
kv_decompressed = op.Reshape(
kv_decompressed,
[0, 0, self.num_heads, self.qk_nope_head_dim + self.v_head_dim],
)
k_nope, value_states = op.Split(
kv_decompressed,
[self.qk_nope_head_dim, self.v_head_dim],
axis=-1,
_outputs=2,
)
# k_nope: (B, S, H, nope_dim) → (B, S, H*nope_dim)... not needed yet
# value_states: (B, S, H, v_dim) → (B, S, H*v_dim) for Attention op
value_states = op.Reshape(value_states, [0, 0, -1])
Expand Down
129 changes: 114 additions & 15 deletions src/mobius/integrations/gguf/_arch_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,6 @@
"Q/K permutation, optional long/short RoPE tensors, and a conditional dense-or-MoE "
"loader. The existing MiniCPM graph does not prove this complete GGUF contract."
),
"minimax-m2": (
"MiniMax-M2 uses full-vector Q/K norms, partial RoPE, and all-layer "
"correction-biased routed experts under metadata-selected gating. Mobius has no "
"exact graph or suffix-safe expert import for that topology."
),
"minimax-m3": (
"MiniMax-M3 adds F32 sparse-indexer tensors and a second index-key cache with "
"position/cell maps, block masks, rollback, and reorder semantics alongside main "
Expand Down Expand Up @@ -584,11 +579,6 @@
"KV cache, and three chained heads selected by offsets. Mobius permits one head "
"and cannot preserve that state or FP8 converter transform."
),
"mistral4": (
"Mistral4 has no NextN metadata or MTP graph; it inherits Mistral3's conditional "
"dense/MoE tensor loader and overrides graph construction. It is not llama, "
"mistral alias text, or any DeepSeek/Qwen MTP family."
),
"step35": (
"Step3.5 executes one or more interleaved-SWA NextN heads with optional gates, "
"routed/shared experts, centered-norm transforms, per-layer head geometry, and "
Expand Down Expand Up @@ -1334,6 +1324,42 @@
"runtime packaging remains tracked by #605."
),
),
GGUFArchitectureSpec(
gguf_arch="minimax-m2",
model_type="minimax_m2_gguf",
module_type="minimax_m2_gguf",
config_key_map="minimax_m2",
config_postprocessor="minimax_m2",
tensor_map_recipe=("minimax_m2",),
required_metadata=(
"context_length",
"embedding_length",
"feed_forward_length",
"block_count",
"attention.head_count",
"attention.head_count_kv",
"attention.key_length",
"attention.value_length",
"attention.layer_norm_rms_epsilon",
"rope.freq_base",
"rope.dimension_count",
"expert_count",
"expert_used_count",
"expert_feed_forward_length",
"expert_gating_func",
),
runtime=Support.DEFERRED,
quantized_import=Support.REJECTED,
reason=(
"Exact explicit-float import owns MiniMax-M2's non-square Q/O geometry, "
"full-vector Q/K RMSNorm, partial NeoX RoPE, standard dynamic/static KV cache, "
"and F32 selection-biased sigmoid MoE routing. Loader-accepted Q/K/V biases "
"and fused QKV are rejected because the pinned graph ignores or cannot execute "
"them. Packed projection/expert import and runtime packaging remain deferred; "
"the smallest immutable public GGUF is 46,514,882,176 bytes, above the "
"16 GiB evidence budget. Use keep_quantized=False."
),
),
GGUFArchitectureSpec(
gguf_arch="plamo",
model_type="plamo",
Expand Down Expand Up @@ -2131,6 +2157,48 @@
runtime=Support.DEFERRED,
reason=_DEEPSEEK4_GGUF_GRAPH_REASON,
),
GGUFArchitectureSpec(
gguf_arch="mistral4",
model_type="mistral4_gguf",
module_type="mistral4_gguf",
config_key_map="mistral4",
config_postprocessor="mistral4",
tensor_map_recipe=("mistral4",),
tensor_processor="mistral4",
required_metadata=(
"context_length",
"embedding_length",
"feed_forward_length",
"block_count",
"attention.head_count",
"attention.head_count_kv",
"attention.key_length",
"attention.value_length",
"attention.key_length_mla",
"attention.value_length_mla",
"attention.q_lora_rank",
"attention.kv_lora_rank",
"attention.layer_norm_rms_epsilon",
"rope.freq_base",
"rope.dimension_count",
"expert_count",
"expert_used_count",
"expert_feed_forward_length",
"expert_shared_count",
),
rope_interleave=True,
runtime=Support.DEFERRED,
quantized_import=Support.REJECTED,
reason=(
"Exact explicit-float import owns Mistral4's DeepSeek-V2 MLA projections, "
"dense-prefix plus mandatory shared/routed MoE blocks, YaRN scaling, and one "
"graph-visible latent-plus-RoPE K cache per layer. Legacy non-MLA, Q-LoRA-free, "
"temperature-scaled, and NextN layouts fail closed. Packed import and runtime "
"packaging remain deferred; the smallest immutable public GGUF is "
"32,306,941,632 bytes, above the 16 GiB evidence budget. "
"Use keep_quantized=False."
),
),
# GLM-5.2 GGUFs (e.g. unsloth/GLM-5.2-GGUF) tag the architecture 'glm-dsa'
# (MLA + DeepSeek Sparse Attention + MoE) and mobius's registry key is
# 'glm_moe_dsa'. The format bridge is keyed on the authoritative
Expand All @@ -2142,12 +2210,43 @@
model_type="glm_moe_dsa",
aliases=frozenset({"glm_dsa"}),
config_key_map="glm_dsa",
tensor_map=Support.DEFERRED,
config_postprocessor="glm_dsa",
tensor_map_recipe=("glm_dsa",),
tensor_processor="glm_dsa",
required_metadata=(
"context_length",
"embedding_length",
"feed_forward_length",
"block_count",
"attention.head_count",
"attention.head_count_kv",
"attention.key_length",
"attention.value_length",
"attention.key_length_mla",
"attention.value_length_mla",
"attention.q_lora_rank",
"attention.kv_lora_rank",
"attention.layer_norm_rms_epsilon",
"rope.freq_base",
"rope.dimension_count",
"expert_count",
"expert_used_count",
"expert_feed_forward_length",
"expert_shared_count",
"attention.indexer.head_count",
"attention.indexer.key_length",
"attention.indexer.top_k",
),
rope_interleave=True,
quantized_import=Support.REJECTED,
reason=(
"Config extraction and the glm_moe_dsa graph are both available, but "
"no GGUF→HuggingFace tensor-name mapping has been written for GLM-5.2's "
"MLA + DSA-indexer tensor families yet, so weights cannot be routed "
"into the graph. " + _NO_TENSOR_MAP
"Exact explicit-float MLA, DSA indexer, dense-prefix/routed-MoE tensor routing, "
"and packed dynamic-cache graph construction are covered. Packed import remains "
"rejected because GLM-5.2's rank-3 K/V-B transforms and correction-biased routed "
"experts do not have an independently proven storage-preserving runtime path. "
"The released GGUF also carries a routed DSA/MLA MTP block outside the current "
"sidecar ABI, and representative real-weight runtime evidence exceeds the 16 GiB "
"artifact budget. Use keep_quantized=False."
),
),
GGUFArchitectureSpec(
Expand Down
7 changes: 6 additions & 1 deletion src/mobius/integrations/gguf/_arch_registry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
#: Number of importable architectures. Pinned so that adding support is a
#: deliberate act that also updates the documented support matrix, and so that
#: accidentally losing an architecture is a failure rather than a silence.
_EXPECTED_SUPPORTED_COUNT = 102
_EXPECTED_SUPPORTED_COUNT = 105
_PROMOTED_CONVENTIONAL_DECODERS = frozenset(
{
"bitnet",
Expand All @@ -85,6 +85,8 @@
"gemma-embedding",
"llama-embed",
"maincoder",
"minimax-m2",
"mistral4",
"starcoder",
"xverse",
}
Expand Down Expand Up @@ -328,6 +330,8 @@ def test_every_float_importable_architecture_has_a_quantized_verdict(self) -> No
"mamba",
"mamba2",
"maincoder",
"minimax-m2",
"mistral4",
"nemotron_h",
"nemotron_h_moe",
"neo-bert",
Expand All @@ -343,6 +347,7 @@ def test_every_float_importable_architecture_has_a_quantized_verdict(self) -> No
"grok",
"grovemoe",
"hunyuan-moe",
"glm-dsa",
"hy_v3",
"jais",
"mpt",
Expand Down
Loading
Loading