From d094822fc36cc3a59743114af5efc7acc2c17604 Mon Sep 17 00:00:00 2001 From: s3np4 Date: Mon, 17 Aug 2026 07:58:36 -0500 Subject: [PATCH] fix(deps): upgrade @atjsh/llmlingua-2 to 2.0.5 and drop @tensorflow/tfjs from the LLMLingua SLM stack @atjsh/llmlingua-2@2.0.5 adds official Transformers.js v4 support (peers "@huggingface/transformers": "^3.5.2 || ^4.0.0") and 2.0.4+ no longer requires @tensorflow/tfjs, restoring compatibility with OmniRoute's Transformers.js v4 while removing the largest single contributor to the optional runtime footprint. Updates the worker gate comments, colocate/standalone/optional-pack scripts, Docker standalone validation, dependency allowlist, tests, and docs (COMPRESSION_ENGINES.md, release checklists + i18n). Closes #10536. --- .github/dependabot.yml | 14 +- Dockerfile | 2 +- .../10536-llmlingua-2-2.0.5-drop-tfjs.md | 1 + config/quality/dependency-allowlist.json | 1 - docs/compression/COMPRESSION_ENGINES.md | 31 +- .../docs/compression/COMPRESSION_ENGINES.md | 31 +- docs/i18n/pl/docs/ops/RELEASE_CHECKLIST.md | 8 +- docs/i18n/zh-CN/docs/ops/RELEASE_CHECKLIST.md | 10 +- docs/i18n/zh-TW/docs/ops/RELEASE_CHECKLIST.md | 10 +- docs/ops/RELEASE_CHECKLIST.md | 10 +- .../compression/engines/llmlingua/worker.ts | 6 +- package-lock.json | 367 +----------------- package.json | 3 +- scripts/build/colocate-standalone.mjs | 2 +- scripts/build/colocateOptionals.mjs | 17 +- scripts/build/prepublish.ts | 2 +- scripts/packs/optionalPackManifest.mjs | 3 +- tests/unit/colocate-optionals.test.ts | 47 +-- .../unit/compression/llmlingua-worker.test.ts | 7 +- .../docker-llmlingua-optionals-9166.test.ts | 27 +- 20 files changed, 98 insertions(+), 501 deletions(-) create mode 100644 changelog.d/fixes/10536-llmlingua-2-2.0.5-drop-tfjs.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8db8504007..3dfad903a7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -50,13 +50,13 @@ updates: # bumps; majors here need their own PR and a deliberate migration review. - dependency-name: "ioredis" update-types: ["version-update:semver-major"] - # @huggingface/transformers is HARD-PINNED at 3.5.2 (exact, no caret) — FROZEN. - # It is load-bearing for the LLMLingua ONNX compression engine (open-sse/services/ - # compression/engines/llmlingua/ — worker.ts pins @huggingface/transformers@3.5.2) - # and for local memory embeddings (src/lib/memory/embedding/transformersLocal.ts), - # and was VPS-validated at 3.5.2 (#4014). 4.x breaks both, and even 3.x minors must - # be re-validated on the VPS — so freeze ALL auto-bumps (no update-types = ignore - # every version). Migrate it intentionally, not via dependabot (#4050). + # @huggingface/transformers is VPS-validated at ^4.2.0 (migrated intentionally in + # #9962). It is load-bearing for the LLMLingua ONNX compression engine (open-sse/ + # services/compression/engines/llmlingua/ — @atjsh/llmlingua-2@2.0.5 peers on + # "@huggingface/transformers": "^3.5.2 || ^4.0.0") and for local memory embeddings + # (src/lib/memory/embedding/transformersLocal.ts). Further majors must be re-validated + # on the VPS — so keep auto-bumps frozen (no update-types = ignore every version). + # Migrate it intentionally, not via dependabot (#4050). - dependency-name: "@huggingface/transformers" - package-ecosystem: "github-actions" diff --git a/Dockerfile b/Dockerfile index eefd6ed57d..b6ad0466c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -161,7 +161,7 @@ COPY . ./ RUN --mount=type=cache,id=s/92ca8a61-c1ba-421f-a389-d48ac7258c2d-next-cache,target=/app/.build/next/cache \ mkdir -p /app/data \ && npm run build \ - && node --input-type=module -e "import { createRequire } from 'node:module'; import { pathToFileURL } from 'node:url'; const standaloneRoot = '/app/.build/next/standalone/node_modules/'; const require = createRequire('/app/.build/next/standalone/package.json'); for (const pkg of ['@atjsh/llmlingua-2', '@huggingface/transformers', '@tensorflow/tfjs', 'js-tiktoken']) { const resolved = require.resolve(pkg); if (!resolved.startsWith(standaloneRoot)) throw new Error(pkg + ' resolved outside standalone: ' + resolved); await import(pathToFileURL(resolved).href); } const onnxRuntime = require.resolve('onnxruntime-node'); if (!onnxRuntime.startsWith(standaloneRoot)) throw new Error('onnxruntime-node resolved outside standalone: ' + onnxRuntime); await import(pathToFileURL(onnxRuntime).href);" + && node --input-type=module -e "import { createRequire } from 'node:module'; import { pathToFileURL } from 'node:url'; const standaloneRoot = '/app/.build/next/standalone/node_modules/'; const require = createRequire('/app/.build/next/standalone/package.json'); for (const pkg of ['@atjsh/llmlingua-2', '@huggingface/transformers', 'js-tiktoken']) { const resolved = require.resolve(pkg); if (!resolved.startsWith(standaloneRoot)) throw new Error(pkg + ' resolved outside standalone: ' + resolved); await import(pathToFileURL(resolved).href); } const onnxRuntime = require.resolve('onnxruntime-node'); if (!onnxRuntime.startsWith(standaloneRoot)) throw new Error('onnxruntime-node resolved outside standalone: ' + onnxRuntime); await import(pathToFileURL(onnxRuntime).href);" # ── Runner base ──────────────────────────────────────────────────────────── FROM base AS runner-base diff --git a/changelog.d/fixes/10536-llmlingua-2-2.0.5-drop-tfjs.md b/changelog.d/fixes/10536-llmlingua-2-2.0.5-drop-tfjs.md new file mode 100644 index 0000000000..11a1845715 --- /dev/null +++ b/changelog.d/fixes/10536-llmlingua-2-2.0.5-drop-tfjs.md @@ -0,0 +1 @@ +- **fix(deps):** upgrade `@atjsh/llmlingua-2` from 2.0.3 to 2.0.5 and remove `@tensorflow/tfjs` from the LLMLingua SLM stack — 2.0.5 adds official Transformers.js v4 support (peers `@huggingface/transformers` at `^3.5.2 || ^4.0.0`) and 2.0.4+ no longer requires TensorFlow.js, restoring compatibility with OmniRoute's Transformers.js v4 while dropping the largest single contributor to the optional runtime footprint ([#10536](https://github.com/diegosouzapw/OmniRoute/issues/10536)) diff --git a/config/quality/dependency-allowlist.json b/config/quality/dependency-allowlist.json index f276fc45d2..c4476f95d1 100644 --- a/config/quality/dependency-allowlist.json +++ b/config/quality/dependency-allowlist.json @@ -20,7 +20,6 @@ "@stryker-mutator/tap-runner", "@swc/helpers", "@tailwindcss/postcss", - "@tensorflow/tfjs", "@testing-library/jest-dom", "@testing-library/react", "@toon-format/toon", diff --git a/docs/compression/COMPRESSION_ENGINES.md b/docs/compression/COMPRESSION_ENGINES.md index 8b0268b800..40e38aaf55 100644 --- a/docs/compression/COMPRESSION_ENGINES.md +++ b/docs/compression/COMPRESSION_ENGINES.md @@ -142,22 +142,22 @@ override points it at a local copy instead (offline / air-gapped installs). ### Optional dependencies & on-demand install -The prunable LLMLingua runtime peer stack is **optional**. Three packages are declared as +The prunable LLMLingua runtime peer stack is **optional**. Two packages are declared as `optionalDependencies` in `package.json` and kept **external** by the production build (`scripts/build/prepublish.ts` does not bundle them): -| Package | Version (pin) | Notes | -| -------------------- | ------------- | ---------------------------------------------- | -| `@atjsh/llmlingua-2` | `2.0.3` | Entry package; declares the others as peers | -| `@tensorflow/tfjs` | `4.22.0` | Heaviest dep — dominates the ~800 MB footprint | -| `js-tiktoken` | `^1.0.20` | Tokenizer | +| Package | Version (pin) | Notes | +| -------------------- | ------------- | ------------------------------------------- | +| `@atjsh/llmlingua-2` | `2.0.5` | Entry package; declares the others as peers | +| `js-tiktoken` | `^1.0.20` | Tokenizer | -`@huggingface/transformers` is pinned at `3.5.2` as an **optional** dependency (shared with -the local embeddings path and also traced into the standalone bundle). Keeping it optional prevents -`onnxruntime-node` CUDA provider postinstall failures on CUDA 11 hosts from aborting the whole -OmniRoute install; when the optional stack is absent, LLMLingua still fail-opens. Only the three -packages above are prunable SLM peers. A standard `npm install` (dev) installs the optional stack -automatically unless optional dependencies are omitted. +`@huggingface/transformers` is pinned at `^4.2.0` (shared with the local embeddings path and +also traced into the standalone bundle); `@atjsh/llmlingua-2@2.0.5` peers on it with +`"^3.5.2 || ^4.0.0"`, so both Transformers.js v3 and v4 are supported. Since 2.0.4, +`@atjsh/llmlingua-2` no longer requires `@tensorflow/tfjs`, which removed the largest single +contributor (TensorFlow.js) from the SLM stack. Only the two packages above are prunable SLM +peers. A standard `npm install` (dev) installs the optional stack automatically unless optional +dependencies are omitted. **Why on-demand:** the npm-published package, the standalone bundle, and the Docker image ship **without** these deps to stay slim. When they are absent, the worker's dependency @@ -167,11 +167,12 @@ error logged). To activate it in a pruned environment, install the optional stac ```bash # pin to the versions declared in package.json optionalDependencies -npm install @atjsh/llmlingua-2@2.0.3 @tensorflow/tfjs@4.22.0 js-tiktoken +npm install @atjsh/llmlingua-2@2.0.5 js-tiktoken ``` -Roughly **~800 MB** total: the TensorFlow.js + transformers runtimes dominate; the -TinyBERT model adds ~57 MB downloaded at first use (not via npm). +The `@tensorflow/tfjs` removal (2.0.4+) eliminates the previously dominant ~800 MB +contributor — the remaining footprint is the transformers.js + onnxruntime-node runtimes, +plus the TinyBERT model (~57 MB) downloaded at first use (not via npm). Per environment: diff --git a/docs/i18n/pl/docs/compression/COMPRESSION_ENGINES.md b/docs/i18n/pl/docs/compression/COMPRESSION_ENGINES.md index b8488d6872..ea266af05b 100644 --- a/docs/i18n/pl/docs/compression/COMPRESSION_ENGINES.md +++ b/docs/i18n/pl/docs/compression/COMPRESSION_ENGINES.md @@ -142,22 +142,22 @@ wskazuje zamiast tego lokalną kopię (instalacje offline / air-gapped). ### Opcjonalne zależności i instalacja on-demand -Przycinany stos peerów runtime LLMLingua jest **opcjonalny**. Trzy pakiety są zadeklarowane jako +Przycinany stos peerów runtime LLMLingua jest **opcjonalny**. Dwa pakiety są zadeklarowane jako `optionalDependencies` w `package.json` i utrzymywane jako **external** przez build produkcyjny (`scripts/build/prepublish.ts` ich nie bundluje): -| Package | Version (pin) | Notes | -| -------------------- | ------------- | ------------------------------------------------- | -| `@atjsh/llmlingua-2` | `2.0.3` | Pakiet wejściowy; deklaruje pozostałe jako peery | -| `@tensorflow/tfjs` | `4.22.0` | Najcięższa zależność — dominuje footprint ~800 MB | -| `js-tiktoken` | `^1.0.20` | Tokenizer | +| Package | Version (pin) | Notes | +| -------------------- | ------------- | ------------------------------------------- | +| `@atjsh/llmlingua-2` | `2.0.5` | Pakiet wejściowy; deklaruje pozostałe jako peery | +| `js-tiktoken` | `^1.0.20` | Tokenizer | -`@huggingface/transformers` jest pinowany na `3.5.2` jako **opcjonalna** zależność (współdzielona ze -ścieżką lokalnych embeddings i również śledzona do standalone bundle). Utrzymanie jej jako optional -zapobiega awariom postinstall providera CUDA `onnxruntime-node` na hostach CUDA 11, które przerywałyby -całą instalację OmniRoute; gdy opcjonalny stos jest nieobecny, LLMLingua nadal fail-openuje. Tylko trzy -powyższe pakiety to przycinane peery SLM. Standardowe `npm install` (dev) instaluje opcjonalny stos -automatycznie, o ile opcjonalne zależności nie zostaną pominięte. +`@huggingface/transformers` jest pinowany na `^4.2.0` (współdzielony ze ścieżką lokalnych embeddings +i również śledzony do standalone bundle); `@atjsh/llmlingua-2@2.0.5` peeruje na nim przez +`"^3.5.2 || ^4.0.0"`, więc obsługiwane są zarówno Transformers.js v3, jak i v4. Od 2.0.4 +`@atjsh/llmlingua-2` nie wymaga już `@tensorflow/tfjs`, co usunęło największy pojedynczy wkład +(TensorFlow.js) ze stosu SLM. Tylko dwa powyższe pakiety to przycinane peery SLM. Standardowe +`npm install` (dev) instaluje opcjonalny stos automatycznie, o ile opcjonalne zależności nie zostaną +pominięte. **Dlaczego on-demand:** pakiet publikowany w npm, standalone bundle i obraz Docker dostarczane są **bez** tych zależności, aby pozostać lekkie. Gdy ich brakuje, bramka zależności @@ -167,11 +167,12 @@ logowanego błędu). Aby aktywować go w przyciętym środowisku, zainstaluj opc ```bash # pin to the versions declared in package.json optionalDependencies -npm install @atjsh/llmlingua-2@2.0.3 @tensorflow/tfjs@4.22.0 js-tiktoken +npm install @atjsh/llmlingua-2@2.0.5 js-tiktoken ``` -Łącznie mniej więcej **~800 MB**: dominują runtime’y TensorFlow.js + transformers; model -TinyBERT dodaje ~57 MB pobierane przy pierwszym użyciu (nie przez npm). +Usunięcie `@tensorflow/tfjs` (2.0.4+) eliminuje wcześniej dominujący wkład ~800 MB — pozostały +footprint to runtime’y transformers.js + onnxruntime-node oraz model TinyBERT (~57 MB) pobierany +przy pierwszym użyciu (nie przez npm). Per środowisko: diff --git a/docs/i18n/pl/docs/ops/RELEASE_CHECKLIST.md b/docs/i18n/pl/docs/ops/RELEASE_CHECKLIST.md index 1d9cdb29f6..535dadd53c 100644 --- a/docs/i18n/pl/docs/ops/RELEASE_CHECKLIST.md +++ b/docs/i18n/pl/docs/ops/RELEASE_CHECKLIST.md @@ -326,13 +326,11 @@ Przed wypuszczeniem dowolnego wydania v3.8.x zweryfikuj te dodatkowe pozycje: - [ ] `npm install -g omniroute@` uruchamia postinstall bez fatalnego wyjścia - [ ] Ścieżka update zachowuje optional deps: `omniroute update --apply` i auto-updater uruchamiają `npm install -g … --include=optional`, żeby `optionalDependencies` (better-sqlite3, - keytar, tls-client oraz stack SLM llmlingua: `@atjsh/llmlingua-2`, - `@huggingface/transformers@3.5.2`, `@tensorflow/tfjs`, `js-tiktoken`) przeżyły update. - `@huggingface/transformers` zostaje optional, żeby jego postinstall providera CUDA `onnxruntime-node` - nie mógł przerwać instalacji na hostach CUDA 11. Tier ultra `modelPath` SLM potrzebuje też + keytar, tls-client oraz stack SLM llmlingua: `@atjsh/llmlingua-2@2.0.5`, + `js-tiktoken`) przeżyły update. Tier ultra `modelPath` SLM potrzebuje też modelu tinybert, auto-pobieranego do `${DATA_DIR}/models/llmlingua` przy pierwszym użyciu. Postinstall (`scripts/build/colocateOptionals.mjs`) następnie ko-lokuje opcjonalne zamknięcie SLM do - `dist/node_modules`, żeby worker rozwiązywał JEDNĄ opcjonalną instancję `@huggingface/transformers` 3.5.2 + `dist/node_modules`, żeby worker rozwiązywał JEDNĄ instancję `@huggingface/transformers` ^4.2.0 — standalone trace bundluje tylko transformers, nie dynamicznie importowane optionals, więc bez tego worker załadowałby llmlingua-2 przeciw transformers z roota i tier SLM cicho fail-openowałby. diff --git a/docs/i18n/zh-CN/docs/ops/RELEASE_CHECKLIST.md b/docs/i18n/zh-CN/docs/ops/RELEASE_CHECKLIST.md index d8cefd6497..afdbf398af 100644 --- a/docs/i18n/zh-CN/docs/ops/RELEASE_CHECKLIST.md +++ b/docs/i18n/zh-CN/docs/ops/RELEASE_CHECKLIST.md @@ -275,14 +275,12 @@ npm run build:release - [ ] `npm install -g omniroute@` 运行 postinstall 无致命退出 - [ ] 更新路径保留可选依赖:`omniroute update --apply` 以及自动更新器 运行 `npm install -g … --include=optional` 以确保 `optionalDependencies`(better-sqlite3、 - keytar、tls-client 以及 llmlingua SLM 栈:`@atjsh/llmlingua-2`、 - `@huggingface/transformers@3.5.2`、`@tensorflow/tfjs`、`js-tiktoken`)在更新后仍然存在。 - `@huggingface/transformers` 保持为可选依赖,这样其 `onnxruntime-node` CUDA provider postinstall - 不会在 CUDA 11 主机上中断安装。Ultra 模式的 `modelPath` SLM 层还需要 + keytar、tls-client 以及 llmlingua SLM 栈:`@atjsh/llmlingua-2@2.0.5`、 + `js-tiktoken`)在更新后仍然存在。Ultra 模式的 `modelPath` SLM 层还需要 tinybert 模型,首次使用时自动下载到 `${DATA_DIR}/models/llmlingua`。postinstall (`scripts/build/colocateOptionals.mjs`)随后将 SLM 可选依赖闭包共置到 - `dist/node_modules`,使 Worker 解析单一的 `@huggingface/transformers` 3.5.2 - 可选实例 — standalone trace 仅打包 transformers,不包含动态导入的 + `dist/node_modules`,使 Worker 解析单一的 `@huggingface/transformers` ^4.2.0 + 实例 — standalone trace 仅打包 transformers,不包含动态导入的 可选依赖,否则 Worker 会基于根目录的 transformers 加载 llmlingua-2, SLM 层将静默失效。 - [ ] `omniroute status` 在无 `.env` 的情况下正常工作(CLI Token 路径,仅 loopback) diff --git a/docs/i18n/zh-TW/docs/ops/RELEASE_CHECKLIST.md b/docs/i18n/zh-TW/docs/ops/RELEASE_CHECKLIST.md index bbadf03423..c604668d19 100644 --- a/docs/i18n/zh-TW/docs/ops/RELEASE_CHECKLIST.md +++ b/docs/i18n/zh-TW/docs/ops/RELEASE_CHECKLIST.md @@ -322,14 +322,12 @@ npm run build:release - [ ] `npm install -g omniroute@<此版本>` 執行 postinstall 而不會致命退出 - [ ] 更新路徑保留選擇性依賴:`omniroute update --apply` 和自動更新器 執行 `npm install -g … --include=optional`,因此 `optionalDependencies`(better-sqlite3、 - keytar、tls-client,以及 llmlingua SLM 堆疊:`@atjsh/llmlingua-2`、 - `@huggingface/transformers@3.5.2`、`@tensorflow/tfjs`、`js-tiktoken`)在更新後仍會保留。 - `@huggingface/transformers` 維持選擇性,因此其 `onnxruntime-node` CUDA 提供者的 postinstall - 不會在 CUDA 11 主機上中斷安裝。Ultra `modelPath` SLM 層還需要 + keytar、tls-client,以及 llmlingua SLM 堆疊:`@atjsh/llmlingua-2@2.0.5`、 + `js-tiktoken`)在更新後仍會保留。Ultra `modelPath` SLM 層還需要 tinybert 模型,會在首次使用時自動下載到 `${DATA_DIR}/models/llmlingua`。Postinstall (`scripts/build/colocateOptionals.mjs`)接著將 SLM 選擇性閉包複製到 - `dist/node_modules`,使工作者解析到**單一** `@huggingface/transformers` 3.5.2 - 選擇性實例——獨立追蹤僅捆綁 transformers,而非動態匯入的 + `dist/node_modules`,使工作者解析到**單一** `@huggingface/transformers` ^4.2.0 + 實例——獨立追蹤僅捆綁 transformers,而非動態匯入的 選擇性套件,因此若無此步驟,工作者會載入 llmlingua-2 並使用根目錄的 transformers, 導致 SLM 層靜默地失敗但仍保持運作。 - [ ] `omniroute status` 在無 `.env` 的情況下正常運作(僅限 CLI 權杖路徑,迴環介面) diff --git a/docs/ops/RELEASE_CHECKLIST.md b/docs/ops/RELEASE_CHECKLIST.md index b800d6fc22..1a1a2d2ffc 100644 --- a/docs/ops/RELEASE_CHECKLIST.md +++ b/docs/ops/RELEASE_CHECKLIST.md @@ -342,14 +342,12 @@ Before shipping any v3.8.x release, verify these additional items: - [ ] `npm install -g omniroute@` runs postinstall without fatal exit - [ ] Update path keeps optional deps: `omniroute update --apply` and the auto-updater run `npm install -g … --include=optional` so `optionalDependencies` (better-sqlite3, - keytar, tls-client, and the llmlingua SLM stack: `@atjsh/llmlingua-2`, - `@huggingface/transformers@3.5.2`, `@tensorflow/tfjs`, `js-tiktoken`) survive an update. - `@huggingface/transformers` stays optional so its `onnxruntime-node` CUDA provider postinstall - cannot abort installation on CUDA 11 hosts. The ultra `modelPath` SLM tier also needs the + keytar, tls-client, and the llmlingua SLM stack: `@atjsh/llmlingua-2@2.0.5`, + `js-tiktoken`) survive an update. The ultra `modelPath` SLM tier also needs the tinybert model, auto-downloaded to `${DATA_DIR}/models/llmlingua` on first use. Postinstall (`scripts/build/colocateOptionals.mjs`) then co-locates the SLM optional closure into - `dist/node_modules` so the worker resolves a SINGLE `@huggingface/transformers` 3.5.2 - optional instance — the standalone trace bundles only transformers, not the dynamically-imported + `dist/node_modules` so the worker resolves a SINGLE `@huggingface/transformers` ^4.2.0 + instance — the standalone trace bundles only transformers, not the dynamically-imported optionals, so without this the worker would load llmlingua-2 against the root's transformers and the SLM tier would silently fail-open. - [ ] `omniroute status` works with no `.env` (CLI token path, loopback only) diff --git a/open-sse/services/compression/engines/llmlingua/worker.ts b/open-sse/services/compression/engines/llmlingua/worker.ts index bf6fba4963..00ba3e1255 100644 --- a/open-sse/services/compression/engines/llmlingua/worker.ts +++ b/open-sse/services/compression/engines/llmlingua/worker.ts @@ -8,7 +8,7 @@ * * ## Fail-open paths * 1. Optional-deps gate: if any of `@atjsh/llmlingua-2`, `@huggingface/transformers`, - * `@tensorflow/tfjs`, `js-tiktoken` does not resolve, return `text` immediately — + * `js-tiktoken` does not resolve, return `text` immediately — * NO worker spawn. This is the default in CI / most installs (deps are OPTIONAL). * 2. Per-call timeout: first call for a model gets `FIRST_CALL_TIMEOUT_MS` (one-time * model load); warm calls get `LLMLINGUA_WORKER_TIMEOUT_MS`. On timeout → original @@ -16,7 +16,7 @@ * 3. Worker error/exit → resolve all pending with their original text + respawn next. * * ## Serialization - * ONNX/tfjs are not reentrant — calls are queued FIFO and only one message is + * ONNX inference is not reentrant — calls are queued FIFO and only one message is * in-flight at a time (the next is posted after the previous reply or its timeout). * * ## Idle eviction @@ -45,7 +45,7 @@ const FIRST_CALL_TIMEOUT_MS = 60000; /** * Gate probe: `@atjsh/llmlingua-2` is the entry package that declares the others - * (`@huggingface/transformers`, `@tensorflow/tfjs`, `js-tiktoken`) as peers. We probe + * (`@huggingface/transformers`, `js-tiktoken`) as peers. We probe * ONLY it (by manifest existence) because the peers are ESM-only and `require.resolve` * throws for them even when installed; the worker still fail-opens if a peer is * genuinely missing at `import()` time. diff --git a/package-lock.json b/package-lock.json index 4769372f63..fd0220aac6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -155,8 +155,7 @@ "node": ">=22.22.2 <23 || >=24.0.0 <27" }, "optionalDependencies": { - "@atjsh/llmlingua-2": "2.0.3", - "@tensorflow/tfjs": "4.22.0", + "@atjsh/llmlingua-2": "2.0.5", "better-sqlite3": "^13.0.2", "js-tiktoken": "^1.0.20", "keytar": "^7.9.0", @@ -556,17 +555,16 @@ } }, "node_modules/@atjsh/llmlingua-2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@atjsh/llmlingua-2/-/llmlingua-2-2.0.3.tgz", - "integrity": "sha512-UJJFMbzYldkZ4qX5CrSZtmytOnXf6aXhmr1sBhbpVMHdmQG+7GCnrx5rIwPSOmozXD9KiPv5nnV6pvzxdtHdYQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@atjsh/llmlingua-2/-/llmlingua-2-2.0.5.tgz", + "integrity": "sha512-cXdGUJgx0e2Sui5gYC8kapOhw1HAxwzh9IuYPdqyB+VlP6SL9imIfyB7I4GTCl/iG+BUxaOqSrLqWsWYvDZuVQ==", "license": "MIT", "optional": true, "dependencies": { "es-toolkit": "^1.38.0" }, "peerDependencies": { - "@huggingface/transformers": "*", - "@tensorflow/tfjs": "*", + "@huggingface/transformers": "^3.5.2 || ^4.0.0", "js-tiktoken": "*" } }, @@ -11150,241 +11148,6 @@ "tailwindcss": "4.3.3" } }, - "node_modules/@tensorflow/tfjs": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.22.0.tgz", - "integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "4.22.0", - "@tensorflow/tfjs-backend-webgl": "4.22.0", - "@tensorflow/tfjs-converter": "4.22.0", - "@tensorflow/tfjs-core": "4.22.0", - "@tensorflow/tfjs-data": "4.22.0", - "@tensorflow/tfjs-layers": "4.22.0", - "argparse": "^1.0.10", - "chalk": "^4.1.0", - "core-js": "3.29.1", - "regenerator-runtime": "^0.13.5", - "yargs": "^16.0.3" - }, - "bin": { - "tfjs-custom-module": "dist/tools/custom_module/cli.js" - } - }, - "node_modules/@tensorflow/tfjs-backend-cpu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz", - "integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.22.0" - } - }, - "node_modules/@tensorflow/tfjs-backend-webgl": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz", - "integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "4.22.0", - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.22.0" - } - }, - "node_modules/@tensorflow/tfjs-converter": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz", - "integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==", - "license": "Apache-2.0", - "optional": true, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.22.0" - } - }, - "node_modules/@tensorflow/tfjs-core": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz", - "integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@types/long": "^4.0.1", - "@types/offscreencanvas": "~2019.7.0", - "@types/seedrandom": "^2.4.28", - "@webgpu/types": "0.1.38", - "long": "4.0.0", - "node-fetch": "~2.6.1", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - } - }, - "node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": { - "version": "2019.7.3", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", - "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", - "license": "MIT", - "optional": true - }, - "node_modules/@tensorflow/tfjs-data": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz", - "integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@types/node-fetch": "^2.1.2", - "node-fetch": "~2.6.1", - "string_decoder": "^1.3.0" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.22.0", - "seedrandom": "^3.0.5" - } - }, - "node_modules/@tensorflow/tfjs-layers": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz", - "integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==", - "license": "Apache-2.0 AND MIT", - "optional": true, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.22.0" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "optional": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, - "node_modules/@tensorflow/tfjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tensorflow/tfjs/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "license": "ISC", - "optional": true, - "engines": { - "node": ">=10" - } - }, "node_modules/@testing-library/jest-dom": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", @@ -11918,13 +11681,6 @@ "@types/node": "*" } }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT", - "optional": true - }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -11955,24 +11711,6 @@ "undici-types": "~8.3.0" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" - } - }, - "node_modules/@types/offscreencanvas": { - "version": "2019.3.0", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", - "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==", - "license": "MIT", - "optional": true - }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -12049,13 +11787,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/seedrandom": { - "version": "2.4.34", - "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz", - "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==", - "license": "MIT", - "optional": true - }, "node_modules/@types/tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -12874,13 +12605,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@webgpu/types": { - "version": "0.1.38", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz", - "integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==", - "license": "BSD-3-Clause", - "optional": true - }, "node_modules/@xmldom/xmldom": { "version": "0.9.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", @@ -16104,18 +15828,6 @@ "node": ">=6.6.0" } }, - "node_modules/core-js": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", - "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -25703,13 +25415,6 @@ "node": ">=0.1.90" } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0", - "optional": true - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -28208,52 +27913,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", - "license": "MIT", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "optional": true - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-forge": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", @@ -32014,13 +31673,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "optional": true - }, "node_modules/regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", @@ -32859,7 +32511,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/selfsigned": { @@ -33704,13 +33356,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause", - "optional": true - }, "node_modules/sql.js": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", diff --git a/package.json b/package.json index a4998e690f..98407eee96 100644 --- a/package.json +++ b/package.json @@ -340,8 +340,7 @@ "onnxruntime-node": "~1.24.3" }, "optionalDependencies": { - "@atjsh/llmlingua-2": "2.0.3", - "@tensorflow/tfjs": "4.22.0", + "@atjsh/llmlingua-2": "2.0.5", "better-sqlite3": "^13.0.2", "js-tiktoken": "^1.0.20", "keytar": "^7.9.0", diff --git a/scripts/build/colocate-standalone.mjs b/scripts/build/colocate-standalone.mjs index b1bf44f8c0..7dedd3b3f3 100644 --- a/scripts/build/colocate-standalone.mjs +++ b/scripts/build/colocate-standalone.mjs @@ -8,7 +8,7 @@ * - does NOT bundle `open-sse/services/compression/engines/llmlingua/onnxWorker.js` * (dynamically spawned via worker_threads — untraceable by webpack), and * - does NOT include the optional SLM deps (`@atjsh/llmlingua-2`, - * `@tensorflow/tfjs`, `js-tiktoken`) — they are optionalDependencies and are + * `js-tiktoken`) — they are optionalDependencies and are * only installed at the ROOT `node_modules`. * * Result: after every plain `npm run build`, the LLMLingua engine silently diff --git a/scripts/build/colocateOptionals.mjs b/scripts/build/colocateOptionals.mjs index 073a59fbed..0aa3f38fab 100644 --- a/scripts/build/colocateOptionals.mjs +++ b/scripts/build/colocateOptionals.mjs @@ -4,31 +4,32 @@ * 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 + * `@huggingface/transformers` + `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 + * (4.2.0, 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` + * `dist/node_modules/@huggingface/transformers` (4.2.0) 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). + * tier silently fails-open (no compression). (Before `@atjsh/llmlingua-2@2.0.5` a root + * transformers on the 4.x line also made llmlingua-2 throw on a tokenizer-API change + * — `decoder.decode` is undefined; 2.0.5+ supports both v3 and v4.) * * ## 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 + * `dist/node_modules` (NO-CLOBBER, so the pinned `dist` transformers 4.2.0 / 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 + * SAME `dist/node_modules` — a single 4.2.0 instance — so the env config applies and the local * model loads. * * `@huggingface/transformers` is intentionally NOT a closure seed: it is a PEER of @@ -54,7 +55,7 @@ import { dirname, join, sep } 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"]; +export const SEED_PACKAGES = ["@atjsh/llmlingua-2", "js-tiktoken"]; /** * Compute the transitive dependency closure of `seeds` by walking each package's `dependencies` + diff --git a/scripts/build/prepublish.ts b/scripts/build/prepublish.ts index e8872c0a9a..cbcab24e1c 100644 --- a/scripts/build/prepublish.ts +++ b/scripts/build/prepublish.ts @@ -380,7 +380,7 @@ if (existsSync(chatGptWebCodexMcpSrcFile)) { // The worker is spawned via worker_threads at a path the Next.js bundler cannot // statically trace, so it must ship as a standalone .js (mirrors the MCP-server // bundling above). Heavy deps (@atjsh/llmlingua-2 / @huggingface/transformers / -// @tensorflow/tfjs / js-tiktoken) stay EXTERNAL — they are optionalDependencies, +// js-tiktoken) stay EXTERNAL — they are optionalDependencies, // dynamically imported at runtime, and the worker fail-opens if any is absent. const llmWorkerSrc = join( ROOT, diff --git a/scripts/packs/optionalPackManifest.mjs b/scripts/packs/optionalPackManifest.mjs index c1e9495cae..f20fbc5df2 100644 --- a/scripts/packs/optionalPackManifest.mjs +++ b/scripts/packs/optionalPackManifest.mjs @@ -50,7 +50,6 @@ export const OPTIONAL_PACKS = [ { name: "@huggingface/transformers" }, { name: "onnxruntime-node" }, { name: "@atjsh/llmlingua-2" }, - { name: "@tensorflow/tfjs" }, { name: "js-tiktoken" }, ], }, @@ -156,7 +155,7 @@ export async function dirChecksum(dir) { hash.update(String(size)); hash.update("\0"); try { - // Stream to keep memory bounded on multi-hundred-MB packages (tfjs). + // Stream to keep memory bounded on multi-hundred-MB packages (onnxruntime-node). for await (const chunk of createReadStream(absolute)) hash.update(chunk); } catch { hash.update(""); diff --git a/tests/unit/colocate-optionals.test.ts b/tests/unit/colocate-optionals.test.ts index 711c405f99..8250c46317 100644 --- a/tests/unit/colocate-optionals.test.ts +++ b/tests/unit/colocate-optionals.test.ts @@ -29,10 +29,9 @@ function mkPkg( /** * Build a root tree mirroring the real SLM optional shape: - * @atjsh/llmlingua-2 → dep es-toolkit, PEER @huggingface/transformers (+ tfjs, js-tiktoken) - * @tensorflow/tfjs → dep @tensorflow/tfjs-core → dep long + * @atjsh/llmlingua-2 → dep es-toolkit, PEER @huggingface/transformers (+ js-tiktoken) * js-tiktoken → dep base64-js - * @huggingface/transformers present at root as a (stale) 4.2.0 + * @huggingface/transformers present at root as a (hypothetical future) 5.0.0 * * Each mock package gets a resolvable entrypoint so that isPackageIntact (which * checks entrypoint integrity via require.resolve) can validate the co-located @@ -49,26 +48,12 @@ function buildRoot(rootDir: string): void { dependencies: { "es-toolkit": "^1.38.0" }, peerDependencies: { "@huggingface/transformers": "*", - "@tensorflow/tfjs": "*", "js-tiktoken": "*", }, }, { "dist/index.js": "export const llmlingua = true;\n" } ); mkPkg(rootNm, "es-toolkit", { main: "index.js" }, { "index.js": "export const esToolkit = true;\n" }); - mkPkg( - rootNm, - "@tensorflow/tfjs", - { main: "index.js", dependencies: { "@tensorflow/tfjs-core": "4.22.0" } }, - { "index.js": "export const tfjs = true;\n" } - ); - mkPkg( - rootNm, - "@tensorflow/tfjs-core", - { main: "index.js", dependencies: { long: "^5.0.0" } }, - { "index.js": "export const tfjsCore = true;\n" } - ); - mkPkg(rootNm, "long", { main: "index.js" }, { "index.js": "export const long = true;\n" }); mkPkg( rootNm, "js-tiktoken", @@ -76,8 +61,8 @@ function buildRoot(rootDir: string): void { { "index.js": "export const tiktoken = true;\n" } ); mkPkg(rootNm, "base64-js", { main: "index.js" }, { "index.js": "export const base64 = true;\n" }); - // Root transformers is the STALE 4.x line — the bug we must not propagate into dist. - mkPkg(rootNm, "@huggingface/transformers", { version: "4.2.0" }); + // Root transformers is a hypothetical FUTURE line — the version we must not propagate into dist. + mkPkg(rootNm, "@huggingface/transformers", { version: "5.0.0" }); } test("computeDependencyClosure walks deps transitively and skips peers (transformers)", () => { @@ -88,11 +73,8 @@ test("computeDependencyClosure walks deps transitively and skips peers (transfor for (const expected of [ "@atjsh/llmlingua-2", - "@tensorflow/tfjs", "js-tiktoken", "es-toolkit", - "@tensorflow/tfjs-core", - "long", "base64-js", ]) { assert.ok(closure.includes(expected), `closure should include ${expected}`); @@ -111,23 +93,20 @@ test("colocateLlmlinguaOptionals copies the closure into dist and never clobbers const root = mkdtempSync(join(tmpdir(), "omniroute-colocate-copy-")); try { buildRoot(root); - // dist already ships the PINNED transformers (3.5.2) — must survive untouched. + // dist already ships the PINNED transformers (4.2.0) — must survive untouched. const distNm = join(root, "dist", "node_modules"); - mkPkg(distNm, "@huggingface/transformers", { version: "3.5.2" }); + mkPkg(distNm, "@huggingface/transformers", { version: "4.2.0" }); const result = colocateLlmlinguaOptionals({ rootDir: root }); assert.equal(result.skipped, false); if (result.skipped === false) { - assert.ok(result.copied >= 6, `expected >=6 packages copied, got ${result.copied}`); + assert.ok(result.copied >= 4, `expected >=4 packages copied, got ${result.copied}`); } // Full closure landed in dist/node_modules. for (const name of [ "@atjsh/llmlingua-2", "es-toolkit", - "@tensorflow/tfjs", - "@tensorflow/tfjs-core", - "long", "js-tiktoken", "base64-js", ]) { @@ -136,11 +115,11 @@ test("colocateLlmlinguaOptionals copies the closure into dist and never clobbers // The package payload came along (not just the manifest). assert.ok(existsSync(join(distNm, "@atjsh", "llmlingua-2", "dist", "index.js"))); - // CRITICAL: dist's pinned transformers is preserved — root's 4.2.0 must NOT win. + // CRITICAL: dist's pinned transformers is preserved — root's 5.0.0 must NOT win. const distTransformers = JSON.parse( readFileSync(join(distNm, "@huggingface", "transformers", "package.json"), "utf8") ); - assert.equal(distTransformers.version, "3.5.2", "dist transformers must remain 3.5.2"); + assert.equal(distTransformers.version, "4.2.0", "dist transformers must remain 4.2.0"); } finally { rmSync(root, { recursive: true, force: true }); } @@ -150,7 +129,7 @@ test("colocateLlmlinguaOptionals is idempotent (second run is a no-op)", () => { const root = mkdtempSync(join(tmpdir(), "omniroute-colocate-idem-")); try { buildRoot(root); - mkPkg(join(root, "dist", "node_modules"), "@huggingface/transformers", { version: "3.5.2" }); + mkPkg(join(root, "dist", "node_modules"), "@huggingface/transformers", { version: "4.2.0" }); const first = colocateLlmlinguaOptionals({ rootDir: root }); assert.equal(first.skipped, false); @@ -169,7 +148,7 @@ test("colocateLlmlinguaOptionals skips when SLM optionals are not installed", () const root = mkdtempSync(join(tmpdir(), "omniroute-colocate-noopt-")); try { // dist bundle exists, but the optional seeds were never installed at root. - mkPkg(join(root, "dist", "node_modules"), "@huggingface/transformers", { version: "3.5.2" }); + mkPkg(join(root, "dist", "node_modules"), "@huggingface/transformers", { version: "4.2.0" }); mkdirSync(join(root, "node_modules"), { recursive: true }); const result = colocateLlmlinguaOptionals({ rootDir: root }); @@ -208,7 +187,7 @@ test("colocateLlmlinguaOptionals fills a Next-traced stub (package.json only, no try { buildRoot(root); const distNm = join(root, "dist", "node_modules"); - mkPkg(distNm, "@huggingface/transformers", { version: "3.5.2" }); + mkPkg(distNm, "@huggingface/transformers", { version: "4.2.0" }); // Simulate the Next-traced stub: directory exists, package.json only. const stubDir = join(distNm, "@atjsh", "llmlingua-2"); @@ -233,5 +212,5 @@ test("colocateLlmlinguaOptionals fills a Next-traced stub (package.json only, no test("SEED_PACKAGES excludes transformers (it is a dist-pinned peer, not a seed)", () => { assert.ok(!SEED_PACKAGES.includes("@huggingface/transformers")); - assert.deepEqual(SEED_PACKAGES, ["@atjsh/llmlingua-2", "@tensorflow/tfjs", "js-tiktoken"]); + assert.deepEqual(SEED_PACKAGES, ["@atjsh/llmlingua-2", "js-tiktoken"]); }); diff --git a/tests/unit/compression/llmlingua-worker.test.ts b/tests/unit/compression/llmlingua-worker.test.ts index aea8931da6..7bb74f2288 100644 --- a/tests/unit/compression/llmlingua-worker.test.ts +++ b/tests/unit/compression/llmlingua-worker.test.ts @@ -1,8 +1,8 @@ /** * Tests for the real LLMLingua worker-thread backend (`worker.ts` + `onnxWorker.ts`). * - * The four optional deps (`@atjsh/llmlingua-2`, `@huggingface/transformers`, - * `@tensorflow/tfjs`, `js-tiktoken`) are NOT installed in this worktree, so the + * The three optional deps (`@atjsh/llmlingua-2`, `@huggingface/transformers`, + * `js-tiktoken`) are NOT installed in this worktree, so the * default path MUST fail-open WITHOUT spawning a worker: * * 1. Deps absent → fail-open, no spawn (ALWAYS runs here): the backend returns the @@ -23,12 +23,11 @@ import { const require = createRequire(import.meta.url); -/** Whether all four optional deps resolve in this environment. */ +/** Whether all three optional deps resolve in this environment. */ function depsResolve(): boolean { try { require.resolve("@atjsh/llmlingua-2"); require.resolve("@huggingface/transformers"); - require.resolve("@tensorflow/tfjs"); require.resolve("js-tiktoken"); return true; } catch { diff --git a/tests/unit/docker-llmlingua-optionals-9166.test.ts b/tests/unit/docker-llmlingua-optionals-9166.test.ts index da994ed5c4..ac57d0d362 100644 --- a/tests/unit/docker-llmlingua-optionals-9166.test.ts +++ b/tests/unit/docker-llmlingua-optionals-9166.test.ts @@ -16,7 +16,6 @@ import { assembleStandalone } from "../../scripts/build/assembleStandalone.mjs"; const REQUIRED_RUNTIME_PACKAGES = [ "@atjsh/llmlingua-2", "@huggingface/transformers", - "@tensorflow/tfjs", "js-tiktoken", ]; @@ -47,7 +46,7 @@ function mkPkg( function buildLlmlinguaRoot( rootDir: string, - transformersVersion = "3.5.2" + transformersVersion = "4.2.0" ): void { const rootNm = join(rootDir, "node_modules"); @@ -61,7 +60,6 @@ function buildLlmlinguaRoot( }, peerDependencies: { "@huggingface/transformers": "*", - "@tensorflow/tfjs": "*", "js-tiktoken": "*", }, }, @@ -72,18 +70,6 @@ function buildLlmlinguaRoot( mkPkg(rootNm, "es-toolkit"); - mkPkg(rootNm, "@tensorflow/tfjs", { - dependencies: { - "@tensorflow/tfjs-core": "4.22.0", - }, - }); - mkPkg(rootNm, "@tensorflow/tfjs-core", { - dependencies: { - long: "^5.0.0", - }, - }); - mkPkg(rootNm, "long"); - mkPkg(rootNm, "js-tiktoken", { dependencies: { "base64-js": "^1.5.1", @@ -138,8 +124,6 @@ test("#9166 standalone assembly includes the complete LLMLingua runtime closure" for (const packageName of [ ...REQUIRED_RUNTIME_PACKAGES, "es-toolkit", - "@tensorflow/tfjs-core", - "long", "base64-js", "onnxruntime-node", ]) { @@ -175,14 +159,14 @@ test("#9166 standalone assembly never overwrites an already pinned transformers ); try { - buildLlmlinguaRoot(root, "4.2.0"); + buildLlmlinguaRoot(root, "5.0.0"); const { distDir, standaloneDir } = createStandalone(root); mkPkg( join(standaloneDir, "node_modules"), "@huggingface/transformers", { - version: "3.5.2", + version: "4.2.0", } ); @@ -208,7 +192,7 @@ test("#9166 standalone assembly never overwrites an already pinned transformers assert.equal( targetManifest.version, - "3.5.2", + "4.2.0", "standalone's pinned transformers version must not be overwritten" ); @@ -286,9 +270,6 @@ test("#9166 co-location is not skipped when every closure dir exists but one is // llmlingua-2 one is the partial NFT-trace shell without its main. for (const packageName of [ "es-toolkit", - "@tensorflow/tfjs", - "@tensorflow/tfjs-core", - "long", "js-tiktoken", "base64-js", "@huggingface/transformers",