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
7 changes: 4 additions & 3 deletions docs/api/build_from_gguf.md

Large diffs are not rendered by default.

44 changes: 43 additions & 1 deletion src/mobius/integrations/gguf/_apertus_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ def test_apertus_registry_promotes_exact_float_and_quantized_graph_import() -> N
assert spec.tensor_map_recipe == ("llama", "apertus_extras")
assert spec.config_postprocessor == "apertus"
assert spec.quantized_import.value == "supported"
assert spec.runtime.value == "deferred"
assert spec.runtime.value == "supported"
assert spec.runtime_evidence_ids == ("apertus-v1.1-1.5b-instruct-bf16-ort-genai-0.15.2",)


def test_apertus_consumes_serialized_rope_and_xielu_values_exactly() -> None:
Expand All @@ -132,6 +133,42 @@ def test_apertus_consumes_serialized_rope_and_xielu_values_exactly() -> None:
assert config.attn_o_bias


def test_apertus_accepts_generic_xielu_metadata_and_default_rope() -> None:
metadata = _metadata()
for suffix in ("alpha_p", "alpha_n", "beta", "eps"):
metadata[f"xielu.{suffix}"] = metadata.pop(f"apertus.xielu.{suffix}")
tensors = _tensors()
tensors.pop("rope_freqs.weight")

config = gguf_to_config(_FakeGGUF(metadata, tensors))

assert config.rope_type == "default"
assert config.rope_scaling is None
assert config.xielu_alpha_p == (-0.2, -0.1)
assert config.xielu_alpha_n == (-1.0, -0.9)


def test_apertus_rejects_conflicting_xielu_metadata_or_unserialized_rope_scaling() -> None:
metadata = _metadata()
metadata["xielu.beta"] = 0.25
with pytest.raises(ValueError, match=r"conflicting apertus\.xielu\.beta and xielu\.beta"):
gguf_to_config(_FakeGGUF(metadata, _tensors()))

metadata = _metadata()
metadata["apertus.rope.scaling.type"] = "longrope"
tensors = _tensors()
tensors.pop("rope_freqs.weight")
with pytest.raises(ValueError, match="factorless RoPE"):
gguf_to_config(_FakeGGUF(metadata, tensors))

metadata = _metadata()
metadata["apertus.rope.scaling.original_context_length"] = 16
tensors = _tensors()
tensors.pop("rope_freqs.weight")
with pytest.raises(ValueError, match="original_context_length requires"):
gguf_to_config(_FakeGGUF(metadata, tensors))


def test_apertus_maps_qk_norm_and_output_bias_without_value_transform() -> None:
assert (
map_gguf_to_hf_names("blk.1.attn_q_norm.bias", "apertus")
Expand Down Expand Up @@ -164,6 +201,11 @@ def test_apertus_rejects_quantized_or_malformed_serialized_rope_factors() -> Non
with pytest.raises(ValueError, match=r"shape \(2,\)"):
gguf_to_config(_FakeGGUF(_metadata(), tensors))

tensors = _tensors()
tensors["rope_factors_short.weight"] = np.ones((2,), np.float32)
with pytest.raises(ValueError, match="must contain exactly"):
gguf_to_config(_FakeGGUF(_metadata(), tensors))


def test_apertus_consumes_complete_longrope_factor_pair_exactly() -> None:
tensors = _tensors()
Expand Down
15 changes: 7 additions & 8 deletions src/mobius/integrations/gguf/_arch_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2258,15 +2258,14 @@
model_type="apertus",
tensor_map_recipe=("llama", "apertus_extras"),
config_postprocessor="apertus",
required_metadata=(
"attention.layer_norm_rms_epsilon",
"xielu.alpha_n",
"xielu.alpha_p",
"xielu.beta",
"xielu.eps",
required_metadata=("attention.layer_norm_rms_epsilon",),
runtime=Support.SUPPORTED,
runtime_evidence_ids=("apertus-v1.1-1.5b-instruct-bf16-ort-genai-0.15.2",),
reason=(
"Runtime support is restricted to the pinned Apertus-v1.1-1.5B-Instruct "
"BF16 artifact's exact-float CPU route, official tokenizer revision, full-logit "
"stateful evidence, and ORT GenAI 0.15.2."
),
runtime=Support.DEFERRED,
reason=_RUNTIME_VALIDATION_PENDING,
),
GGUFArchitectureSpec(
gguf_arch="minicpm",
Expand Down
5 changes: 4 additions & 1 deletion src/mobius/integrations/gguf/_component_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ def attach_runtime_unvalidated_report(
else None
)
tokenizer_verdict = getattr(pkg, "gguf_tokenizer_verdict", None)
if tokenizer is None:
if tokenizer is None or (
tokenizer_exported
and (tokenizer.support != "supported" or tokenizer.output != "exported")
):
if tokenizer_verdict is None:
raise ValueError(
"Runtime-unvalidated packaging requires the tokenizer disposition captured "
Expand Down
92 changes: 71 additions & 21 deletions src/mobius/integrations/gguf/_config_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ def _validate_closed_rope_scaling_metadata(
arch: str,
*,
allowed_suffixes: set[str] | None = None,
allowed_types: set[str] | None = None,
) -> None:
"""Reject RoPE-scaling metadata outside an architecture's exact subset."""
allowed = {f"{arch}.rope.scaling.{suffix}" for suffix in (allowed_suffixes or set())}
Expand All @@ -495,7 +496,8 @@ def _validate_closed_rope_scaling_metadata(
f"{arch} has unsupported RoPE scaling metadata: {', '.join(sorted(unsupported))}"
)
scaling_type = metadata.get(f"{arch}.rope.scaling.type")
if "type" in (allowed_suffixes or set()) and scaling_type not in (None, "", "none"):
accepted_types = {None, "", "none", *(allowed_types or set())}
if "type" in (allowed_suffixes or set()) and scaling_type not in accepted_types:
raise ValueError(
f"{arch} rope.scaling.type={scaling_type!r} is not in the exact supported subset"
)
Expand Down Expand Up @@ -1415,7 +1417,24 @@ def _apertus_postprocess(
layers = config.num_hidden_layers

def per_layer(suffix: str) -> tuple[float, ...]:
raw = metadata[f"{arch}.xielu.{suffix}"]
qualified = f"{arch}.xielu.{suffix}"
generic = f"xielu.{suffix}"
if qualified in metadata and generic in metadata:
if not np.array_equal(
np.asarray(metadata[qualified]), np.asarray(metadata[generic])
):
raise ValueError(
f"GGUF architecture {arch!r} has conflicting {qualified} and {generic}"
)
raw = metadata[qualified]
elif qualified in metadata:
raw = metadata[qualified]
elif generic in metadata:
raw = metadata[generic]
else:
raise ValueError(
f"GGUF architecture {arch!r} is missing required metadata: xielu.{suffix}"
)
values = list(raw) if isinstance(raw, (list, tuple, np.ndarray)) else [raw] * layers
if len(values) != layers:
raise ValueError(
Expand All @@ -1440,15 +1459,36 @@ def per_layer(suffix: str) -> tuple[float, ...]:
}
if any(type_id not in {0, 1, 30} for type_id in raw_types.values()):
raise ValueError("Apertus serialized RoPE factors must use F32/F16/BF16 storage")
_validate_closed_rope_scaling_metadata(
metadata,
arch,
allowed_suffixes={"original_context_length", "type"},
allowed_types={"longrope"},
)
scaling_type = metadata.get(f"{arch}.rope.scaling.type")
original_context_key = f"{arch}.rope.scaling.original_context_length"
factor_pair = {"rope_factors_long.weight", "rope_factors_short.weight"}
if original_context_key in metadata and rope_names != factor_pair:
raise ValueError(
"Apertus rope.scaling.original_context_length requires the complete "
"rope_factors_long.weight/rope_factors_short.weight pair"
)

if rope_names == {"rope_freqs.weight"}:
if not rope_names:
if scaling_type not in {None, "", "none"}:
raise ValueError(
"Apertus factorless RoPE requires rope.scaling.type to be absent or 'none'"
)
short_factors = long_factors = None
Comment thread
justinchuby marked this conversation as resolved.
original_context = config.max_position_embeddings
elif rope_names == {"rope_freqs.weight"}:
Comment thread
justinchuby marked this conversation as resolved.
factors = np.asarray(model.get_tensor("rope_freqs.weight"), dtype=np.float32).reshape(
-1
)
short_factors = long_factors = factors
original_context = config.max_position_embeddings
elif rope_names == {"rope_factors_long.weight", "rope_factors_short.weight"}:
original_context_raw = metadata.get(f"{arch}.rope.scaling.original_context_length")
elif rope_names == factor_pair:
original_context_raw = metadata.get(original_context_key)
if original_context_raw is None:
raise ValueError(
"Apertus LongRoPE factors require apertus.rope.scaling.original_context_length"
Expand All @@ -1470,18 +1510,24 @@ def per_layer(suffix: str) -> tuple[float, ...]:
"Apertus GGUF must contain exactly rope_freqs.weight or the complete "
"rope_factors_long.weight/rope_factors_short.weight pair"
)
if rope_names and scaling_type not in {None, "", "none", "longrope"}:
raise ValueError(
"Apertus serialized RoPE factors require rope.scaling.type to be absent, "
"'none', or 'longrope'"
)

expected = config.head_dim // 2
for name, factors in (("short", short_factors), ("long", long_factors)):
if (
factors.shape != (expected,)
or not np.all(np.isfinite(factors))
or np.any(factors <= 0)
):
raise ValueError(
f"Apertus {name} RoPE factors must have shape ({expected},) and be "
"finite positive values"
)
if short_factors is not None and long_factors is not None:
expected = config.head_dim // 2
for name, factors in (("short", short_factors), ("long", long_factors)):
if (
factors.shape != (expected,)
or not np.all(np.isfinite(factors))
or np.any(factors <= 0)
):
raise ValueError(
f"Apertus {name} RoPE factors must have shape ({expected},) and be "
"finite positive values"
)

q_biases = tuple(f"blk.{layer}.attn_q_norm.bias" in names for layer in range(layers))
k_biases = tuple(f"blk.{layer}.attn_k_norm.bias" in names for layer in range(layers))
Expand All @@ -1491,11 +1537,15 @@ def per_layer(suffix: str) -> tuple[float, ...]:
attn_qk_norm=True,
attn_q_norm_biases=q_biases,
attn_k_norm_biases=k_biases,
rope_type="longrope",
rope_scaling={
"short_factor": short_factors.tolist(),
"long_factor": long_factors.tolist(),
},
rope_type="default" if short_factors is None else "longrope",
rope_scaling=(
None
if short_factors is None or long_factors is None
else {
"short_factor": short_factors.tolist(),
"long_factor": long_factors.tolist(),
}
),
original_max_position_embeddings=original_context,
xielu_alpha_p=per_layer("alpha_p"),
xielu_alpha_n=per_layer("alpha_n"),
Expand Down
4 changes: 4 additions & 0 deletions src/mobius/integrations/gguf/_docs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ def test_runtime_support_requires_structured_evidence() -> None:
("gpt2", ("gpt2-q2-k-ort-genai-0.15.2",)),
("starcoder2", ("tiny-starcoder2-q2-k-ort-genai-0.15.2",)),
("olmo", ("tiny-olmo-q2-k-ort-genai-0.15.2",)),
(
"apertus",
("apertus-v1.1-1.5b-instruct-bf16-ort-genai-0.15.2",),
),
("mpt", ("tiny-mpt-q2-k-ort-genai-0.15.2",)),
("gptneox", ("pythia-70m-q2-k-ort-genai-0.15.2",)),
("starcoder", ("tiny-starcoder-q2-k-ort-genai-0.15.2",)),
Expand Down
4 changes: 2 additions & 2 deletions src/mobius/integrations/gguf/_quant_capabilities_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ def test_selected_real_artifacts_stay_within_global_budget() -> None:
assert isinstance(policy, dict)
assert isinstance(artifacts, list)
assert isinstance(lossy, list)
assert len(artifacts) == 10
assert len(artifacts) == 11
assert len(lossy) == 1
selected = sum(int(record["size"]) for record in [*artifacts, *lossy])
assert selected == 2_724_371_296
assert selected == 5_752_423_904
assert selected == policy["selected_artifact_bytes"]
assert selected <= policy["max_selected_artifact_bytes"]
assert lossy[0]["lfs_sha256"] == (
Expand Down
4 changes: 2 additions & 2 deletions src/mobius/integrations/gguf/_route_census_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ def test_every_route_has_one_actionable_classification() -> None:
assert {item.category for item in items} == allowed
assert all(item.batch and item.dependencies and item.reason.strip() for item in items)
assert Counter(item.kind for item in items) == {
"architecture": 136,
"architecture": 135,
"projector": 60,
"tokenizer": 56,
"mtp": 22,
}
assert Counter(item.category for item in items) == {
"dependency-or-mobius-abi-blocked": 99,
"evidence-only": 151,
"evidence-only": 150,
"intentionally-rejected": 19,
"artifact-unavailable": 5,
}
Expand Down
83 changes: 75 additions & 8 deletions src/mobius/integrations/gguf/_runtime_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,71 @@ def _is_hex(value: str) -> bool:
"dynamic KV cache prefill, full-sequence replay, rollback, reorder, and 20 decode steps"
)

_APERTUS_15B_BF16_ORT_GENAI = GGUFRuntimeEvidence(
evidence_id="apertus-v1.1-1.5b-instruct-bf16-ort-genai-0.15.2",
architecture="apertus",
repository="MrMeOrYou/Apertus-v1.1-1.5B-Instruct-GGUF",
revision="88c75ad49566d3c2157d03709bf772262c3241ed",
filename="Apertus-v1.1-1.5B-Instruct-BF16.gguf",
size=3_028_052_608,
lfs_sha256="f9ec154d0ec29dad1f6465b458b7f27bd25ad7b9a3899233ae98ca6d358501c2",
config_repository="swiss-ai/Apertus-v1.1-1.5B-Instruct",
config_revision="9e9d01154446a645d30f04174cf1515a38058be7",
tokenizer_repository="swiss-ai/Apertus-v1.1-1.5B-Instruct",
tokenizer_revision="9e9d01154446a645d30f04174cf1515a38058be7",
tokenizer_metadata_sha256=(
"3097bd9f22efd32db9045c4705d978539dc8adeeae763324062a5e1a73fc24a5"
),
tokenizer_assets=(
(
"chat_template.jinja",
5_250,
"4afab8361a4bd0c2994404e0b0851dbaad461a06e56bdfdb635aae3977473c19",
),
(
"special_tokens_map.json",
560,
"9f69883bd70fc5d8b55822799837a216d3ac4fb565e05256a0d4f9850404bbc5",
),
(
"tokenizer.json",
17_078_368,
"be12f4375d655cc740864e3a9041bcddd8477942f209d9e7f27f6c8767162638",
),
(
"tokenizer_config.json",
177_274,
"77b14a0664585c26065f07d7a4c852a4615c83348d9378e23def01957bbd3f57",
),
),
tensor_count=163,
tensor_qtypes=(("BF16", 98), ("F32", 65)),
import_route='{"architecture":"apertus","config_sha256":"2a9660c48656c0980e76f1b374262bf8383256603e8b4221aa99bcfa11c510b0","execution_provider":"cpu","model_type":"apertus","module_type":"apertus","preserve_quantization":false,"registry_import":{"config_key_map":null,"config_postprocessor":"apertus","llama_qk_permute":false,"offset_norm":false,"required_metadata":["attention.layer_norm_rms_epsilon"],"rope_interleave":false,"tensor_processor":null,"v_head_reorder":false,"vlm_builder":null},"route_schema":1,"static_cache":false,"task":{"class":"builtins.str","state":"text-generation"},"tensor_map_recipe":["llama","apertus_extras"]}',
source_fidelity=True,
storage_quantized=False,
target_storage_format="float",
compute_mode="float operators",
graph_files=_LOW_COST_GRAPH_FILES,
graph_sha256="4bb91bade19d41559cb524e28692453851fe67274cc58109787ee968df3e0fe5",
runtime_package_files=(
"chat_template.jinja",
"export_report.json",
*_LOW_COST_RUNTIME_PACKAGE_FILES,
),
runtime_package_sha256="97582549e9c5b4114f3bcfa81c92f16aba9d14dd0ea0d2b20acaefd9060e6486",
parity_test=("test_promoted_gguf_full_runtime_evidence[apertus-v1.1-1.5b-instruct-bf16]"),
parity_kind="full-logit",
deterministic_test=(
"test_promoted_gguf_full_runtime_evidence[apertus-v1.1-1.5b-instruct-bf16]"
),
stateful_semantics=_LOW_COST_STATEFUL_SEMANTICS,
execution_provider="CPUExecutionProvider",
onnxruntime_version="1.29.0",
runtime="ort-genai",
runtime_version="0.15.2",
runtime_package_schema=FINAL_RUNTIME_PACKAGE_SCHEMA,
)

_GPT2_Q2_K_ORT_GENAI = GGUFRuntimeEvidence(
evidence_id="gpt2-q2-k-ort-genai-0.15.2",
architecture="gpt2",
Expand Down Expand Up @@ -863,6 +928,7 @@ def _is_hex(value: str) -> bool:
{
record.evidence_id: record
for record in (
_APERTUS_15B_BF16_ORT_GENAI,
_LFM2_350M_F16_ORT_GENAI,
_QWEN25_Q8_ORT_GENAI,
_QWEN35MOE_087B_Q2_K_ORT_GENAI,
Expand Down Expand Up @@ -993,12 +1059,7 @@ def find_matching_runtime_evidence(
"The GGUF source no longer matches the exact artifact identity captured during "
f"graph construction: built={built_identity!r}, current={current_identity!r}."
)
if (
not evidence_ids
or runtime_version is None
or tokenizer_repository is None
or tokenizer_revision is None
):
if not evidence_ids or runtime_version is None:
return None
identity = built_identity
candidates = [
Expand All @@ -1014,8 +1075,14 @@ def find_matching_runtime_evidence(
and _RUNTIME_EVIDENCE[evidence_id].tensor_count == identity.tensor_count
and _RUNTIME_EVIDENCE[evidence_id].tensor_qtypes == identity.tensor_qtypes
and _RUNTIME_EVIDENCE[evidence_id].import_route == import_route
and _RUNTIME_EVIDENCE[evidence_id].tokenizer_repository == tokenizer_repository
and _RUNTIME_EVIDENCE[evidence_id].tokenizer_revision == tokenizer_revision
and (
tokenizer_repository is None
or _RUNTIME_EVIDENCE[evidence_id].tokenizer_repository == tokenizer_repository
)
and (
tokenizer_revision is None
or _RUNTIME_EVIDENCE[evidence_id].tokenizer_revision == tokenizer_revision
)
]
if len(candidates) > 1:
raise RuntimeError(
Expand Down
Loading
Loading