Files
wehub-resource-sync 311a3666c4
Build documentation / build (push) Failing after 0s
Unit tests / build (18) (push) Has been cancelled
Unit tests / build (20) (push) Has been cancelled
Unit tests / build (22) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:44:39 +08:00

184 lines
6.1 KiB
JavaScript

import { jest } from "@jest/globals";
// Mock get_file_metadata before importing the module under test
const mockGetFileMetadata = jest.fn();
jest.unstable_mockModule("../../src/utils/model_registry/get_file_metadata.js", () => ({
get_file_metadata: mockGetFileMetadata,
}));
// Import registry to populate MODEL_TYPE_MAPPING (side-effect import)
await import("../../src/models/registry.js");
// Dynamic import after mock setup (required for ESM)
const { get_available_dtypes } = await import("../../src/utils/model_registry/get_available_dtypes.js");
// A minimal config that mimics a BERT-like encoder-only model
const ENCODER_ONLY_CONFIG = {
architectures: ["BertModel"],
model_type: "bert",
};
// A minimal config for a decoder-only (causal LM) model
const DECODER_ONLY_CONFIG = {
architectures: ["LlamaForCausalLM"],
model_type: "llama",
};
// A minimal config for a Seq2Seq model (encoder + decoder)
const SEQ2SEQ_CONFIG = {
architectures: ["T5ForConditionalGeneration"],
model_type: "t5",
};
// A config with an unknown architecture (falls back to EncoderOnly)
const UNKNOWN_ARCH_CONFIG = {
architectures: ["SomeUnknownArchitecture"],
model_type: "unknown_type",
};
/**
* Helper: given a set of files that "exist", returns a mock implementation
* for get_file_metadata that resolves { exists: true } for those files.
* @param {string[]} existingFiles
*/
function setupExistingFiles(...existingFiles) {
mockGetFileMetadata.mockImplementation((_modelId, filename, _options) => {
return Promise.resolve({
exists: existingFiles.includes(filename),
fromCache: false,
});
});
}
describe("get_available_dtypes", () => {
beforeEach(() => {
mockGetFileMetadata.mockReset();
});
it("should detect fp32 and q4 for an encoder-only model", async () => {
setupExistingFiles(
"onnx/model.onnx", // fp32
"onnx/model_q4.onnx", // q4
);
const dtypes = await get_available_dtypes("test/model", { config: ENCODER_ONLY_CONFIG });
expect(dtypes).toContain("fp32");
expect(dtypes).toContain("q4");
expect(dtypes).not.toContain("fp16");
expect(dtypes).not.toContain("q8");
expect(dtypes).not.toContain("int8");
});
it("should detect all dtypes when all files exist", async () => {
setupExistingFiles(
"onnx/model.onnx", // fp32
"onnx/model_fp16.onnx", // fp16
"onnx/model_int8.onnx", // int8
"onnx/model_uint8.onnx", // uint8
"onnx/model_quantized.onnx", // q8
"onnx/model_q4.onnx", // q4
"onnx/model_q4f16.onnx", // q4f16
"onnx/model_bnb4.onnx", // bnb4
);
const dtypes = await get_available_dtypes("test/model", { config: ENCODER_ONLY_CONFIG });
expect(dtypes).toEqual(["fp32", "fp16", "int8", "uint8", "q8", "q4", "q4f16", "bnb4"]);
});
it("should return empty array when no ONNX files exist", async () => {
setupExistingFiles();
const dtypes = await get_available_dtypes("test/model", { config: ENCODER_ONLY_CONFIG });
expect(dtypes).toEqual([]);
});
it("should require all session files for seq2seq models", async () => {
// Only encoder has q4, decoder does not — q4 should NOT be available
setupExistingFiles(
"onnx/encoder_model.onnx", // fp32 encoder
"onnx/decoder_model_merged.onnx", // fp32 decoder
"onnx/encoder_model_q4.onnx", // q4 encoder (but no q4 decoder)
);
const dtypes = await get_available_dtypes("test/model", { config: SEQ2SEQ_CONFIG });
expect(dtypes).toContain("fp32");
expect(dtypes).not.toContain("q4");
});
it("should list dtype only when all session files exist for seq2seq", async () => {
// Both encoder and decoder have fp32 and q8
setupExistingFiles("onnx/encoder_model.onnx", "onnx/decoder_model_merged.onnx", "onnx/encoder_model_quantized.onnx", "onnx/decoder_model_merged_quantized.onnx");
const dtypes = await get_available_dtypes("test/model", { config: SEQ2SEQ_CONFIG });
expect(dtypes).toContain("fp32");
expect(dtypes).toContain("q8");
expect(dtypes).not.toContain("fp16");
expect(dtypes).not.toContain("q4");
});
it("should handle decoder-only models", async () => {
setupExistingFiles("onnx/model.onnx", "onnx/model_q4.onnx", "onnx/model_q4f16.onnx");
const dtypes = await get_available_dtypes("test/model", { config: DECODER_ONLY_CONFIG });
expect(dtypes).toContain("fp32");
expect(dtypes).toContain("q4");
expect(dtypes).toContain("q4f16");
expect(dtypes).toHaveLength(3);
});
it("should fall back to EncoderOnly for unknown architectures", async () => {
setupExistingFiles("onnx/model.onnx", "onnx/model_fp16.onnx");
const dtypes = await get_available_dtypes("test/model", { config: UNKNOWN_ARCH_CONFIG });
expect(dtypes).toContain("fp32");
expect(dtypes).toContain("fp16");
expect(dtypes).toHaveLength(2);
});
it("should support custom model_file_name", async () => {
setupExistingFiles("onnx/custom_model.onnx", "onnx/custom_model_q4.onnx");
const dtypes = await get_available_dtypes("test/model", {
config: ENCODER_ONLY_CONFIG,
model_file_name: "custom_model",
});
expect(dtypes).toContain("fp32");
expect(dtypes).toContain("q4");
expect(dtypes).not.toContain("fp16");
});
it("should pass revision and cache_dir to get_file_metadata", async () => {
setupExistingFiles("onnx/model.onnx");
await get_available_dtypes("test/model", {
config: ENCODER_ONLY_CONFIG,
revision: "v2",
cache_dir: "/tmp/cache",
});
// Verify that metadata calls received the correct options
for (const call of mockGetFileMetadata.mock.calls) {
expect(call[0]).toBe("test/model");
expect(call[2]).toMatchObject({ revision: "v2", cache_dir: "/tmp/cache" });
}
});
it("should only return valid dtype strings", async () => {
setupExistingFiles("onnx/model.onnx", "onnx/model_fp16.onnx");
const dtypes = await get_available_dtypes("test/model", { config: ENCODER_ONLY_CONFIG });
const validDtypes = ["fp32", "fp16", "int8", "uint8", "q8", "q4", "q4f16", "bnb4"];
for (const dtype of dtypes) {
expect(validDtypes).toContain(dtype);
}
});
});