fix(model): preserve unsupported GGUF version diagnostics (#5789)

Preserve unsupported GGUF version bytes and both UINT32 interpretations without inventing byte order at the shared parser. Keep existing layout support, typed errors, evidence identity, scoped reads and resource limits.

Validation: 78 focused tests, full static/architecture and documentation checks, real CLI/MCP version controls and an independently checked public model. Latest-head Codex found no issues; Greptile is 5/5 and all review threads are resolved.

Some CI remained pending at merge qualification. Merge under the human owner's explicit standing bypass authorization does not claim the full CI suite passed.

Fixes #5749.
This commit is contained in:
gensokyo-dev
2026-10-21 13:59:18 +08:00
committed by GitHub
parent bd8d73ab1d
commit 25bea07b42
3 changed files with 68 additions and 7 deletions
+5
View File
@@ -5,6 +5,11 @@ GGUF v2/v3 or safetensors file. Detection uses content, including GGUF byte
order, rather than its extension. REA does not load a model, interpret weights,
run inference, import Python, open external-data references or use the network.
Unsupported GGUF versions remain refused. Their diagnostic preserves the four
version bytes and both UINT32 interpretations with byte order unknown, rather
than inferring big-endian from an unrecognized little-endian value. Little-endian
v2/v3 and big-endian v3 remain supported; big-endian v2 remains refused.
```bash
rea inspect-model-artifact /absolute/path/model.gguf --json
rea inspect-model-artifact /absolute/path/model.safetensors --hash full --json
+12 -7
View File
@@ -247,16 +247,21 @@ export class GgufHeader {
tensors: ModelTensor[];
}> {
const versionBytes = await this.reader.take(4);
this.#little =
versionBytes.readUInt32LE() === 2 || versionBytes.readUInt32LE() === 3;
const version = this.#little
? versionBytes.readUInt32LE()
: versionBytes.readUInt32BE();
if (version !== 2 && version !== 3)
const littleVersion = versionBytes.readUInt32LE();
const bigVersion = versionBytes.readUInt32BE();
let version: number;
if (littleVersion === 2 || littleVersion === 3) {
this.#little = true;
version = littleVersion;
} else if (bigVersion === 2 || bigVersion === 3) {
this.#little = false;
version = bigVersion;
} else {
throw new ModelFormatFailure(
"unsupported",
`GGUF version ${String(version)} is unsupported; supported versions are 2 and 3`,
`GGUF version bytes ${versionBytes.toString("hex")} are unsupported (little-endian ${String(littleVersion)}, big-endian ${String(bigVersion)}; byte order unknown); supported versions are 2 and 3`,
);
}
if (!this.#little && version !== 3)
rejectModel("Big-endian GGUF requires version 3");
const tensorCount = await this.#unsigned(8);
@@ -22,6 +22,57 @@ const providerAt = async (bytes: Buffer) => {
return { provider, path, root };
};
it.each([
["01000000", 1, 16_777_216],
["00000001", 16_777_216, 1],
["04000000", 4, 67_108_864],
["00000004", 67_108_864, 4],
["e7030000", 999, 3_875_733_504],
["000003e7", 3_875_733_504, 999],
["00000000", 0, 0],
["ffffffff", 4_294_967_295, 4_294_967_295],
] as const)(
"preserves unsupported GGUF version bytes %s without assuming byte order",
async (versionBytes, little, big) => {
// No counts are needed to explain a version refusal. Do not reinterpret
// an unknown version's subsequent layout or publish a header observation.
const { provider, path } = await providerAt(
Buffer.from(`47475546${versionBytes}`, "hex"),
);
const result = await provider.inspect({ path, hash: "header" });
if (result.ok) throw new Error("Unknown version admitted");
expect(result.error).toMatchObject({
_tag: "AnalysisUnsupportedTargetError",
reason: `GGUF version bytes ${versionBytes} are unsupported (little-endian ${String(little)}, big-endian ${String(big)}; byte order unknown); supported versions are 2 and 3`,
});
},
);
it("continues reading little-endian GGUF v2 declarations", async () => {
const { provider, path } = await providerAt(
ggufFixture({ version: 2 }).bytes,
);
const result = await provider.inspect({ path, hash: "header" });
if (!result.ok) throw result.error;
expect(result.value).toMatchObject({ version: 2, byte_order: "little" });
expect(result.value.tensors.map(({ name }) => name)).toEqual([
"weight",
"quant",
]);
});
it("continues refusing the unsupported big-endian GGUF v2 layout", async () => {
const { provider, path } = await providerAt(
ggufFixture({ version: 2, bigEndian: true }).bytes,
);
const result = await provider.inspect({ path, hash: "header" });
if (result.ok) throw new Error("Big-endian GGUF v2 admitted");
expect(result.error).toMatchObject({
_tag: "BinaryTargetError",
constraint: "file_format_rejected",
});
});
it.each([false, true])(
"reads exact GGUF declarations, endian=%s, without hashing tensor payload",
async (bigEndian) => {