mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(model): retain resolved GGUF alignment under cancellation
Publish alignment from the parser when explicit metadata validates it, or when metadata completion establishes the default. Preserve that fact through later parsing and hashing failures. Fixes #6579
This commit is contained in:
1 parent
b6833b9f61
commit
275095a9c6
4 files changed
+197
-95
No files matched your search
@@ -44,7 +44,10 @@ that defeats filesystem metadata checks.
|
||||
GGUF reports version, byte order, alignment, typed metadata, and each tensor's
|
||||
name, original type ID, dimensions, relative data offset, file offset and derived
|
||||
byte length. Alignment accepts every positive UINT32 multiple of eight, including
|
||||
non-powers of two. Metadata keys use ASCII lower-snake-case hierarchy components;
|
||||
non-powers of two. Partial Evidence retains explicit alignment once validated;
|
||||
the default alignment becomes known after metadata completes. Cancellation
|
||||
during tensor parsing or hashing retains that fact without claiming finalized
|
||||
digests or file identity. Metadata keys use ASCII lower-snake-case hierarchy components;
|
||||
standard numeric components such as `general.base_model.0.name` are preserved.
|
||||
Scalar metadata is complete. Arrays have declared element types,
|
||||
counts and at most eight preview elements per array, with an explicit
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface GgufProgress {
|
||||
declared_tensor_count: number;
|
||||
}): void;
|
||||
onMetadata(row: ModelMetadata): void;
|
||||
onAlignment(alignment: number): void;
|
||||
onMetadataComplete(): void;
|
||||
onTensorsStarted(): void;
|
||||
onTensorDeclaration(row: ModelArtifactPartialTensor): void;
|
||||
@@ -318,10 +319,12 @@ export class GgufHeader {
|
||||
"GGUF general.alignment must be a positive UINT32 multiple of eight",
|
||||
);
|
||||
alignment = parsed.value;
|
||||
progress?.onAlignment(alignment);
|
||||
}
|
||||
metadata.push(row);
|
||||
progress?.onMetadata(row);
|
||||
}
|
||||
if (!keys.has("general.alignment")) progress?.onAlignment(alignment);
|
||||
progress?.onMetadataComplete();
|
||||
progress?.onTensorsStarted();
|
||||
const tensors: ModelArtifactPartialTensor[] = [];
|
||||
|
||||
@@ -322,6 +322,9 @@ export class ModelArtifactProvider implements ModelArtifactPort {
|
||||
partial.coverage.metadata = "partial";
|
||||
},
|
||||
onMetadata: (row) => partial?.metadata.push(row),
|
||||
onAlignment: (alignment) => {
|
||||
if (partial !== undefined) partial.alignment = alignment;
|
||||
},
|
||||
onMetadataComplete: () => {
|
||||
if (partial !== undefined) partial.coverage.metadata = "complete";
|
||||
},
|
||||
|
||||
@@ -7,12 +7,15 @@ import { parseEvidence } from "../../../src/domain/evidence.js";
|
||||
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
||||
import { ggufFixture } from "../../fixtures/model/model.js";
|
||||
|
||||
const cancelledArrayFixture = () => {
|
||||
const cancelledArrayFixture = (alignment?: number) => {
|
||||
const seed = ggufFixture();
|
||||
const count = 200_000;
|
||||
const fixture = ggufFixture({
|
||||
tensors: [],
|
||||
metadata: [
|
||||
...(alignment === undefined
|
||||
? []
|
||||
: [{ key: "general.alignment", type: 4, value: seed.u32(alignment) }]),
|
||||
{ key: "general.name", type: 8, value: seed.text("First row") },
|
||||
{
|
||||
key: "custom.bools",
|
||||
@@ -28,103 +31,105 @@ const cancelledArrayFixture = () => {
|
||||
return {
|
||||
fixture,
|
||||
count,
|
||||
arrayStart:
|
||||
24 +
|
||||
8 +
|
||||
Buffer.byteLength("general.name") +
|
||||
4 +
|
||||
8 +
|
||||
Buffer.byteLength("First row") +
|
||||
8 +
|
||||
Buffer.byteLength("custom.bools") +
|
||||
4 +
|
||||
4 +
|
||||
8,
|
||||
arrayStart: fixture.header.length - count,
|
||||
};
|
||||
};
|
||||
|
||||
it("retains completed GGUF rows when cancellation interrupts a later value", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-model-partial-cancel-"));
|
||||
const path = join(root, "model.gguf");
|
||||
const { fixture, count, arrayStart } = cancelledArrayFixture();
|
||||
await writeFile(path, fixture.bytes);
|
||||
const sample = await open(path, "r");
|
||||
const handlePrototype = Object.getPrototypeOf(sample) as {
|
||||
read: (
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) => Promise<{ bytesRead: number }>;
|
||||
};
|
||||
await sample.close();
|
||||
const originalRead = handlePrototype.read;
|
||||
const controller = new AbortController();
|
||||
const reason = { marker: "caller-selected-parser-cancel" };
|
||||
let arrayBytesObserved = 0;
|
||||
const readSpy = vi
|
||||
.spyOn(handlePrototype, "read")
|
||||
.mockImplementation(async function (
|
||||
this: unknown,
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) {
|
||||
const result = await originalRead.call(
|
||||
this,
|
||||
buffer,
|
||||
offset,
|
||||
length,
|
||||
position,
|
||||
it.each([undefined, 64])(
|
||||
"retains completed GGUF facts when cancellation interrupts a later value (alignment=%s)",
|
||||
async (alignment) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-model-partial-cancel-"));
|
||||
const path = join(root, "model.gguf");
|
||||
const { fixture, count, arrayStart } = cancelledArrayFixture(alignment);
|
||||
await writeFile(path, fixture.bytes);
|
||||
const sample = await open(path, "r");
|
||||
const handlePrototype = Object.getPrototypeOf(sample) as {
|
||||
read: (
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) => Promise<{ bytesRead: number }>;
|
||||
};
|
||||
await sample.close();
|
||||
const originalRead = handlePrototype.read;
|
||||
const controller = new AbortController();
|
||||
const reason = { marker: "caller-selected-parser-cancel" };
|
||||
let arrayBytesObserved = 0;
|
||||
const readSpy = vi
|
||||
.spyOn(handlePrototype, "read")
|
||||
.mockImplementation(async function (
|
||||
this: unknown,
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) {
|
||||
const result = await originalRead.call(
|
||||
this,
|
||||
buffer,
|
||||
offset,
|
||||
length,
|
||||
position,
|
||||
);
|
||||
if (position === arrayStart && result.bytesRead > 0) {
|
||||
arrayBytesObserved = result.bytesRead;
|
||||
controller.abort(reason);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
const provider = new ModelArtifactProvider();
|
||||
try {
|
||||
const result = await provider.inspect(
|
||||
{ path, hash: "header" },
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
if (position === arrayStart && result.bytesRead > 0) {
|
||||
arrayBytesObserved = result.bytesRead;
|
||||
controller.abort(reason);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
const provider = new ModelArtifactProvider();
|
||||
try {
|
||||
const result = await provider.inspect(
|
||||
{ path, hash: "header" },
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
if (result.ok) throw new Error("Expected parser cancellation");
|
||||
expect(arrayBytesObserved).toBeGreaterThan(0);
|
||||
expect(arrayBytesObserved).toBeLessThan(count);
|
||||
expect(result.error.cause).toBe(reason);
|
||||
const evidence = parseEvidence(result.error.partialObservation);
|
||||
expect(evidence.subject).toBeNull();
|
||||
expect(evidence.operation).toBe("inspect_model_artifact");
|
||||
expect(evidence.normalized_result).toMatchObject({
|
||||
kind: "model-artifact-partial",
|
||||
artifact: {
|
||||
path,
|
||||
header_sha256: null,
|
||||
sha256: null,
|
||||
file_identity: "unverified",
|
||||
},
|
||||
coverage: {
|
||||
header: "partial",
|
||||
metadata: "partial",
|
||||
tensors: "not-started",
|
||||
},
|
||||
metadata: [{ index: 0, key: "general.name", value: "First row" }],
|
||||
tensors: [],
|
||||
});
|
||||
const projected = projectAnalysisError(result.error);
|
||||
if (projected.details === undefined)
|
||||
throw new Error("Projected error omitted its partial observation");
|
||||
expect(parseEvidence(projected.details.partial_observation)).toEqual(
|
||||
evidence,
|
||||
);
|
||||
} finally {
|
||||
readSpy.mockRestore();
|
||||
await provider.close();
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
if (result.ok) throw new Error("Expected parser cancellation");
|
||||
expect(arrayBytesObserved).toBeGreaterThan(0);
|
||||
expect(arrayBytesObserved).toBeLessThan(count);
|
||||
expect(result.error.cause).toBe(reason);
|
||||
const evidence = parseEvidence(result.error.partialObservation);
|
||||
expect(evidence.subject).toBeNull();
|
||||
expect(evidence.operation).toBe("inspect_model_artifact");
|
||||
expect(evidence.normalized_result).toMatchObject({
|
||||
kind: "model-artifact-partial",
|
||||
alignment: alignment ?? null,
|
||||
artifact: {
|
||||
path,
|
||||
header_sha256: null,
|
||||
sha256: null,
|
||||
file_identity: "unverified",
|
||||
},
|
||||
coverage: {
|
||||
header: "partial",
|
||||
metadata: "partial",
|
||||
tensors: "not-started",
|
||||
},
|
||||
metadata: [
|
||||
...(alignment === undefined
|
||||
? []
|
||||
: [{ index: 0, key: "general.alignment", value: alignment }]),
|
||||
{
|
||||
index: alignment === undefined ? 0 : 1,
|
||||
key: "general.name",
|
||||
value: "First row",
|
||||
},
|
||||
],
|
||||
tensors: [],
|
||||
});
|
||||
const projected = projectAnalysisError(result.error);
|
||||
if (projected.details === undefined)
|
||||
throw new Error("Projected error omitted its partial observation");
|
||||
expect(parseEvidence(projected.details.partial_observation)).toEqual(
|
||||
evidence,
|
||||
);
|
||||
} finally {
|
||||
readSpy.mockRestore();
|
||||
await provider.close();
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("retains completed GGUF tensor declarations before later tensor bytes cancel", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-model-tensor-partial-"));
|
||||
@@ -210,6 +215,7 @@ it("retains completed GGUF tensor declarations before later tensor bytes cancel"
|
||||
});
|
||||
expect(partial.tensors).toEqual([]);
|
||||
expect(partial.coverage.tensors).toBe("partial");
|
||||
expect(evidence.normalized_result).toMatchObject({ alignment: 32 });
|
||||
expect(partial.artifact).toMatchObject({
|
||||
path,
|
||||
header_sha256: null,
|
||||
@@ -222,3 +228,90 @@ it("retains completed GGUF tensor declarations before later tensor bytes cancel"
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it.each([32, 64])(
|
||||
"retains resolved GGUF alignment %s when full hashing is cancelled",
|
||||
async (alignment) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-model-hash-cancel-"));
|
||||
const path = join(root, "model.gguf");
|
||||
const fixture = ggufFixture({
|
||||
...(alignment === 32 ? {} : { alignment }),
|
||||
tensors: [
|
||||
{
|
||||
name: "weight",
|
||||
type: 0,
|
||||
shape: [524288n],
|
||||
offset: 0n,
|
||||
bytes: 2 * 1024 * 1024,
|
||||
},
|
||||
],
|
||||
});
|
||||
await writeFile(path, fixture.bytes);
|
||||
const sample = await open(path, "r");
|
||||
const prototype = Object.getPrototypeOf(sample) as {
|
||||
read: (
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) => Promise<{ bytesRead: number }>;
|
||||
};
|
||||
await sample.close();
|
||||
const originalRead = prototype.read;
|
||||
const controller = new AbortController();
|
||||
const reason = { marker: "cancel-during-full-model-hash" };
|
||||
let payloadRead = false;
|
||||
const readSpy = vi
|
||||
.spyOn(prototype, "read")
|
||||
.mockImplementation(async function (
|
||||
this: unknown,
|
||||
buffer: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
position: number,
|
||||
) {
|
||||
const result = await originalRead.call(
|
||||
this,
|
||||
buffer,
|
||||
offset,
|
||||
length,
|
||||
position,
|
||||
);
|
||||
if (position >= fixture.header.length && result.bytesRead > 0) {
|
||||
payloadRead = true;
|
||||
controller.abort(reason);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
const provider = new ModelArtifactProvider();
|
||||
try {
|
||||
const result = await provider.inspect(
|
||||
{ path, hash: "full" },
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
if (result.ok) throw new Error("Expected full-hash cancellation");
|
||||
expect(payloadRead).toBe(true);
|
||||
expect(result.error.cause).toBe(reason);
|
||||
expect(
|
||||
parseEvidence(result.error.partialObservation).normalized_result,
|
||||
).toMatchObject({
|
||||
kind: "model-artifact-partial",
|
||||
alignment,
|
||||
coverage: {
|
||||
header: "complete",
|
||||
metadata: "complete",
|
||||
tensors: "complete",
|
||||
},
|
||||
artifact: {
|
||||
sha256: null,
|
||||
header_sha256: null,
|
||||
file_identity: "unverified",
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
readSpy.mockRestore();
|
||||
await provider.close();
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user