The frontend splitter accepted any suffix after a .gguf head, while the backend requires that suffix to be the label the scanner derives from the filename. A colon is legal in a POSIX filename and POSIX is case sensitive, so "/models/llama.gguf:Bar.gguf" and "/models/llama.gguf:bar.gguf" are two real, distinct files. The one-time backfill folded both onto one override key, since the variant half is stored lowercased, and then re-read the local configs by that key, so the first entry was sent twice, the second file's context and KV cache settings never left the browser, and the done flag was set anyway. splitQuantSuffix now ports extract_quant_label for a bare filename, shard suffix and float-precision fallback included, and takes the suffix only when it equals that label. Checked against the backend over twenty-nine keys with identical answers on both sides, up from twelve, and the backfill now migrates both files with their own settings. The identity helpers come straight from features/hub/lib/model-identity rather than the hub barrel, which also re-exports the download manager and its React components. Same bindings, and it puts the module within reach of a test. The 27 new frontend assertions cover the split case by case against the backend's answers, and pin the storage rule the backfill's re-read depends on: two spellings of one repo id or one Windows path keep a single record, a POSIX path keeps two, and importing the legacy load settings never adds a duplicate.
36 lines
1.3 KiB
JavaScript
36 lines
1.3 KiB
JavaScript
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
// The two resolution rules vite and tsconfig's "bundler" mode give the app that
|
|
// bare node does not have: the "@/*" path alias, and a relative import written
|
|
// without its extension. Register this from a test that needs to import a src
|
|
// module using either, which is otherwise unreachable from the test runner.
|
|
import { existsSync } from "node:fs";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
const SRC = fileURLToPath(new URL("../src/", import.meta.url));
|
|
|
|
function firstExisting(base) {
|
|
for (const candidate of [`${base}.ts`, `${base}/index.ts`, base]) {
|
|
if (existsSync(candidate)) {
|
|
return pathToFileURL(candidate).href;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function resolve(specifier, context, next) {
|
|
if (specifier.startsWith("@/")) {
|
|
const resolved = firstExisting(SRC + specifier.slice(2));
|
|
return next(resolved ?? specifier, context);
|
|
}
|
|
if (specifier.startsWith(".") && context.parentURL?.startsWith("file:")) {
|
|
const resolved = firstExisting(
|
|
fileURLToPath(new URL(specifier, context.parentURL)),
|
|
);
|
|
if (resolved) {
|
|
return next(resolved, context);
|
|
}
|
|
}
|
|
return next(specifier, context);
|
|
}
|