Spaces:
Runtime error
Runtime error
File size: 6,553 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #!/usr/bin/env node
/**
* OmniRoute β Co-locate the LLMLingua-2 optional dependency closure into the standalone bundle.
*
* The compression "ultra" SLM tier (PR #4257) runs `@atjsh/llmlingua-2` +
* `@huggingface/transformers` + `@tensorflow/tfjs` + `js-tiktoken` inside a worker thread
* (`open-sse/services/compression/engines/llmlingua/onnxWorker.js`, shipped under `dist/`). These
* are `optionalDependencies`: npm installs them into the ROOT `node_modules` on
* `--include=optional`, but the Next.js standalone trace bundles ONLY `@huggingface/transformers`
* (3.5.2, pinned) into `dist/node_modules` β it does NOT trace the optional, dynamically-imported
* SLM packages.
*
* ## Why this matters (the instance-split bug)
*
* The worker lives under `dist/`, so its `import("@huggingface/transformers")` resolves
* `dist/node_modules/@huggingface/transformers` (3.5.2) and the worker sets the model `cacheDir`
* on THAT instance's `env`. But its `import("@atjsh/llmlingua-2")` walks past `dist/node_modules`
* (no `@atjsh` there) up to the ROOT `node_modules`, and llmlingua-2's own
* `import("@huggingface/transformers")` then resolves the ROOT transformers β a DIFFERENT instance.
* The `cacheDir`/`localModelPath` config the worker set never reaches the instance llmlingua-2
* actually uses, so the local model under `DATA_DIR/models/llmlingua` is never found and the SLM
* tier silently fails-open (no compression). Worse, if the root transformers is a 4.x line,
* llmlingua-2 throws on a tokenizer-API change (`decoder.decode` is undefined).
*
* ## The fix
*
* Co-locate the SLM optional dependency CLOSURE from the root `node_modules` into
* `dist/node_modules` (NO-CLOBBER, so the pinned `dist` transformers 3.5.2 / onnxruntime / sharp
* stay). Then the worker resolves `@atjsh/llmlingua-2` AND `@huggingface/transformers` from the
* SAME `dist/node_modules` β a single 3.5.2 instance β so the env config applies and the local
* model loads.
*
* `@huggingface/transformers` is intentionally NOT a closure seed: it is a PEER of
* `@atjsh/llmlingua-2` (not a regular dependency) and is already bundled in `dist/node_modules`,
* so the closure walk never reaches it and the no-clobber guard would skip it anyway.
*
* ## Validation (Hard Rule #18)
*
* Manual co-location of this exact closure on the production VPS produced real 54.8% compression
* (11520 β 5203 chars) via real ONNX inference β both the default and the `modelPath` (PR #4257)
* code paths. See the unit test for the closure-walk + no-clobber contract.
*
* Idempotent + fail-soft: skips when the optionals are absent (the common case β they are OPTIONAL)
* or already co-located; a per-package copy failure only disables the SLM tier, which is itself
* fail-open, so this never throws into the install.
*/
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
/**
* Entry packages of the SLM optional stack (the closure roots). `@huggingface/transformers` is
* deliberately absent β it is the pinned instance already present in `dist/node_modules`.
*/
export const SEED_PACKAGES = ["@atjsh/llmlingua-2", "@tensorflow/tfjs", "js-tiktoken"];
/**
* Compute the transitive dependency closure of `seeds` by walking each package's `dependencies` +
* `optionalDependencies` from a `node_modules` directory. Packages that are not present in that
* tree (e.g. peers provided elsewhere, like `@huggingface/transformers` in `dist`) are skipped β
* the closure only contains packages that actually exist in `nodeModulesDir`.
*
* @param {string} nodeModulesDir absolute path to the source `node_modules`
* @param {string[]} [seeds] closure roots (defaults to {@link SEED_PACKAGES})
* @returns {string[]} package names in discovery order, seeds first
*/
export function computeDependencyClosure(nodeModulesDir, seeds = SEED_PACKAGES) {
const closure = [];
const seen = new Set();
const stack = [...seeds];
while (stack.length) {
const name = stack.shift();
if (seen.has(name)) continue;
seen.add(name);
const pkgDir = join(nodeModulesDir, name);
if (!existsSync(pkgDir)) continue; // absent in this tree (peer provided elsewhere) β skip
closure.push(name);
let manifest;
try {
manifest = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf8"));
} catch {
continue; // unreadable/absent manifest β copy the dir but do not recurse
}
const deps = { ...manifest.dependencies, ...manifest.optionalDependencies };
for (const dep of Object.keys(deps)) {
if (!seen.has(dep)) stack.push(dep);
}
}
return closure;
}
/**
* 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.
*
* @param {{ rootDir: string, log?: (message: string) => void }} opts
* @returns {{ skipped: true, reason: string }
* | { skipped: false, copied: number, closure: number }}
*/
export function colocateLlmlinguaOptionals({ rootDir, log = () => {} }) {
const rootNm = join(rootDir, "node_modules");
const distNm = join(rootDir, "dist", "node_modules");
if (!existsSync(distNm)) {
return { skipped: true, reason: "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)))) {
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"))) {
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, β¦)
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}`);
}
}
if (copied > 0) {
log(` β
Co-located ${copied} LLMLingua SLM optional package(s) into dist/node_modules.\n`);
}
return { skipped: false, copied, closure: closure.length };
}
|