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

211 lines 8.81 kB
import process from "process"; import UpdateManager from "stdout-update"; import sliceAnsi from "slice-ansi"; import stripAnsi from "strip-ansi"; import chalk from "chalk"; import logSymbols from "log-symbols"; import prettyMilliseconds from "pretty-ms"; import { useCiLogs } from "../config.js"; import { clockChar } from "../consts.js"; import { ConsoleInteraction, ConsoleInteractionKey } from "../cli/utils/ConsoleInteraction.js"; import { getConsoleLogPrefix } from "./getConsoleLogPrefix.js"; import withOra from "./withOra.js"; export async function withProgressLog({ loadingText, successText, failText, liveUpdates = true, statusIcons = true, initialPercentage = 0, initialProgressBarText, progressBarLength = 40, minPercentageChangeForNonLiveUpdates = 0.1, eta = true, etaUpdateInterval = 1000, noProgress = false, progressFractionDigits = true, noSuccessLiveStatus = false, liveCtrlCSendsAbortSignal = false }, callback) { const shouldLiveUpdate = !useCiLogs && liveUpdates; const startTime = Date.now(); const abortController = new AbortController(); let currentProgress = initialPercentage; let currentProgressBarText = initialProgressBarText; let isAborted = false; const getEta = () => { const now = Date.now(); if (!eta || currentProgress === 1 || now - startTime < 1000) return null; const timeRemaining = ((now - startTime) / currentProgress) * (1 - currentProgress); if (!Number.isFinite(timeRemaining) || typeof timeRemaining === "bigint") return null; if (timeRemaining < 1000) return "0s left"; try { return prettyMilliseconds(timeRemaining, { keepDecimalsOnWholeSeconds: true, secondsDecimalDigits: 2, compact: true }) + " left"; } catch (err) { return null; } }; if (noProgress) { return withOra({ loading: loadingText, success: successText, fail: failText, useStatusLogs: !shouldLiveUpdate, noSuccessLiveStatus }, () => { const progressUpdater = { setProgress: () => progressUpdater }; return callback(progressUpdater); }); } else if (!shouldLiveUpdate) { const getLoadingText = () => { const formattedProgress = (currentProgress * 100) .toLocaleString("en-US", { minimumIntegerDigits: 1, minimumFractionDigits: 0, maximumFractionDigits: progressFractionDigits ? 3 : 0 }) + "%"; const etaText = getEta(); return [ chalk.cyan(clockChar), loadingText, chalk.yellow(formattedProgress), (currentProgressBarText != null && currentProgressBarText !== "") ? chalk.gray(currentProgressBarText + (etaText != null ? (" | " + etaText) : "")) : chalk.gray(etaText ?? "") ].join(" "); }; let lastLogProgress = initialPercentage; let lastLogProgressBarText = initialProgressBarText; const progressUpdater = { setProgress(progress, progressText) { currentProgress = progress; currentProgressBarText = progressText; if (Math.abs(currentProgress - lastLogProgress) >= minPercentageChangeForNonLiveUpdates || currentProgressBarText !== lastLogProgressBarText || (progress === 1 && lastLogProgress !== 1)) { console.log(getConsoleLogPrefix() + getLoadingText()); lastLogProgress = currentProgress; lastLogProgressBarText = currentProgressBarText; } return progressUpdater; } }; console.log(getConsoleLogPrefix() + getLoadingText()); try { const res = await callback(progressUpdater); console.log(getConsoleLogPrefix() + `${logSymbols.success} ${successText}`); return res; } catch (er) { console.log(getConsoleLogPrefix() + `${logSymbols.error} ${failText}`); throw er; } } const updateManager = UpdateManager.getInstance(); let etaUpdateTimeout = undefined; function getProgressLine() { const formattedProgress = (currentProgress * 100) .toLocaleString("en-US", { minimumIntegerDigits: 1, minimumFractionDigits: progressFractionDigits ? 4 : 0, maximumFractionDigits: progressFractionDigits ? 4 : 0 }) .slice(0, 5) + "%"; const addedText = (currentProgressBarText != null && currentProgressBarText !== "") ? currentProgressBarText : ""; const leftPad = " ".repeat(Math.floor((progressBarLength - stripAnsi(formattedProgress + (addedText.length > 0 ? (addedText + 1) : 0)).length) / 2)); return [ loadingText, renderProgressBar({ barText: leftPad + ` ${chalk.black.bgWhiteBright(formattedProgress)}${addedText.length === 0 ? "" : (" " + chalk.gray(addedText))} `, backgroundText: leftPad + ` ${chalk.yellow.bgGray(formattedProgress)}${addedText.length === 0 ? "" : (" " + chalk.white(addedText))} `, length: progressBarLength, loadedPercentage: Math.max(0, Math.min(1, currentProgress)), barStyle: chalk.black.bgWhiteBright, backgroundStyle: chalk.bgGray }), isAborted ? chalk.red("Aborted") : chalk.gray(getEta() ?? "") ].join(" "); } function updateProgressBar() { updateManager.update([ getConsoleLogPrefix() + getProgressLine() ]); clearTimeout(etaUpdateTimeout); if (eta && currentProgress !== 1) etaUpdateTimeout = setTimeout(updateProgressBar, etaUpdateInterval); } const progressUpdater = { setProgress(progress, progressText) { currentProgress = progress; currentProgressBarText = progressText; if (!isAborted) updateProgressBar(); return progressUpdater; }, abortSignal: liveCtrlCSendsAbortSignal ? abortController.signal : undefined }; updateManager.hook(); const consoleInteraction = new ConsoleInteraction(); let moveCursorUpAfterUnhook = false; consoleInteraction.onKey(ConsoleInteractionKey.ctrlC, () => { isAborted = true; if (liveCtrlCSendsAbortSignal) { abortController.abort(); consoleInteraction.stop(); updateProgressBar(); updateManager.unhook(true); } else { consoleInteraction.stop(); updateManager.unhook(true); updateProgressBar(); process.exit(0); } }); try { updateProgressBar(); consoleInteraction.start(); const res = await callback(progressUpdater); clearTimeout(etaUpdateTimeout); if (noSuccessLiveStatus) { updateManager.update([""]); moveCursorUpAfterUnhook = true; } else updateManager.update([ getConsoleLogPrefix() + (statusIcons ? (logSymbols.success + " ") : "") + successText ]); return res; } catch (err) { updateManager.update([ getConsoleLogPrefix() + (statusIcons ? (logSymbols.error + " ") : "") + failText ]); throw err; } finally { consoleInteraction.stop(); updateManager.unhook(true); if (moveCursorUpAfterUnhook) process.stdout.moveCursor(0, -1); } } function renderProgressBar({ barText, backgroundText, length, loadedPercentage, barStyle, backgroundStyle }) { const barChars = Math.floor(length * loadedPercentage); const backgroundChars = length - barChars; const slicedBarText = sliceAnsi(barText, 0, barChars); const paddedBarText = slicedBarText + " ".repeat(barChars - stripAnsi(slicedBarText).length); const slicedBackgroundText = sliceAnsi(backgroundText, barChars, barChars + backgroundChars); const paddedBackgroundText = slicedBackgroundText + " ".repeat(backgroundChars - stripAnsi(slicedBackgroundText).length); return barStyle(paddedBarText) + backgroundStyle(paddedBackgroundText); } //# sourceMappingURL=withProgressLog.js.map