| import gguf |
| import numpy as np |
| from constants import ARCH_FEATURES |
|
|
|
|
| def detect_architecture(tensors: dict) -> str: |
| """Detect model architecture from tensor names.""" |
| names = list(tensors.keys()) |
|
|
| has_ssm = any("ssm_" in n for n in names) |
| has_qkv = any("attn_qkv" in n for n in names) |
| has_nextn = any("nextn" in n for n in names) |
| has_moe = any("exps" in n for n in names) |
| has_separate_qkv = any("attn_q.weight" in n for n in names) |
| has_gemma_specific = any( |
| t in n for t in ("layer_output_scale", "post_attention_norm", "post_ffw_norm") |
| for n in names |
| ) |
|
|
| if has_ssm and has_qkv: |
| return "qwen35" |
| if has_moe: |
| return "mellum2" |
| |
| if has_separate_qkv or has_gemma_specific: |
| return "gemma4" |
| return "unknown" |
|
|
|
|
| def _detect_prefix(tensors: dict) -> str: |
| for name in tensors: |
| if name.startswith("BLK."): |
| return "BLK" |
| return "blk" |
|
|
|
|
| def _estimate_layers(tensors: dict) -> int: |
| max_layer = 0 |
| for name in tensors: |
| parts = name.split(".") |
| if len(parts) >= 2 and parts[0] in ("blk", "BLK"): |
| try: |
| layer = int(parts[1]) |
| if layer > max_layer: |
| max_layer = layer |
| except ValueError: |
| pass |
| return max_layer + 1 |
|
|
|
|
| def read_model(path: str) -> dict: |
| """Parse BF16 GGUF, return model info.""" |
| r = gguf.GGUFReader(path) |
| tensors = {} |
| meta = {} |
|
|
| for k, v in r.fields.items(): |
| |
| try: |
| data = v.data |
| if isinstance(data, np.ndarray): |
| data = data.tolist() |
| elif isinstance(data, (np.generic,)): |
| data = data.item() |
| meta[k] = data |
| except Exception: |
| meta[k] = str(v) |
|
|
| for t in r.tensors: |
| shape = list(t.shape) |
| name = t.name |
| n_elements = int(np.prod(shape)) |
| tensors[name] = { |
| "shape": shape, |
| "n_elements": n_elements, |
| "size_mib": n_elements * 2 / 1024 / 1024, |
| } |
|
|
| arch = detect_architecture(tensors) |
| arch_features = ARCH_FEATURES.get(arch, {}).copy() |
|
|
| prefix = _detect_prefix(tensors) |
| n_layers = _estimate_layers(tensors) |
|
|
| if arch == "mellum2" and arch_features.get("moe_intermediate_size", 0) == 0: |
| arch_features["moe_intermediate_size"] = 896 |
|
|
| arch_features["prefix"] = prefix |
| if n_layers > 0: |
| arch_features["n_layers"] = n_layers |
|
|
| has_moe = any("exps" in n for n in tensors) |
| if has_moe: |
| arch_features["has_moe"] = True |
|
|
| has_nextn = any("nextn" in n for n in tensors) |
| |
| has_blk32 = n_layers <= 33 and any( |
| n.startswith("blk.32.") or n.startswith("BLK.32.") for n in tensors |
| ) |
| arch_features["has_mtp"] = has_nextn or has_blk32 |
|
|
| return { |
| "path": path, |
| "architecture": arch, |
| "features": arch_features, |
| "tensors": tensors, |
| "n_tensors": len(tensors), |
| "meta": meta, |
| } |
|
|