First upload version 0.0.1
This commit is contained in:
269
node_modules/node-llama-cpp/dist/bindings/utils/testBindingBinary.js
generated
vendored
Normal file
269
node_modules/node-llama-cpp/dist/bindings/utils/testBindingBinary.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
import { fork } from "node:child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import { createRequire } from "module";
|
||||
import path from "path";
|
||||
import { getConsoleLogPrefix } from "../../utils/getConsoleLogPrefix.js";
|
||||
import { runningInBun, runningInElectron } from "../../utils/runtime.js";
|
||||
import { LlamaLogLevelToAddonLogLevel } from "../Llama.js";
|
||||
import { defaultBindingTestLogLevel, newGithubIssueUrl } from "../../config.js";
|
||||
import { getPlatform } from "./getPlatform.js";
|
||||
const require = createRequire(import.meta.url);
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const detectedFileName = path.basename(__filename);
|
||||
const expectedFileName = "testBindingBinary";
|
||||
export async function testBindingBinary(bindingBinaryPath, extBackendsPath, gpu, testTimeout = 1000 * 60 * 5, pipeOutputOnNode = false) {
|
||||
if (!detectedFileName.startsWith(expectedFileName)) {
|
||||
console.warn(getConsoleLogPrefix() +
|
||||
`"${expectedFileName}.js" file is not independent, so testing a binding binary with the current system ` +
|
||||
"prior to importing it cannot be done.\n" +
|
||||
getConsoleLogPrefix() +
|
||||
"Assuming the test passed with the risk that the process may crash due to an incompatible binary.\n" +
|
||||
getConsoleLogPrefix() +
|
||||
'To resolve this issue, make sure that "node-llama-cpp" is not bundled together with other code and is imported as an external module with its original file structure.');
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const runningInsideSnapOnLinux = getPlatform() === "linux" && process.env.SNAP != null;
|
||||
if (runningInsideSnapOnLinux && !runningInBun && !runningInElectron) {
|
||||
const nodeSea = await import("node:sea");
|
||||
if (nodeSea.isSea()) {
|
||||
console.warn(getConsoleLogPrefix() +
|
||||
"node SEA is detected, so testing a binding binary with the current system prior to importing it cannot be done.\n" +
|
||||
getConsoleLogPrefix() +
|
||||
"Assuming the test passed with the risk that the process may crash due to an incompatible binary.\n" +
|
||||
getConsoleLogPrefix() +
|
||||
"If this is an issue in your case, " +
|
||||
"please open an issue on GitHub with the details of the environment where this happens: " + newGithubIssueUrl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
async function getForkFunction() {
|
||||
if (runningInElectron) {
|
||||
try {
|
||||
const { utilityProcess } = await import("electron");
|
||||
return {
|
||||
type: "electron",
|
||||
fork: utilityProcess.fork.bind(utilityProcess)
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: "node",
|
||||
fork
|
||||
};
|
||||
}
|
||||
const forkFunction = await getForkFunction();
|
||||
function createTestProcess({ onMessage, onExit }) {
|
||||
if (forkFunction.type === "electron") {
|
||||
let exited = false;
|
||||
const subProcess = forkFunction.fork(__filename, [], {
|
||||
env: {
|
||||
...process.env,
|
||||
TEST_BINDING_CP: "true"
|
||||
}
|
||||
});
|
||||
function cleanupElectronFork() {
|
||||
if (subProcess.pid != null || !exited) {
|
||||
subProcess.kill();
|
||||
exited = true;
|
||||
}
|
||||
process.off("exit", cleanupElectronFork);
|
||||
}
|
||||
process.on("exit", cleanupElectronFork);
|
||||
subProcess.on("message", onMessage);
|
||||
subProcess.on("exit", (code) => {
|
||||
exited = true;
|
||||
cleanupElectronFork();
|
||||
onExit(code);
|
||||
});
|
||||
return {
|
||||
sendMessage: (message) => subProcess.postMessage(message),
|
||||
killProcess: cleanupElectronFork,
|
||||
pipeMessages: () => void 0
|
||||
};
|
||||
}
|
||||
let pipeSet = false;
|
||||
const subProcess = forkFunction.fork(__filename, [], {
|
||||
detached: false,
|
||||
silent: true,
|
||||
stdio: pipeOutputOnNode
|
||||
? ["ignore", "pipe", "pipe", "ipc"]
|
||||
: ["ignore", "ignore", "ignore", "ipc"],
|
||||
env: {
|
||||
...process.env,
|
||||
TEST_BINDING_CP: "true"
|
||||
}
|
||||
});
|
||||
function cleanupNodeFork() {
|
||||
subProcess.stdout?.off("data", onStdout);
|
||||
subProcess.stderr?.off("data", onStderr);
|
||||
if (subProcess.exitCode == null)
|
||||
subProcess.kill("SIGKILL");
|
||||
process.off("exit", cleanupNodeFork);
|
||||
}
|
||||
process.on("exit", cleanupNodeFork);
|
||||
subProcess.on("message", onMessage);
|
||||
subProcess.on("exit", (code) => {
|
||||
cleanupNodeFork();
|
||||
onExit(code ?? -1);
|
||||
});
|
||||
if (subProcess.killed || subProcess.exitCode != null) {
|
||||
cleanupNodeFork();
|
||||
onExit(subProcess.exitCode ?? -1);
|
||||
}
|
||||
function onStdout(data) {
|
||||
if (!pipeSet)
|
||||
return;
|
||||
process.stdout.write(data);
|
||||
}
|
||||
function onStderr(data) {
|
||||
if (!pipeSet)
|
||||
return;
|
||||
process.stderr.write(data);
|
||||
}
|
||||
if (pipeOutputOnNode) {
|
||||
subProcess.stdout?.on("data", onStdout);
|
||||
subProcess.stderr?.on("data", onStderr);
|
||||
}
|
||||
function pipeMessages() {
|
||||
if (!pipeOutputOnNode || pipeSet)
|
||||
return;
|
||||
pipeSet = true;
|
||||
}
|
||||
return {
|
||||
sendMessage: (message) => subProcess.send(message),
|
||||
killProcess: cleanupNodeFork,
|
||||
pipeMessages
|
||||
};
|
||||
}
|
||||
let testPassed = false;
|
||||
let forkSucceeded = false;
|
||||
let timeoutHandle = null;
|
||||
let subProcess = undefined;
|
||||
let testFinished = false;
|
||||
function cleanup() {
|
||||
testFinished = true;
|
||||
if (timeoutHandle != null)
|
||||
clearTimeout(timeoutHandle);
|
||||
subProcess?.killProcess();
|
||||
}
|
||||
return Promise.race([
|
||||
new Promise((_, reject) => {
|
||||
timeoutHandle = setTimeout(() => {
|
||||
reject(new Error("Binding binary load test timed out"));
|
||||
cleanup();
|
||||
}, testTimeout);
|
||||
}),
|
||||
new Promise((resolve, reject) => {
|
||||
function done() {
|
||||
if (!forkSucceeded)
|
||||
reject(new Error(`Binding binary test failed to run a test process via file "${__filename}"`));
|
||||
else
|
||||
resolve(testPassed);
|
||||
cleanup();
|
||||
}
|
||||
subProcess = createTestProcess({
|
||||
onMessage(message) {
|
||||
if (message.type === "ready") {
|
||||
forkSucceeded = true;
|
||||
subProcess.sendMessage({
|
||||
type: "start",
|
||||
bindingBinaryPath,
|
||||
extBackendsPath,
|
||||
gpu
|
||||
});
|
||||
}
|
||||
else if (message.type === "loaded") {
|
||||
subProcess.pipeMessages(); // only start piping error logs if the binary loaded successfully
|
||||
subProcess.sendMessage({
|
||||
type: "test",
|
||||
bindingBinaryPath,
|
||||
extBackendsPath,
|
||||
gpu
|
||||
});
|
||||
}
|
||||
else if (message.type === "done") {
|
||||
testPassed = true;
|
||||
subProcess.sendMessage({ type: "exit" });
|
||||
}
|
||||
},
|
||||
onExit(code) {
|
||||
if (code !== 0)
|
||||
testPassed = false;
|
||||
done();
|
||||
}
|
||||
});
|
||||
if (testFinished)
|
||||
subProcess.killProcess();
|
||||
})
|
||||
]);
|
||||
}
|
||||
if (process.env.TEST_BINDING_CP === "true" && (process.parentPort != null || process.send != null)) {
|
||||
let binding;
|
||||
const sendMessage = process.parentPort != null
|
||||
? (message) => process.parentPort.postMessage(message)
|
||||
: (message) => process.send(message);
|
||||
const onMessage = async (message) => {
|
||||
if (message.type === "start") {
|
||||
try {
|
||||
binding = require(message.bindingBinaryPath);
|
||||
const errorLogLevel = LlamaLogLevelToAddonLogLevel.get(defaultBindingTestLogLevel);
|
||||
if (errorLogLevel != null)
|
||||
binding.setLoggerLogLevel(errorLogLevel);
|
||||
sendMessage({ type: "loaded" });
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
else if (message.type === "test") {
|
||||
try {
|
||||
if (binding == null)
|
||||
throw new Error("Binding binary is not loaded");
|
||||
binding.loadBackends();
|
||||
let loadedGpu = binding.getGpuType();
|
||||
if (loadedGpu == null || (loadedGpu === false && message.gpu !== false)) {
|
||||
const backendsPath = path.dirname(path.resolve(message.bindingBinaryPath));
|
||||
const fallbackBackendsDir = path.join(path.resolve(message.extBackendsPath ?? backendsPath), "fallback");
|
||||
binding.loadBackends(backendsPath);
|
||||
loadedGpu = binding.getGpuType();
|
||||
if (loadedGpu == null || (loadedGpu === false && message.gpu !== false))
|
||||
binding.loadBackends(fallbackBackendsDir);
|
||||
}
|
||||
await binding.init();
|
||||
binding.getGpuVramInfo();
|
||||
binding.getGpuDeviceInfo();
|
||||
const gpuType = binding.getGpuType();
|
||||
void gpuType;
|
||||
if (gpuType !== message.gpu)
|
||||
throw new Error("Binary GPU type mismatch. " +
|
||||
`Expected: ${message.gpu}, got: ${gpuType}. ` + (message.gpu === "cuda"
|
||||
? "May be due to a linker issue, ensure you don't have multiple conflicting CUDA installations."
|
||||
: "May be due to a linker issue, ensure the native dependencies are not broken."));
|
||||
binding.ensureGpuDeviceIsSupported();
|
||||
sendMessage({ type: "done" });
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
else if (message.type === "exit") {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
if (process.parentPort != null)
|
||||
process.parentPort.on("message", (message) => onMessage(message.data));
|
||||
else
|
||||
process.on("message", onMessage);
|
||||
sendMessage({ type: "ready" });
|
||||
}
|
||||
//# sourceMappingURL=testBindingBinary.js.map
|
||||
Reference in New Issue
Block a user