diff --git a/Makefile b/Makefile
index 1f03db62..bd3366ef 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,6 @@ help:
@echo " make test-saving Model save/load"
@echo " make test-data-format channels_first/last"
@echo " make test-data-format-gpu channels_first on TF GPU"
- @echo " make test-links Link validation (slow)"
@echo " make test-gpu All GPU-only tests"
@echo ""
diff --git a/README.md b/README.md
index ab2417ae..c8e19aef 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@ Documentation sources are also available in [`docs/`](docs/).
| GLM-4 (GLM-4-9B) | [ChatGLM: A Family of Large Language Models from GLM-130B to GLM-4](https://arxiv.org/abs/2406.12793) | `transformers` |
| GLM-4-0414 | [THUDM/GLM-4-9B-0414](https://huggingface.co/THUDM/GLM-4-9B-0414) | `transformers` |
| GLM-4.5 / GLM-4.6 (MoE) | [GLM-4.5: Agentic, Reasoning, and Coding (ARC) Foundation Models](https://arxiv.org/abs/2508.06471) | `transformers` |
+ | GLM-4.7-Flash (MoE Lite) | [zai-org/GLM-4.7-Flash](https://huggingface.co/zai-org/GLM-4.7-Flash) | `transformers` |
| GLM-5 / GLM-5.1 / GLM-5.2 (MoE) | [GLM-5 Technical Report](https://arxiv.org/abs/2602.15763) | `transformers` |
diff --git a/docs/glm4_moe_lite.md b/docs/glm4_moe_lite.md
new file mode 100644
index 00000000..81603e3e
--- /dev/null
+++ b/docs/glm4_moe_lite.md
@@ -0,0 +1,144 @@
+# GLM-4.7-Flash (GLM-4 MoE Lite)
+
+
+
+GLM-4.7-Flash is a compact Mixture-of-Experts text model implemented in pure
+Keras 3. Its ZeroModels architecture name is `glm4_moe_lite`. The decoder uses
+DeepSeek-V3-style Multi-head Latent Attention (MLA) and aux-loss-free
+DeepSeekMoE routing. Unlike GLM-5, it has no DSA sparse-attention indexer.
+
+All experts remain resident in memory even though only four routed experts are
+active for each token.
+
+Links:
+
+- Model card: [zai-org/GLM-4.7-Flash](https://huggingface.co/zai-org/GLM-4.7-Flash)
+- Keras weights: [zeromodels/glm-4.7-flash](https://huggingface.co/zeromodels/glm-4.7-flash)
+- Related architecture: [GLM-4.5 (GLM-4 MoE)](glm4_moe.md)
+- Related architecture: [GLM-5 MoE](glm5_moe.md)
+
+## Variant
+
+GLM-4 MoE Lite currently has one hosted variant.
+
+| Variant | Hosted | Upstream |
+|---|---|---|
+| `glm-4.7-flash` | `zeromodels/glm-4.7-flash` | [`zai-org/GLM-4.7-Flash`](https://huggingface.co/zai-org/GLM-4.7-Flash) |
+
+## API
+
+The family exports these public classes:
+
+- `Glm4MoeLiteConfig`
+- `Glm4MoeLiteModel`
+- `Glm4MoeLiteTextGenerate`
+- `Glm4MoeLiteTokenizer`
+
+### `Glm4MoeLiteModel`
+
+The decoder backbone returns
+`{"last_hidden_state": (batch, sequence, embed_dim)}`.
+
+| Arg | Default | Meaning |
+|---|---|---|
+| `vocab_size` | `154880` | token vocabulary size |
+| `embed_dim` | `2048` | model width |
+| `num_layers` | `47` | decoder blocks |
+| `num_heads` | `20` | query heads |
+| `mlp_dim` | `10240` | dense-layer SwiGLU width |
+| `moe_mlp_dim` | `1536` | per-expert SwiGLU width |
+| `num_experts` | `64` | routed expert count |
+| `num_experts_per_tok` | `4` | routed experts selected per token |
+| `n_shared_experts` | `1` | shared expert count |
+| `n_group` | `1` | expert routing group count |
+| `topk_group` | `1` | routing groups retained per token |
+| `norm_topk_prob` | `True` | renormalize selected routing weights |
+| `routed_scaling_factor` | `1.8` | selected-expert output scale |
+| `first_k_dense` | `1` | leading dense decoder layers |
+| `q_lora_rank` | `768` | query projection bottleneck |
+| `kv_lora_rank` | `512` | key/value projection bottleneck |
+| `qk_nope_head_dim` | `192` | non-rotary query/key width per head |
+| `qk_rope_head_dim` | `64` | rotary query/key width per head |
+| `v_head_dim` | `256` | value width per head |
+| `rope_theta` | `1000000.0` | rotary base frequency |
+| `rope_scaling` | `None` | optional Hugging Face YaRN scaling dictionary |
+| `norm_eps` | `1e-5` | RMSNorm epsilon |
+| `max_position_embeddings` | `202752` | maximum configured context length |
+| `tie_embeddings` | `False` | reuse token embeddings for the LM head |
+
+### `Glm4MoeLiteTextGenerate`
+
+`Glm4MoeLiteModel` plus the language-model head. It returns
+`{"logits": (batch, sequence, vocab_size)}` and adds `.generate()`.
+
+```python
+generate(
+ input_ids,
+ attention_mask=None,
+ max_new_tokens=None,
+ eos_token_id=None,
+ sampler=None,
+ seed=None,
+ **prefill_inputs,
+)
+```
+
+When `eos_token_id` is omitted, the generation class recognizes the model's
+three end markers: `154820`, `154827`, and `154829`.
+
+### `Glm4MoeLiteTokenizer`
+
+The BPE tokenizer uses the `tokenizers` backend and loads `tokenizer.json` from
+the Hub repository unless a local file is provided.
+
+```python
+Glm4MoeLiteTokenizer(hf_id=None, tokenizer_file=None)
+```
+
+Calling it with a string or list of strings returns token IDs. Decode one
+sequence with `.decode(ids)` or a batch with `.batch_decode(ids)`.
+
+## End-to-end example
+
+```python
+import os
+
+os.environ["KERAS_BACKEND"] = "torch" # or "jax" / "tensorflow"
+
+from zeromodels.models.glm4_moe_lite import (
+ Glm4MoeLiteTextGenerate,
+ Glm4MoeLiteTokenizer,
+)
+
+weights = "zeromodels/glm-4.7-flash"
+model = Glm4MoeLiteTextGenerate.from_weights(
+ weights,
+ load_dtype="bfloat16",
+)
+tokenizer = Glm4MoeLiteTokenizer.from_weights(weights)
+
+inputs = tokenizer("Explain mixture-of-experts routing in one sentence.")
+outputs = model.generate(**inputs, max_new_tokens=64)
+print(tokenizer.decode(outputs[0]))
+```
+
+The upstream model is large despite its sparse activation. Make sure the
+selected backend has enough host and accelerator memory before converting it.
+
+## Backbone example
+
+```python
+from zeromodels.models.glm4_moe_lite import Glm4MoeLiteModel
+
+backbone = Glm4MoeLiteModel.from_weights(
+ "zeromodels/glm-4.7-flash",
+ load_dtype="bfloat16",
+)
+hidden = backbone(inputs)["last_hidden_state"]
+```
diff --git a/tests/integration/test_auto_registry.py b/tests/integration/test_auto_registry.py
index 63e3469f..0873cbf5 100644
--- a/tests/integration/test_auto_registry.py
+++ b/tests/integration/test_auto_registry.py
@@ -9,6 +9,7 @@
"""
import inspect
+from pathlib import Path
import pytest
@@ -418,3 +419,84 @@ def family(class_name):
"would return a mismatched (model, config, processor) triple; fix the mapping "
f"(or allowlist a genuinely-shared type with its exact family set): {bad}"
)
+
+
+# Families intentionally covered by a shared or differently named documentation page.
+# Keep this mapping explicit: any new package must get its own docs page or be added here
+# with the page that documents it.
+_SHARED_MODEL_DOCS = {
+ "classification_backbones.md": {
+ "cait",
+ "convmixer",
+ "convnext",
+ "convnextv2",
+ "deit",
+ "densenet",
+ "efficientformer",
+ "efficientnet",
+ "efficientnet_lite",
+ "efficientnetv2",
+ "flexivit",
+ "inception_next",
+ "inception_resnetv2",
+ "inceptionv3",
+ "inceptionv4",
+ "levit",
+ "maxvit",
+ "mit",
+ "mlp_mixer",
+ "mobilenetv2",
+ "mobilenetv3",
+ "mobilenetv4",
+ "nextvit",
+ "pit",
+ "poolformer",
+ "regnet",
+ "res2net",
+ "resmlp",
+ "resnet",
+ "resnetv2",
+ "resnext",
+ "senet",
+ "swin",
+ "swinv2",
+ "vgg",
+ "vit",
+ "xception",
+ },
+ "deberta.md": {"deberta_v2", "deberta_v3"},
+ "dinov2.md": {"dino_v2"},
+ "dinov3.md": {"dino_v3"},
+}
+
+
+def test_every_model_family_is_documented():
+ """Every model package has its own page or an explicit shared-page entry."""
+ root = Path(__file__).resolve().parents[2]
+ docs_dir = root / "docs"
+ model_dirs = {
+ path.name
+ for path in (root / "zeromodels" / "models").iterdir()
+ if path.is_dir() and not path.name.startswith("_")
+ }
+ own_pages = {path.stem for path in docs_dir.glob("*.md")}
+
+ shared_families = set()
+ for page, families in _SHARED_MODEL_DOCS.items():
+ assert (docs_dir / page).is_file(), (
+ f"shared model docs page does not exist: {page}"
+ )
+ overlap = shared_families & families
+ assert not overlap, (
+ f"model families listed under multiple shared pages: {overlap}"
+ )
+ shared_families.update(families)
+
+ stale = sorted(shared_families - model_dirs)
+ assert not stale, f"shared model docs entries without a model package: {stale}"
+
+ missing = sorted(model_dirs - own_pages - shared_families)
+ assert not missing, (
+ "model family package(s) without documentation; add docs/.md or an "
+ f"explicit _SHARED_MODEL_DOCS entry: {missing}"
+ )
diff --git a/website/mkdocs.yml b/website/mkdocs.yml
index 4803be20..70b1dc7d 100644
--- a/website/mkdocs.yml
+++ b/website/mkdocs.yml
@@ -145,6 +145,7 @@ nav:
- GLM: glm.md
- GLM-4: glm4.md
- GLM-4 MoE: glm4_moe.md
+ - GLM-4 MoE Lite: glm4_moe_lite.md
- GLM-5 MoE: glm5_moe.md
- GPT: gpt.md
- GPT-2: gpt2.md