diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55540edc..71a04044 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,13 +75,31 @@ jobs: - name: "macOS arm64" os: macos-26 artifact: "mac-arm64" + - name: "Ubuntu riscv64" + os: ubuntu-24.04-riscv + artifact: "linux-riscv64" steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 + if: matrix.config.artifact != 'linux-riscv64' with: node-version: "20" + - name: Install Node.js (riscv64 unofficial build) + if: matrix.config.artifact == 'linux-riscv64' + run: | + set -e + # node 24 (npm 11): node 20's npm 10.8.2 trips "set.delete is not a + # function" during npm ci on riscv64, and several devDeps require + # node >= 24.10. The built binary still targets node 20 N-API. + NODE_VER=v24.16.0 + curl -fsSL -o /tmp/node.tar.xz "https://unofficial-builds.nodejs.org/download/release/${NODE_VER}/node-${NODE_VER}-linux-riscv64.tar.xz" + mkdir -p "$HOME/node" + tar -xJf /tmp/node.tar.xz -C "$HOME/node" --strip-components=1 + echo "$HOME/node/bin" >> "$GITHUB_PATH" + "$HOME/node/bin/node" --version + - name: Download build artifact uses: actions/download-artifact@v4 with: @@ -131,6 +149,19 @@ jobs: cmake --version + - name: Install dependencies on Ubuntu riscv64 + if: matrix.config.name == 'Ubuntu riscv64' + run: | + sudo apt-get update + sudo apt-get install -y cmake build-essential ninja-build libtbb-dev gcc-14 g++-14 + # llama.cpp's ggml-cpu uses the RVV _Float16 (zvfh) vector intrinsics, + # which only exist in GCC 14+. The runner defaults to GCC 13, so make + # gcc-14/g++-14 the default compilers (same as upstream build-riscv). + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100 + cmake --version + gcc --version + - name: Install Cuda 13.1 on Windows (1) if: matrix.config.name == 'Windows (1)' uses: Jimver/cuda-toolkit@v0.2.30 @@ -207,9 +238,15 @@ jobs: timeout-minutes: 300 env: ARTIFACT_NAME: ${{ matrix.config.artifact }} + # electron has no riscv64 prebuilt binary; the binary build does not + # use it (the electron example is a separate job), so skip its + # postinstall download to let npm ci complete on riscv64. + ELECTRON_SKIP_BINARY_DOWNLOAD: ${{ matrix.config.artifact == 'linux-riscv64' && '1' || '' }} run: | - npm ci - + # npm on the unofficial riscv64 node intermittently throws internal + # TypeErrors (e.g. "key.match is not a function"); retry a few times. + npm ci || npm ci || npm ci + npx zx -y <<'EOF' async function getLatestNodeVersions(maxDate) { @@ -275,6 +312,8 @@ jobs: await buildBinary("x64", ["--gpu", "false"]); } else if (process.env.ARTIFACT_NAME === "mac-arm64") { await buildBinary("arm64", ["--gpu", "metal"]); + } else if (process.env.ARTIFACT_NAME === "linux-riscv64") { + await buildBinary("riscv64", ["--gpu", "false"]); } // move binaries to bins diff --git a/package-lock.json b/package-lock.json index 34dbed59..a1c02768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,6 +103,7 @@ "optionalDependencies": { "@node-llama-cpp/linux-arm64": "0.1.0", "@node-llama-cpp/linux-armv7l": "0.1.0", + "@node-llama-cpp/linux-riscv64": "0.1.0", "@node-llama-cpp/linux-x64": "0.1.0", "@node-llama-cpp/linux-x64-cuda": "0.1.0", "@node-llama-cpp/linux-x64-cuda-ext": "0.1.0", @@ -2378,6 +2379,9 @@ "node_modules/@node-llama-cpp/linux-armv7l": { "optional": true }, + "node_modules/@node-llama-cpp/linux-riscv64": { + "optional": true + }, "node_modules/@node-llama-cpp/linux-x64": { "optional": true }, diff --git a/package.json b/package.json index 1ca551da..777b4720 100644 --- a/package.json +++ b/package.json @@ -226,6 +226,7 @@ "optionalDependencies": { "@node-llama-cpp/linux-arm64": "0.1.0", "@node-llama-cpp/linux-armv7l": "0.1.0", + "@node-llama-cpp/linux-riscv64": "0.1.0", "@node-llama-cpp/linux-x64": "0.1.0", "@node-llama-cpp/linux-x64-cuda": "0.1.0", "@node-llama-cpp/linux-x64-cuda-ext": "0.1.0", diff --git a/packages/@node-llama-cpp/linux-riscv64/.gitignore b/packages/@node-llama-cpp/linux-riscv64/.gitignore new file mode 100644 index 00000000..9b1c8b13 --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/@node-llama-cpp/linux-riscv64/LICENSE b/packages/@node-llama-cpp/linux-riscv64/LICENSE new file mode 100644 index 00000000..22789ae3 --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Gilad S. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/@node-llama-cpp/linux-riscv64/README.md b/packages/@node-llama-cpp/linux-riscv64/README.md new file mode 100644 index 00000000..39501c59 --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/README.md @@ -0,0 +1,4 @@ +# [`node-llama-cpp`](https://github.com/withcatai/node-llama-cpp) +This is a prebuilt binary package for [`node-llama-cpp`](https://github.com/withcatai/node-llama-cpp) for Linux riscv64. + +Do not install this package directly. diff --git a/packages/@node-llama-cpp/linux-riscv64/package-lock.json b/packages/@node-llama-cpp/linux-riscv64/package-lock.json new file mode 100644 index 00000000..db2b4d6e --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/package-lock.json @@ -0,0 +1,39 @@ +{ + "name": "@node-llama-cpp/linux-riscv64", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@node-llama-cpp/linux-riscv64", + "version": "0.1.0", + "cpu": [ + "arm", + "x64" + ], + "license": "MIT", + "os": [ + "linux" + ], + "devDependencies": { + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/typescript": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/packages/@node-llama-cpp/linux-riscv64/package.json b/packages/@node-llama-cpp/linux-riscv64/package.json new file mode 100644 index 00000000..00d03580 --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/package.json @@ -0,0 +1,54 @@ +{ + "name": "@node-llama-cpp/linux-riscv64", + "version": "0.1.0", + "description": "Prebuilt binary for node-llama-cpp for Linux riscv64", + "main": "dist/index.js", + "type": "module", + "files": [ + "dist/", + "bins/", + "package.json", + "README.md", + "LICENSE" + ], + "exports": { + ".": { + "import": "./dist/index.js", + "node": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "engines": { + "node": ">=20.0.0" + }, + "os": [ + "linux" + ], + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "scripts": { + "prebuild": "rimraf ./dist ./tsconfig.tsbuildinfo", + "build": "tsc --build tsconfig.json --force", + "prewatch": "rimraf ./dist ./tsconfig.tsbuildinfo", + "watch": "tsc --build tsconfig.json --watch --force", + "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/withcatai/node-llama-cpp.git" + }, + "author": "Gilad S.", + "license": "MIT", + "preferUnplugged": true, + "bugs": { + "url": "https://github.com/withcatai/node-llama-cpp/issues" + }, + "homepage": "https://node-llama-cpp.withcat.ai", + "devDependencies": { + "typescript": "^5.2.2" + } +} diff --git a/packages/@node-llama-cpp/linux-riscv64/src/index.ts b/packages/@node-llama-cpp/linux-riscv64/src/index.ts new file mode 100644 index 00000000..a4cb56d5 --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/src/index.ts @@ -0,0 +1,14 @@ +import path from "path"; +import {fileURLToPath} from "url"; +import fs from "node:fs/promises"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const binsDir = path.join(__dirname, "..", "bins"); +const packageVersion: string = (JSON.parse(await fs.readFile(path.join(__dirname, "..", "package.json"), "utf8"))).version; + +export function getBinsDir() { + return { + binsDir, + packageVersion + }; +} diff --git a/packages/@node-llama-cpp/linux-riscv64/tsconfig.json b/packages/@node-llama-cpp/linux-riscv64/tsconfig.json new file mode 100644 index 00000000..527d791c --- /dev/null +++ b/packages/@node-llama-cpp/linux-riscv64/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "lib": ["es2022"], + "module": "node16", + "target": "es2022", + "esModuleInterop": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitOverride": true, + "removeComments": false, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "moduleResolution": "node16", + "resolveJsonModule": false, + "strictNullChecks": true, + "isolatedModules": true, + "noEmit": false, + "outDir": "./dist", + "strict": true, + "sourceMap": false, + "composite": false, + "declaration": false, + "stripInternal": true + }, + "files": [ + "./src/index.ts" + ], + "include": [ + "./src" + ] +} diff --git a/scripts/movePrebuiltBinariesToStandaloneModules.ts b/scripts/movePrebuiltBinariesToStandaloneModules.ts index 792b8fb9..5eb1dafb 100644 --- a/scripts/movePrebuiltBinariesToStandaloneModules.ts +++ b/scripts/movePrebuiltBinariesToStandaloneModules.ts @@ -58,6 +58,7 @@ await moveBinariesFolderToStandaloneModule((folderName) => folderName.startsWith await moveBinariesFolderToStandaloneModule((folderName) => folderName.startsWith("linux-arm64"), "@node-llama-cpp/linux-arm64"); await moveBinariesFolderToStandaloneModule((folderName) => folderName.startsWith("linux-armv7l"), "@node-llama-cpp/linux-armv7l"); +await moveBinariesFolderToStandaloneModule((folderName) => folderName.startsWith("linux-riscv64"), "@node-llama-cpp/linux-riscv64"); await moveBinariesFallbackDirToStandaloneExtModule((folderName) => folderName.startsWith("win-x64-cuda"), "@node-llama-cpp/win-x64-cuda-ext"); await moveBinariesFolderToStandaloneModule((folderName) => folderName.startsWith("win-x64-cuda"), "@node-llama-cpp/win-x64-cuda"); diff --git a/src/bindings/utils/compileLLamaCpp.ts b/src/bindings/utils/compileLLamaCpp.ts index 6857890b..2875fbad 100644 --- a/src/bindings/utils/compileLLamaCpp.ts +++ b/src/bindings/utils/compileLLamaCpp.ts @@ -618,6 +618,9 @@ function getPrebuiltBinariesPackageDirectoryForBuildOptions(buildOptions: { else if (buildOptions.arch === "arm") // @ts-ignore return getBinariesPathFromModules(() => import("@node-llama-cpp/linux-armv7l")); + else if (buildOptions.arch === "riscv64") + // @ts-ignore + return getBinariesPathFromModules(() => import("@node-llama-cpp/linux-riscv64")); } else if (buildOptions.platform === "win") { if (buildOptions.arch === "x64") { if (buildOptions.gpu === "cuda") diff --git a/src/bindings/utils/detectGlibc.ts b/src/bindings/utils/detectGlibc.ts index 416ce8e0..5424e808 100644 --- a/src/bindings/utils/detectGlibc.ts +++ b/src/bindings/utils/detectGlibc.ts @@ -19,7 +19,8 @@ export async function detectGlibc({ "/usr/lib64", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", - "/usr/lib/armv7l-linux-gnu" + "/usr/lib/armv7l-linux-gnu", + "/usr/lib/riscv64-linux-gnu" ]; const glibcFileNames = [ @@ -45,7 +46,11 @@ export async function detectGlibc({ "ld-linux-armv7l.so", "ld-linux-armv7l.so.1", "ld-linux-armv7l.so.2", - "ld-linux-armv7l.so.3" // for when the next version comes out + "ld-linux-armv7l.so.3", // for when the next version comes out + "ld-linux-riscv64-lp64d.so", + "ld-linux-riscv64-lp64d.so.1", + "ld-linux-riscv64-lp64d.so.2", + "ld-linux-riscv64-lp64d.so.3" // for when the next version comes out ]; const foundGlibC = await asyncEvery([