mirror of
https://github.com/lmstudio-ai/lms.git
synced 2025-09-22 17:24:32 +08:00
lms ls
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -9,10 +9,13 @@
|
|||||||
"concatenator",
|
"concatenator",
|
||||||
"Fcfs",
|
"Fcfs",
|
||||||
"GGUF",
|
"GGUF",
|
||||||
|
"gptneox",
|
||||||
"immer",
|
"immer",
|
||||||
"inferencing",
|
"inferencing",
|
||||||
"lmstudio",
|
"lmstudio",
|
||||||
"proxying",
|
"proxying",
|
||||||
|
"replit",
|
||||||
|
"starcoder",
|
||||||
"Streamable"
|
"Streamable"
|
||||||
],
|
],
|
||||||
"rewrap.wrappingColumn": 100,
|
"rewrap.wrappingColumn": 100,
|
||||||
|
128
src/architectureStylizations.ts
Normal file
128
src/architectureStylizations.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
export class InfoLookup<TInnerKey, TLookupKey, TValue> {
|
||||||
|
private readonly lookup = new Map<TInnerKey, TValue>();
|
||||||
|
private readonly fallback: (key: TLookupKey) => TValue;
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
private readonly keyMapper: (key: TLookupKey) => TInnerKey,
|
||||||
|
fallback: ((key: TLookupKey) => TValue) | undefined,
|
||||||
|
) {
|
||||||
|
this.fallback =
|
||||||
|
fallback ??
|
||||||
|
(key => {
|
||||||
|
throw new Error(`Key not found: ${key}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create<TKey, TValue>({ fallback }: { fallback?: (key: TKey) => TValue } = {}) {
|
||||||
|
return new InfoLookup<TKey, TKey, TValue>(key => key, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createWithKeyMapper<TInnerKey, TLookupKey, TValue>({
|
||||||
|
fallback,
|
||||||
|
keyMapper,
|
||||||
|
}: {
|
||||||
|
fallback: (key: TLookupKey) => TValue;
|
||||||
|
keyMapper: (key: TLookupKey) => TInnerKey;
|
||||||
|
}) {
|
||||||
|
return new InfoLookup<TInnerKey, TLookupKey, TValue>(keyMapper, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public register(...args: [...TInnerKey[], TValue]): this {
|
||||||
|
const value = args.at(-1) as TValue;
|
||||||
|
for (let i = 0; i < args.length - 1; i++) {
|
||||||
|
this.lookup.set(args[i] as TInnerKey, value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public find(lookupKey: TLookupKey): TValue {
|
||||||
|
const innerKey = this.keyMapper(lookupKey);
|
||||||
|
if (this.lookup.has(innerKey)) {
|
||||||
|
return this.lookup.get(innerKey)!;
|
||||||
|
} else {
|
||||||
|
return this.fallback(lookupKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const llmColorer = chalk.cyanBright;
|
||||||
|
const visionColorer = chalk.yellowBright;
|
||||||
|
const embeddingColorer = chalk.blueBright;
|
||||||
|
|
||||||
|
export const architectureInfoLookup = InfoLookup.createWithKeyMapper({
|
||||||
|
fallback: (arch: string) => ({
|
||||||
|
name: arch,
|
||||||
|
colorer: llmColorer,
|
||||||
|
}),
|
||||||
|
keyMapper: (arch: string) => arch.toLowerCase(),
|
||||||
|
})
|
||||||
|
.register("phi2", "phi-2", {
|
||||||
|
name: "Phi-2",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("mistral", {
|
||||||
|
name: "Mistral",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("llama", {
|
||||||
|
name: "Llama",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("gptneox", "gpt-neo-x", "gpt_neo_x", {
|
||||||
|
name: "GPT-NeoX",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("mpt", {
|
||||||
|
name: "MPT",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("replit", {
|
||||||
|
name: "Replit",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("starcoder", {
|
||||||
|
name: "StarCoder",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("falcon", {
|
||||||
|
name: "Falcon",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("qwen", {
|
||||||
|
name: "Qwen",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("qwen2", {
|
||||||
|
name: "Qwen2",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("stablelm", {
|
||||||
|
name: "StableLM",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("mamba", {
|
||||||
|
name: "mamba",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("command-r", {
|
||||||
|
name: "Command R",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("gemma", {
|
||||||
|
name: "Gemma",
|
||||||
|
colorer: llmColorer,
|
||||||
|
})
|
||||||
|
.register("bert", {
|
||||||
|
name: "BERT",
|
||||||
|
colorer: embeddingColorer,
|
||||||
|
})
|
||||||
|
.register("nomic-bert", {
|
||||||
|
name: "Nomic BERT",
|
||||||
|
colorer: embeddingColorer,
|
||||||
|
})
|
||||||
|
.register("clip", {
|
||||||
|
name: "CLIP",
|
||||||
|
colorer: visionColorer,
|
||||||
|
});
|
@ -1,8 +1,20 @@
|
|||||||
import { type SimpleLogger } from "@lmstudio/lms-common";
|
import { type SimpleLogger } from "@lmstudio/lms-common";
|
||||||
import { LMStudioClient } from "@lmstudio/sdk";
|
import { LMStudioClient } from "@lmstudio/sdk";
|
||||||
|
import { getServerLastStatus } from "./subcommands/server";
|
||||||
|
|
||||||
export function createClient(logger: SimpleLogger) {
|
export async function createClient(logger: SimpleLogger) {
|
||||||
|
let port: number;
|
||||||
|
try {
|
||||||
|
const lastStatus = await getServerLastStatus(logger);
|
||||||
|
port = lastStatus.port;
|
||||||
|
} catch (e) {
|
||||||
|
logger.debug("Failed to get last server status", e);
|
||||||
|
port = 1234;
|
||||||
|
}
|
||||||
|
const baseUrl = `ws://127.0.0.1:${port}`;
|
||||||
|
logger.debug(`Connecting to server with baseUrl ${port}`);
|
||||||
return new LMStudioClient({
|
return new LMStudioClient({
|
||||||
|
baseUrl,
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { run, subcommands } from "cmd-ts";
|
import { run, subcommands } from "cmd-ts";
|
||||||
import { list } from "./subcommands/list";
|
import { ls } from "./subcommands/list";
|
||||||
import { start, status, stop } from "./subcommands/server";
|
import { start, status, stop } from "./subcommands/server";
|
||||||
import { printVersion, version } from "./subcommands/version";
|
import { printVersion, version } from "./subcommands/version";
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ const cli = subcommands({
|
|||||||
start,
|
start,
|
||||||
status,
|
status,
|
||||||
stop,
|
stop,
|
||||||
list,
|
ls,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { type DownloadedModel } from "@lmstudio/sdk";
|
import { type DownloadedModel } from "@lmstudio/sdk";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { command, subcommands } from "cmd-ts";
|
import { command, flag } from "cmd-ts";
|
||||||
import columnify from "columnify";
|
import columnify from "columnify";
|
||||||
|
import { architectureInfoLookup } from "../architectureStylizations";
|
||||||
import { createClient } from "../createClient";
|
import { createClient } from "../createClient";
|
||||||
import { formatSizeBytesWithColor1000 } from "../formatSizeBytes1000";
|
import { formatSizeBytesWithColor1000 } from "../formatSizeBytes1000";
|
||||||
import { createLogger, logLevelArgs } from "../logLevel";
|
import { createLogger, logLevelArgs } from "../logLevel";
|
||||||
@ -16,8 +17,12 @@ function loadedCheck(count: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function coloredArch(arch?: string) {
|
function architecture(architecture?: string) {
|
||||||
return arch ?? "";
|
if (!architecture) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const architectureInfo = architectureInfoLookup.find(architecture);
|
||||||
|
return architectureInfo.colorer(` ${architectureInfo.name} `);
|
||||||
}
|
}
|
||||||
|
|
||||||
function printDownloadedModelsTable(
|
function printDownloadedModelsTable(
|
||||||
@ -60,20 +65,22 @@ function printDownloadedModelsTable(
|
|||||||
// Group is a model itself
|
// Group is a model itself
|
||||||
const model = models[0];
|
const model = models[0];
|
||||||
return {
|
return {
|
||||||
address: chalk.whiteBright(group),
|
address: chalk.grey(" ") + chalk.cyanBright(group),
|
||||||
sizeBytes: formatSizeBytesWithColor1000(model.sizeBytes),
|
sizeBytes: formatSizeBytesWithColor1000(model.sizeBytes),
|
||||||
arch: coloredArch(model.architecture),
|
arch: architecture(model.architecture),
|
||||||
loaded: loadedCheck(model.loadedIdentifiers.length),
|
loaded: loadedCheck(model.loadedIdentifiers.length),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
// Empty line between groups
|
||||||
|
{},
|
||||||
// Group title
|
// Group title
|
||||||
{ address: chalk.whiteBright(group), sizeBytes: "", arch: "", loaded: "" },
|
{ address: chalk.grey(" ") + chalk.cyanBright(group) },
|
||||||
// Models
|
// Models within the group
|
||||||
...models.map(model => ({
|
...models.map(model => ({
|
||||||
address: chalk.black(". ") + chalk.gray("/" + model.remaining),
|
address: chalk.grey(" ") + chalk.white("/" + model.remaining),
|
||||||
sizeBytes: formatSizeBytesWithColor1000(model.sizeBytes),
|
sizeBytes: formatSizeBytesWithColor1000(model.sizeBytes),
|
||||||
arch: coloredArch(model.architecture),
|
arch: architecture(model.architecture),
|
||||||
loaded: loadedCheck(model.loadedIdentifiers.length),
|
loaded: loadedCheck(model.loadedIdentifiers.length),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
@ -87,18 +94,18 @@ function printDownloadedModelsTable(
|
|||||||
columns: ["address", "sizeBytes", "arch", "loaded"],
|
columns: ["address", "sizeBytes", "arch", "loaded"],
|
||||||
config: {
|
config: {
|
||||||
address: {
|
address: {
|
||||||
headingTransform: () => chalk.cyanBright("ADDRESS"),
|
headingTransform: () => chalk.gray(" ") + chalk.greenBright("ADDRESS"),
|
||||||
},
|
},
|
||||||
sizeBytes: {
|
sizeBytes: {
|
||||||
headingTransform: () => chalk.cyanBright("SIZE"),
|
headingTransform: () => chalk.greenBright("SIZE"),
|
||||||
align: "right",
|
align: "right",
|
||||||
},
|
},
|
||||||
arch: {
|
arch: {
|
||||||
headingTransform: () => chalk.cyanBright("ARCHITECTURE"),
|
headingTransform: () => chalk.greenBright("ARCHITECTURE"),
|
||||||
align: "left",
|
align: "center",
|
||||||
},
|
},
|
||||||
loaded: {
|
loaded: {
|
||||||
headingTransform: () => chalk.cyanBright("LOADED"),
|
headingTransform: () => chalk.greenBright("LOADED"),
|
||||||
align: "left",
|
align: "left",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -108,40 +115,89 @@ function printDownloadedModelsTable(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloaded = command({
|
export const ls = command({
|
||||||
name: "downloaded",
|
name: "ls",
|
||||||
description: "List downloaded models",
|
description: "List all downloaded models",
|
||||||
args: {
|
args: {
|
||||||
...logLevelArgs,
|
...logLevelArgs,
|
||||||
|
llm: flag({
|
||||||
|
long: "llm",
|
||||||
|
description: "Show only LLM models",
|
||||||
|
}),
|
||||||
|
embedding: flag({
|
||||||
|
long: "embedding",
|
||||||
|
description: "Show only embedding models",
|
||||||
|
}),
|
||||||
|
json: flag({
|
||||||
|
long: "json",
|
||||||
|
description: "Outputs in JSON format to stdout",
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
handler: async args => {
|
handler: async args => {
|
||||||
const logger = createLogger(args);
|
const logger = createLogger(args);
|
||||||
const client = createClient(logger);
|
const client = await createClient(logger);
|
||||||
|
|
||||||
const downloadedModels = await client.system.listDownloadedModels();
|
const { llm, embedding, json } = args;
|
||||||
|
|
||||||
|
let downloadedModels = await client.system.listDownloadedModels();
|
||||||
const loadedModels = await client.llm.listLoaded();
|
const loadedModels = await client.llm.listLoaded();
|
||||||
|
|
||||||
|
const originalModelsCount = downloadedModels.length;
|
||||||
|
|
||||||
|
if (llm || embedding) {
|
||||||
|
const allowedTypes = new Set<string>();
|
||||||
|
if (llm) {
|
||||||
|
allowedTypes.add("llm");
|
||||||
|
}
|
||||||
|
if (embedding) {
|
||||||
|
allowedTypes.add("embedding");
|
||||||
|
}
|
||||||
|
downloadedModels = downloadedModels.filter(model => allowedTypes.has(model.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterFilteringModelsCount = downloadedModels.length;
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
console.info(JSON.stringify(downloadedModels));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterFilteringModelsCount === 0) {
|
||||||
|
if (originalModelsCount === 0) {
|
||||||
|
console.info(chalk.redBright("You have not downloaded any models yet."));
|
||||||
|
} else {
|
||||||
|
console.info(
|
||||||
|
chalk.redBright(
|
||||||
|
`You have ${originalModelsCount} models, but none of them match the filter.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.info();
|
console.info();
|
||||||
console.info();
|
console.info();
|
||||||
printDownloadedModelsTable(
|
|
||||||
chalk.bgGreenBright.black(" LLM ") + " " + chalk.green("(Large Language Models)"),
|
const llms = downloadedModels.filter(model => model.type === "llm");
|
||||||
downloadedModels.filter(model => model.type === "llm"),
|
if (llms.length > 0) {
|
||||||
loadedModels,
|
printDownloadedModelsTable(
|
||||||
);
|
chalk.bgGreenBright.black(" LLM ") + " " + chalk.green("(Large Language Models)"),
|
||||||
console.info();
|
llms,
|
||||||
console.info();
|
loadedModels,
|
||||||
printDownloadedModelsTable(
|
);
|
||||||
chalk.bgGreenBright.black(" Embeddings "),
|
console.info();
|
||||||
downloadedModels.filter(model => model.type === "embedding"),
|
console.info();
|
||||||
loadedModels,
|
}
|
||||||
);
|
|
||||||
console.info();
|
const embeddings = downloadedModels.filter(model => model.type === "embedding");
|
||||||
console.info();
|
if (embeddings.length > 0) {
|
||||||
|
printDownloadedModelsTable(
|
||||||
|
chalk.bgGreenBright.black(" Embeddings "),
|
||||||
|
embeddings,
|
||||||
|
loadedModels,
|
||||||
|
);
|
||||||
|
console.info();
|
||||||
|
console.info();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const list = subcommands({
|
|
||||||
name: "list",
|
|
||||||
description: "List models",
|
|
||||||
cmds: { downloaded },
|
|
||||||
});
|
|
||||||
|
@ -156,7 +156,7 @@ async function checkHttpServerWithRetries(logger: SimpleLogger, port: number, ma
|
|||||||
/**
|
/**
|
||||||
* Gets the last status of the server.
|
* Gets the last status of the server.
|
||||||
*/
|
*/
|
||||||
async function getServerLastStatus(logger: SimpleLogger) {
|
export async function getServerLastStatus(logger: SimpleLogger) {
|
||||||
const lastStatusPath = getServerLastStatusPath();
|
const lastStatusPath = getServerLastStatusPath();
|
||||||
logger.debug(`Reading last status from ${lastStatusPath}`);
|
logger.debug(`Reading last status from ${lastStatusPath}`);
|
||||||
const lastStatus = JSON.parse(await readFile(lastStatusPath, "utf-8")) as HttpServerLastStatus;
|
const lastStatus = JSON.parse(await readFile(lastStatusPath, "utf-8")) as HttpServerLastStatus;
|
||||||
|
Reference in New Issue
Block a user