@snoai/memory 1.2.13 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{access-tracker-CglGg8uS.d.ts → access-tracker-B1H2AJlH.d.ts} +4 -4
- package/dist/{adapter-Cxyzdq6H.d.ts → adapter-CgdVA88V.d.ts} +2 -2
- package/dist/{agent-llm-port-DY2290EK.d.ts → agent-llm-port-BlFrD2ro.d.ts} +5 -0
- package/dist/{atomic-memory-extraction-DO4yTfch.d.ts → atomic-memory-extraction-DCMTQ0h-.d.ts} +4 -4
- package/dist/{atomic-memory-extraction-DWhlXJT6.js → atomic-memory-extraction-De86y7O-.js} +4 -4
- package/dist/{b-profile-section-dictionary-provider-BkUdS1SY.d.ts → b-profile-section-dictionary-provider-DbqdRYMw.d.ts} +1 -1
- package/dist/{capture-policy-detector-C7MA4jSr.d.ts → capture-policy-detector-D4ziS7KF.d.ts} +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/{config-D_KOwEyg.js → config-DWeuetYo.js} +740 -447
- package/dist/{derived-suppression-cache-CmS4xAaN.d.ts → derived-suppression-cache-2xOq6XHX.d.ts} +1 -1
- package/dist/diagnostic-encoder.js +1 -1
- package/dist/{embedding-provider-client-vynZMuit.d.ts → embedding-provider-client-CELWtPgt.d.ts} +6 -0
- package/dist/{embedding-provider-client-40tpzKou.js → embedding-provider-client-CgpUK_1G.js} +16 -1
- package/dist/{index-Pb9LrHgL.d.ts → index-BGu91bjB.d.ts} +25 -19
- package/dist/{index-CTi1R9T4.d.ts → index-PjHhlG6e.d.ts} +2 -2
- package/dist/index.d.ts +11 -11
- package/dist/index.js +5 -5
- package/dist/internal/config/index.d.ts +2 -2
- package/dist/internal/config/index.js +25 -19
- package/dist/internal/config/plugin-config-schema.d.ts +1 -1
- package/dist/internal/config/plugin-config-schema.js +1 -1
- package/dist/internal/config/product-mode.d.ts +1 -1
- package/dist/internal/config/settings.d.ts +1 -1
- package/dist/internal/config/settings.js +3 -30
- package/dist/internal/config/skin-defaults.d.ts +1 -1
- package/dist/internal/contract/profile.d.ts +1 -1
- package/dist/internal/contract/routes.d.ts +1 -1
- package/dist/internal/engine/bindings/memory-cli-shared.d.ts +5 -5
- package/dist/internal/engine/bindings/memory-forget-tool.js +1 -1
- package/dist/internal/engine/bindings/memory-list-tool.js +1 -1
- package/dist/internal/engine/bindings/memory-recall-tool.js +1 -1
- package/dist/internal/engine/bindings/memory-reflection-resolve-tool.js +1 -1
- package/dist/internal/engine/bindings/memory-stats-tool.js +1 -1
- package/dist/internal/engine/bindings/memory-store-tool.js +3 -3
- package/dist/internal/engine/bindings/memory-tool-access.d.ts +1 -1
- package/dist/internal/engine/bindings/memory-tool-access.js +1 -1
- package/dist/internal/engine/bindings/memory-tool-results.d.ts +1 -1
- package/dist/internal/engine/bindings/memory-tool-results.js +1 -1
- package/dist/internal/engine/bindings/memory-tool-schemas.d.ts +6 -6
- package/dist/internal/engine/bindings/memory-tool-schemas.js +1 -1
- package/dist/internal/engine/bindings/memory-update-tool.js +2 -2
- package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.d.ts +5 -5
- package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.js +2 -2
- package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.d.ts +4 -4
- package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.js +1 -1
- package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.d.ts +7 -7
- package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.js +2 -2
- package/dist/internal/engine/bindings/sno-station-mem-message-transcript.js +2 -2
- package/dist/internal/engine/bindings/sno-station-mem-reset-hook.d.ts +4 -4
- package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.d.ts +1 -1
- package/dist/internal/engine/extraction/atomic-memory-extraction.d.ts +1 -1
- package/dist/internal/engine/extraction/atomic-memory-extraction.js +1 -1
- package/dist/internal/engine/extraction/b-profile-section-dictionary-provider.d.ts +1 -1
- package/dist/internal/engine/extraction/b-profile-section-rekey.d.ts +5 -5
- package/dist/internal/engine/extraction/capture-policy-detector.d.ts +1 -1
- package/dist/internal/engine/extraction/embedding-provider-client.d.ts +1 -1
- package/dist/internal/engine/extraction/embedding-provider-client.js +1 -1
- package/dist/internal/engine/extraction/memory-metadata-codec.d.ts +2 -2
- package/dist/internal/engine/extraction/memory-temporality-classifier.d.ts +1 -1
- package/dist/internal/engine/extraction/memory-temporality-classifier.js +1 -1
- package/dist/internal/engine/observability/adapter.d.ts +1 -1
- package/dist/internal/engine/observability/memory-snapshot.d.ts +1 -1
- package/dist/internal/engine/observability/observable-embedder.d.ts +1 -1
- package/dist/internal/engine/observability/observable-embedder.js +1 -1
- package/dist/internal/engine/observability/observable-llm-client.d.ts +1 -1
- package/dist/internal/engine/observability/observable-memory-store.d.ts +1 -1
- package/dist/internal/engine/observability/observable-memory-store.js +1 -1
- package/dist/internal/engine/observability/observable-retriever.d.ts +1 -1
- package/dist/internal/engine/observability/observable-retriever.js +1 -1
- package/dist/internal/engine/observability/runtime-diagnostics.d.ts +1 -1
- package/dist/internal/engine/observability/runtime-diagnostics.js +1 -1
- package/dist/internal/engine/operations/memory-tier-promoter.d.ts +1 -1
- package/dist/internal/engine/operations/runtime-audit-log.d.ts +2 -2
- package/dist/internal/engine/provider/provider-registration.d.ts +4 -4
- package/dist/internal/engine/provider/provider-row-renderer.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-command-hooks.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-command-hooks.js +1 -1
- package/dist/internal/engine/reflection/reflection-deps.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-embedded-generator.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-embedded-generator.js +1 -1
- package/dist/internal/engine/reflection/reflection-injection-hooks.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-injection-hooks.js +1 -1
- package/dist/internal/engine/reflection/reflection-lifecycle-hooks.d.ts +1 -1
- package/dist/internal/engine/reflection/reflection-lifecycle-hooks.js +1 -1
- package/dist/internal/engine/reflection/strategy-hook-runner.d.ts +3 -3
- package/dist/internal/engine/reflection/strategy-hook-runner.js +2 -2
- package/dist/internal/engine/retrieval/access-tracker.d.ts +1 -1
- package/dist/internal/engine/retrieval/retriever.d.ts +2 -2
- package/dist/internal/engine/retrieval/retriever.js +1 -1
- package/dist/internal/engine/shared/memory-kind-policy.d.ts +1 -1
- package/dist/internal/engine/shared/paths.d.ts +2 -2
- package/dist/internal/engine/shared/types.d.ts +2 -2
- package/dist/internal/engine/shared/types.js +1 -1
- package/dist/internal/engine/telemetry/observe-ledger.d.ts +1 -1
- package/dist/internal/model/agent-llm-port.d.ts +1 -1
- package/dist/internal/model/llm-client.d.ts +2 -2
- package/dist/internal/model/llm-client.js +1 -1
- package/dist/internal/model/llm-mode-routing.d.ts +1 -1
- package/dist/internal/store/maintenance.d.ts +2 -2
- package/dist/internal/store/maintenance.js +4 -4
- package/dist/internal/store/store.d.ts +2 -2
- package/dist/internal/store/store.js +1 -1
- package/dist/{llm-client-B7LSFL_m.d.ts → llm-client-C-K7oyfu.d.ts} +1 -1
- package/dist/{llm-client-C7yyFWu-.js → llm-client-DDdUjYWS.js} +10 -3
- package/dist/{log-site-catalog.generated-BODn_6Kx.js → log-site-catalog.generated-Cn8rTa65.js} +94 -73
- package/dist/log-site-catalog.json +1 -1
- package/dist/{memory-kind-policy-B4IBMAlv.d.ts → memory-kind-policy-DgPCjfY4.d.ts} +1 -1
- package/dist/{memory-metadata-codec-BQ8tBY-g.d.ts → memory-metadata-codec-CfP9JLVO.d.ts} +1 -1
- package/dist/{memory-runtime-4iOpeP09.js → memory-runtime-jQke2sBc.js} +118 -78
- package/dist/{memory-snapshot-B8mQbWR-.d.ts → memory-snapshot-B6a-6nJ7.d.ts} +2 -2
- package/dist/{memory-store-base-CTAMKigD.d.ts → memory-store-base-upd9MuG6.d.ts} +4 -4
- package/dist/{memory-temporality-classifier-epuZlfXa.js → memory-temporality-classifier-BwBKEQbL.js} +1 -1
- package/dist/{memory-temporality-classifier-LByFBiFJ.d.ts → memory-temporality-classifier-DPk3_9Or.d.ts} +1 -1
- package/dist/{memory-tier-promoter-CSGa4LBH.d.ts → memory-tier-promoter-Cq23yveQ.d.ts} +1 -1
- package/dist/memory-tool-dependencies-C9qgVGOn.d.ts +14 -0
- package/dist/{memory-tool-dependencies-xLaWzwpI.js → memory-tool-dependencies-aAr7Pkuj.js} +1 -1
- package/dist/{observable-embedder-Da0teBqx.d.ts → observable-embedder-DihQuuQD.d.ts} +2 -2
- package/dist/{observable-llm-client-i0sp0veF.d.ts → observable-llm-client-BglDBFN5.d.ts} +3 -3
- package/dist/{observable-memory-store-CS87pitJ.d.ts → observable-memory-store--n02Dre8.d.ts} +5 -5
- package/dist/{observable-retriever-Bhfg9DsZ.d.ts → observable-retriever-CV6odifA.d.ts} +6 -6
- package/dist/paths-qJqwGcKl.d.ts +1 -0
- package/dist/{plugin-config-schema-CvAGFPyE.js → plugin-config-schema-D9riSXDg.js} +3 -20
- package/dist/{plugin-config-schema-CcEeAEdi.d.ts → plugin-config-schema-dV2_HgBo.d.ts} +2 -8
- package/dist/{profile-BMvnRqf5.d.ts → profile-CoLKGobS.d.ts} +1 -1
- package/dist/{profile-section-writer-DeD9GPQJ.js → profile-section-writer-Dd4FKpt5.js} +2 -2
- package/dist/{reflection-command-hooks-S5L6ZWeh.js → reflection-command-hooks-B2P4zDr6.js} +3 -3
- package/dist/{reflection-command-hooks-CFXyoVvE.d.ts → reflection-command-hooks-Cfv836Wc.d.ts} +4 -4
- package/dist/{reflection-deps-BPMrDU0a.d.ts → reflection-deps-DihlwQxE.d.ts} +4 -4
- package/dist/{reflection-embedded-generator-CF-NbuPS.d.ts → reflection-embedded-generator-DCZaS6kz.d.ts} +4 -4
- package/dist/{reflection-embedded-generator-nmVrj4AZ.js → reflection-embedded-generator-DV-NkeU-.js} +1 -1
- package/dist/{reflection-injection-hooks-BRIr3lJM.d.ts → reflection-injection-hooks-BYxkrds6.d.ts} +4 -4
- package/dist/{reflection-lifecycle-hooks-Ci8y4qHk.d.ts → reflection-lifecycle-hooks-Bwq2y5pe.d.ts} +3 -3
- package/dist/{rem-batch-executor-CfWChDAk.js → rem-batch-executor-B_49KivZ.js} +1 -1
- package/dist/{rem-trigger-B1P5_RqV.js → rem-trigger-DL0MlLeo.js} +180 -160
- package/dist/{retriever-CpyZs2ND.js → retriever-CsBW6MZv.js} +158 -162
- package/dist/{retriever-DdikFSDC.d.ts → retriever-hTIkt3CJ.d.ts} +19 -26
- package/dist/{runtime-audit-log-BVOxzkh7.d.ts → runtime-audit-log-eBrt_iPp.d.ts} +1 -1
- package/dist/{server-DOL3D6bb.js → server-BnBzsvMF.js} +9 -9
- package/dist/sidecar/main.js +2 -2
- package/dist/sno-station-mem-runtime-dependencies-9L8WkMMV.d.ts +31 -0
- package/dist/{sno-station-mem-runtime-dependencies-DZe3z4RY.js → sno-station-mem-runtime-dependencies-DrVDRAmf.js} +6 -6
- package/dist/{store-DY_rWx__.js → store-COwW7mIh.js} +1 -1
- package/dist/{store-DraRJ-HI.d.ts → store-DEG06TLb.d.ts} +2 -2
- package/dist/{strategy-hook-runner-CltVTtzk.js → strategy-hook-runner-CALbWcys.js} +1 -1
- package/dist/{strategy-hook-runner-DH7OEQrH.d.ts → strategy-hook-runner-D1sRVThs.d.ts} +7 -7
- package/dist/{types-DZGL4QXi.d.ts → types-DBEwNYdR.d.ts} +1 -1
- package/package.json +2 -2
- package/settings.default.json +0 -4
- package/dist/memory-tool-dependencies-Bl11T6Xm.d.ts +0 -14
- package/dist/paths-B5y-npWs.d.ts +0 -1
- package/dist/sno-station-mem-runtime-dependencies-DgAm7WqA.d.ts +0 -31
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createLogger } from "@snoai/utils/logger";
|
|
2
2
|
import path, { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { closeSync,
|
|
4
|
-
import { homedir,
|
|
3
|
+
import { closeSync, createReadStream, createWriteStream, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { homedir, userInfo } from "node:os";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import http from "node:http";
|
|
8
|
+
import https from "node:https";
|
|
9
|
+
import { pipeline } from "node:stream/promises";
|
|
6
10
|
import { LRUCache } from "lru-cache";
|
|
7
11
|
import { isLowercaseCanonicalUUIDv7 } from "@snoai/common-core";
|
|
8
12
|
import { z } from "zod";
|
|
@@ -94,13 +98,552 @@ const LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT = {
|
|
|
94
98
|
*/
|
|
95
99
|
const LOCAL_EMBEDDING_CACHE_DIR_DEFAULT = join(CONSTANTS_DIR, "..", ".cache", "models");
|
|
96
100
|
/**
|
|
101
|
+
* Downloads a model's files into <cacheDir>/<model>/<revision>/, the one place the loaders read a model from.
|
|
102
|
+
*
|
|
103
|
+
* A revision directory is complete when its marker names the same revision and file hashes and every file has its
|
|
104
|
+
* expected size; the marker is written last, so an interrupted download is never mistaken for a complete one. One process
|
|
105
|
+
* downloads a directory at a time (an O_EXCL lock with a heartbeat; a dead holder's lock is broken at once).
|
|
106
|
+
*/
|
|
107
|
+
const log$3 = createLogger("embedder:downloader");
|
|
108
|
+
const HUB = "https://huggingface.co/";
|
|
109
|
+
const HUB_HOSTS = /* @__PURE__ */ new Set(["huggingface.co", "hf.co"]);
|
|
110
|
+
const MARKER = ".complete";
|
|
111
|
+
const MAX_REDIRECTS = 5;
|
|
112
|
+
/** A connection that sends nothing for this long is dropped (Node's own limit is five minutes). */
|
|
113
|
+
const STALL_MS = 3e4;
|
|
114
|
+
/** Tries per file; network errors, stalls, 408, 429 and 5xx wait 1, 2, 4, 8 s (or the server's Retry-After). */
|
|
115
|
+
const ATTEMPTS = 5;
|
|
116
|
+
const RETRY_AFTER_CAP_MS = 6e4;
|
|
117
|
+
const LOCK_HEARTBEAT_MS = 5e3;
|
|
118
|
+
const LOCK_POLL_MS = 2e3;
|
|
119
|
+
/** A lock not touched for this long belongs to a holder that hung or died. */
|
|
120
|
+
const LOCK_STALE_MS = 9e5;
|
|
121
|
+
function modelDir(cacheDir, spec) {
|
|
122
|
+
return join(resolve(cacheDir), spec.id, revisionDirName(spec.revision));
|
|
123
|
+
}
|
|
124
|
+
/** One directory level per revision, so a ref such as `refs/pr/1` is not nested (and not mistaken for an older copy). */
|
|
125
|
+
function revisionDirName(revision) {
|
|
126
|
+
return revision.replaceAll("/", "--");
|
|
127
|
+
}
|
|
128
|
+
/** Complete: the marker names this revision and the same hashes, and every file has its expected size. */
|
|
129
|
+
function isDownloaded(cacheDir, spec) {
|
|
130
|
+
const dir = modelDir(cacheDir, spec);
|
|
131
|
+
try {
|
|
132
|
+
const marker = JSON.parse(readFileSync(join(dir, MARKER), "utf8"));
|
|
133
|
+
if (marker.revision !== spec.revision || marker.verify === true) return false;
|
|
134
|
+
const recorded = new Map((readMarkerFiles(dir) ?? []).map((file) => [file.path, file]));
|
|
135
|
+
return spec.files.every((file) => statSync(join(dir, file.path), { throwIfNoEntry: false })?.size === file.size && sameIdentity(file, recorded.get(file.path)));
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function sameIdentity(file, recorded) {
|
|
141
|
+
return (!file.sha256 || recorded?.sha256 === file.sha256) && (!file.oid || recorded?.oid === file.oid);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* For a loader that could not open the model: marks the download for checking, so the next start checks every file
|
|
145
|
+
* again and fetches any that differ (a file kept despite a SHA-256 mismatch, a copy that went bad on disk). The marker
|
|
146
|
+
* keeps its file list for offline use. Once per process: a load that keeps failing for another reason (memory, an
|
|
147
|
+
* unsupported op) does not re-hash the files each time.
|
|
148
|
+
*/
|
|
149
|
+
function forgetDownload(cacheDir, spec) {
|
|
150
|
+
const dir = modelDir(cacheDir, spec);
|
|
151
|
+
if (forgotten.has(dir)) return;
|
|
152
|
+
forgotten.add(dir);
|
|
153
|
+
try {
|
|
154
|
+
const marker = JSON.parse(readFileSync(join(dir, MARKER), "utf8"));
|
|
155
|
+
writeFileSync(join(dir, MARKER), JSON.stringify({
|
|
156
|
+
...marker,
|
|
157
|
+
verify: true
|
|
158
|
+
}));
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
const forgotten = /* @__PURE__ */ new Set();
|
|
162
|
+
const downloads = /* @__PURE__ */ new Map();
|
|
163
|
+
/** Makes the model's revision directory complete and returns it. */
|
|
164
|
+
function downloadModel(spec, options) {
|
|
165
|
+
const dir = modelDir(options.cacheDir, spec);
|
|
166
|
+
let download = downloads.get(dir);
|
|
167
|
+
if (!download) {
|
|
168
|
+
download = fetchModel(spec, options, dir).finally(() => downloads.delete(dir));
|
|
169
|
+
downloads.set(dir, download);
|
|
170
|
+
}
|
|
171
|
+
return download;
|
|
172
|
+
}
|
|
173
|
+
async function fetchModel(spec, options, dir) {
|
|
174
|
+
if (isDownloaded(options.cacheDir, spec)) return dir;
|
|
175
|
+
const lock = `${dir}.lock`;
|
|
176
|
+
await acquireLock(lock);
|
|
177
|
+
const heartbeat = setInterval(() => touch(lock), LOCK_HEARTBEAT_MS);
|
|
178
|
+
try {
|
|
179
|
+
if (isDownloaded(options.cacheDir, spec)) return dir;
|
|
180
|
+
const started = performance.now();
|
|
181
|
+
const host = hostOf(options.mirror);
|
|
182
|
+
log$3.info("model download started", {
|
|
183
|
+
model: spec.id,
|
|
184
|
+
revision: spec.revision,
|
|
185
|
+
cache_dir: dir
|
|
186
|
+
}, {
|
|
187
|
+
event_name: "embedder.downloader.started",
|
|
188
|
+
file: "packages/embedder/src/downloader.ts",
|
|
189
|
+
function: "fetchModel",
|
|
190
|
+
site_id: "embedder.downloader.started"
|
|
191
|
+
});
|
|
192
|
+
adoptFlatFiles(spec, options.cacheDir, dir);
|
|
193
|
+
const recorded = new Map((readMarkerFiles(dir) ?? []).map((file) => [file.path, file]));
|
|
194
|
+
for (const file of spec.files) {
|
|
195
|
+
const target = join(dir, file.path);
|
|
196
|
+
if (statSync(target, { throwIfNoEntry: false })?.size === file.size && (!file.oid || recorded.get(file.path)?.oid === file.oid) && (options.offline || (await hashCheck(target, file)).ok)) continue;
|
|
197
|
+
if (options.offline) throw new Error(`${spec.id}@${spec.revision.slice(0, 12)}: ${file.path} is not in ${dir} and downloads are off`);
|
|
198
|
+
await fetchFile(spec, `${host}${spec.id}/resolve/${encodeURIComponent(spec.revision)}/${file.path}`, target, file);
|
|
199
|
+
}
|
|
200
|
+
writeFileSync(join(dir, MARKER), JSON.stringify({
|
|
201
|
+
model: spec.id,
|
|
202
|
+
revision: spec.revision,
|
|
203
|
+
files: spec.files,
|
|
204
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
205
|
+
}));
|
|
206
|
+
removeOlderCopies(spec, options.cacheDir);
|
|
207
|
+
log$3.info("model download complete", {
|
|
208
|
+
model: spec.id,
|
|
209
|
+
revision: spec.revision,
|
|
210
|
+
duration_ms: Math.round(performance.now() - started)
|
|
211
|
+
}, {
|
|
212
|
+
event_name: "embedder.downloader.complete",
|
|
213
|
+
file: "packages/embedder/src/downloader.ts",
|
|
214
|
+
function: "fetchModel",
|
|
215
|
+
site_id: "embedder.downloader.complete"
|
|
216
|
+
});
|
|
217
|
+
return dir;
|
|
218
|
+
} finally {
|
|
219
|
+
clearInterval(heartbeat);
|
|
220
|
+
rmSync(lock, { force: true });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Releases before the revision directories kept one copy at <cacheDir>/<model>/<file>: a file of the expected size
|
|
225
|
+
* there moves into the revision directory instead of being fetched again.
|
|
226
|
+
*/
|
|
227
|
+
function adoptFlatFiles(spec, cacheDir, dir) {
|
|
228
|
+
for (const file of spec.files) {
|
|
229
|
+
const flat = join(resolve(cacheDir), spec.id, file.path);
|
|
230
|
+
const target = join(dir, file.path);
|
|
231
|
+
if (statSync(target, { throwIfNoEntry: false })?.size === file.size) continue;
|
|
232
|
+
if (statSync(flat, { throwIfNoEntry: false })?.size !== file.size) continue;
|
|
233
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
234
|
+
renameSync(flat, target);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Everything else under <cacheDir>/<model>/ is an older copy, except another revision's complete download, which an
|
|
239
|
+
* install of another version sharing this cache may still load; a copy that cannot be removed only costs disk.
|
|
240
|
+
*/
|
|
241
|
+
function removeOlderCopies(spec, cacheDir) {
|
|
242
|
+
const root = join(resolve(cacheDir), spec.id);
|
|
243
|
+
const keep = /* @__PURE__ */ new Set([revisionDirName(spec.revision), `${revisionDirName(spec.revision)}.lock`]);
|
|
244
|
+
const downloading = (entry) => !lockIsStale(join(root, entry.endsWith(".lock") ? entry : `${entry}.lock`));
|
|
245
|
+
const complete = (entry) => statSync(join(root, entry, MARKER), { throwIfNoEntry: false }) !== void 0;
|
|
246
|
+
const older = readdirSync(root).filter((entry) => !keep.has(entry) && !downloading(entry) && !complete(entry)).map((entry) => join(root, entry));
|
|
247
|
+
older.push(join(resolve(cacheDir), ".download-complete"));
|
|
248
|
+
for (const path of older) try {
|
|
249
|
+
rmSync(path, {
|
|
250
|
+
recursive: true,
|
|
251
|
+
force: true
|
|
252
|
+
});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
log$3.warn("older model copy not removed", {
|
|
255
|
+
path,
|
|
256
|
+
error
|
|
257
|
+
}, {
|
|
258
|
+
event_name: "embedder.downloader.cleanup_failed",
|
|
259
|
+
file: "packages/embedder/src/downloader.ts",
|
|
260
|
+
function: "removeOlderCopies",
|
|
261
|
+
site_id: "embedder.downloader.cleanup_failed"
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const DTYPE_SUFFIX = {
|
|
266
|
+
fp32: "",
|
|
267
|
+
fp16: "_fp16",
|
|
268
|
+
int8: "_int8",
|
|
269
|
+
uint8: "_uint8",
|
|
270
|
+
q8: "_quantized",
|
|
271
|
+
q4: "_q4",
|
|
272
|
+
q4f16: "_q4f16",
|
|
273
|
+
bnb4: "_bnb4"
|
|
274
|
+
};
|
|
275
|
+
const LOADER_FILES = /* @__PURE__ */ new Set([
|
|
276
|
+
"config.json",
|
|
277
|
+
"tokenizer.json",
|
|
278
|
+
"tokenizer_config.json",
|
|
279
|
+
"special_tokens_map.json"
|
|
280
|
+
]);
|
|
281
|
+
/**
|
|
282
|
+
* The files transformers.js loads for a model that is not pinned in code, as the hub lists them at `revision`;
|
|
283
|
+
* offline, as the marker of the last complete download lists them.
|
|
284
|
+
*/
|
|
285
|
+
async function hubModelSpec(id, revision, dtype, options) {
|
|
286
|
+
const dir = modelDir(options.cacheDir, {
|
|
287
|
+
id,
|
|
288
|
+
revision
|
|
289
|
+
});
|
|
290
|
+
if (options.offline) {
|
|
291
|
+
const files = readMarkerFiles(dir);
|
|
292
|
+
if (!files) throw new Error(`${id}@${revision} is not in ${dir} and downloads are off`);
|
|
293
|
+
return {
|
|
294
|
+
id,
|
|
295
|
+
revision,
|
|
296
|
+
files
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const weights = `onnx/model${DTYPE_SUFFIX[dtype] ?? ""}.onnx`;
|
|
300
|
+
const url = `${hostOf(options.mirror)}api/models/${id}/tree/${encodeURIComponent(revision)}?recursive=true`;
|
|
301
|
+
const cached = readMarkerFiles(dir);
|
|
302
|
+
let tree;
|
|
303
|
+
try {
|
|
304
|
+
tree = cached ? await getText(url) : await withRetries(`${id} file list`, () => getText(url));
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (!cached) throw error;
|
|
307
|
+
log$3.warn("hub file list unavailable; using the copy on disk", {
|
|
308
|
+
model: id,
|
|
309
|
+
revision,
|
|
310
|
+
error
|
|
311
|
+
}, {
|
|
312
|
+
event_name: "embedder.downloader.hub_unavailable",
|
|
313
|
+
file: "packages/embedder/src/downloader.ts",
|
|
314
|
+
function: "hubModelSpec",
|
|
315
|
+
site_id: "embedder.downloader.hub_unavailable"
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
id,
|
|
319
|
+
revision,
|
|
320
|
+
files: cached
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const files = parseTree(tree).filter((file) => LOADER_FILES.has(file.path) || file.path === weights || file.path.startsWith(`${weights}_data`));
|
|
324
|
+
if (!files.some((file) => file.path === weights)) throw new Error(`${id}@${revision} has no ${weights}`);
|
|
325
|
+
return {
|
|
326
|
+
id,
|
|
327
|
+
revision,
|
|
328
|
+
files
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function readMarkerFiles(dir) {
|
|
332
|
+
try {
|
|
333
|
+
const marker = JSON.parse(readFileSync(join(dir, MARKER), "utf8"));
|
|
334
|
+
return Array.isArray(marker.files) ? parseTree(marker.files.map((file) => ({
|
|
335
|
+
type: "file",
|
|
336
|
+
...file
|
|
337
|
+
}))) : void 0;
|
|
338
|
+
} catch {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/** The hub's tree listing: [{ type, path, size, lfs?: { oid } }]; anything else is not a file it can serve. */
|
|
343
|
+
function parseTree(raw) {
|
|
344
|
+
const entries = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
345
|
+
if (!Array.isArray(entries)) throw new Error("the hub's file list is not a list");
|
|
346
|
+
return entries.flatMap((entry) => {
|
|
347
|
+
if (typeof entry !== "object" || entry === null) return [];
|
|
348
|
+
const { type, path, size, lfs, sha256, oid } = entry;
|
|
349
|
+
if (type !== "file" || typeof path !== "string" || typeof size !== "number") return [];
|
|
350
|
+
const hash = typeof sha256 === "string" ? sha256 : typeof lfs?.oid === "string" ? lfs.oid : void 0;
|
|
351
|
+
return [{
|
|
352
|
+
path,
|
|
353
|
+
size,
|
|
354
|
+
...hash ? { sha256: hash } : {},
|
|
355
|
+
...typeof oid === "string" ? { oid } : {}
|
|
356
|
+
}];
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function hostOf(mirror) {
|
|
360
|
+
const host = mirror?.trim() || HUB;
|
|
361
|
+
return host.endsWith("/") ? host : `${host}/`;
|
|
362
|
+
}
|
|
363
|
+
async function fetchFile(spec, url, target, file) {
|
|
364
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
365
|
+
const part = `${target}.part`;
|
|
366
|
+
let mismatches = 0;
|
|
367
|
+
return withRetries(`${spec.id} ${file.path}`, async () => {
|
|
368
|
+
if ((statSync(part, { throwIfNoEntry: false })?.size ?? 0) < file.size) await get(url, part);
|
|
369
|
+
const size = statSync(part).size;
|
|
370
|
+
if (size > file.size) rmSync(part, { force: true });
|
|
371
|
+
if (size !== file.size) throw new Error(`got ${size} bytes, expected ${file.size} (${url})`);
|
|
372
|
+
if (!(await hashCheck(part, file)).ok && ++mismatches === 1) {
|
|
373
|
+
rmSync(part, { force: true });
|
|
374
|
+
throw new Error(`content does not match its SHA-256 (${url})`);
|
|
375
|
+
}
|
|
376
|
+
renameSync(part, target);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Whether a file has its pinned SHA-256, and its actual hash. The check never blocks the model: a second fresh
|
|
381
|
+
* download that still differs is kept with an error in the log, and a check that fails to run counts as a match.
|
|
382
|
+
*/
|
|
383
|
+
async function hashCheck(path, file) {
|
|
384
|
+
if (!file.sha256) return { ok: true };
|
|
385
|
+
try {
|
|
386
|
+
const hash = createHash("sha256");
|
|
387
|
+
await pipeline(createReadStream(path), hash);
|
|
388
|
+
const actual = hash.digest("hex");
|
|
389
|
+
if (actual === file.sha256) return {
|
|
390
|
+
ok: true,
|
|
391
|
+
actual
|
|
392
|
+
};
|
|
393
|
+
log$3.error("model file SHA-256 differs", {
|
|
394
|
+
path: file.path,
|
|
395
|
+
expected_sha256: file.sha256,
|
|
396
|
+
actual_sha256: actual
|
|
397
|
+
}, {
|
|
398
|
+
event_name: "embedder.downloader.sha256_mismatch",
|
|
399
|
+
file: "packages/embedder/src/downloader.ts",
|
|
400
|
+
function: "hashCheck",
|
|
401
|
+
site_id: "embedder.downloader.sha256_mismatch"
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
ok: false,
|
|
405
|
+
actual
|
|
406
|
+
};
|
|
407
|
+
} catch (error) {
|
|
408
|
+
log$3.warn("model file SHA-256 not checked", {
|
|
409
|
+
path: file.path,
|
|
410
|
+
error
|
|
411
|
+
}, {
|
|
412
|
+
event_name: "embedder.downloader.sha256_unchecked",
|
|
413
|
+
file: "packages/embedder/src/downloader.ts",
|
|
414
|
+
function: "hashCheck",
|
|
415
|
+
site_id: "embedder.downloader.sha256_unchecked"
|
|
416
|
+
});
|
|
417
|
+
return { ok: true };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/** Runs `attemptOnce` until it succeeds, another try cannot help, or ATTEMPTS run out; the error names `label`. */
|
|
421
|
+
async function withRetries(label, attemptOnce) {
|
|
422
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
423
|
+
return await attemptOnce();
|
|
424
|
+
} catch (error) {
|
|
425
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
426
|
+
const delay = attempt < ATTEMPTS ? retryDelay(error, attempt) : void 0;
|
|
427
|
+
if (delay === void 0) throw new Error(`${label}: ${message} after ${attempt} attempt${attempt === 1 ? "" : "s"}`, { cause: error });
|
|
428
|
+
log$3.warn("model download failed; retrying", {
|
|
429
|
+
download_label: label,
|
|
430
|
+
attempt,
|
|
431
|
+
delay_ms: delay,
|
|
432
|
+
error
|
|
433
|
+
}, {
|
|
434
|
+
event_name: "embedder.downloader.retry",
|
|
435
|
+
file: "packages/embedder/src/downloader.ts",
|
|
436
|
+
function: "withRetries",
|
|
437
|
+
site_id: "embedder.downloader.retry"
|
|
438
|
+
});
|
|
439
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, delay));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
var HttpError = class extends Error {
|
|
443
|
+
status;
|
|
444
|
+
retryAfterMs;
|
|
445
|
+
constructor(status, retryAfterMs, url) {
|
|
446
|
+
super(`HTTP ${status} (${url})`);
|
|
447
|
+
this.status = status;
|
|
448
|
+
this.retryAfterMs = retryAfterMs;
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
/** Milliseconds before the next try, or undefined when another try cannot help (404, 401, 403 and other 4xx). */
|
|
452
|
+
function retryDelay(error, attempt) {
|
|
453
|
+
if (error instanceof HttpError) {
|
|
454
|
+
if (error.status !== 408 && error.status !== 429 && error.status < 500) return void 0;
|
|
455
|
+
if (error.retryAfterMs !== void 0) return Math.min(error.retryAfterMs, RETRY_AFTER_CAP_MS);
|
|
456
|
+
}
|
|
457
|
+
return 1e3 * 2 ** (attempt - 1);
|
|
458
|
+
}
|
|
459
|
+
/** Retry-After in seconds or as an HTTP date. */
|
|
460
|
+
function retryAfterMs(header) {
|
|
461
|
+
if (!header) return void 0;
|
|
462
|
+
const seconds = Number(header);
|
|
463
|
+
const ms = Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(header) - Date.now();
|
|
464
|
+
return Number.isFinite(ms) ? Math.max(0, ms) : void 0;
|
|
465
|
+
}
|
|
466
|
+
/** The response to `url` after redirects. A socket silent for STALL_MS errors the request and its response. */
|
|
467
|
+
function open(url, headers, redirects = 0) {
|
|
468
|
+
return new Promise((resolvePromise, reject) => {
|
|
469
|
+
const client = url.startsWith("https:") ? https : http;
|
|
470
|
+
const agent = new client.Agent({ proxyEnv: process.env });
|
|
471
|
+
const token = HUB_HOSTS.has(new URL(url).hostname) ? process.env["HF_TOKEN"] ?? process.env["HF_ACCESS_TOKEN"] : void 0;
|
|
472
|
+
const auth = token ? { authorization: `Bearer ${token}` } : {};
|
|
473
|
+
const request = client.get(url, {
|
|
474
|
+
agent,
|
|
475
|
+
headers: {
|
|
476
|
+
"user-agent": "sno-station",
|
|
477
|
+
...auth,
|
|
478
|
+
...headers
|
|
479
|
+
}
|
|
480
|
+
}, (response) => {
|
|
481
|
+
const location = response.headers.location;
|
|
482
|
+
const status = response.statusCode ?? 0;
|
|
483
|
+
if (status < 300 || status >= 400 || !location) return resolvePromise(response);
|
|
484
|
+
response.resume();
|
|
485
|
+
if (redirects >= MAX_REDIRECTS) return reject(/* @__PURE__ */ new Error(`too many redirects (${url})`));
|
|
486
|
+
open(new URL(location, url).toString(), headers, redirects + 1).then(resolvePromise, reject);
|
|
487
|
+
});
|
|
488
|
+
request.setTimeout(STALL_MS, () => request.destroy(/* @__PURE__ */ new Error(`no data for ${STALL_MS / 1e3} s (${url})`)));
|
|
489
|
+
request.on("error", reject);
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
/** Streams one URL into `part`, resuming after the bytes already there. */
|
|
493
|
+
async function get(url, part) {
|
|
494
|
+
const start = statSync(part, { throwIfNoEntry: false })?.size ?? 0;
|
|
495
|
+
const response = await open(url, start > 0 ? { range: `bytes=${start}-` } : {});
|
|
496
|
+
const status = response.statusCode ?? 0;
|
|
497
|
+
const resumed = status === 206 && response.headers["content-range"]?.startsWith(`bytes ${start}-`) === true;
|
|
498
|
+
if (status === 416 || status === 206 && !resumed) {
|
|
499
|
+
response.resume();
|
|
500
|
+
rmSync(part, { force: true });
|
|
501
|
+
throw new Error(`HTTP ${status}: the host did not resume at byte ${start} (${url})`);
|
|
502
|
+
}
|
|
503
|
+
if (status !== 200 && status !== 206) {
|
|
504
|
+
response.resume();
|
|
505
|
+
throw new HttpError(status, retryAfterMs(response.headers["retry-after"]), url);
|
|
506
|
+
}
|
|
507
|
+
await pipeline(response, createWriteStream(part, { flags: resumed ? "a" : "w" }));
|
|
508
|
+
}
|
|
509
|
+
async function getText(url) {
|
|
510
|
+
const response = await open(url, { accept: "application/json" });
|
|
511
|
+
if (response.statusCode !== 200) {
|
|
512
|
+
response.resume();
|
|
513
|
+
throw new HttpError(response.statusCode ?? 0, retryAfterMs(response.headers["retry-after"]), url);
|
|
514
|
+
}
|
|
515
|
+
let text = "";
|
|
516
|
+
for await (const chunk of response) text += String(chunk);
|
|
517
|
+
return text;
|
|
518
|
+
}
|
|
519
|
+
function touch(lock) {
|
|
520
|
+
try {
|
|
521
|
+
const now = /* @__PURE__ */ new Date();
|
|
522
|
+
utimesSync(lock, now, now);
|
|
523
|
+
} catch {}
|
|
524
|
+
}
|
|
525
|
+
function tryLock(lock) {
|
|
526
|
+
mkdirSync(dirname(lock), { recursive: true });
|
|
527
|
+
try {
|
|
528
|
+
const fd = openSync(lock, "wx");
|
|
529
|
+
writeFileSync(fd, `${process.pid}\n${Date.now()}\n`);
|
|
530
|
+
closeSync(fd);
|
|
531
|
+
return true;
|
|
532
|
+
} catch (error) {
|
|
533
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") return false;
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function lockIsStale(lock) {
|
|
538
|
+
try {
|
|
539
|
+
if (Date.now() - statSync(lock).mtimeMs > LOCK_STALE_MS) return true;
|
|
540
|
+
const pid = Number.parseInt(readFileSync(lock, "utf8").split("\n")[0] ?? "", 10);
|
|
541
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
542
|
+
process.kill(pid, 0);
|
|
543
|
+
return false;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
return !(error instanceof Error && "code" in error && error.code === "EPERM");
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/** Waits while a live holder keeps its lock fresh; breaks the lock of a holder that died or hung. */
|
|
549
|
+
async function acquireLock(lock) {
|
|
550
|
+
while (!tryLock(lock)) {
|
|
551
|
+
if (lockIsStale(lock)) {
|
|
552
|
+
try {
|
|
553
|
+
unlinkSync(lock);
|
|
554
|
+
} catch {}
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, LOCK_POLL_MS));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* model-download.ts — the bundled embedding model's files at its pinned revision, fetched through the shared
|
|
562
|
+
* downloader. User-facing progress goes to stderr (not stdout) so it doesn't interfere with structured log output
|
|
563
|
+
* or piped commands.
|
|
564
|
+
*/
|
|
565
|
+
const log$2 = createLogger("embedder:model-download");
|
|
566
|
+
const DTYPE_SIZES = {
|
|
567
|
+
q4: "~400MB",
|
|
568
|
+
q8: "~639MB",
|
|
569
|
+
fp16: "~1.2GB",
|
|
570
|
+
fp32: "~639MB"
|
|
571
|
+
};
|
|
572
|
+
const TOKENIZER_FILES = [
|
|
573
|
+
{
|
|
574
|
+
path: "config.json",
|
|
575
|
+
size: 1651,
|
|
576
|
+
sha256: "c851a73845113421a2dd85b34446371635844b3875859794fb0d5fc9aee9948d"
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
path: "tokenizer.json",
|
|
580
|
+
size: 11423016,
|
|
581
|
+
sha256: "db86c3db621fcbcb43e5dd8981d46a9b30b715a41ce8994a9420ec5c8c3797d3"
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
path: "tokenizer_config.json",
|
|
585
|
+
size: 768,
|
|
586
|
+
sha256: "62b1e4690e44d8c7bec7f22f228331db2d9886160ed7dc4b2068ca24af87d68a"
|
|
587
|
+
}
|
|
588
|
+
];
|
|
589
|
+
/** The weights for each dtype at the pinned revision; the repository ships one int8 file under both names. */
|
|
590
|
+
const WEIGHTS = {
|
|
591
|
+
q8: {
|
|
592
|
+
path: "onnx/model_quantized.onnx",
|
|
593
|
+
size: 602401737,
|
|
594
|
+
sha256: "b514c342c20aec2431ea213bde1a03083e117ac3b6e77ba6eee57cac8b79cd29"
|
|
595
|
+
},
|
|
596
|
+
fp32: {
|
|
597
|
+
path: "onnx/model.onnx",
|
|
598
|
+
size: 602401737,
|
|
599
|
+
sha256: "b514c342c20aec2431ea213bde1a03083e117ac3b6e77ba6eee57cac8b79cd29"
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
/** The bundled embedding model's files at its pinned revision for one dtype. */
|
|
603
|
+
function embeddingModelSpec(dtype = "q8") {
|
|
604
|
+
const weights = WEIGHTS[dtype];
|
|
605
|
+
if (!weights) throw new Error(`${LOCAL_EMBEDDING_MODEL} has no ${dtype} file at revision ${LOCAL_EMBEDDING_MODEL_REVISION}; use q8 or fp32`);
|
|
606
|
+
return {
|
|
607
|
+
id: LOCAL_EMBEDDING_MODEL,
|
|
608
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
609
|
+
files: [...TOKENIZER_FILES, weights]
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function getDtypeDisplaySize(dtype) {
|
|
613
|
+
return DTYPE_SIZES[dtype] ?? "unknown size";
|
|
614
|
+
}
|
|
615
|
+
/** Downloads the bundled embedding model into cacheDir unless it is already complete there. */
|
|
616
|
+
async function ensureModelDownloaded(opts) {
|
|
617
|
+
const cacheDir = opts?.cacheDir ?? LOCAL_EMBEDDING_CACHE_DIR_DEFAULT;
|
|
618
|
+
const dtype = opts?.dtype ?? "q8";
|
|
619
|
+
const spec = embeddingModelSpec(dtype);
|
|
620
|
+
const fetching = !opts?.offline && !isDownloaded(cacheDir, spec);
|
|
621
|
+
const started = performance.now();
|
|
622
|
+
if (fetching) process.stderr.write(`\nPreparing the local embedding model (a first run downloads ${getDtypeDisplaySize(dtype)})...\n`);
|
|
623
|
+
await downloadModel(spec, {
|
|
624
|
+
cacheDir,
|
|
625
|
+
offline: opts?.offline,
|
|
626
|
+
mirror: opts?.mirror
|
|
627
|
+
});
|
|
628
|
+
if (fetching) process.stderr.write(` Ready in ${Math.round((performance.now() - started) / 1e3)}s: ${cacheDir}\n`);
|
|
629
|
+
log$2.info("local embedding model ready", {
|
|
630
|
+
cacheDir,
|
|
631
|
+
dtype
|
|
632
|
+
}, {
|
|
633
|
+
event_name: "embedder.model.download.complete",
|
|
634
|
+
file: "packages/embedder/src/model-download.ts",
|
|
635
|
+
function: "ensureModelDownloaded",
|
|
636
|
+
site_id: "embedder.model.download.complete"
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
97
640
|
* Local ONNX embedding provider — PPLX embed v1 0.6B INT8, 1024-d vectors.
|
|
98
641
|
*
|
|
99
642
|
* Self-contained: all params via constructor; no app-level config dependency.
|
|
100
643
|
*/
|
|
101
644
|
const log$1$1 = createLogger("embedder:local");
|
|
102
645
|
let sharedExtractor;
|
|
103
|
-
let sharedLoading;
|
|
646
|
+
let sharedLoading$1;
|
|
104
647
|
let sharedRefCount = 0;
|
|
105
648
|
let sharedPipelineKey;
|
|
106
649
|
function buildSessionOptions(options) {
|
|
@@ -167,7 +710,7 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
167
710
|
static resetStaticState() {
|
|
168
711
|
LocalEmbedProvider.configuredCacheDir = void 0;
|
|
169
712
|
sharedExtractor = void 0;
|
|
170
|
-
sharedLoading = void 0;
|
|
713
|
+
sharedLoading$1 = void 0;
|
|
171
714
|
sharedRefCount = 0;
|
|
172
715
|
sharedPipelineKey = void 0;
|
|
173
716
|
}
|
|
@@ -179,7 +722,7 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
179
722
|
this.cacheDir = resolve(config?.cacheDir ?? LOCAL_EMBEDDING_CACHE_DIR_DEFAULT);
|
|
180
723
|
this.dtype = config?.dtype ?? "q8";
|
|
181
724
|
this.modelId = config?.model ?? "tss-deposium/pplx-embed-v1-0.6b-onnx-int8-standard";
|
|
182
|
-
this.revision = config?.revision ?? (
|
|
725
|
+
this.revision = config?.revision ?? (this.modelId === "tss-deposium/pplx-embed-v1-0.6b-onnx-int8-standard" ? "a18fdffe7480e6ea5643acb7757142b33838817a" : void 0);
|
|
183
726
|
this.nativeDim = config?.nativeDim ?? 1024;
|
|
184
727
|
this.outputDim = config?.outputDim ?? this.nativeDim;
|
|
185
728
|
if (this.outputDim > this.nativeDim) throw new Error(`outputDim (${this.outputDim}) cannot exceed nativeDim (${this.nativeDim}) — Matryoshka truncation only shrinks dimensions.`);
|
|
@@ -200,21 +743,21 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
200
743
|
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
201
744
|
this.assertCompatibleSharedPipeline();
|
|
202
745
|
if (sharedExtractor) return sharedExtractor;
|
|
203
|
-
if (sharedLoading) {
|
|
204
|
-
const extractor = await sharedLoading;
|
|
746
|
+
if (sharedLoading$1) {
|
|
747
|
+
const extractor = await sharedLoading$1;
|
|
205
748
|
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
206
749
|
if (!sharedExtractor) sharedExtractor = extractor;
|
|
207
750
|
return extractor;
|
|
208
751
|
}
|
|
209
752
|
sharedPipelineKey = this.pipelineKey;
|
|
210
|
-
sharedLoading = this.initPipeline();
|
|
753
|
+
sharedLoading$1 = this.initPipeline();
|
|
211
754
|
try {
|
|
212
|
-
const extractor = await sharedLoading;
|
|
755
|
+
const extractor = await sharedLoading$1;
|
|
213
756
|
sharedExtractor = extractor;
|
|
214
757
|
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
215
758
|
return extractor;
|
|
216
759
|
} finally {
|
|
217
|
-
sharedLoading = void 0;
|
|
760
|
+
sharedLoading$1 = void 0;
|
|
218
761
|
if (!sharedExtractor && sharedRefCount === 0) sharedPipelineKey = void 0;
|
|
219
762
|
}
|
|
220
763
|
}
|
|
@@ -225,13 +768,7 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
225
768
|
async initPipeline() {
|
|
226
769
|
if (LocalEmbedProvider.configuredCacheDir !== void 0 && LocalEmbedProvider.configuredCacheDir !== this.cacheDir) throw new Error(`transformers env is global; LocalEmbedProvider cacheDir must be consistent (existing: ${LocalEmbedProvider.configuredCacheDir}, requested: ${this.cacheDir})`);
|
|
227
770
|
LocalEmbedProvider.configuredCacheDir = this.cacheDir;
|
|
228
|
-
const {
|
|
229
|
-
env.cacheDir = this.cacheDir;
|
|
230
|
-
env.allowRemoteModels = !this.offline;
|
|
231
|
-
env.localModelPath = this.cacheDir;
|
|
232
|
-
env.remotePathTemplate = `{model}/resolve/${this.revision ?? "main"}/`;
|
|
233
|
-
const hfEndpoint = this.mirror?.trim();
|
|
234
|
-
if (hfEndpoint) env.remoteHost = hfEndpoint.endsWith("/") ? hfEndpoint : `${hfEndpoint}/`;
|
|
771
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
235
772
|
log$1$1.info("loading ONNX model", {
|
|
236
773
|
model: this.modelId,
|
|
237
774
|
cacheDir: this.cacheDir,
|
|
@@ -248,10 +785,20 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
248
785
|
const t0 = performance.now();
|
|
249
786
|
try {
|
|
250
787
|
const sessionOptions = buildSessionOptions(this.sessionOptions);
|
|
251
|
-
const
|
|
788
|
+
const download = {
|
|
789
|
+
cacheDir: this.cacheDir,
|
|
790
|
+
offline: this.offline,
|
|
791
|
+
mirror: this.mirror
|
|
792
|
+
};
|
|
793
|
+
const spec = this.modelId === "tss-deposium/pplx-embed-v1-0.6b-onnx-int8-standard" && this.revision === "a18fdffe7480e6ea5643acb7757142b33838817a" ? embeddingModelSpec(this.dtype) : await hubModelSpec(this.modelId, this.revision ?? "main", this.dtype, download);
|
|
794
|
+
const extractor = await pipeline("feature-extraction", await downloadModel(spec, download), {
|
|
252
795
|
dtype: this.dtype,
|
|
253
796
|
device: "cpu",
|
|
254
|
-
session_options: sessionOptions
|
|
797
|
+
session_options: sessionOptions,
|
|
798
|
+
local_files_only: true
|
|
799
|
+
}).catch((error) => {
|
|
800
|
+
forgetDownload(this.cacheDir, spec);
|
|
801
|
+
throw error;
|
|
255
802
|
});
|
|
256
803
|
const durationMs = Math.round(performance.now() - t0);
|
|
257
804
|
log$1$1.info("ONNX model loaded", {
|
|
@@ -335,7 +882,7 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
335
882
|
this._disposed = true;
|
|
336
883
|
sharedRefCount = Math.max(0, sharedRefCount - 1);
|
|
337
884
|
if (sharedRefCount > 0) return;
|
|
338
|
-
const pending = sharedLoading;
|
|
885
|
+
const pending = sharedLoading$1;
|
|
339
886
|
if (pending) {
|
|
340
887
|
const extractor = await pending.catch(() => void 0);
|
|
341
888
|
if (sharedRefCount > 0) {
|
|
@@ -349,7 +896,7 @@ var LocalEmbedProvider = class LocalEmbedProvider {
|
|
|
349
896
|
await sharedExtractor.dispose();
|
|
350
897
|
sharedExtractor = void 0;
|
|
351
898
|
}
|
|
352
|
-
if (sharedLoading === void 0) sharedPipelineKey = void 0;
|
|
899
|
+
if (sharedLoading$1 === void 0) sharedPipelineKey = void 0;
|
|
353
900
|
}
|
|
354
901
|
async warmup() {
|
|
355
902
|
await this.embed("warmup");
|
|
@@ -502,397 +1049,180 @@ var CachedEmbeddingProvider = class {
|
|
|
502
1049
|
}
|
|
503
1050
|
};
|
|
504
1051
|
/**
|
|
505
|
-
*
|
|
1052
|
+
* Local cross-encoder that scores how well each text answers a query, on the CPU.
|
|
506
1053
|
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* with structured log output or piped commands.
|
|
1054
|
+
* The model reads Chinese and English (and other languages). It downloads through the shared downloader and loads in
|
|
1055
|
+
* the background on first use: until it is ready, or if it cannot load (offline with nothing cached, say), `score`
|
|
1056
|
+
* answers undefined and the caller keeps its own order.
|
|
511
1057
|
*/
|
|
512
|
-
const log$
|
|
513
|
-
/** Marker file written after a successful download to distinguish complete vs partial cache. */
|
|
514
|
-
const DOWNLOAD_COMPLETE_MARKER = ".download-complete";
|
|
515
|
-
/** Lock file for cross-process download serialization (O_EXCL based). */
|
|
516
|
-
const DOWNLOAD_LOCK_FILE = ".download-lock";
|
|
517
|
-
/** Max time (ms) to wait for another process's download before giving up. */
|
|
518
|
-
const LOCK_WAIT_TIMEOUT_MS = 6e5;
|
|
519
|
-
/** Poll interval (ms) when waiting for a competing download to finish. */
|
|
520
|
-
const LOCK_POLL_INTERVAL_MS = 2e3;
|
|
521
|
-
/** Stale lock threshold (ms) — locks older than this are assumed abandoned. */
|
|
522
|
-
const LOCK_STALE_MS = 9e5;
|
|
523
|
-
/** Lock heartbeat interval (ms) while owning the download lock. */
|
|
524
|
-
const LOCK_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
525
|
-
function parseDownloadMarker(raw) {
|
|
526
|
-
try {
|
|
527
|
-
const parsed = JSON.parse(raw);
|
|
528
|
-
const model = parsed.model;
|
|
529
|
-
const revision = parsed.revision;
|
|
530
|
-
const dtype = parsed.dtype;
|
|
531
|
-
const completedAt = parsed.completedAt;
|
|
532
|
-
if (typeof model !== "string" || typeof revision !== "string" || typeof dtype !== "string" || typeof completedAt !== "string") return null;
|
|
533
|
-
return {
|
|
534
|
-
model,
|
|
535
|
-
revision,
|
|
536
|
-
dtype,
|
|
537
|
-
completedAt
|
|
538
|
-
};
|
|
539
|
-
} catch {
|
|
540
|
-
return null;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
const DTYPE_SIZES = {
|
|
544
|
-
q4: "~400MB",
|
|
545
|
-
q8: "~639MB",
|
|
546
|
-
fp16: "~1.2GB",
|
|
547
|
-
fp32: "~2.4GB"
|
|
548
|
-
};
|
|
549
|
-
function getSystemRamGB() {
|
|
550
|
-
return Math.round(totalmem() / 1073741824);
|
|
551
|
-
}
|
|
1058
|
+
const log$4 = createLogger("embedder:local-reranker");
|
|
552
1059
|
/**
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* When `expectedRevision` and/or `expectedDtype` are provided, the marker
|
|
556
|
-
* metadata is validated against them — a mismatch (e.g. after model upgrade)
|
|
557
|
-
* returns false so the caller re-downloads. Without these params the check
|
|
558
|
-
* is structural only (marker + .onnx files), which is appropriate for
|
|
559
|
-
* lock-wait polling where dtype/revision aren't available.
|
|
1060
|
+
* Horizon-Labs/multilingual-reranker-small (Apache-2.0) in an 8-bit ONNX file Sno published; pinned revision and
|
|
1061
|
+
* file hashes, the same supply-chain rule as the embedding model.
|
|
560
1062
|
*/
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
if (expectedDtype !== void 0 && marker.dtype !== expectedDtype) {
|
|
592
|
-
log$2.info("cached model dtype mismatch, re-download needed", {
|
|
593
|
-
cached: String(marker.dtype),
|
|
594
|
-
expected: expectedDtype
|
|
595
|
-
}, {
|
|
596
|
-
event_name: "embedder.model.download.ismodelcached",
|
|
597
|
-
file: "packages/embedder/src/model-download.ts",
|
|
598
|
-
function: "isModelCached",
|
|
599
|
-
site_id: "embedder.model.download.ismodelcached.3"
|
|
600
|
-
});
|
|
601
|
-
return false;
|
|
602
|
-
}
|
|
1063
|
+
const LOCAL_RERANK_MODEL = {
|
|
1064
|
+
id: "sno-lab/multilingual-reranker-small-int8",
|
|
1065
|
+
revision: "ce76796ae42628f54f501271aeac16df8ecc8a6f",
|
|
1066
|
+
files: [
|
|
1067
|
+
{
|
|
1068
|
+
path: "config.json",
|
|
1069
|
+
size: 1995,
|
|
1070
|
+
sha256: "e9fd043628cd796a364020164a7a6ca78d29b0ec5cf135464489f54110f4b1da"
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
path: "special_tokens_map.json",
|
|
1074
|
+
size: 636,
|
|
1075
|
+
sha256: "baec30ea10906f16adb8c18af7a34023002c1746542612b8b41c9f09e1351351"
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
path: "tokenizer.json",
|
|
1079
|
+
size: 34363439,
|
|
1080
|
+
sha256: "7925f638b5444897efe3b4608eed56c1652f0ed39c2662c8302de1bc264c6088"
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
path: "tokenizer_config.json",
|
|
1084
|
+
size: 46440,
|
|
1085
|
+
sha256: "ea349495764b24570b4560fe2e4a557e5c0a47e6bce782c2409b1d6445cf8633"
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
path: "onnx/model_quantized.onnx",
|
|
1089
|
+
size: 171363319,
|
|
1090
|
+
sha256: "d2b03e74b3ea60ca486a118b4e2ab6bc96b173c64224a689d653e135753d89ad"
|
|
603
1091
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1092
|
+
]
|
|
1093
|
+
};
|
|
1094
|
+
const SCORE_BATCH = 5;
|
|
1095
|
+
const LOAD_RETRY_MS = 6e5;
|
|
1096
|
+
let shared;
|
|
1097
|
+
let sharedLoading = false;
|
|
1098
|
+
let sharedUsers = 0;
|
|
1099
|
+
let sharedFailedAt;
|
|
1100
|
+
let releases = 0;
|
|
1101
|
+
const scoring = /* @__PURE__ */ new Set();
|
|
1102
|
+
var LocalReranker = class {
|
|
1103
|
+
options;
|
|
1104
|
+
disposed = false;
|
|
1105
|
+
constructor(options) {
|
|
1106
|
+
this.options = options;
|
|
1107
|
+
sharedUsers += 1;
|
|
616
1108
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Raw relevance logits, one per text (higher is more relevant), or undefined while the model is not loaded.
|
|
1111
|
+
* The first call starts the load and does not wait for it.
|
|
1112
|
+
*/
|
|
1113
|
+
async score(query, texts, signal) {
|
|
1114
|
+
if (texts.length === 0) return [];
|
|
1115
|
+
const loaded = shared;
|
|
1116
|
+
if (!loaded) {
|
|
1117
|
+
if (sharedFailedAt !== void 0 && Date.now() - sharedFailedAt >= LOAD_RETRY_MS) {
|
|
1118
|
+
sharedFailedAt = void 0;
|
|
1119
|
+
sharedLoading = false;
|
|
1120
|
+
}
|
|
1121
|
+
if (!sharedLoading) {
|
|
1122
|
+
sharedLoading = true;
|
|
1123
|
+
this.load();
|
|
1124
|
+
}
|
|
1125
|
+
return;
|
|
629
1126
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
dirPath,
|
|
633
|
-
error
|
|
634
|
-
}, {
|
|
635
|
-
event_name: "embedder.model.download.getdirsizemb",
|
|
636
|
-
file: "packages/embedder/src/model-download.ts",
|
|
637
|
-
function: "getDirSizeMB",
|
|
638
|
-
site_id: "embedder.model.download.getdirsizemb.5"
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
return Math.round(total / 1048576);
|
|
642
|
-
}
|
|
643
|
-
function getDtypeDisplaySize(dtype) {
|
|
644
|
-
return DTYPE_SIZES[dtype] ?? "unknown size";
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Acquire an exclusive download lock using O_EXCL (atomic create-if-not-exists).
|
|
648
|
-
* Returns true if lock acquired, false if another process holds it.
|
|
649
|
-
*/
|
|
650
|
-
function tryAcquireLock(cacheDir) {
|
|
651
|
-
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
652
|
-
try {
|
|
653
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
654
|
-
const fd = openSync(lockPath, "wx");
|
|
655
|
-
writeFileSync(fd, `${process.pid}\n${Date.now()}\n`);
|
|
656
|
-
closeSync(fd);
|
|
657
|
-
return true;
|
|
658
|
-
} catch (error) {
|
|
659
|
-
if (error instanceof Error && "code" in error && error.code === "EEXIST") return false;
|
|
660
|
-
throw error;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
function releaseLock(cacheDir) {
|
|
664
|
-
try {
|
|
665
|
-
unlinkSync(join(cacheDir, DOWNLOAD_LOCK_FILE));
|
|
666
|
-
} catch {}
|
|
667
|
-
}
|
|
668
|
-
function heartbeatLock(cacheDir) {
|
|
669
|
-
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
670
|
-
try {
|
|
671
|
-
const now = /* @__PURE__ */ new Date();
|
|
672
|
-
utimesSync(lockPath, now, now);
|
|
673
|
-
} catch {}
|
|
674
|
-
}
|
|
675
|
-
function isLockStale(cacheDir) {
|
|
676
|
-
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
677
|
-
try {
|
|
678
|
-
const stat = statSync(lockPath);
|
|
679
|
-
if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) return true;
|
|
680
|
-
const pid = Number.parseInt(readFileSync(lockPath, "utf8").split("\n")[0] ?? "", 10);
|
|
681
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
1127
|
+
const run = this.run(loaded, query, texts, signal);
|
|
1128
|
+
scoring.add(run);
|
|
682
1129
|
try {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
687
|
-
}
|
|
688
|
-
} catch {
|
|
689
|
-
return true;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
function sleep(ms) {
|
|
693
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Wait for an existing download lock to be released (another process finishing).
|
|
697
|
-
* If the lock is stale (abandoned), break it and return so the caller can retry.
|
|
698
|
-
*
|
|
699
|
-
* `expectedRevision` / `expectedDtype` are forwarded to `isModelCached` so a
|
|
700
|
-
* concurrent revision/dtype upgrade is not satisfied by a stale matching-shape
|
|
701
|
-
* cache from the previous version. Without them, two processes upgrading the
|
|
702
|
-
* same cacheDir can both return "finished" on the old artifacts.
|
|
703
|
-
*/
|
|
704
|
-
async function waitForLock(cacheDir, expectedRevision, expectedDtype) {
|
|
705
|
-
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
706
|
-
const deadline = Date.now() + LOCK_WAIT_TIMEOUT_MS;
|
|
707
|
-
log$2.info("another process is downloading the model, waiting...", { cacheDir }, {
|
|
708
|
-
event_name: "embedder.model.download.waitforlock",
|
|
709
|
-
file: "packages/embedder/src/model-download.ts",
|
|
710
|
-
function: "waitForLock",
|
|
711
|
-
site_id: "embedder.model.download.waitforlock.6"
|
|
712
|
-
});
|
|
713
|
-
process.stderr.write(" Waiting for another download to finish...\n");
|
|
714
|
-
while (Date.now() < deadline) {
|
|
715
|
-
if (!existsSync(lockPath)) return;
|
|
716
|
-
if (isModelCached(cacheDir, expectedRevision, expectedDtype)) return;
|
|
717
|
-
if (isLockStale(cacheDir)) {
|
|
718
|
-
log$2.warn("breaking stale download lock", { cacheDir }, {
|
|
719
|
-
event_name: "embedder.model.download.waitforlock",
|
|
720
|
-
file: "packages/embedder/src/model-download.ts",
|
|
721
|
-
function: "waitForLock",
|
|
722
|
-
site_id: "embedder.model.download.waitforlock.7"
|
|
723
|
-
});
|
|
724
|
-
releaseLock(cacheDir);
|
|
725
|
-
return;
|
|
1130
|
+
return await run;
|
|
1131
|
+
} finally {
|
|
1132
|
+
scoring.delete(run);
|
|
726
1133
|
}
|
|
727
|
-
await sleep(LOCK_POLL_INTERVAL_MS);
|
|
728
1134
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
const ramGB = getSystemRamGB();
|
|
742
|
-
const dtype = opts?.dtype ?? "q8";
|
|
743
|
-
if (isModelCached(cacheDir, "a18fdffe7480e6ea5643acb7757142b33838817a", dtype)) {
|
|
744
|
-
const sizeMB = getDirSizeMB(cacheDir);
|
|
745
|
-
log$2.info("local embedding model already cached", {
|
|
746
|
-
sizeMB,
|
|
747
|
-
cacheDir
|
|
748
|
-
}, {
|
|
749
|
-
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
750
|
-
file: "packages/embedder/src/model-download.ts",
|
|
751
|
-
function: "ensureModelDownloaded",
|
|
752
|
-
site_id: "embedder.model.download.ensuremodeldownloaded.8"
|
|
753
|
-
});
|
|
754
|
-
return;
|
|
1135
|
+
async dispose() {
|
|
1136
|
+
if (this.disposed) return;
|
|
1137
|
+
this.disposed = true;
|
|
1138
|
+
sharedUsers = Math.max(0, sharedUsers - 1);
|
|
1139
|
+
if (sharedUsers > 0) return;
|
|
1140
|
+
const loaded = shared;
|
|
1141
|
+
releases += 1;
|
|
1142
|
+
shared = void 0;
|
|
1143
|
+
sharedLoading = false;
|
|
1144
|
+
sharedFailedAt = void 0;
|
|
1145
|
+
await Promise.allSettled(scoring);
|
|
1146
|
+
await loaded?.dispose();
|
|
755
1147
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
file: "packages/embedder/src/model-download.ts",
|
|
768
|
-
function: "ensureModelDownloaded",
|
|
769
|
-
site_id: "embedder.model.download.ensuremodeldownloaded.9"
|
|
1148
|
+
async run(loaded, query, texts, signal) {
|
|
1149
|
+
const order = texts.map((_, index) => index).sort((a, b) => (texts[a]?.length ?? 0) - (texts[b]?.length ?? 0));
|
|
1150
|
+
const scores = new Array(texts.length).fill(0);
|
|
1151
|
+
for (let start = 0; start < order.length; start += SCORE_BATCH) {
|
|
1152
|
+
if (signal?.aborted) return void 0;
|
|
1153
|
+
const run = order.slice(start, start + SCORE_BATCH);
|
|
1154
|
+
const inputs = loaded.tokenizer(new Array(run.length).fill(query), {
|
|
1155
|
+
text_pair: run.map((index) => texts[index] ?? ""),
|
|
1156
|
+
padding: true,
|
|
1157
|
+
truncation: true,
|
|
1158
|
+
max_length: 512
|
|
770
1159
|
});
|
|
771
|
-
|
|
1160
|
+
const { logits } = await loaded.model(inputs);
|
|
1161
|
+
for (const [i, index] of run.entries()) scores[index] = Number(logits.data[i] ?? 0);
|
|
772
1162
|
}
|
|
773
|
-
|
|
1163
|
+
return scores;
|
|
774
1164
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
const lockHeartbeat = setInterval(() => heartbeatLock(cacheDir), LOCK_HEARTBEAT_INTERVAL_MS);
|
|
779
|
-
try {
|
|
780
|
-
heartbeatLock(cacheDir);
|
|
781
|
-
if (lockWasPresent) {
|
|
782
|
-
rmSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), { force: true });
|
|
783
|
-
rmSync(modelDir, {
|
|
784
|
-
recursive: true,
|
|
785
|
-
force: true
|
|
786
|
-
});
|
|
787
|
-
} else if (!existsSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER)) && existsSync(modelDir) && existsSync(join(modelDir, "onnx")) && readdirSync(join(modelDir, "onnx")).some((e) => e.endsWith(".onnx"))) {
|
|
788
|
-
writeFileSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), JSON.stringify({
|
|
789
|
-
model: LOCAL_EMBEDDING_MODEL,
|
|
790
|
-
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
791
|
-
dtype,
|
|
792
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
793
|
-
}));
|
|
794
|
-
completed = true;
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
startedEmpty = !existsSync(modelDir);
|
|
798
|
-
const banner = [
|
|
799
|
-
"",
|
|
800
|
-
"Downloading local embedding model (first run only)",
|
|
801
|
-
`${"─".repeat(52)}`,
|
|
802
|
-
` Model: ${LOCAL_EMBEDDING_MODEL}`,
|
|
803
|
-
` Revision: ${LOCAL_EMBEDDING_MODEL_REVISION.slice(0, 12)}`,
|
|
804
|
-
` Dtype: ${dtype} (${getDtypeDisplaySize(dtype)})`,
|
|
805
|
-
` RAM: ${ramGB}GB`,
|
|
806
|
-
` Cache dir: ${cacheDir}`,
|
|
807
|
-
"",
|
|
808
|
-
" This may take a few minutes on the first run...",
|
|
809
|
-
""
|
|
810
|
-
];
|
|
811
|
-
process.stderr.write(`${banner.join("\n")}\n`);
|
|
812
|
-
log$2.info("starting model download", {
|
|
813
|
-
model: LOCAL_EMBEDDING_MODEL,
|
|
814
|
-
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
815
|
-
dtype,
|
|
816
|
-
cacheDir
|
|
817
|
-
}, {
|
|
818
|
-
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
819
|
-
file: "packages/embedder/src/model-download.ts",
|
|
820
|
-
function: "ensureModelDownloaded",
|
|
821
|
-
site_id: "embedder.model.download.ensuremodeldownloaded.10"
|
|
822
|
-
});
|
|
823
|
-
const { env, pipeline } = await import("@huggingface/transformers");
|
|
824
|
-
const prevCacheDir = env.cacheDir;
|
|
825
|
-
const prevAllowRemote = env.allowRemoteModels;
|
|
826
|
-
const prevLocalModelPath = env.localModelPath;
|
|
827
|
-
const prevRemoteHost = env.remoteHost;
|
|
828
|
-
const prevRemotePathTemplate = env.remotePathTemplate;
|
|
829
|
-
env.cacheDir = cacheDir;
|
|
830
|
-
env.allowRemoteModels = true;
|
|
831
|
-
env.localModelPath = cacheDir;
|
|
832
|
-
env.remotePathTemplate = `{model}/resolve/${LOCAL_EMBEDDING_MODEL_REVISION}/`;
|
|
833
|
-
if (opts?.mirror) env.remoteHost = opts.mirror.endsWith("/") ? opts.mirror : `${opts.mirror}/`;
|
|
834
|
-
let lastProgress = 0;
|
|
835
|
-
const startTime = performance.now();
|
|
1165
|
+
async load() {
|
|
1166
|
+
const release = releases;
|
|
1167
|
+
const started = performance.now();
|
|
836
1168
|
try {
|
|
837
|
-
await
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1169
|
+
const { AutoModelForSequenceClassification, AutoTokenizer } = await import("@huggingface/transformers");
|
|
1170
|
+
const dir = await downloadModel(LOCAL_RERANK_MODEL, this.options);
|
|
1171
|
+
const open = async () => {
|
|
1172
|
+
return {
|
|
1173
|
+
tokenizer: await AutoTokenizer.from_pretrained(dir, { local_files_only: true }),
|
|
1174
|
+
model: await AutoModelForSequenceClassification.from_pretrained(dir, {
|
|
1175
|
+
dtype: "q8",
|
|
1176
|
+
device: "cpu",
|
|
1177
|
+
local_files_only: true,
|
|
1178
|
+
session_options: {
|
|
1179
|
+
...LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT,
|
|
1180
|
+
interOpNumThreads: 1,
|
|
1181
|
+
...this.options.intraOpNumThreads ? { intraOpNumThreads: this.options.intraOpNumThreads } : {}
|
|
846
1182
|
}
|
|
847
|
-
}
|
|
1183
|
+
})
|
|
1184
|
+
};
|
|
1185
|
+
};
|
|
1186
|
+
const { tokenizer, model } = await open().catch((error) => {
|
|
1187
|
+
forgetDownload(this.options.cacheDir, LOCAL_RERANK_MODEL);
|
|
1188
|
+
throw error;
|
|
1189
|
+
});
|
|
1190
|
+
if (release !== releases) {
|
|
1191
|
+
await model.dispose();
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
shared = {
|
|
1195
|
+
tokenizer: (texts, options) => tokenizer(texts, options),
|
|
1196
|
+
model: (inputs) => model(inputs),
|
|
1197
|
+
dispose: async () => {
|
|
1198
|
+
await model.dispose();
|
|
848
1199
|
}
|
|
1200
|
+
};
|
|
1201
|
+
log$4.info("local reranker loaded", {
|
|
1202
|
+
duration_ms: Math.round(performance.now() - started),
|
|
1203
|
+
model: LOCAL_RERANK_MODEL.id
|
|
1204
|
+
}, {
|
|
1205
|
+
event_name: "embedder.local_reranker.loaded",
|
|
1206
|
+
file: "packages/embedder/src/local-reranker.ts",
|
|
1207
|
+
function: "load",
|
|
1208
|
+
site_id: "embedder.local_reranker.loaded"
|
|
849
1209
|
});
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1210
|
+
return shared;
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
log$4.warn("local reranker unavailable; recall keeps its cosine order", {
|
|
1213
|
+
error,
|
|
1214
|
+
offline: this.options.offline === true
|
|
1215
|
+
}, {
|
|
1216
|
+
event_name: "embedder.local_reranker.unavailable",
|
|
1217
|
+
file: "packages/embedder/src/local-reranker.ts",
|
|
1218
|
+
function: "load",
|
|
1219
|
+
site_id: "embedder.local_reranker.unavailable"
|
|
1220
|
+
});
|
|
1221
|
+
sharedFailedAt = Date.now();
|
|
1222
|
+
return;
|
|
856
1223
|
}
|
|
857
|
-
writeFileSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), JSON.stringify({
|
|
858
|
-
model: LOCAL_EMBEDDING_MODEL,
|
|
859
|
-
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
860
|
-
dtype,
|
|
861
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
862
|
-
}));
|
|
863
|
-
completed = true;
|
|
864
|
-
const elapsedSec = Math.round((performance.now() - startTime) / 1e3);
|
|
865
|
-
const finalSizeMB = getDirSizeMB(cacheDir);
|
|
866
|
-
const summary = [
|
|
867
|
-
"",
|
|
868
|
-
" Download complete!",
|
|
869
|
-
` Size: ${finalSizeMB}MB | Time: ${elapsedSec}s`,
|
|
870
|
-
` Location: ${cacheDir}`,
|
|
871
|
-
""
|
|
872
|
-
];
|
|
873
|
-
process.stderr.write(`${summary.join("\n")}\n`);
|
|
874
|
-
log$2.info("model download complete", {
|
|
875
|
-
model: LOCAL_EMBEDDING_MODEL,
|
|
876
|
-
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
877
|
-
dtype,
|
|
878
|
-
sizeMB: finalSizeMB,
|
|
879
|
-
elapsedSec,
|
|
880
|
-
cacheDir
|
|
881
|
-
}, {
|
|
882
|
-
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
883
|
-
file: "packages/embedder/src/model-download.ts",
|
|
884
|
-
function: "ensureModelDownloaded",
|
|
885
|
-
site_id: "embedder.model.download.ensuremodeldownloaded.11"
|
|
886
|
-
});
|
|
887
|
-
} finally {
|
|
888
|
-
clearInterval(lockHeartbeat);
|
|
889
|
-
if (!completed && startedEmpty) rmSync(modelDir, {
|
|
890
|
-
recursive: true,
|
|
891
|
-
force: true
|
|
892
|
-
});
|
|
893
|
-
releaseLock(cacheDir);
|
|
894
1224
|
}
|
|
895
|
-
}
|
|
1225
|
+
};
|
|
896
1226
|
//#endregion
|
|
897
1227
|
//#region config/index.ts
|
|
898
1228
|
/**
|
|
@@ -906,7 +1236,7 @@ const MEMORY_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
|
906
1236
|
const DEFAULT_IMPORTANCE = .7;
|
|
907
1237
|
/** Default scope for memories without explicit scope */
|
|
908
1238
|
const DEFAULT_SCOPE = "global";
|
|
909
|
-
/** Default embedding vector dimension (
|
|
1239
|
+
/** Default embedding vector dimension (the local PPLX embedder outputs 1024-d) */
|
|
910
1240
|
const VECTOR_DIMENSION_DEFAULT = 1024;
|
|
911
1241
|
/** Production sno.ai host origin used by @snoai/observability. */
|
|
912
1242
|
const SNO_OBSERVE_DEFAULT_BASE_URL = "https://www.sno.ai";
|
|
@@ -1015,12 +1345,19 @@ const MMR_LAMBDA = .7;
|
|
|
1015
1345
|
const RERANK_BLEND_VECTOR = .3;
|
|
1016
1346
|
/** Weight for cross-encoder score in rerank blend */
|
|
1017
1347
|
const RERANK_BLEND_CROSS = .7;
|
|
1018
|
-
/**
|
|
1019
|
-
|
|
1348
|
+
/**
|
|
1349
|
+
* Fusion-score weight in local cosine-blend rerank.
|
|
1350
|
+
*
|
|
1351
|
+
* The fused score keeps a keyword-only hit at its full keyword score, so a memory sharing one word with the query
|
|
1352
|
+
* outranks the one that answers it; at 0.7 the local ranking passed that order through almost unchanged. Measured
|
|
1353
|
+
* 2026-10-15 on LoCoMo (1,540 questions, the product store and retriever, weights fitted on half the conversations
|
|
1354
|
+
* and checked on the other half): the right memory in the first five went from 27% to 68% in English and from 9% to
|
|
1355
|
+
* 58% in a Chinese translation at 0.1 / 0.9. A candidate with no stored vector keeps its fused score and would now
|
|
1356
|
+
* rank high; none of 27,064 candidates in 299 real local rankings lacked one.
|
|
1357
|
+
*/
|
|
1358
|
+
const LIGHTWEIGHT_FUSION_WEIGHT = .1;
|
|
1020
1359
|
/** Cosine-similarity weight in local cosine-blend rerank */
|
|
1021
|
-
const LIGHTWEIGHT_COSINE_WEIGHT = .
|
|
1022
|
-
/** Default rerank model identifier */
|
|
1023
|
-
const DEFAULT_RERANK_MODEL = "rerank-2";
|
|
1360
|
+
const LIGHTWEIGHT_COSINE_WEIGHT = .9;
|
|
1024
1361
|
/** Default timeout for rerank API calls in milliseconds */
|
|
1025
1362
|
const DEFAULT_RERANK_TIMEOUT_MS = 15e3;
|
|
1026
1363
|
/**
|
|
@@ -1477,7 +1814,6 @@ const retrievalConfigSchema = z.object({
|
|
|
1477
1814
|
]).default("lightweight"),
|
|
1478
1815
|
candidatePoolSize: z.number().int().min(10).max(2e3).default(64),
|
|
1479
1816
|
rerankApiKey: z.string().optional(),
|
|
1480
|
-
rerankModel: z.string().default(DEFAULT_RERANK_MODEL),
|
|
1481
1817
|
rerankTimeoutMs: z.number().int().min(1e3).max(12e4).default(DEFAULT_RERANK_TIMEOUT_MS),
|
|
1482
1818
|
recencyHalfLifeDays: z.number().min(0).max(365).default(14),
|
|
1483
1819
|
recencyWeight: z.number().min(0).max(RECENCY_WEIGHT_MAX).default(RECENCY_WEIGHT),
|
|
@@ -1504,20 +1840,9 @@ const retrievalConfigSchema = z.object({
|
|
|
1504
1840
|
timeDecayFloor: z.number().min(0).max(1).optional(),
|
|
1505
1841
|
/** Relevance vs diversity tradeoff (default: 0.7) */
|
|
1506
1842
|
mmrLambda: z.number().min(0).max(1).optional(),
|
|
1507
|
-
/**
|
|
1843
|
+
/** Address of Sno's reranker (the Sno GPU address plus `/rerank`). */
|
|
1508
1844
|
rerankEndpoint: z.string().optional(),
|
|
1509
|
-
/**
|
|
1510
|
-
rerankProvider: z.enum([
|
|
1511
|
-
"jina",
|
|
1512
|
-
"siliconflow",
|
|
1513
|
-
"pinecone",
|
|
1514
|
-
"voyage",
|
|
1515
|
-
"dashscope",
|
|
1516
|
-
"tei",
|
|
1517
|
-
"sno-gpu",
|
|
1518
|
-
"custom"
|
|
1519
|
-
]).optional(),
|
|
1520
|
-
/** Hard cap on candidates sent per rerank API request (some deployments reject over-limit batches) */
|
|
1845
|
+
/** Cap on candidates sent to Sno's reranker in total */
|
|
1521
1846
|
rerankMaxCandidates: z.number().int().min(1).max(2e3).optional(),
|
|
1522
1847
|
/** Scaling factor for access-based reinforcement (0 = disabled, default: 0.5) */
|
|
1523
1848
|
reinforcementFactor: z.number().min(0).max(5).default(.5),
|
|
@@ -1525,11 +1850,6 @@ const retrievalConfigSchema = z.object({
|
|
|
1525
1850
|
maxHalfLifeMultiplier: z.number().min(1).max(10).default(3)
|
|
1526
1851
|
}).prefault({}).transform((ret, ctx) => {
|
|
1527
1852
|
if (ret.rerank === "none") return ret;
|
|
1528
|
-
if ((ret.rerank === "cross-encoder" || ret.rerankEndpoint !== void 0) && ret.rerankProvider === void 0) ctx.addIssue({
|
|
1529
|
-
code: z.ZodIssueCode.custom,
|
|
1530
|
-
path: ["rerankProvider"],
|
|
1531
|
-
message: "retrieval.rerankProvider is required when retrieval.rerank is \"cross-encoder\" or retrieval.rerankEndpoint is set: the provider decides the request shape and the per-request batch limit"
|
|
1532
|
-
});
|
|
1533
1853
|
if (ret.rerankEndpoint !== void 0) try {
|
|
1534
1854
|
new URL(ret.rerankEndpoint);
|
|
1535
1855
|
} catch {
|
|
@@ -2296,19 +2616,6 @@ const settingsSchema = z.strictObject({
|
|
|
2296
2616
|
"cross-encoder",
|
|
2297
2617
|
"none"
|
|
2298
2618
|
]),
|
|
2299
|
-
provider: z.enum([
|
|
2300
|
-
"jina",
|
|
2301
|
-
"siliconflow",
|
|
2302
|
-
"pinecone",
|
|
2303
|
-
"voyage",
|
|
2304
|
-
"dashscope",
|
|
2305
|
-
"tei",
|
|
2306
|
-
"sno-gpu",
|
|
2307
|
-
"custom"
|
|
2308
|
-
]),
|
|
2309
|
-
endpoint: z.string(),
|
|
2310
|
-
model: z.string(),
|
|
2311
|
-
apiKey: z.string(),
|
|
2312
2619
|
timeoutMs: z.number().int().min(1e3).max(12e4),
|
|
2313
2620
|
maxCandidates: z.number().int().min(1).max(2e3)
|
|
2314
2621
|
}),
|
|
@@ -2401,10 +2708,6 @@ function defaultSettings() {
|
|
|
2401
2708
|
},
|
|
2402
2709
|
rerank: {
|
|
2403
2710
|
mode: "cross-encoder",
|
|
2404
|
-
provider: "sno-gpu",
|
|
2405
|
-
endpoint: "",
|
|
2406
|
-
model: engine.retrieval.rerankModel,
|
|
2407
|
-
apiKey: "",
|
|
2408
2711
|
timeoutMs: engine.retrieval.rerankTimeoutMs,
|
|
2409
2712
|
maxCandidates: 64
|
|
2410
2713
|
},
|
|
@@ -2474,18 +2777,11 @@ function defaultSettings() {
|
|
|
2474
2777
|
};
|
|
2475
2778
|
}
|
|
2476
2779
|
/**
|
|
2477
|
-
*
|
|
2478
|
-
*
|
|
2479
|
-
* any other mode, or no key yet, ranks locally, which is the configured strategy, not an error.
|
|
2780
|
+
* Recall ranking is local, or Sno's reranker: its address is the Sno GPU address and its key is the Sno GPU key. Only
|
|
2781
|
+
* REM Enhanced calls Sno GPU; any other mode, or no key yet, ranks locally, which is the configured strategy, not an error.
|
|
2480
2782
|
*/
|
|
2481
2783
|
function rerankSettings(settings) {
|
|
2482
2784
|
const { rerank, snoGpu } = settings;
|
|
2483
|
-
if (rerank.provider !== "sno-gpu") return {
|
|
2484
|
-
rerank: rerank.mode,
|
|
2485
|
-
provider: rerank.provider,
|
|
2486
|
-
endpoint: rerank.endpoint || void 0,
|
|
2487
|
-
apiKey: rerank.apiKey
|
|
2488
|
-
};
|
|
2489
2785
|
let endpoint;
|
|
2490
2786
|
try {
|
|
2491
2787
|
endpoint = new URL("rerank", snoGpu.baseUrl.endsWith("/") ? snoGpu.baseUrl : `${snoGpu.baseUrl}/`).toString();
|
|
@@ -2500,7 +2796,6 @@ function rerankSettings(settings) {
|
|
|
2500
2796
|
const local = rerank.mode !== "cross-encoder" || settings.mode !== "rem-enhanced" || !snoGpu.apiKey || endpoint === void 0;
|
|
2501
2797
|
return {
|
|
2502
2798
|
rerank: local && rerank.mode === "cross-encoder" ? "lightweight" : rerank.mode,
|
|
2503
|
-
provider: local ? void 0 : rerank.provider,
|
|
2504
2799
|
endpoint: local ? void 0 : endpoint,
|
|
2505
2800
|
apiKey: local ? "" : snoGpu.apiKey
|
|
2506
2801
|
};
|
|
@@ -2524,11 +2819,9 @@ function settingsToPluginConfig(settings) {
|
|
|
2524
2819
|
},
|
|
2525
2820
|
retrieval: {
|
|
2526
2821
|
rerank: sno.rerank,
|
|
2527
|
-
rerankProvider: sno.provider,
|
|
2528
2822
|
recallTopK: settings.recall.prompt.limit,
|
|
2529
2823
|
minScore: settings.recall.prompt.minScore,
|
|
2530
2824
|
rerankEndpoint: sno.endpoint,
|
|
2531
|
-
rerankModel: settings.rerank.model,
|
|
2532
2825
|
rerankApiKey: sno.apiKey,
|
|
2533
2826
|
rerankTimeoutMs: settings.rerank.timeoutMs,
|
|
2534
2827
|
rerankMaxCandidates: settings.rerank.maxCandidates
|
|
@@ -2712,4 +3005,4 @@ function readMaintenanceOverrides() {
|
|
|
2712
3005
|
};
|
|
2713
3006
|
}
|
|
2714
3007
|
//#endregion
|
|
2715
|
-
export { FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION as $,
|
|
3008
|
+
export { FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION as $, MAX_CHUNK_FETCH_LIMIT as $n, FIXED_PROTOCOL_VALUE_79 as $t, modelCallDestination as A, DEFAULT_REFLECTION_ERROR_SCAN_MAX_CHARS as An, MEMORY_CANCEL_ON_DISCONNECT_HEADER as Ar, FIXED_MEMORY_PROFILE_RECOVERY_ENTRIES as At, FIXED_EXTERNAL_VALUE_49 as B, FTS_MERGE_INTERVAL_MS as Bn, FIXED_PROTOCOL_VALUE_63 as Bt, getSnoStationMemStateDir as C, CHUNK_OVERLAP_TOKENS as Cn, LOCAL_EMBEDDING_CACHE_DIR_DEFAULT as Cr, FIXED_MEMORY_CHUNKS_MEM_FACET_IDX as Ct, settingsToPluginConfig as D, DEFAULT_IMPORTANCE as Dn, LocalReranker as Dr, FIXED_MEMORY_MEMORIES_FACT_ID_INSERT_GUARD as Dt, defaultSettings as E, DEFAULT_BM25_WEIGHT as En, LocalEmbedProvider as Er, FIXED_MEMORY_MEMORIES as Et, observeConfigSchema as F, EMBEDDER_DIM as Fn, MEMORY_SKIN_HEADER as Fr, FIXED_MEMORY_SNO_AI_EXTRACT as Ft, FIXED_EXTERNAL_VALUE_55 as G, INTEGRITY_CHECK_INTERVAL_MS as Gn, FIXED_PROTOCOL_VALUE_68 as Gt, FIXED_EXTERNAL_VALUE_52 as H, HEAD_EXTRACT_OVERLAP_DROP_RATIO as Hn, FIXED_PROTOCOL_VALUE_65 as Ht, extractionConfigSchema as I, EMBEDDER_MODEL_DEFAULT as In, REM_SIDECAR_TOKEN_HEADER as Ir, FIXED_MEMORY_SNO_EXTRACT_CHAT as It, FIXED_EXTERNAL_VALUE_58 as J, LIGHTWEIGHT_FUSION_WEIGHT as Jn, FIXED_PROTOCOL_VALUE_74 as Jt, FIXED_EXTERNAL_VALUE_56 as K, INTRA_MEMORY_COSINE_THRESHOLD as Kn, FIXED_PROTOCOL_VALUE_69 as Kt, memoryReflectionConfigSchema as L, EMBEDDER_PROVIDER_DEFAULT as Ln, memoryMethod as Lr, FIXED_MEMORY_SNO_EXTRACT_PROFILE as Lt, retrievalConfigSchema as M, DEFAULT_RERANK_TIMEOUT_MS as Mn, MEMORY_ERROR_STATUS as Mr, FIXED_MEMORY_PROVIDER_PROJECT_AGENTS as Mt, isObserveAgentId as N, DEFAULT_SCOPE as Nn, MEMORY_RECONNECT_INTERVAL_MS as Nr, FIXED_MEMORY_PROVIDER_PROJECT_MAPPINGS as Nt, MODEL_CALLS as O, DEFAULT_RECALL_LIFECYCLE as On, ensureModelDownloaded as Or, FIXED_MEMORY_MEMORIES_FACT_ID_UPDATE_GUARD as Ot, observeAgentId as P, DEFAULT_VECTOR_WEIGHT as Pn, MEMORY_ROUTES as Pr, FIXED_MEMORY_ROW_PATH_PATTERN as Pt, FIXED_IDX_MEMORY as Q, MAX_CANDIDATE_POOL_SIZE as Qn, FIXED_PROTOCOL_VALUE_78 as Qt, selfImprovementConfigSchema as R, EVAL_QA_TRACES_FILENAME as Rn, FIXED_MEMORY_TASK_LIFECYCLE_COMMANDS as Rt, getSidecarSocketPath as S, CHUNK_MIN_TOKENS as Sn, CachedEmbeddingProvider as Sr, FIXED_MEMORY_CHUNKS_MEMORY_ID_IDX as St, readSettings as T, CHUNK_TOKENIZER_MODE as Tn, LOCAL_EMBEDDING_MODEL_REVISION as Tr, FIXED_MEMORY_EXTRACTION_TIMESTAMPS as Tt, FIXED_EXTERNAL_VALUE_53 as U, HEAD_EXTRACT_TOKEN_BUDGET as Un, FIXED_PROTOCOL_VALUE_66 as Ut, FIXED_EXTERNAL_VALUE_51 as V, HEAD_EXTRACT_MIN_CONTENT_TOKENS as Vn, FIXED_PROTOCOL_VALUE_64 as Vt, FIXED_EXTERNAL_VALUE_54 as W, IMPORTANCE_WEIGHT_BASE as Wn, FIXED_PROTOCOL_VALUE_67 as Wt, FIXED_EXTERNAL_VALUE_8 as X, MAINTENANCE_TICK_MS as Xn, FIXED_PROTOCOL_VALUE_76 as Xt, FIXED_EXTERNAL_VALUE_62 as Y, LIGHTWEIGHT_RERANK_PENALTY as Yn, FIXED_PROTOCOL_VALUE_75 as Yt, FIXED_EXTERNAL_VALUE_9 as Z, MAX_AGGREGATION_RESULT_TOKENS as Zn, FIXED_PROTOCOL_VALUE_77 as Zt, readRemTestHoldMs as _, BACKUP_MIN_FREE_DISK_RATIO as _n, USAGE_OUTBOX_INTERVAL_MS as _r, FIXED_MEMORY_CHUNKS as _t, REM_MODEL_OUTPUT_TOKEN_CAP as a, PERSISTED_PROVIDER_SYSTEM as an, MULTI_HIT_BONUS_CAP as ar, FIXED_IDX_MEMORY_MEMORIES_LANE_PROJECT as at, getPrincipal as b, CHUNKING_VERSION as bn, experimentMmrDisabled as br, FIXED_MEMORY_CHUNKS_AU as bt, REM_SIDECAR_ORIGIN as c, SUPPORTED_LOCALES as cn, RECENCY_WEIGHT as cr, FIXED_IDX_MEMORY_MEMORIES_PROJECT_FACT_KEY_ACTIVE as ct, getRemDiscoveryPath as d, ADJACENCY_BONUS_PER_PAIR as dn, RERANK_BLEND_CROSS as dr, FIXED_MEMORY_ACTIVE_TASK_EVIDENCE as dt, FIXED_VEC_MEMORY as en, MAX_LIST_LIMIT as er, FIXED_IDX_MEMORY_ACTIVE_TASK_EVIDENCE_INSTANCE as et, getRemJobJournalPath as f, ATOMIC_CAPTURE_OUTPUT_TOKEN_BUDGET as fn, RERANK_BLEND_VECTOR as fr, FIXED_MEMORY_ACTIVE_TASK_INSTANCES as ft, readRemOperationalConfig as g, BACKUP_INTERVAL_MS as gn, USAGE_EVENT_RETENTION_INTERVAL_MS as gr, FIXED_MEMORY_ACTIVE_TASK_TRANSITIONS as gt, readMaintenanceOverrides as h, ATOMIC_EXTRACTION_MAX_INPUT_TOKENS as hn, TIME_DECAY_FLOOR as hr, FIXED_MEMORY_ACTIVE_TASK_REVISIONS as ht, REM_JOBS_PATH_PREFIX as i, PERSISTED_CONTENT_HASH_V3 as in, MMR_LAMBDA as ir, FIXED_IDX_MEMORY_MEMORIES_FACT_ID as it, SESSION_STRATEGIES as j, DEFAULT_REFLECTION_SESSION_TTL_MS as jn, MEMORY_DEFAULT_SKIN_ID as jr, FIXED_MEMORY_PROVIDER_AGENT_MAPPINGS as jt, MODEL_CALL_WORKLOAD as k, DEFAULT_RECALL_TOKEN_BUDGET as kn, MEMORY_BODY_LIMIT_BYTES as kr, FIXED_MEMORY_MIGRATION_MARKERS as kt, REM_SOURCE as l, isSupportedLocale as ln, REFLECTION_SLICE_CACHE_TTL_MS as lr, FIXED_IDX_MEMORY_MEMORIES_PROJECT_TIMESTAMP as lt, isRemTraceEnabled as m, ATOMIC_ENRICHMENT_OUTPUT_TOKEN_BUDGET as mn, SNO_OBSERVE_FLUSH_TIMEOUT_MS as mr, FIXED_MEMORY_ACTIVE_TASK_MIGRATION_MANIFESTS as mt, MEMORY_USAGE_FLUSH_INTERVAL_MS as n, PERSISTED_CANDIDATE_HASH_V1 as nn, MEMORY_SHUTDOWN_TIMEOUT_MS as nr, FIXED_IDX_MEMORY_MEMORIES_CATEGORY as nt, REM_RUN_PATH as o, embeddingConfigSchema as on, MULTI_HIT_BONUS_PER_HIT as or, FIXED_IDX_MEMORY_MEMORIES_PROJECT as ot, getRemTraceLogPath as p, ATOMIC_CAPTURE_TIMEOUT_MS as pn, RETRIEVAL_STORAGE_CHUNK_PROFILE as pr, FIXED_MEMORY_ACTIVE_TASK_MIGRATION_EVIDENCE as pt, FIXED_EXTERNAL_VALUE_57 as q, LIGHTWEIGHT_COSINE_WEIGHT as qn, FIXED_PROTOCOL_VALUE_73 as qt, REM_CORRELATION_ID_HEADER as r, PERSISTED_CONTENT_HASH_V1 as rn, MIN_RECALL_TOKEN_BUDGET as rr, FIXED_IDX_MEMORY_MEMORIES_CONTENT_HASH as rt, REM_SIDECAR_HOST as s, llmRoutingConfigSchema as sn, PLANNER_STATISTICS_INTERVAL_MS as sr, FIXED_IDX_MEMORY_MEMORIES_PROJECT_CONTENT_HASH as st, HEALTH_PATH as t, FIXED_VEC_MEMORY_CHUNKS as tn, MAX_RECALLED_TODO_TOKENS as tr, FIXED_IDX_MEMORY_ACTIVE_TASK_INSTANCES_PROJECTION as tt, getRemChassisJournalPath as u, ADJACENCY_BONUS_CAP as un, REM_TRIGGER_CHECK_INTERVAL_MS as ur, FIXED_MEMORY as ut, SettingsUnavailableError as v, BONUS_TOTAL_INVARIANT as vn, VECTOR_DIMENSION_DEFAULT as vr, FIXED_MEMORY_CHUNKS_AD as vt, getStateDir as w, CHUNK_TARGET_TOKENS as wn, LOCAL_EMBEDDING_MODEL as wr, FIXED_MEMORY_CHUNKS_MEM_IDX as wt, getSettingsPath as x, CHUNK_MAX_TOKENS as xn, recallLifecycleSchema as xr, FIXED_MEMORY_CHUNKS_FTS as xt, getDiscoveryPath as y, CACHE_TTL_MS as yn, experimentCandidatePoolOverride as yr, FIXED_MEMORY_CHUNKS_AI as yt, sessionMemoryConfigSchema as z, FORGET_QUERY_MIN_SCORE as zn, FIXED_MEMORY_UNPLACED_CANDIDATES as zt };
|