Skip to content

Commit 0e83243

Browse files
committed
feat(inspect gguf command): print a single key flag
1 parent f0c630d commit 0e83243

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

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

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import {documentationPageUrls} from "../../../../config.js";
1313
import withOra from "../../../../utils/withOra.js";
1414
import {resolveModelDestination} from "../../../../utils/resolveModelDestination.js";
1515
import {printModelDestination} from "../../../utils/printModelDestination.js";
16+
import {getGgufMetadataKeyValue} from "../../../../gguf/utils/getGgufMetadataKeyValue.js";
1617

1718
type InspectGgufCommand = {
1819
modelPath: string,
1920
header?: string[],
21+
key?: string,
2022
noSplice: boolean,
2123
fullTensorInfo: boolean,
2224
fullMetadataArrays: boolean,
@@ -46,6 +48,12 @@ export const InspectGgufCommand: CommandModule<object, InspectGgufCommand> = {
4648
description: "Headers to use when reading a model file from a URL, in the format `key: value`. You can pass this option multiple times to add multiple headers.",
4749
group: "Optional:"
4850
})
51+
.option("key", {
52+
alias: ["k"],
53+
type: "string",
54+
description: "A single metadata key to print the value of. If not provided, all metadata will be printed",
55+
group: "Optional:"
56+
})
4957
.option("noSplice", {
5058
alias: "s",
5159
type: "boolean",
@@ -80,7 +88,7 @@ export const InspectGgufCommand: CommandModule<object, InspectGgufCommand> = {
8088
});
8189
},
8290
async handler({
83-
modelPath: ggufPath, header: headerArg, noSplice, fullTensorInfo, fullMetadataArrays, plainJson, outputToJsonFile
91+
modelPath: ggufPath, header: headerArg, key, noSplice, fullTensorInfo, fullMetadataArrays, plainJson, outputToJsonFile
8492
}: InspectGgufCommand) {
8593
const resolvedModelDestination = resolveModelDestination(ggufPath);
8694
const resolvedGgufPath = resolvedModelDestination.type == "file"
@@ -116,16 +124,30 @@ export const InspectGgufCommand: CommandModule<object, InspectGgufCommand> = {
116124
const fileTypeName = getGgufFileTypeName(parsedMetadata.metadata.general?.file_type);
117125

118126
if (plainJson || outputToJsonFile != null) {
119-
const outputJson = JSON.stringify({
120-
splicedParts: parsedMetadata.splicedParts,
121-
version: parsedMetadata.version,
122-
fileType: fileTypeName,
123-
tensorCount: parsedMetadata.totalTensorCount,
124-
metadataSize: parsedMetadata.totalMetadataSize,
125-
tensorInfoSize: parsedMetadata.totalTensorInfoSize,
126-
metadata: parsedMetadata.metadata,
127-
tensorInfo: parsedMetadata.fullTensorInfo
128-
}, undefined, 4);
127+
const getOutputJson = () => {
128+
if (key != null) {
129+
const keyValue = getGgufMetadataKeyValue(parsedMetadata.metadata, key);
130+
if (keyValue === undefined) {
131+
console.log(`Key not found: ${key}`);
132+
process.exit(1);
133+
}
134+
135+
return JSON.stringify(keyValue, undefined, 4);
136+
}
137+
138+
return JSON.stringify({
139+
splicedParts: parsedMetadata.splicedParts,
140+
version: parsedMetadata.version,
141+
fileType: fileTypeName,
142+
tensorCount: parsedMetadata.totalTensorCount,
143+
metadataSize: parsedMetadata.totalMetadataSize,
144+
tensorInfoSize: parsedMetadata.totalTensorInfoSize,
145+
metadata: parsedMetadata.metadata,
146+
tensorInfo: parsedMetadata.fullTensorInfo
147+
}, undefined, 4);
148+
};
149+
150+
const outputJson = getOutputJson();
129151

130152
if (outputToJsonFile != null) {
131153
const filePath = path.resolve(process.cwd(), outputToJsonFile);
@@ -134,6 +156,27 @@ export const InspectGgufCommand: CommandModule<object, InspectGgufCommand> = {
134156
} else {
135157
console.info(outputJson);
136158
}
159+
} else if (key != null) {
160+
const keyValue = getGgufMetadataKeyValue(parsedMetadata.metadata, key);
161+
if (keyValue === undefined) {
162+
console.log(`${chalk.red("Metadata key not found:")} ${key}`);
163+
process.exit(1);
164+
}
165+
166+
const metadataPrettyPrintOptions: PrettyPrintObjectOptions = {
167+
maxArrayValues: fullMetadataArrays
168+
? undefined
169+
: 10,
170+
useNumberGrouping: true,
171+
maxArrayItemsWidth: process.stdout.columns - 1
172+
};
173+
174+
console.info(`${chalk.yellow("Metadata key:")} ${prettyPrintObject(key)}`);
175+
console.info(`${chalk.yellow("Metadata:")} ${
176+
typeof keyValue === "string"
177+
? keyValue
178+
: prettyPrintObject(keyValue, undefined, metadataPrettyPrintOptions)
179+
}`);
137180
} else {
138181
const metadataPrettyPrintOptions: PrettyPrintObjectOptions = {
139182
maxArrayValues: fullMetadataArrays

src/cli/commands/inspect/commands/InspectGpuCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ async function logSwapUsage(llama: Llama) {
208208
const swapState = await llama.getSwapState();
209209

210210
console.info(`${chalk.yellow("Used swap:")} ${getPercentageString(swapState.used, swapState.allocated)}% ${chalk.gray("(" + bytes(swapState.used) + "/" + bytes(swapState.allocated) + ")")}`);
211-
console.info(`${chalk.yellow("Max size:")} ${swapState.maxSize === Infinity ? "dynamic" : bytes(swapState.maxSize)}`);
211+
console.info(`${chalk.yellow("Max swap size:")} ${swapState.maxSize === Infinity ? "dynamic" : bytes(swapState.maxSize)}`);
212212
}
213213

214214
function getPercentageString(amount: number, total: number) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export function getGgufMetadataKeyValue(metadata: Record<string, any>, key: string) {
2+
return readMedataKey(metadata, key.split("."));
3+
}
4+
5+
function readMedataKey(metadata: Record<string, any>, keyParts: string[]): any {
6+
for (const [metadataKey, value] of Object.entries(metadata)) {
7+
const matchLength = checkMatchLength(metadataKey, keyParts);
8+
if (matchLength === 0)
9+
continue;
10+
11+
if (matchLength === keyParts.length)
12+
return value;
13+
14+
const res = readMedataKey(value, keyParts.slice(matchLength));
15+
if (res !== undefined)
16+
return res;
17+
}
18+
19+
return undefined;
20+
}
21+
22+
function checkMatchLength(metadataKey: string, keyParts: string[]) {
23+
const metadataKeyParts = metadataKey.split(".");
24+
25+
if (metadataKeyParts.length > keyParts.length)
26+
return 0;
27+
28+
for (let i = 0; i < metadataKeyParts.length; i++) {
29+
if (metadataKeyParts[i] !== keyParts[i])
30+
return 0;
31+
}
32+
33+
return metadataKeyParts.length;
34+
}

0 commit comments

Comments
 (0)