@adhd/sox-embedding-provider 0.1.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 +20 -0
- package/dist/cache.d.ts +70 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +217 -0
- package/dist/cache.js.map +1 -0
- package/dist/embedWorker.d.ts +77 -0
- package/dist/embedWorker.d.ts.map +1 -0
- package/dist/embedWorker.js +326 -0
- package/dist/embedWorker.js.map +1 -0
- package/dist/fastembed.d.ts +62 -0
- package/dist/fastembed.d.ts.map +1 -0
- package/dist/fastembed.js +259 -0
- package/dist/fastembed.js.map +1 -0
- package/dist/fastembedProcessHost.d.ts +60 -0
- package/dist/fastembedProcessHost.d.ts.map +1 -0
- package/dist/fastembedProcessHost.js +166 -0
- package/dist/fastembedProcessHost.js.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +52 -0
- package/dist/remote.d.ts +23 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +91 -0
- package/dist/remote.js.map +1 -0
- package/dist/sharedFastembedProcess.d.ts +56 -0
- package/dist/sharedFastembedProcess.d.ts.map +1 -0
- package/dist/sharedFastembedProcess.js +180 -0
- package/dist/sharedFastembedProcess.js.map +1 -0
- package/dist/sharedOnnxWorker.d.ts +135 -0
- package/dist/sharedOnnxWorker.d.ts.map +1 -0
- package/dist/sharedOnnxWorker.js +255 -0
- package/dist/sharedOnnxWorker.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide singleton client for the ONE onnxruntime-native-bearing
|
|
3
|
+
* `worker_threads.Worker` allowed to exist in this process (BL-238/BL-171).
|
|
4
|
+
*
|
|
5
|
+
* This worker (`embedWorker.ts`) hosts the MS-MARCO cross-encoder reranker
|
|
6
|
+
* (`@adhd/sox-hybrid-search`) and the DeBERTa NLI verifier
|
|
7
|
+
* (`@adhd/sox-claim-verification`) — both driven by
|
|
8
|
+
* `@huggingface/transformers`' onnxruntime-node@1.24.3. fastembed embeddings
|
|
9
|
+
* (`fastembed.ts`, onnxruntime-node@1.21.0) are DELIBERATELY routed
|
|
10
|
+
* elsewhere — see `sharedFastembedProcess.ts` / `fastembedProcessHost.ts` —
|
|
11
|
+
* for a second, independent native hazard (see below).
|
|
12
|
+
*
|
|
13
|
+
* ── Root cause #1 (proven via a from-scratch minimal repro, no test
|
|
14
|
+
* harness, no mocks — two independent worker_threads.Worker instances, each
|
|
15
|
+
* performing real ONNX inference, concurrently) ──
|
|
16
|
+
*
|
|
17
|
+
* onnxruntime-node's native N-API addon crashes the ENTIRE process (not
|
|
18
|
+
* just the offending worker) with:
|
|
19
|
+
*
|
|
20
|
+
* FATAL ERROR: HandleScope::HandleScope Entering the V8 API without
|
|
21
|
+
* proper locking in place
|
|
22
|
+
* ... Napi::FunctionReference::New(...)
|
|
23
|
+
* ... OrtValueToNapiValue(Napi::Env, Ort::Value&&)
|
|
24
|
+
* ... InferenceSessionWrap::Run(...)
|
|
25
|
+
*
|
|
26
|
+
* whenever 2+ *separate* `worker_threads.Worker` instances (i.e. 2+ separate
|
|
27
|
+
* V8 isolates) each hold an active onnxruntime-node `InferenceSession` and
|
|
28
|
+
* run inference concurrently. The crash fires from inside a `setImmediate`
|
|
29
|
+
* completion callback (`onnxruntime-node/dist/backend.js`), which strongly
|
|
30
|
+
* suggests the native binding keeps some global/static Napi reference that
|
|
31
|
+
* is not isolate-scoped, so a background completion callback belonging to
|
|
32
|
+
* one isolate's session fires while the wrong isolate (or no isolate/
|
|
33
|
+
* HandleScope at all) is active.
|
|
34
|
+
*
|
|
35
|
+
* This reproduces even with TWO workers using the exact SAME
|
|
36
|
+
* onnxruntime-node version (fastembed's 1.21.0, twice) — so it is NOT an
|
|
37
|
+
* ABI/version-mismatch bug between fastembed's onnxruntime-node@1.21.0 and
|
|
38
|
+
* @huggingface/transformers' onnxruntime-node@1.24.3; it is a genuine
|
|
39
|
+
* thread-safety limitation of the onnxruntime-node native addon itself when
|
|
40
|
+
* 2+ instances are concurrently active in one process, regardless of which
|
|
41
|
+
* package loaded which version.
|
|
42
|
+
*
|
|
43
|
+
* ── Root cause #2 (why fastembed is NOT also hosted in this same worker) ──
|
|
44
|
+
*
|
|
45
|
+
* A single shared worker hosting BOTH onnxruntime-node@1.21.0 (fastembed)
|
|
46
|
+
* AND onnxruntime-node@1.24.3 (transformers) was tried and instrumented:
|
|
47
|
+
* even with the two `init` calls strictly serialized in JS (proven via
|
|
48
|
+
* tracing — the second `init` provably did not begin until the first's
|
|
49
|
+
* promise had fully settled), fastembed's init still deterministically threw
|
|
50
|
+
* `std::bad_alloc` when it ran second. This means the two onnxruntime-node
|
|
51
|
+
* major versions leave native state that JS-level Promise resolution does
|
|
52
|
+
* not observe/synchronize (e.g. lingering background native thread-pool
|
|
53
|
+
* teardown) — a hazard below what JS scheduling can prevent. Only a real OS
|
|
54
|
+
* process boundary is proven safe for mixing the two versions; see
|
|
55
|
+
* `sharedFastembedProcess.ts` for that isolation.
|
|
56
|
+
*
|
|
57
|
+
* ── Fix ──
|
|
58
|
+
*
|
|
59
|
+
* Every rerank/verify ONNX consumer in this codebase — the MS-MARCO
|
|
60
|
+
* cross-encoder reranker (`@adhd/sox-hybrid-search`) and the DeBERTa NLI
|
|
61
|
+
* verifier (`@adhd/sox-claim-verification`) — is routed through exactly ONE
|
|
62
|
+
* lazily created, process-wide `worker_threads.Worker` running
|
|
63
|
+
* `embedWorker.ts`. There is never a second onnxruntime-bearing worker alive
|
|
64
|
+
* in the process, so root cause #1 is structurally impossible. fastembed
|
|
65
|
+
* never shares a thread (or process) with this worker at all, so root cause
|
|
66
|
+
* #2 is structurally impossible too.
|
|
67
|
+
*
|
|
68
|
+
* This is the ONLY place a `new Worker(embedWorker.js)` (or any other
|
|
69
|
+
* onnxruntime-bearing worker) should be constructed anywhere in the
|
|
70
|
+
* `@adhd/sox-embedding-provider` / `@adhd/sox-hybrid-search` /
|
|
71
|
+
* `@adhd/sox-claim-verification` triangle. `CrossEncoderWorker` and
|
|
72
|
+
* `WorkerProxy` delegate their wire traffic to
|
|
73
|
+
* `getSharedOnnxWorker().request(...)` instead of spawning their own
|
|
74
|
+
* `Worker`; `FastembedProvider` delegates to
|
|
75
|
+
* `getSharedFastembedProcess().request(...)` instead (a separate child
|
|
76
|
+
* PROCESS, not this worker).
|
|
77
|
+
*
|
|
78
|
+
* Trade-off (accepted, documented): `@adhd/sox-claim-verification`'s
|
|
79
|
+
* `workerCount` pool option (`ClaimVerifierConfig.workerCount`) previously
|
|
80
|
+
* gave real parallelism by spawning N separate worker threads. Since
|
|
81
|
+
* rerank+verify work in the process now funnels through this single shared
|
|
82
|
+
* worker, a `workerCount > 1` no longer buys extra parallelism (every
|
|
83
|
+
* `WorkerProxy` in the pool proxies to the same underlying worker) — but it
|
|
84
|
+
* remains safe (no crash) and does not regress correctness, only
|
|
85
|
+
* throughput under artificially-forced pool concurrency. This is the
|
|
86
|
+
* correct trade for a HIGH-severity whole-process crash.
|
|
87
|
+
*/
|
|
88
|
+
import { Worker } from 'node:worker_threads';
|
|
89
|
+
import { fileURLToPath } from 'node:url';
|
|
90
|
+
import { dirname, join } from 'node:path';
|
|
91
|
+
import { existsSync } from 'node:fs';
|
|
92
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
93
|
+
/**
|
|
94
|
+
* Resolve `embedWorker.js` regardless of whether this module is running
|
|
95
|
+
* compiled (`dist/sharedOnnxWorker.js`, sitting next to the compiled
|
|
96
|
+
* `dist/embedWorker.js`) or transformed-in-place from source (vitest runs
|
|
97
|
+
* `.ts` files directly via its SSR transform, so `__dirname` resolves to
|
|
98
|
+
* `src/`, which never contains a compiled `.js`) — mirrors the same
|
|
99
|
+
* `dist`-fallback pattern already proven in `embedWorker.spec.ts`.
|
|
100
|
+
*/
|
|
101
|
+
function resolveEmbedWorkerPath() {
|
|
102
|
+
const sibling = join(__dirname, 'embedWorker.js');
|
|
103
|
+
if (existsSync(sibling))
|
|
104
|
+
return sibling;
|
|
105
|
+
const distSibling = join(__dirname, '..', 'dist', 'embedWorker.js');
|
|
106
|
+
if (existsSync(distSibling))
|
|
107
|
+
return distSibling;
|
|
108
|
+
// Last resort: return the original candidate so the resulting error names
|
|
109
|
+
// the path that was actually attempted.
|
|
110
|
+
return sibling;
|
|
111
|
+
}
|
|
112
|
+
export class SharedOnnxWorkerClient {
|
|
113
|
+
worker = null;
|
|
114
|
+
startingPromise = null;
|
|
115
|
+
nextId = 1;
|
|
116
|
+
pending = new Map();
|
|
117
|
+
/** True once the underlying Worker has been created. */
|
|
118
|
+
get started() {
|
|
119
|
+
return this.worker !== null;
|
|
120
|
+
}
|
|
121
|
+
/** Lazily create (exactly once) and return the single shared Worker instance. */
|
|
122
|
+
ensureWorker() {
|
|
123
|
+
if (this.worker)
|
|
124
|
+
return Promise.resolve(this.worker);
|
|
125
|
+
if (this.startingPromise)
|
|
126
|
+
return this.startingPromise;
|
|
127
|
+
this.startingPromise = new Promise((resolveStart) => {
|
|
128
|
+
const workerPath = resolveEmbedWorkerPath();
|
|
129
|
+
const w = new Worker(workerPath);
|
|
130
|
+
w.unref();
|
|
131
|
+
w.on('message', (msg) => {
|
|
132
|
+
const pending = this.pending.get(msg.id);
|
|
133
|
+
if (!pending)
|
|
134
|
+
return;
|
|
135
|
+
this.pending.delete(msg.id);
|
|
136
|
+
if ('error' in msg && typeof msg['error'] === 'string') {
|
|
137
|
+
pending.reject(new Error(msg['error']));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
pending.resolve(msg);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
w.on('error', (err) => {
|
|
144
|
+
for (const { reject } of this.pending.values())
|
|
145
|
+
reject(err);
|
|
146
|
+
this.pending.clear();
|
|
147
|
+
this.worker = null;
|
|
148
|
+
this.startingPromise = null;
|
|
149
|
+
});
|
|
150
|
+
w.on('exit', (code) => {
|
|
151
|
+
if (code !== 0) {
|
|
152
|
+
const err = new Error(`shared ONNX worker exited with code ${code}`);
|
|
153
|
+
for (const { reject } of this.pending.values())
|
|
154
|
+
reject(err);
|
|
155
|
+
this.pending.clear();
|
|
156
|
+
}
|
|
157
|
+
this.worker = null;
|
|
158
|
+
this.startingPromise = null;
|
|
159
|
+
});
|
|
160
|
+
// Attaching a 'message'/'error'/'exit' listener re-refs a Worker's
|
|
161
|
+
// underlying MessagePort even if `.unref()` already ran once before
|
|
162
|
+
// any listener existed (carried over from the BL-fix already applied
|
|
163
|
+
// to the 3 previous per-consumer worker implementations this module
|
|
164
|
+
// replaces). Re-assert unref now that every listener is attached, so
|
|
165
|
+
// a real process can exit once its own work is done instead of
|
|
166
|
+
// hanging on this worker forever.
|
|
167
|
+
w.unref();
|
|
168
|
+
this.worker = w;
|
|
169
|
+
resolveStart(w);
|
|
170
|
+
});
|
|
171
|
+
return this.startingPromise;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Send a request to the shared worker and await its correlated response.
|
|
175
|
+
* Assigns a globally-unique `id` — the caller must NOT set its own `id`
|
|
176
|
+
* (any `id` field on `payload` is ignored/overwritten).
|
|
177
|
+
*
|
|
178
|
+
* Resolves with the raw response message (everything embedWorker.ts sent
|
|
179
|
+
* back except the correlation `id` is semantically meaningful to the
|
|
180
|
+
* caller — e.g. `{ initOk, dim }`, `{ embedding }`, `{ scores }`,
|
|
181
|
+
* `{ result }`). Rejects if the response carries an `error` string, if
|
|
182
|
+
* the shared worker itself errors/exits non-zero while the request is
|
|
183
|
+
* pending, or if `timeoutMs` elapses first.
|
|
184
|
+
*/
|
|
185
|
+
async request(payload, timeoutMs) {
|
|
186
|
+
const worker = await this.ensureWorker();
|
|
187
|
+
const id = this.nextId++;
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
let to;
|
|
190
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
191
|
+
to = setTimeout(() => {
|
|
192
|
+
this.pending.delete(id);
|
|
193
|
+
reject(new Error(`shared ONNX worker request timed out after ${timeoutMs}ms`));
|
|
194
|
+
}, timeoutMs);
|
|
195
|
+
if (typeof to.unref === 'function')
|
|
196
|
+
to.unref();
|
|
197
|
+
}
|
|
198
|
+
this.pending.set(id, {
|
|
199
|
+
resolve: (v) => {
|
|
200
|
+
if (to)
|
|
201
|
+
clearTimeout(to);
|
|
202
|
+
resolve(v);
|
|
203
|
+
},
|
|
204
|
+
reject: (e) => {
|
|
205
|
+
if (to)
|
|
206
|
+
clearTimeout(to);
|
|
207
|
+
reject(e);
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
worker.postMessage({ ...payload, id });
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Forcefully terminate the shared worker. Intended ONLY for full process
|
|
215
|
+
* shutdown or test teardown that genuinely owns the whole process's ONNX
|
|
216
|
+
* lifecycle — an individual consumer's dispose()/stop() must NOT call
|
|
217
|
+
* this, since other consumers (embed / rerank / verify) may still depend
|
|
218
|
+
* on the shared worker.
|
|
219
|
+
*/
|
|
220
|
+
async terminate() {
|
|
221
|
+
const w = this.worker;
|
|
222
|
+
this.worker = null;
|
|
223
|
+
this.startingPromise = null;
|
|
224
|
+
for (const { reject } of this.pending.values()) {
|
|
225
|
+
reject(new Error('shared ONNX worker terminated'));
|
|
226
|
+
}
|
|
227
|
+
this.pending.clear();
|
|
228
|
+
if (w)
|
|
229
|
+
await w.terminate();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
let _singleton = null;
|
|
233
|
+
/**
|
|
234
|
+
* Process-wide singleton accessor — the ONLY sanctioned place a
|
|
235
|
+
* `new Worker(embedWorker.js)` is constructed anywhere in the embed/rerank/
|
|
236
|
+
* verify triangle (BL-238/BL-171). Every ONNX consumer (fastembed
|
|
237
|
+
* embeddings, cross-encoder rerank, NLI verify) must obtain its worker
|
|
238
|
+
* handle through this function instead of constructing its own `Worker`.
|
|
239
|
+
*/
|
|
240
|
+
export function getSharedOnnxWorker() {
|
|
241
|
+
if (!_singleton)
|
|
242
|
+
_singleton = new SharedOnnxWorkerClient();
|
|
243
|
+
return _singleton;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Test-only: reset the module-level singleton so a test can exercise a
|
|
247
|
+
* fresh shared-worker lifecycle (e.g. after deliberately crashing/
|
|
248
|
+
* terminating it). Does NOT terminate any existing worker itself — call
|
|
249
|
+
* `.terminate()` on the previous instance first if a clean shutdown is
|
|
250
|
+
* needed.
|
|
251
|
+
*/
|
|
252
|
+
export function __resetSharedOnnxWorkerForTests() {
|
|
253
|
+
_singleton = null;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=sharedOnnxWorker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharedOnnxWorker.js","sourceRoot":"","sources":["../src/sharedOnnxWorker.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adhd/sox-embedding-provider",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pluggable text→vector embedding provider — generic EmbeddingProvider interface, config-driven model resolution, async batch-first API (AsyncIterable). Default: fastembed (local ONNX, >=3 model dims proven). Loud-fail: createEmbeddingProvider() throws ResolutionError if config is invalid or model cannot load — no silent downgrade.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=22"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@huggingface/transformers": "^4.2.0",
|
|
27
|
+
"fastembed": "^2.1.0"
|
|
28
|
+
},
|
|
29
|
+
"sox": {
|
|
30
|
+
"area": "data",
|
|
31
|
+
"group": "embed",
|
|
32
|
+
"concerns": [
|
|
33
|
+
"text→vector (EmbeddingProvider interface)",
|
|
34
|
+
"config-driven model resolution (createEmbeddingProvider factory)",
|
|
35
|
+
"async batch embed (AsyncIterable<Float32Array>)",
|
|
36
|
+
"EmbedRole param (document | query) accepted on embedSingle/embedBatch for interface compatibility — currently ignored (not yet applied) by the fastembed provider",
|
|
37
|
+
"loud-fail ResolutionError at factory time (never mid-call)",
|
|
38
|
+
"three-tier error taxonomy (Transient / Permanent / Resolution)"
|
|
39
|
+
],
|
|
40
|
+
"invariants": [
|
|
41
|
+
"createEmbeddingProvider() THROWS ResolutionError synchronously or as a rejection if the config is invalid or the model/runtime cannot load — never silently downgrades",
|
|
42
|
+
"every provider advertises { modelId, dimensions, isRemote, isDeterministic, providerUri? } via metadata — callers never hardcode dims",
|
|
43
|
+
"embedBatch() returns AsyncIterable<Float32Array> — callers receive first result before last batch finishes (critical for sequential local inference)",
|
|
44
|
+
"warmUp() is a no-op when isDeterministic === false — currently ALWAYS true, since every shipped provider (fastembed, remote) hard-codes isDeterministic: false, so warmUp() is a no-op on every path today",
|
|
45
|
+
"TransientEmbeddingError → caller may retry; PermanentEmbeddingError → caller must not retry; ResolutionError → factory-time only, never thrown mid-call"
|
|
46
|
+
],
|
|
47
|
+
"entrypoints": [
|
|
48
|
+
"dist/index.js"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|