UNPKG

node-llama-cpp

Version:

Run AI models locally on your machine with node.js bindings for llama.cpp. Enforce a JSON schema on the model output on the generation level

158 lines 7.36 kB
import process from "process"; import fs from "fs-extra"; import chalk from "chalk"; import { cliModelsDirectory, documentationPageUrls } from "../../config.js"; import { combineModelDownloaders, createModelDownloader } from "../../utils/createModelDownloader.js"; import { getReadablePath } from "../utils/getReadablePath.js"; import { ConsoleInteraction, ConsoleInteractionKey } from "../utils/ConsoleInteraction.js"; import { getIsInDocumentationMode } from "../../state.js"; import { resolveHeaderFlag } from "../utils/resolveHeaderFlag.js"; import { withCliCommandDescriptionDocsUrl } from "../utils/withCliCommandDescriptionDocsUrl.js"; export const PullCommand = { command: "pull [urls..]", aliases: ["get"], describe: withCliCommandDescriptionDocsUrl("Download models from URLs", documentationPageUrls.CLI.Pull), builder(yargs) { const isInDocumentationMode = getIsInDocumentationMode(); return yargs .option("urls", { type: "string", alias: ["url", "uris", "uri"], array: true, description: [ "A `.gguf` model URI to pull.", !isInDocumentationMode && "Automatically handles split and binary-split models files, so only pass the URI to the first file of a model.", !isInDocumentationMode && "If a file already exists and its size matches the expected size, it will not be downloaded again unless the `--override` flag is used.", "Pass multiple URIs to download multiple models at once." ].filter(Boolean).join(isInDocumentationMode ? "\n" : " "), demandOption: true, group: "Required:" }) .option("header", { alias: ["H"], type: "string", array: true, description: "Headers to use when downloading a model from a URL, in the format `key: value`. You can pass this option multiple times to add multiple headers.", group: "Optional:" }) .option("override", { alias: ["o"], type: "boolean", description: "Override existing model files", default: false, group: "Optional:" }) .option("noProgress", { type: "boolean", description: "Do not show a progress bar while downloading", default: false, group: "Optional:" }) .option("noTempFile", { alias: ["noTemp"], type: "boolean", description: "Delete the temporary file when canceling the download", default: false, group: "Optional:" }) .option("directory", { alias: ["d", "dir"], type: "string", description: "Directory to save the model to", default: cliModelsDirectory, defaultDescription: isInDocumentationMode ? "`" + getReadablePath(cliModelsDirectory) + "`" : getReadablePath(cliModelsDirectory), group: "Optional:" }) .option("filename", { alias: ["n", "name"], type: "string", description: "Filename to save the model as. Can only be used if a single URL is passed", group: "Optional:" }) .option("parallel", { alias: ["p"], type: "number", description: "Maximum parallel downloads", default: 4, group: "Optional:" }); }, async handler({ urls, header: headerArg, override, noProgress, noTempFile, directory, filename, parallel }) { const headers = resolveHeaderFlag(headerArg); if (urls.length === 0) throw new Error("At least one URI must be provided"); else if (urls.length > 1 && filename != null) throw new Error("The `--filename` flag can only be used when a single URI is passed"); if (urls.length === 1) { const downloader = await createModelDownloader({ modelUri: urls[0], dirPath: directory, headers, showCliProgress: !noProgress, deleteTempFileOnCancel: noTempFile, skipExisting: !override, fileName: filename || undefined, parallelDownloads: parallel, _showUriResolvingProgress: !noProgress }); if (!override && downloader.totalFiles === 1 && await fs.pathExists(downloader.entrypointFilePath)) { const fileStats = await fs.stat(downloader.entrypointFilePath); if (downloader.totalSize === fileStats.size) { console.info(`${chalk.yellow("File:")} ${getReadablePath(downloader.entrypointFilePath)}`); console.info("Skipping download of an existing file: " + chalk.yellow(getReadablePath(downloader.entrypointFilePath))); process.exit(0); } } const consoleInteraction = new ConsoleInteraction(); consoleInteraction.onKey(ConsoleInteractionKey.ctrlC, async () => { await downloader.cancel(); consoleInteraction.stop(); process.exit(0); }); if (!noProgress) { console.info(`Downloading to ${chalk.yellow(getReadablePath(directory))}${downloader.splitBinaryParts != null ? chalk.gray(` (combining ${downloader.splitBinaryParts} parts into a single file)`) : ""}`); consoleInteraction.start(); } await downloader.download(); if (!noProgress) consoleInteraction.stop(); console.info(`Downloaded to ${chalk.yellow(getReadablePath(downloader.entrypointFilePath))}`); } else { const downloader = await combineModelDownloaders(urls.map((uri) => createModelDownloader({ modelUri: uri, dirPath: directory, headers, showCliProgress: false, deleteTempFileOnCancel: noTempFile, skipExisting: !override })), { showCliProgress: !noProgress, parallelDownloads: parallel }); const consoleInteraction = new ConsoleInteraction(); consoleInteraction.onKey(ConsoleInteractionKey.ctrlC, async () => { await downloader.cancel(); consoleInteraction.stop(); process.exit(0); }); if (!noProgress) { console.info(`Downloading to ${chalk.yellow(getReadablePath(directory))}`); consoleInteraction.start(); } await downloader.download(); if (!noProgress) consoleInteraction.stop(); console.info(`Downloaded ${downloader.modelDownloaders.length} models to ${chalk.yellow(getReadablePath(directory))}\n${chalk.gray("*")} ` + downloader.modelDownloaders.map((downloader) => chalk.yellow(downloader.entrypointFilename)) .join(`\n${chalk.gray("*")} `)); } } }; //# sourceMappingURL=PullCommand.js.map