mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 15:22:12 +03:00
fix(docker): bundle LLMLingua optional dependencies (#9185)
Validated in local merge-train T4 (HouMinXi+Zartharas+Andrian+artickc)
This commit is contained in:
@@ -48,6 +48,10 @@
|
||||
import fs from "node:fs/promises";
|
||||
import fsSync from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
colocateLlmlinguaOptionals,
|
||||
SEED_PACKAGES,
|
||||
} from "./colocateOptionals.mjs";
|
||||
|
||||
/**
|
||||
* Check whether a path exists (async).
|
||||
@@ -736,6 +740,19 @@ export function assembleStandalone({
|
||||
// 6. Optionally copy native assets + extra modules (synchronous)
|
||||
if (copyNatives) {
|
||||
copyNativeAssetsAndExtraModules(projectRoot, resolvedOutDir);
|
||||
|
||||
// #9166: dynamically imported LLMLingua packages are not reliably traced
|
||||
// into the standalone bundle. Copy their complete dependency closure from
|
||||
// the installed root tree without overwriting packages already traced by
|
||||
// Next.js. Include transformers here so its ONNX runtime closure is also
|
||||
// guaranteed in Docker/standalone builds.
|
||||
colocateLlmlinguaOptionals({
|
||||
rootDir: projectRoot,
|
||||
targetNodeModulesDir: path.join(resolvedOutDir, "node_modules"),
|
||||
seeds: [...SEED_PACKAGES, "@huggingface/transformers"],
|
||||
log: (message) =>
|
||||
console.log(`[assembleStandalone] ${message.trim()}`),
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Optionally dereference Turbopack hashed-module symlinks so the bundle is
|
||||
|
||||
@@ -97,47 +97,81 @@ export function computeDependencyClosure(nodeModulesDir, seeds = SEED_PACKAGES)
|
||||
}
|
||||
|
||||
/**
|
||||
* Co-locate the SLM optional closure from `<rootDir>/node_modules` into
|
||||
* `<rootDir>/dist/node_modules`. No-op when the standalone `dist` bundle or the optional seeds are
|
||||
* absent, and idempotent once co-located. Never throws.
|
||||
* Co-locate the SLM optional dependency closure from `<rootDir>/node_modules`
|
||||
* into a standalone bundle's `node_modules`.
|
||||
*
|
||||
* @param {{ rootDir: string, log?: (message: string) => void }} opts
|
||||
* The default destination remains `<rootDir>/dist/node_modules` for the npm
|
||||
* postinstall path. Standalone builders, including Docker, may provide
|
||||
* `targetNodeModulesDir`.
|
||||
*
|
||||
* Packages already present in the destination are never overwritten. This
|
||||
* preserves the standalone bundle's pinned dependency instances while filling
|
||||
* dynamically imported packages that Next.js did not trace.
|
||||
*
|
||||
* @param {{
|
||||
* rootDir: string,
|
||||
* targetNodeModulesDir?: string,
|
||||
* seeds?: string[],
|
||||
* log?: (message: string) => void
|
||||
* }} opts
|
||||
* @returns {{ skipped: true, reason: string }
|
||||
* | { skipped: false, copied: number, closure: number }}
|
||||
*/
|
||||
export function colocateLlmlinguaOptionals({ rootDir, log = () => {} }) {
|
||||
export function colocateLlmlinguaOptionals({
|
||||
rootDir,
|
||||
targetNodeModulesDir,
|
||||
seeds = SEED_PACKAGES,
|
||||
log = () => {},
|
||||
}) {
|
||||
const rootNm = join(rootDir, "node_modules");
|
||||
const distNm = join(rootDir, "dist", "node_modules");
|
||||
const targetNm = targetNodeModulesDir ?? join(rootDir, "dist", "node_modules");
|
||||
|
||||
if (!existsSync(distNm)) {
|
||||
return { skipped: true, reason: "no standalone dist/node_modules" };
|
||||
if (!existsSync(targetNm)) {
|
||||
return {
|
||||
skipped: true,
|
||||
reason: targetNodeModulesDir
|
||||
? "no target node_modules"
|
||||
: "no standalone dist/node_modules",
|
||||
};
|
||||
}
|
||||
// Gate: only run when the optional stack was actually installed (`npm install --include=optional`).
|
||||
if (!SEED_PACKAGES.every((seed) => existsSync(join(rootNm, seed)))) {
|
||||
|
||||
// Only run when every requested closure root was installed.
|
||||
if (!seeds.every((seed) => existsSync(join(rootNm, seed)))) {
|
||||
return { skipped: true, reason: "SLM optionals not installed at root" };
|
||||
}
|
||||
// Idempotent: the entry package is already co-located → nothing to do.
|
||||
if (existsSync(join(distNm, "@atjsh", "llmlingua-2"))) {
|
||||
|
||||
const closure = computeDependencyClosure(rootNm, seeds);
|
||||
|
||||
// Check the complete closure rather than only the entry package. A partially
|
||||
// populated bundle must still receive any missing transitive dependencies.
|
||||
if (
|
||||
closure.length > 0 &&
|
||||
closure.every((name) => existsSync(join(targetNm, name)))
|
||||
) {
|
||||
return { skipped: true, reason: "already co-located" };
|
||||
}
|
||||
|
||||
const closure = computeDependencyClosure(rootNm);
|
||||
let copied = 0;
|
||||
|
||||
for (const name of closure) {
|
||||
const dest = join(distNm, name);
|
||||
if (existsSync(dest)) continue; // no-clobber: keep dist's pinned copy (transformers 3.5.2, …)
|
||||
const dest = join(targetNm, name);
|
||||
if (existsSync(dest)) continue;
|
||||
|
||||
try {
|
||||
mkdirSync(dirname(dest), { recursive: true });
|
||||
cpSync(join(rootNm, name), dest, { recursive: true });
|
||||
copied++;
|
||||
} catch (err) {
|
||||
log(` ⚠️ LLMLingua optional co-location failed for ${name}: ${err.message}`);
|
||||
log(
|
||||
` ⚠️ LLMLingua optional co-location failed for ${name}: ${err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (copied > 0) {
|
||||
log(` ✅ Co-located ${copied} LLMLingua SLM optional package(s) into dist/node_modules.\n`);
|
||||
log(
|
||||
` ✅ Co-located ${copied} LLMLingua SLM optional package(s) into standalone node_modules.\n`
|
||||
);
|
||||
}
|
||||
|
||||
return { skipped: false, copied, closure: closure.length };
|
||||
|
||||
Reference in New Issue
Block a user