Skip to content

Commit 507d804

Browse files
committed
feat: shorter model URI
1 parent 314d7e8 commit 507d804

14 files changed

+658
-127
lines changed

src/cli/commands/inspect/commands/InspectEstimateCommand.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {Llama} from "../../../../bindings/Llama.js";
1717
import {getGgufFileTypeName} from "../../../../gguf/utils/getGgufFileTypeName.js";
1818
import {getPrettyBuildGpuName} from "../../../../bindings/consts.js";
1919
import withOra from "../../../../utils/withOra.js";
20-
import {resolveModelDestination} from "../../../../utils/resolveModelDestination.js";
20+
import {resolveModelArgToFilePathOrUrl} from "../../../../utils/resolveModelDestination.js";
2121
import {printModelDestination} from "../../../utils/printModelDestination.js";
2222
import {toBytes} from "../../../utils/toBytes.js";
2323

@@ -121,13 +121,10 @@ export const InspectEstimateCommand: CommandModule<object, InspectEstimateComman
121121
if (contextSizeArg === -1) contextSizeArg = undefined;
122122
if (contextSizeArg === -2) contextSizeArg = "train";
123123

124-
const resolvedModelDestination = resolveModelDestination(ggufPath);
125-
const resolvedGgufPath = resolvedModelDestination.type == "file"
126-
? resolvedModelDestination.path
127-
: resolvedModelDestination.url;
128-
129124
const headers = resolveHeaderFlag(headerArg);
130125

126+
const [resolvedModelDestination, resolvedGgufPath] = await resolveModelArgToFilePathOrUrl(ggufPath, headers);
127+
131128
const llama = gpu == null
132129
? await getLlama("lastBuild", {
133130
logLevel: LlamaLogLevel.error

src/cli/commands/inspect/commands/InspectGgufCommand.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {resolveHeaderFlag} from "../../../utils/resolveHeaderFlag.js";
1010
import {withCliCommandDescriptionDocsUrl} from "../../../utils/withCliCommandDescriptionDocsUrl.js";
1111
import {documentationPageUrls} from "../../../../config.js";
1212
import withOra from "../../../../utils/withOra.js";
13-
import {resolveModelDestination} from "../../../../utils/resolveModelDestination.js";
13+
import {resolveModelArgToFilePathOrUrl} from "../../../../utils/resolveModelDestination.js";
1414
import {printModelDestination} from "../../../utils/printModelDestination.js";
1515
import {getGgufMetadataKeyValue} from "../../../../gguf/utils/getGgufMetadataKeyValue.js";
1616
import {GgufTensorInfo} from "../../../../gguf/types/GgufTensorInfoTypes.js";
@@ -91,13 +91,10 @@ export const InspectGgufCommand: CommandModule<object, InspectGgufCommand> = {
9191
async handler({
9292
modelPath: ggufPath, header: headerArg, key, noSplice, fullTensorInfo, fullMetadataArrays, plainJson, outputToJsonFile
9393
}: InspectGgufCommand) {
94-
const resolvedModelDestination = resolveModelDestination(ggufPath);
95-
const resolvedGgufPath = resolvedModelDestination.type == "file"
96-
? resolvedModelDestination.path
97-
: resolvedModelDestination.url;
98-
9994
const headers = resolveHeaderFlag(headerArg);
10095

96+
const [resolvedModelDestination, resolvedGgufPath] = await resolveModelArgToFilePathOrUrl(ggufPath, headers);
97+
10198
if (!plainJson)
10299
printModelDestination(resolvedModelDestination);
103100

src/gguf/readGgufFileInfo.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import retry from "async-retry";
22
import {isUrl} from "../utils/isUrl.js";
33
import {ModelFileAccessTokens} from "../utils/modelFileAccesTokens.js";
4-
import {isModelUri, parseModelUri} from "../utils/parseModelUri.js";
4+
import {getAuthorizationHeader, isModelUri, parseModelUri, resolveParsedModelUri} from "../utils/parseModelUri.js";
55
import {Writable} from "../utils/utilTypes.js";
66
import {parseGguf} from "./parser/parseGguf.js";
77
import {GgufNetworkFetchFileReader} from "./fileReaders/GgufNetworkFetchFileReader.js";
@@ -75,9 +75,12 @@ export async function readGgufFileInfo(pathOrUri: string, {
7575
} = {}) {
7676
const useNetworkReader = sourceType === "network" || (sourceType == null && (isUrl(pathOrUri) || isModelUri(pathOrUri)));
7777

78-
function createFileReader(pathOrUri: string) {
78+
async function createFileReader(pathOrUri: string) {
7979
if (useNetworkReader) {
80-
const parsedModelUri = parseModelUri(pathOrUri);
80+
const parsedModelUri = await resolveParsedModelUri(parseModelUri(pathOrUri), {
81+
tokens, signal,
82+
authorizationHeader: getAuthorizationHeader(fetchHeaders)
83+
});
8184
return new GgufNetworkFetchFileReader({
8285
url: parsedModelUri?.resolvedUrl ?? normalizeGgufDownloadUrl(pathOrUri),
8386
retryOptions: fetchRetryOptions,
@@ -97,7 +100,7 @@ export async function readGgufFileInfo(pathOrUri: string, {
97100
}
98101

99102
async function readSingleFile(pathOrUri: string, splitPartNumber: number = 1) {
100-
const fileReader = createFileReader(pathOrUri);
103+
const fileReader = await createFileReader(pathOrUri);
101104
const res = await parseGguf({
102105
fileReader,
103106
ignoreKeys,

src/gguf/types/GgufMetadataTypes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ export enum GgufFileType {
118118
MOSTLY_Q4_0_4_4 = 33, // deprecated
119119
MOSTLY_Q4_0_4_8 = 34, // deprecated
120120
MOSTLY_Q4_0_8_8 = 35, // deprecated
121-
LLAMA_FTYPE_MOSTLY_TQ1_0 = 36, // deprecated
122-
LLAMA_FTYPE_MOSTLY_TQ2_0 = 37 // deprecated
121+
MOSTLY_TQ1_0 = 36, // deprecated
122+
MOSTLY_TQ2_0 = 37 // deprecated
123123
}
124124

125125

src/gguf/utils/ggufQuantNames.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {GgufFileType} from "../types/GgufMetadataTypes.js";
2+
3+
export const ggufQuantNames = new Map<string, GgufFileType>([
4+
["Q4_0", GgufFileType.MOSTLY_Q4_0],
5+
["Q4_1", GgufFileType.MOSTLY_Q4_1],
6+
["Q5_0", GgufFileType.MOSTLY_Q5_0],
7+
["Q5_1", GgufFileType.MOSTLY_Q5_1],
8+
["IQ2_XXS", GgufFileType.MOSTLY_IQ2_XXS],
9+
["IQ2_XS", GgufFileType.MOSTLY_IQ2_XS],
10+
["IQ2_S", GgufFileType.MOSTLY_IQ2_S],
11+
["IQ2_M", GgufFileType.MOSTLY_IQ2_M],
12+
["IQ1_S", GgufFileType.MOSTLY_IQ1_S],
13+
["IQ1_M", GgufFileType.MOSTLY_IQ1_M],
14+
["TQ1_0", GgufFileType.MOSTLY_TQ1_0],
15+
["TQ2_0", GgufFileType.MOSTLY_TQ2_0],
16+
["Q2_K", GgufFileType.MOSTLY_Q2_K],
17+
["Q2_K_S", GgufFileType.MOSTLY_Q2_K_S],
18+
["IQ3_XXS", GgufFileType.MOSTLY_IQ3_XXS],
19+
["IQ3_S", GgufFileType.MOSTLY_IQ3_S],
20+
["IQ3_M", GgufFileType.MOSTLY_IQ3_M],
21+
["Q3_K", GgufFileType.MOSTLY_Q3_K_M],
22+
["IQ3_XS", GgufFileType.MOSTLY_IQ3_XS],
23+
["Q3_K_S", GgufFileType.MOSTLY_Q3_K_S],
24+
["Q3_K_M", GgufFileType.MOSTLY_Q3_K_M],
25+
["Q3_K_L", GgufFileType.MOSTLY_Q3_K_L],
26+
["IQ4_NL", GgufFileType.MOSTLY_IQ4_NL],
27+
["IQ4_XS", GgufFileType.MOSTLY_IQ4_XS],
28+
["Q4_K", GgufFileType.MOSTLY_Q4_K_M],
29+
["Q4_K_S", GgufFileType.MOSTLY_Q4_K_S],
30+
["Q4_K_M", GgufFileType.MOSTLY_Q4_K_M],
31+
["Q5_K", GgufFileType.MOSTLY_Q5_K_M],
32+
["Q5_K_S", GgufFileType.MOSTLY_Q5_K_S],
33+
["Q5_K_M", GgufFileType.MOSTLY_Q5_K_M],
34+
["Q6_K", GgufFileType.MOSTLY_Q6_K],
35+
["Q8_0", GgufFileType.MOSTLY_Q8_0],
36+
["F16", GgufFileType.MOSTLY_F16],
37+
["BF16", GgufFileType.MOSTLY_BF16],
38+
["F32", GgufFileType.ALL_F32],
39+
["COPY", GgufFileType.ALL_F32]
40+
]);

src/gguf/utils/normalizeGgufDownloadUrl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export function normalizeGgufDownloadUrl(url: string) {
22
const parsedUrl = new URL(url);
33

4-
if (parsedUrl.hostname === "huggingface.co") {
4+
if (parsedUrl.hostname === "huggingface.co" || parsedUrl.hostname === "hf.co") {
55
const pathnameParts = parsedUrl.pathname.split("/");
66

77
if (pathnameParts.length > 3 && pathnameParts[3] === "blob") {

src/gguf/utils/resolveBinarySplitGgufPartUrls.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ export function resolveBinarySplitGgufPartUrls(ggufUrl: string) {
2222
const res: string[] = [];
2323
for (let i = 1; i <= parts; i++) {
2424
const url = new URL(parsedGgufUrl.href);
25-
url.pathname = pathnameWithoutPart + `.part${String(i)
26-
.padStart(partString.length, "0")}of${partsString}`;
25+
url.pathname = pathnameWithoutPart + `.part${String(i).padStart(partString.length, "0")}of${partsString}`;
2726
res.push(url.href);
2827
}
2928

src/utils/createModelDownloader.ts

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {safeEventCallback} from "./safeEventCallback.js";
99
import {ModelFileAccessTokens, resolveModelFileAccessTokensTryHeaders} from "./modelFileAccesTokens.js";
1010
import {pushAll} from "./pushAll.js";
1111
import {resolveModelDestination} from "./resolveModelDestination.js";
12+
import {getAuthorizationHeader, resolveParsedModelUri} from "./parseModelUri.js";
1213

1314
export type ModelDownloaderOptions = ({
1415
/**
@@ -122,17 +123,22 @@ export type ModelDownloaderOptions = ({
122123
* });
123124
* ```
124125
*/
125-
export async function createModelDownloader(options: ModelDownloaderOptions) {
126-
const downloader = ModelDownloader._create(options);
127-
await downloader._init();
128-
return downloader;
126+
export function createModelDownloader(options: ModelDownloaderOptions) {
127+
return ModelDownloader._create(options);
129128
}
130129

131130
/**
132131
* Combine multiple models downloaders to a single downloader to download everything using as much parallelism as possible.
133132
*
134133
* You can check each individual model downloader for its download progress,
135134
* but only the `onProgress` passed to the combined downloader will be called during the download.
135+
*
136+
* When combining `ModelDownloader` instances, the following options on each individual `ModelDownloader` are ignored:
137+
* - `showCliProgress`
138+
* - `onProgress`
139+
* - `parallelDownloads`
140+
*
141+
* To set any of those options for the combined downloader, you have to pass them to the combined downloader instance.
136142
* @example
137143
* ```typescript
138144
* import {fileURLToPath} from "url";
@@ -196,31 +202,18 @@ export class ModelDownloader {
196202
/** @internal */ private _totalFiles?: number;
197203
/** @internal */ private _tryHeaders: Record<string, string>[] = [];
198204

199-
private constructor(options: ModelDownloaderOptions) {
205+
private constructor(options: ModelDownloaderOptions, {resolvedModelUrl, resolvedFileName}: {
206+
resolvedModelUrl: string,
207+
resolvedFileName?: string
208+
}) {
200209
const {
201-
modelUri, modelUrl,
202-
dirPath = cliModelsDirectory, fileName, headers, showCliProgress = false, onProgress, deleteTempFileOnCancel = true,
210+
dirPath = cliModelsDirectory, headers, showCliProgress = false, onProgress, deleteTempFileOnCancel = true,
203211
skipExisting = true, parallelDownloads = 4, tokens
204-
} = options as ModelDownloaderOptions & {
205-
modelUri: string,
206-
modelUrl: string
207-
};
208-
const resolvedModelUri = modelUri || modelUrl;
212+
} = options;
209213

210-
if (resolvedModelUri == null || dirPath == null)
211-
throw new Error("modelUri and dirPath cannot be null");
212-
213-
const resolvedModelDestination = resolveModelDestination(resolvedModelUri);
214-
215-
this._modelUrl = resolvedModelDestination.type === "file"
216-
? path.join(dirPath, resolvedModelUri)
217-
: resolvedModelDestination.url;
214+
this._modelUrl = resolvedModelUrl;
218215
this._dirPath = path.resolve(process.cwd(), dirPath);
219-
this._fileName = fileName || (
220-
resolvedModelDestination.type === "uri"
221-
? resolvedModelDestination.parsedUri.fullFilename
222-
: undefined
223-
);
216+
this._fileName = resolvedFileName;
224217
this._headers = headers;
225218
this._showCliProgress = showCliProgress;
226219
this._onProgress = safeEventCallback(onProgress);
@@ -324,15 +317,11 @@ export class ModelDownloader {
324317
*/
325318
deleteTempFile?: boolean
326319
} = {}) {
327-
for (const downloader of this._specificFileDownloaders) {
328-
if (deleteTempFile)
329-
await downloader.closeAndDeleteFile();
330-
else
331-
await downloader.close();
332-
}
320+
for (const downloader of this._specificFileDownloaders)
321+
await downloader.close({deleteTempFile});
333322

334323
if (this._downloader !== this._specificFileDownloaders[0])
335-
await this._downloader?.close();
324+
await this._downloader?.close({deleteTempFile});
336325
}
337326

338327
/** @internal */
@@ -435,8 +424,54 @@ export class ModelDownloader {
435424
}
436425

437426
/** @internal */
438-
public static _create(options: ModelDownloaderOptions) {
439-
return new ModelDownloader(options);
427+
public static async _create(options: ModelDownloaderOptions) {
428+
const {
429+
modelUri, modelUrl, dirPath = cliModelsDirectory, fileName
430+
} = options as ModelDownloaderOptions & {
431+
modelUri?: string,
432+
modelUrl?: string
433+
};
434+
const resolvedModelUri = modelUri || modelUrl;
435+
436+
if (resolvedModelUri == null || dirPath == null)
437+
throw new Error("modelUri and dirPath cannot be null");
438+
439+
async function getModelUrlAndFilename(): Promise<{
440+
resolvedModelUrl: string,
441+
resolvedFileName?: string
442+
}> {
443+
const resolvedModelDestination = resolveModelDestination(resolvedModelUri!);
444+
445+
if (resolvedModelDestination.type == "file")
446+
return {
447+
resolvedModelUrl: path.resolve(dirPath, resolvedModelDestination.path),
448+
resolvedFileName: fileName
449+
};
450+
else if (resolvedModelDestination.type === "url")
451+
return {
452+
resolvedModelUrl: resolvedModelDestination.url,
453+
resolvedFileName: fileName
454+
};
455+
else if (resolvedModelDestination.parsedUri.type === "resolved")
456+
return {
457+
resolvedModelUrl: resolvedModelDestination.parsedUri.resolvedUrl,
458+
resolvedFileName: fileName || resolvedModelDestination.parsedUri.filename
459+
};
460+
461+
const resolvedUri = await resolveParsedModelUri(resolvedModelDestination.parsedUri, {
462+
tokens: options.tokens,
463+
authorizationHeader: getAuthorizationHeader(options.headers)
464+
});
465+
466+
return {
467+
resolvedModelUrl: resolvedUri.resolvedUrl,
468+
resolvedFileName: fileName || resolvedUri.filename
469+
};
470+
}
471+
472+
const modelDownloader = new ModelDownloader(options, await getModelUrlAndFilename());
473+
await modelDownloader._init();
474+
return modelDownloader;
440475
}
441476
}
442477

@@ -472,7 +507,7 @@ export class CombinedModelDownloader {
472507
*
473508
* To set any of those options for the combined downloader, you have to pass them to the combined downloader instance
474509
*/
475-
public constructor(downloaders: ModelDownloader[], options?: CombinedModelDownloaderOptions) {
510+
private constructor(downloaders: ModelDownloader[], options?: CombinedModelDownloaderOptions) {
476511
const {
477512
showCliProgress = false,
478513
onProgress,
@@ -488,18 +523,16 @@ export class CombinedModelDownloader {
488523
}
489524

490525
public async cancel() {
491-
for (const modelDownloader of await Promise.all(this._downloaders)) {
526+
for (const modelDownloader of this._downloaders) {
492527
if (modelDownloader._specificFileDownloaders.every(
493528
(downloader) => downloader.status.downloadStatus === "Finished"
494529
))
495530
continue;
496531

497-
for (const downloader of modelDownloader._specificFileDownloaders) {
498-
if (modelDownloader._deleteTempFileOnCancel)
499-
await downloader.closeAndDeleteFile();
500-
else
501-
await downloader.close();
502-
}
532+
for (const downloader of modelDownloader._specificFileDownloaders)
533+
await downloader.close({
534+
deleteTempFile: modelDownloader._deleteTempFileOnCancel
535+
});
503536
}
504537
}
505538

@@ -543,7 +576,7 @@ export class CombinedModelDownloader {
543576
return this.entrypointFilePaths;
544577
}
545578

546-
public get modelDownloaders() {
579+
public get modelDownloaders(): readonly ModelDownloader[] {
547580
return this._downloaders;
548581
}
549582

@@ -598,7 +631,7 @@ export class CombinedModelDownloader {
598631
cliStyle: isCI ? "ci" : "fancy",
599632
parallelDownloads: this._parallelDownloads
600633
},
601-
...(await Promise.all(this._downloaders)).flatMap((downloader) => downloader._specificFileDownloaders)
634+
...this._downloaders.flatMap((downloader) => downloader._specificFileDownloaders)
602635
);
603636
}
604637

src/utils/modelFileAccesTokens.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export async function resolveModelFileAccessTokensTryHeaders(
2121
const parsedUrl = new URL(modelUrl);
2222
const {huggingFace} = tokens;
2323

24-
if (parsedUrl.hostname === "huggingface.co") {
24+
if (parsedUrl.hostname === "huggingface.co" || parsedUrl.hostname === "hf.co") {
2525
const hfToken = resolveHfToken(huggingFace);
2626

2727
res.push({

0 commit comments

Comments
 (0)