mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 09:42:15 +03:00
fix(build): register onnxruntime-node's native bin/ as a standalone asset (#9687)
Docker/standalone builds of the LLMLingua SLM compression tier failed at
runtime with "Error: libonnxruntime.so.1: cannot open shared object file:
No such file or directory" (open-sse/services/compression/engines/llmlingua's
worker, via @huggingface/transformers -> onnxruntime-node).
onnxruntime-node's dist/binding.js is a normal JS file Next.js's standalone
trace bundles correctly, but binding.js dlopen()s a platform-specific native
library shipped under bin/napi-v3/<platform>/<arch>/libonnxruntime.so.1 — a
dynamic native load static file tracing can't see (same blind-spot class as
the separate colocateLlmlinguaOptionals stub bug, just for a .so instead of
a JS import, via NATIVE_ASSET_ENTRIES instead). That directory was simply
never registered, unlike better-sqlite3's native binary, which already goes
through the exact same mechanism correctly.
Fix: add an entry for onnxruntime-node/bin, mirroring the existing
better-sqlite3 entry. Confirmed against a real Docker build of the
Dockerfile's own post-build verification step: this was the very next
failure once the separate llmlingua-2 stub bug was fixed and the build
progressed far enough to reach it.
Covered by tests/unit/assemble-standalone-onnxruntime-native-asset.test.ts
(fails against the pre-fix code on both assertions, passes after).
(cherry picked from commit 8c98a59f26)
This commit is contained in:
committed by
diegosouzapw
parent
4d816a4d8a
commit
4d6f2d28fe
@@ -48,10 +48,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import fsSync from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
colocateLlmlinguaOptionals,
|
||||
SEED_PACKAGES,
|
||||
} from "./colocateOptionals.mjs";
|
||||
import { colocateLlmlinguaOptionals, SEED_PACKAGES } from "./colocateOptionals.mjs";
|
||||
|
||||
/**
|
||||
* Check whether a path exists (async).
|
||||
@@ -78,7 +75,7 @@ async function exists(targetPath) {
|
||||
* (relative to projectRoot) and destination (relative to outDir) can be joined
|
||||
* for either path/platform. @type {{label:string, src:string[], dest:string[]}[]}
|
||||
*/
|
||||
const NATIVE_ASSET_ENTRIES = [
|
||||
export const NATIVE_ASSET_ENTRIES = [
|
||||
{
|
||||
label: "wreq-js native runtime",
|
||||
src: ["node_modules", "wreq-js", "rust"],
|
||||
@@ -90,13 +87,17 @@ const NATIVE_ASSET_ENTRIES = [
|
||||
dest: ["node_modules", "better-sqlite3", "build"],
|
||||
},
|
||||
{
|
||||
// #8847: Bun (and npx -g global installs) resolve better-sqlite3's native
|
||||
// binary from prebuilds/ instead of build/Release/, so the compiled build/
|
||||
// copy alone leaves a hollow package that falls back to sql.js (OOM under
|
||||
// Bun). Ship the prebuilds alongside the compiled binary.
|
||||
label: "better-sqlite3 prebuilds (Bun / global installs)",
|
||||
src: ["node_modules", "better-sqlite3", "prebuilds"],
|
||||
dest: ["node_modules", "better-sqlite3", "prebuilds"],
|
||||
// onnxruntime-node's dist/binding.js dlopen()s a platform-specific
|
||||
// libonnxruntime.so.1 shipped under bin/napi-v3/<platform>/<arch>/ — a
|
||||
// *dynamic* native load Next.js's standalone file trace can't see (same
|
||||
// blind spot class as the LLMLingua closure below, just for a .so instead
|
||||
// of a JS import). Without this the standalone bundle boots with
|
||||
// "Error: libonnxruntime.so.1: cannot open shared object file: No such
|
||||
// file or directory" the first time transformers/llmlingua actually try
|
||||
// to run ONNX inference.
|
||||
label: "onnxruntime-node native binaries (libonnxruntime .so + .node addon)",
|
||||
src: ["node_modules", "onnxruntime-node", "bin"],
|
||||
dest: ["node_modules", "onnxruntime-node", "bin"],
|
||||
},
|
||||
{
|
||||
// TPROXY IP_TRANSPARENT addon (Fase 3 / Epic A). Built by build-tproxy-native
|
||||
@@ -759,8 +760,7 @@ export function assembleStandalone({
|
||||
rootDir: projectRoot,
|
||||
targetNodeModulesDir: path.join(resolvedOutDir, "node_modules"),
|
||||
seeds: [...SEED_PACKAGES, "@huggingface/transformers"],
|
||||
log: (message) =>
|
||||
console.log(`[assembleStandalone] ${message.trim()}`),
|
||||
log: (message) => console.log(`[assembleStandalone] ${message.trim()}`),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Regression test — onnxruntime-node's native libonnxruntime.so.1 was never
|
||||
* registered in NATIVE_ASSET_ENTRIES, so the standalone bundle shipped
|
||||
* dist/binding.js (traced by Next.js as a normal JS require) without its
|
||||
* sibling native bin/ directory (loaded via a dynamic dlopen() Next's static
|
||||
* file trace can't see — the same blind-spot class as the LLMLingua closure,
|
||||
* for a .so instead of a JS import). The bundle then failed at runtime with
|
||||
* "Error: libonnxruntime.so.1: cannot open shared object file: No such file
|
||||
* or directory" the first time transformers/llmlingua tried to run ONNX
|
||||
* inference — reproduced live via the Dockerfile's post-build verification
|
||||
* step once the separate llmlingua-2 stub bug (#9653-adjacent fix) was
|
||||
* resolved and the build progressed far enough to reach this package.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
|
||||
import {
|
||||
NATIVE_ASSET_ENTRIES,
|
||||
syncStandaloneNativeAssets,
|
||||
} from "../../scripts/build/assembleStandalone.mjs";
|
||||
|
||||
test("NATIVE_ASSET_ENTRIES registers onnxruntime-node's native bin/ directory", () => {
|
||||
const entry = NATIVE_ASSET_ENTRIES.find(
|
||||
(e) => e.src.join("/") === "node_modules/onnxruntime-node/bin"
|
||||
);
|
||||
assert.ok(entry, "onnxruntime-node/bin must be a registered native asset entry");
|
||||
assert.deepEqual(entry.dest, ["node_modules", "onnxruntime-node", "bin"]);
|
||||
});
|
||||
|
||||
test("syncStandaloneNativeAssets copies onnxruntime-node's libonnxruntime.so.1 into the standalone bundle", async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "omniroute-assemble-onnx-"));
|
||||
try {
|
||||
// Mirror the real package's shape: dist/binding.js (traced fine by Next)
|
||||
// plus the platform-specific native .so under bin/napi-v3/<platform>/<arch>/.
|
||||
const pkgDir = join(root, "node_modules", "onnxruntime-node");
|
||||
mkdirSync(join(pkgDir, "dist"), { recursive: true });
|
||||
writeFileSync(join(pkgDir, "dist", "binding.js"), "// native binding loader\n");
|
||||
const soDir = join(pkgDir, "bin", "napi-v3", "linux", "x64");
|
||||
mkdirSync(soDir, { recursive: true });
|
||||
writeFileSync(join(soDir, "libonnxruntime.so.1"), "fake-shared-library-bytes");
|
||||
|
||||
const outDir = join(root, ".build", "next", "standalone");
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
|
||||
const changed = await syncStandaloneNativeAssets(root, undefined, { log: () => {} }, outDir);
|
||||
assert.equal(changed, true);
|
||||
|
||||
const destSo = join(
|
||||
outDir,
|
||||
"node_modules",
|
||||
"onnxruntime-node",
|
||||
"bin",
|
||||
"napi-v3",
|
||||
"linux",
|
||||
"x64",
|
||||
"libonnxruntime.so.1"
|
||||
);
|
||||
assert.ok(existsSync(destSo), "libonnxruntime.so.1 must be copied into the standalone bundle");
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user