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
673 changes: 397 additions & 276 deletions docs/api/build_from_gguf.md

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions docs/cli_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ mobius build --model Qwen/Qwen2.5-0.5B --output output_dir/ \

## `mobius build-gguf`

Build an ONNX model from a GGUF file (e.g. from llama.cpp).
Build an ONNX model from a GGUF file (e.g. from llama.cpp). This is an explicit
opt-in import path; `mobius build` does not auto-discover or select GGUF files.
Supported GGUF quantization is preserved by default. This can involve
byte-preserving native blocks in text-only builds, affine repacking, or
dequantize/requantize for multimodal and mixed source qtypes.
Expand All @@ -338,7 +339,7 @@ mobius build-gguf GGUF_PATH --output OUTPUT_DIR [options]

| Argument | Description |
|----------|-------------|
| `GGUF_PATH` | Path to a `.gguf` model file. |
| `GGUF_PATH` | Local `.gguf` path or exact `owner/repo:filename.gguf` Hub reference. Hub preflight range-reads only that filename, resolves the requested ref to an immutable commit, and downloads that exact revision; repository-level metadata is never used. |

### Options

Expand All @@ -350,7 +351,10 @@ mobius build-gguf GGUF_PATH --output OUTPUT_DIR [options]
| `--dtype DTYPE` | Target dtype for model weights: `f16`, `bf16`, `f32`. |
| `--external-data FORMAT` | External data format: `onnx` (default) or `safetensors`. |
| `--ep EP` | Target execution provider for EP-aware optimization. |
| `--runtime RUNTIME` | `onnx-genai` emits supported runtime metadata. `ort-genai` is rejected until GGUF cache/tokenizer generation coverage exists. |
| `--runtime RUNTIME` | Request `onnx-genai` or `ort-genai` metadata. Emission is rejected unless the architecture has structured runtime evidence and the GGUF embeds an exact validated tokenizer; neither format bypasses the architecture verdict. |
| `--runtime-version VERSION` | Exact selected runtime version. Once runtime support exists, this must equal the version in the matching evidence record; compatible-version ranges are not inferred. |
| `--mmproj PATH` | Exact companion `clip` GGUF. Pairing validates source identity, target architecture, modality, tensor closure, and dimensions before graph construction. |
| `--target-config PATH` | Exact target config directory for `dflash`/`eagle3`; requires the adjacent complete `tokenizer.json` and emits a target-binding draft manifest. |
| `--release` | Strip build-time debug and provenance metadata before saving while preserving functional `mobius.*` metadata. |
| `--static-cache` | Build a fixed-width cache where supported. |
| `--max-seq-len N` | Set the fixed cache length; requires `--static-cache`. |
Expand All @@ -371,7 +375,7 @@ mobius build-gguf model.gguf --output output/ --dtype f16
F32-, F16-, and BF16-only files build normally as float models because they
contain no quantization to preserve.
Quantized files containing only qtypes with no supported preservation target
(for example, pure Q6_K or Q5_K weights) fail instead of silently becoming
(for example, pure Q5_K weights) fail instead of silently becoming
float. Re-run with `--dequantize` to request explicit float conversion.

Encoder-only BERT and ModernBERT GGUF backbones auto-select
Expand All @@ -390,6 +394,13 @@ parity, mixed expert quantization, tokenizer provenance, and real ORT/ORT GenAI
generation are validated. See
[`build_from_gguf()`](api/build_from_gguf.md#nvidia-nemotron-35-lightning-waiver).

Runtime packaging requires a validated embedded `tokenizer.huggingface.json`;
opaque tokenizer pre-types are never reconstructed. Deferred/rejected
architecture, tokenizer, draft-pairing, or mmproj checks run before durable
output. Multimodal packages use `decoder`, `vision_encoder`, optional
`audio_encoder`, and `embedding`; an admitted trailing MTP head is persisted
under `mtp/`.
Comment on lines +397 to +402

---

## `mobius list`
Expand Down
34 changes: 34 additions & 0 deletions scripts/generate_gguf_support_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) Microsoft Corporation.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
# Licensed under the MIT License.

"""Refresh or check the generated GGUF support census documentation."""

from __future__ import annotations
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))

from mobius.integrations.gguf._docs import DOC_PATH, update_document


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--check", action="store_true")
args = parser.parse_args()
generated = update_document()
current = DOC_PATH.read_text(encoding="utf-8")
if args.check:
if current != generated:
raise SystemExit(
"docs/api/build_from_gguf.md is stale; run "
"`python scripts/generate_gguf_support_docs.py`"
)
return
DOC_PATH.write_text(generated, encoding="utf-8")


if __name__ == "__main__":
main()
28 changes: 23 additions & 5 deletions src/mobius/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ def _cmd_build_gguf(args: argparse.Namespace) -> None:
print("Dequantized mode: converting GGUF weights to float...")

gguf_path = args.gguf_path
gguf_reference = gguf_path
output_dir = args.output_dir
target_config = getattr(args, "target_config", None)
runtime = getattr(args, "runtime", None)
Expand All @@ -638,28 +639,36 @@ def _cmd_build_gguf(args: argparse.Namespace) -> None:
)

if runtime is not None:
from mobius.integrations.gguf._arch_registry import get_arch_spec
from mobius.integrations.gguf._builder import (
_resolve_gguf_path,
_validate_gguf_model,
)
from mobius.integrations.gguf._reader import GGUFModel
from mobius.integrations.gguf._spec import Support
from mobius.integrations.gguf._tokenizer import inspect_gguf_tokenizer

# Resolve and validate the exact selected source before graph construction
# so a deferred tokenizer cannot leave a graph-only directory behind.
gguf_path = _resolve_gguf_path(gguf_path)
gguf_model = GGUFModel(gguf_path)
_validate_gguf_model(gguf_model, source=str(gguf_path))
resolved_gguf_path = _resolve_gguf_path(gguf_path)
gguf_model = GGUFModel(resolved_gguf_path)
_validate_gguf_model(gguf_model, source=str(resolved_gguf_path))
architecture_spec = get_arch_spec(gguf_model.architecture)
if architecture_spec.runtime is not Support.SUPPORTED:
raise SystemExit(
f"Error: GGUF runtime packaging for {architecture_spec.gguf_arch!r} is "
f"{architecture_spec.runtime.value}: {architecture_spec.reason}"
)
tokenizer_verdict = inspect_gguf_tokenizer(
gguf_model.metadata, source=str(gguf_path), require_complete=True
gguf_model.metadata, source=str(resolved_gguf_path), require_complete=True
)
if not tokenizer_verdict.materialized:
raise SystemExit(
f"Error: cannot emit a complete {runtime} package: {tokenizer_verdict.reason}"
)

pkg = build_from_gguf(
gguf_path,
gguf_reference,
mmproj=mmproj_path,
dtype=args.dtype,
keep_quantized=keep_quantized,
Expand Down Expand Up @@ -715,6 +724,7 @@ def _cmd_build_gguf(args: argparse.Namespace) -> None:
gguf_path,
output_dir,
runtime=runtime,
runtime_version=getattr(args, "runtime_version", None),
external_data=args.external_data,
Comment on lines 724 to 728
max_shard_size_bytes=(
_parse_size(args.max_shard_size) if args.max_shard_size else None
Expand Down Expand Up @@ -1251,6 +1261,14 @@ def build_parser() -> argparse.ArgumentParser:
"genai_config.json."
),
)
gguf_parser.add_argument(
"--runtime-version",
default=None,
help=(
"Exact selected runtime version. Required once an architecture has a "
"runtime-supported evidence record; it must equal the version validated there."
),
)
gguf_parser.add_argument(
"--static-cache",
action="store_true",
Expand Down
6 changes: 2 additions & 4 deletions src/mobius/integrations/gguf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
# Multimodal (text + companion mmproj vision/audio encoder)
pkg = build_from_gguf("path/to/model.gguf", mmproj="path/to/mmproj.gguf")

# Write a directory the runtime can actually load (graph + tokenizer +
# inference metadata). Saving only the graph produces a package that
# loads nowhere.
write_gguf_runtime_package(pkg, "path/to/model.gguf", "out_dir")
# Runtime packaging is fail-closed and currently unavailable because no
# architecture has complete real-artifact runtime evidence.

:func:`build_from_gguf` is the single entry point; passing ``mmproj`` delegates
to :func:`build_gemma4_vlm_from_gguf` for the multimodal assembly.
Expand Down
42 changes: 41 additions & 1 deletion src/mobius/integrations/gguf/_arch_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,24 +690,32 @@
tensor_map_recipe=("llama",),
tensor_processor="llama",
llama_qk_permute=True,
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="deci",
model_type="llama",
tensor_map_recipe=("llama",),
tensor_processor="llama",
llama_qk_permute=True,
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
# ----------------------------------------------------------------- Qwen
GGUFArchitectureSpec(
gguf_arch="qwen2",
model_type="qwen2",
tensor_map_recipe=("llama",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="qwen3",
model_type="qwen3",
tensor_map_recipe=("llama",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="dflash",
Expand Down Expand Up @@ -903,13 +911,17 @@
model_type="gemma",
tensor_map_recipe=("llama",),
tensor_processor="unoffset_norm",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="gemma2",
model_type="gemma2",
tensor_map_recipe=("llama", "gemma2_extras"),
tensor_processor="unoffset_norm",
config_postprocessor="gemma2",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="gemma3",
Expand All @@ -919,6 +931,8 @@
# `+1` baked into every *norm.weight must be removed on import.
tensor_processor="unoffset_norm",
config_postprocessor="gemma3",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="gemma4",
Expand All @@ -930,12 +944,16 @@
# corrupt every norm.
config_postprocessor="gemma4",
vlm_builder="gemma4",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
# -------------------------------------------------------------- Various
GGUFArchitectureSpec(
gguf_arch="phi3",
model_type="phi3",
tensor_map_recipe=("phi3",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="baichuan",
Expand Down Expand Up @@ -990,12 +1008,16 @@
gguf_arch="falcon",
model_type="falcon",
tensor_map_recipe=("falcon",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="gpt2",
model_type="gpt2",
tensor_map_recipe=("gpt2",),
tensor_processor="gpt2",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="mamba",
Expand Down Expand Up @@ -1198,20 +1220,25 @@
gguf_arch="starcoder2",
model_type="starcoder2",
tensor_map_recipe=("llama",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="stablelm",
model_type="stablelm",
tensor_map_recipe=("llama",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="internlm2",
model_type="internlm2",
tensor_map_recipe=("llama",),
tensor_processor="llama",
llama_qk_permute=True,
runtime=Support.DEFERRED,
quantized_import=Support.REJECTED,
reason=_NO_QUANTIZED_PROJECTION_REASON,
reason=_RUNTIME_VALIDATION_PENDING + " " + _NO_QUANTIZED_PROJECTION_REASON,
),
GGUFArchitectureSpec(
gguf_arch="olmo",
Expand All @@ -1221,13 +1248,17 @@
required_metadata=("attention.layer_norm_epsilon",),
tensor_processor="llama",
llama_qk_permute=True,
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="olmo2",
model_type="olmo2",
tensor_map_recipe=("llama", "olmo2_extras"),
config_postprocessor="dense_sliding",
required_metadata=("attention.layer_norm_rms_epsilon",),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="olmoe",
Expand Down Expand Up @@ -1439,6 +1470,8 @@
required_metadata=("attention.layer_norm_rms_epsilon",),
tensor_processor="llama",
llama_qk_permute=True,
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="exaone",
Expand All @@ -1453,12 +1486,16 @@
model_type="nemotron",
tensor_map_recipe=("llama",),
tensor_processor="unoffset_norm",
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="hunyuan-dense",
model_type="hunyuan_v1_dense",
aliases=frozenset({"hunyuan_v1_dense"}),
tensor_map_recipe=("llama", "hunyuan_extras"),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="muse-glimmer",
Expand All @@ -1470,6 +1507,8 @@
config_postprocessor="muse_glimmer",
vlm_builder="muse_glimmer",
llama_qk_permute=True,
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="deepseek4",
Expand Down Expand Up @@ -1535,6 +1574,7 @@
gguf_arch="bloom",
model_type="bloom",
tensor_map=Support.DEFERRED,
runtime=Support.DEFERRED,
quantized_import=Support.REJECTED,
reason=_NO_TENSOR_MAP,
),
Expand Down
14 changes: 5 additions & 9 deletions src/mobius/integrations/gguf/_arch_registry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,15 +1551,11 @@ def _expected_rows() -> list[str]:
return rows

def test_the_doc_table_matches_the_registry(self) -> None:
text = self._DOC.read_text(encoding="utf-8")
assert self._BEGIN in text and self._END in text, (
f"{self._DOC} is missing the generated support-matrix markers"
)
block = text.split(self._BEGIN, 1)[1].split(self._END, 1)[0]
documented = [line for line in block.splitlines() if line.startswith("| `")]
assert documented == self._expected_rows(), (
"docs/api/build_from_gguf.md is out of date with the architecture "
"registry. Regenerate the support matrix between its markers."
from mobius.integrations.gguf._docs import check_document

assert check_document(), (
"docs/api/build_from_gguf.md is out of date; run "
"`python scripts/generate_gguf_support_docs.py`."
)


Expand Down
Loading
Loading