@adhd/sox-embedding-provider 0.1.0 → 0.2.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/dist/fastembed.d.ts +9 -1
- package/dist/fastembed.d.ts.map +1 -1
- package/dist/fastembed.js +16 -4
- package/dist/fastembed.js.map +1 -1
- package/dist/fastembedLock.d.ts +33 -0
- package/dist/fastembedLock.d.ts.map +1 -0
- package/dist/fastembedLock.js +32 -0
- package/dist/fastembedLock.js.map +1 -0
- package/dist/fastembedProcessHost.d.ts +9 -1
- package/dist/fastembedProcessHost.d.ts.map +1 -1
- package/dist/fastembedProcessHost.js +178 -6
- package/dist/fastembedProcessHost.js.map +1 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -2
- package/dist/index.js.map +1 -1
- package/dist/package.json +13 -2
- package/dist/sharedFastembedProcess.d.ts +61 -3
- package/dist/sharedFastembedProcess.d.ts.map +1 -1
- package/dist/sharedFastembedProcess.js +195 -7
- package/dist/sharedFastembedProcess.js.map +1 -1
- package/package.json +14 -3
package/dist/fastembed.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SharedFastembedProcessClient } from './sharedFastembedProcess.js';
|
|
1
2
|
import type { EmbeddingHealth, EmbeddingProvider, EmbeddingProviderMetadata, EmbedRole, FastEmbedModelConfig } from './index.js';
|
|
2
3
|
declare const MODEL_CONFIGS: Record<string, FastEmbedModelConfig>;
|
|
3
4
|
/** @deprecated Use MODEL_CONFIGS[modelId].dim instead. */
|
|
@@ -26,7 +27,14 @@ export declare class FastembedProvider implements EmbeddingProvider {
|
|
|
26
27
|
private embedDim;
|
|
27
28
|
private maxTokensVal;
|
|
28
29
|
private _lastError;
|
|
29
|
-
|
|
30
|
+
private _executionProvider;
|
|
31
|
+
/**
|
|
32
|
+
* @param sharedClient Test-only injection point (BL-376): production code
|
|
33
|
+
* always relies on the default `getSharedFastembedProcess()` singleton.
|
|
34
|
+
* Tests pass a fake client here to inject an artificial init delay without
|
|
35
|
+
* forking a real fastembed child process or downloading a model.
|
|
36
|
+
*/
|
|
37
|
+
constructor(model: string, dimensions: number, cacheDir: string, sharedClient?: SharedFastembedProcessClient);
|
|
30
38
|
health(): EmbeddingHealth;
|
|
31
39
|
embedSingle(text: string, _role?: EmbedRole): Promise<Float32Array>;
|
|
32
40
|
embedBatch(texts: string[], opts?: {
|
package/dist/fastembed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastembed.d.ts","sourceRoot":"","sources":["../src/fastembed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fastembed.d.ts","sourceRoot":"","sources":["../src/fastembed.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,KAAK,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3G,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAgBjI,QAAA,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAoCvD,CAAC;AAEF,0DAA0D;AAC1D,QAAA,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEtC,CAAC;AAEF,gEAAgE;AAChE,QAAA,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAE5C,CAAC;AAEF,QAAA,MAAM,aAAa,qBAAqB,CAAC;AACzC,QAAA,MAAM,kBAAkB,MAAM,CAAC;AAE/B;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,iBAAiB;IACzD,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IAKzB,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,kBAAkB,CAAiB;IAE3C;;;;;OAKG;gBAED,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,GAAE,4BAA0D;IAkB1E,MAAM,IAAI,eAAe;IAmBnB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBlE,UAAU,CACf,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9C,aAAa,CAAC,YAAY,CAAC;IAuBxB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAmBjB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IA0BhB;;;OAGG;IACH,OAAO,CAAC,WAAW;YAQL,SAAS;YAwBT,SAAS;IAKvB,OAAO,CAAC,mBAAmB;CAc5B;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC"}
|
package/dist/fastembed.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { warmupTimeoutMs } from './index.js';
|
|
1
|
+
import { warmupTimeoutMs, isModelCached } from './index.js';
|
|
2
2
|
import { getSharedFastembedProcess } from './sharedFastembedProcess.js';
|
|
3
3
|
const MODEL_CONFIGS = {
|
|
4
4
|
'bge-small-en-v1.5': {
|
|
@@ -61,16 +61,24 @@ export class FastembedProvider {
|
|
|
61
61
|
// process-wide shared fastembed CHILD PROCESS singleton instead of
|
|
62
62
|
// spawning our own `Worker`/process — see `sharedFastembedProcess.ts` for
|
|
63
63
|
// the full root-cause writeup.
|
|
64
|
-
shared
|
|
64
|
+
shared;
|
|
65
65
|
ready = false;
|
|
66
66
|
readyPromise = null;
|
|
67
67
|
embedDim = 0;
|
|
68
68
|
maxTokensVal = 512;
|
|
69
69
|
_lastError = null;
|
|
70
|
-
|
|
70
|
+
_executionProvider = 'cpu';
|
|
71
|
+
/**
|
|
72
|
+
* @param sharedClient Test-only injection point (BL-376): production code
|
|
73
|
+
* always relies on the default `getSharedFastembedProcess()` singleton.
|
|
74
|
+
* Tests pass a fake client here to inject an artificial init delay without
|
|
75
|
+
* forking a real fastembed child process or downloading a model.
|
|
76
|
+
*/
|
|
77
|
+
constructor(model, dimensions, cacheDir, sharedClient = getSharedFastembedProcess()) {
|
|
71
78
|
this.model = model;
|
|
72
79
|
this.cacheDir = cacheDir;
|
|
73
80
|
this.embedDim = dimensions;
|
|
81
|
+
this.shared = sharedClient;
|
|
74
82
|
const cfg = MODEL_CONFIGS[model];
|
|
75
83
|
this.maxTokensVal = cfg?.maxTokens ?? 512;
|
|
76
84
|
this.metadata = {
|
|
@@ -99,6 +107,7 @@ export class FastembedProvider {
|
|
|
99
107
|
state,
|
|
100
108
|
dimensions: this.embedDim || this.metadata.dimensions,
|
|
101
109
|
last_error: this._lastError,
|
|
110
|
+
execution_provider: this._executionProvider,
|
|
102
111
|
};
|
|
103
112
|
}
|
|
104
113
|
async embedSingle(text, _role) {
|
|
@@ -220,10 +229,13 @@ export class FastembedProvider {
|
|
|
220
229
|
}
|
|
221
230
|
async initModel() {
|
|
222
231
|
try {
|
|
223
|
-
const
|
|
232
|
+
const hfRepoId = MODEL_CONFIGS[this.model]?.hfRepoId;
|
|
233
|
+
const cacheHit = hfRepoId ? isModelCached(this.cacheDir, hfRepoId) : false;
|
|
234
|
+
const res = await this.shared.request({ type: 'init', model: this.model, cacheDir: this.cacheDir }, warmupTimeoutMs(cacheHit));
|
|
224
235
|
if (res.dim > 0) {
|
|
225
236
|
this.embedDim = res.dim;
|
|
226
237
|
}
|
|
238
|
+
this._executionProvider = res.execution_provider || 'cpu';
|
|
227
239
|
this.ready = true;
|
|
228
240
|
this._lastError = null;
|
|
229
241
|
}
|
package/dist/fastembed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastembed.js","sourceRoot":"","sources":["../src/fastembed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"fastembed.js","sourceRoot":"","sources":["../src/fastembed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BL-471: single shared definition of the BL-331 advisory fastembed-host
|
|
3
|
+
* lock file's path convention and payload shape, imported by BOTH the
|
|
4
|
+
* WRITER (`fastembedProcessHost.ts`, running in the forked child) and the
|
|
5
|
+
* READER (`sharedFastembedProcess.ts`'s `detectCompetingFastembedHost`,
|
|
6
|
+
* running in the parent).
|
|
7
|
+
*
|
|
8
|
+
* Before this module existed, `resolveFastembedLockPath()` and the
|
|
9
|
+
* `{ pid, startedAt }` shape were spelled out independently in both files.
|
|
10
|
+
* Nothing failed when they drifted: renaming the lock path or the
|
|
11
|
+
* `startedAt` field in one file would silently make BL-432's
|
|
12
|
+
* `competing_host_pid` telemetry field permanently `null` — indistinguishable
|
|
13
|
+
* from "no competing host was ever present". See BL-471.
|
|
14
|
+
*
|
|
15
|
+
* This module is deliberately NOT `fastembedProcessHost.ts` itself — that
|
|
16
|
+
* file registers `process.on('message')` and calls
|
|
17
|
+
* `checkAndClaimFastembedLock()` at MODULE SCOPE, both meant only for the
|
|
18
|
+
* forked child process and unsafe to run in the parent. This module has
|
|
19
|
+
* zero module-scope side effects (no I/O, no listeners) so it is safe for
|
|
20
|
+
* the parent process (`sharedFastembedProcess.ts`) to import directly.
|
|
21
|
+
*/
|
|
22
|
+
/** Shape of the advisory lock file's JSON contents. */
|
|
23
|
+
export interface FastembedLockInfo {
|
|
24
|
+
pid: number;
|
|
25
|
+
startedAt: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolved fresh on every call (not a module-level constant) so tests can
|
|
29
|
+
* point it at an isolated temp path via `SOX_FASTEMBED_LOCK_PATH` without
|
|
30
|
+
* needing `vi.resetModules()`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveFastembedLockPath(): string;
|
|
33
|
+
//# sourceMappingURL=fastembedLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastembedLock.d.ts","sourceRoot":"","sources":["../src/fastembedLock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BL-471: single shared definition of the BL-331 advisory fastembed-host
|
|
3
|
+
* lock file's path convention and payload shape, imported by BOTH the
|
|
4
|
+
* WRITER (`fastembedProcessHost.ts`, running in the forked child) and the
|
|
5
|
+
* READER (`sharedFastembedProcess.ts`'s `detectCompetingFastembedHost`,
|
|
6
|
+
* running in the parent).
|
|
7
|
+
*
|
|
8
|
+
* Before this module existed, `resolveFastembedLockPath()` and the
|
|
9
|
+
* `{ pid, startedAt }` shape were spelled out independently in both files.
|
|
10
|
+
* Nothing failed when they drifted: renaming the lock path or the
|
|
11
|
+
* `startedAt` field in one file would silently make BL-432's
|
|
12
|
+
* `competing_host_pid` telemetry field permanently `null` — indistinguishable
|
|
13
|
+
* from "no competing host was ever present". See BL-471.
|
|
14
|
+
*
|
|
15
|
+
* This module is deliberately NOT `fastembedProcessHost.ts` itself — that
|
|
16
|
+
* file registers `process.on('message')` and calls
|
|
17
|
+
* `checkAndClaimFastembedLock()` at MODULE SCOPE, both meant only for the
|
|
18
|
+
* forked child process and unsafe to run in the parent. This module has
|
|
19
|
+
* zero module-scope side effects (no I/O, no listeners) so it is safe for
|
|
20
|
+
* the parent process (`sharedFastembedProcess.ts`) to import directly.
|
|
21
|
+
*/
|
|
22
|
+
import { tmpdir } from 'node:os';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
/**
|
|
25
|
+
* Resolved fresh on every call (not a module-level constant) so tests can
|
|
26
|
+
* point it at an isolated temp path via `SOX_FASTEMBED_LOCK_PATH` without
|
|
27
|
+
* needing `vi.resetModules()`.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveFastembedLockPath() {
|
|
30
|
+
return process.env['SOX_FASTEMBED_LOCK_PATH'] ?? join(tmpdir(), 'sox-fastembed-host.lock');
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=fastembedLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastembedLock.js","sourceRoot":"","sources":["../src/fastembedLock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -56,5 +56,13 @@
|
|
|
56
56
|
* response: { id, error: string }
|
|
57
57
|
* internal: { __shutdown: true }
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
/** True if a process with this pid is alive (best-effort; ESRCH => dead). */
|
|
60
|
+
export declare function isPidAlive(pid: number): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Check for (and then claim) the advisory single-host lock. Logs a loud,
|
|
63
|
+
* greppable warning to stderr — naming the conflicting pid — if another LIVE
|
|
64
|
+
* process already holds it. Always overwrites the lock with our own pid
|
|
65
|
+
* afterward (last writer wins; this is advisory, not exclusive).
|
|
66
|
+
*/
|
|
67
|
+
export declare function checkAndClaimFastembedLock(): void;
|
|
60
68
|
//# sourceMappingURL=fastembedProcessHost.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastembedProcessHost.d.ts","sourceRoot":"","sources":["../src/fastembedProcessHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG"}
|
|
1
|
+
{"version":3,"file":"fastembedProcessHost.d.ts","sourceRoot":"","sources":["../src/fastembedProcessHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAqDH,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAoCjD"}
|
|
@@ -57,6 +57,103 @@
|
|
|
57
57
|
* internal: { __shutdown: true }
|
|
58
58
|
*/
|
|
59
59
|
import * as fs from 'node:fs';
|
|
60
|
+
import { resolveFastembedLockPath } from './fastembedLock.js';
|
|
61
|
+
// ── BL-331: cross-process CoreML/ANE contention advisory lock ──────────────
|
|
62
|
+
//
|
|
63
|
+
// Root cause of BL-331 (production embed ~25-50x slower than a clean-room
|
|
64
|
+
// harness on the same machine): two orphaned one-shot debug scripts had each
|
|
65
|
+
// forked their OWN `fastembedProcessHost.js` (via `sharedFastembedProcess.ts`'s
|
|
66
|
+
// `getSharedFastembedProcess()`, but from a DIFFERENT `dist/` copy than the
|
|
67
|
+
// real server's, so it's a distinct Node module singleton and thus a distinct
|
|
68
|
+
// OS process) and never exited — each holding an idle CoreML
|
|
69
|
+
// `InferenceSession` for hours.
|
|
70
|
+
//
|
|
71
|
+
// ⚠️ MEASUREMENT CORRECTION (2026-07-31): an earlier revision of this comment
|
|
72
|
+
// claimed those orphans held "~900MB" each. That was wrong by more than an
|
|
73
|
+
// order of magnitude. Measured at kill time via `ps -eo pid,ppid,rss`:
|
|
74
|
+
// orphan hosts 32MB each, their parent scripts 28MB each, and the LIVE server's
|
|
75
|
+
// host 46MB. Reaping both freed ~120MB, not ~1.8GB — and did NOT change embed
|
|
76
|
+
// latency. So cross-process ANE contention is NOT an established cause of
|
|
77
|
+
// BL-331; treat it as UNPROVEN.
|
|
78
|
+
//
|
|
79
|
+
// What IS measured: the real server's embed calls take 8-20s wall-clock for
|
|
80
|
+
// tiny (<600 char) inputs at only ~34% CPU (NOT compute-bound — waiting), while
|
|
81
|
+
// the identical model/EP in a clean-room single-process harness measured ~0.4s.
|
|
82
|
+
// Note the clean-room number was taken on a quiet machine and the production
|
|
83
|
+
// number under load average 18-25 on 10 cores, so the ratio itself is partly
|
|
84
|
+
// load-confounded — record ambient load with any future comparison.
|
|
85
|
+
//
|
|
86
|
+
// The leading UNTESTED hypothesis is EP graph partitioning: CoreML cannot
|
|
87
|
+
// execute bge-base's 30522x768 `word_embeddings` tensor (see the
|
|
88
|
+
// `IsInputSupported` warning logged at every startup), so onnxruntime splits the
|
|
89
|
+
// graph and every inference pays a CPU/ANE boundary crossing. A CoreML-vs-CPU
|
|
90
|
+
// A/B would settle it; it has not been run. See BL-331.
|
|
91
|
+
//
|
|
92
|
+
// This lock remains useful regardless: nothing in this process logs the
|
|
93
|
+
// existence of sibling fastembed hosts, and that blindness cost an afternoon
|
|
94
|
+
// of `ps`/`vm_stat` archaeology.
|
|
95
|
+
//
|
|
96
|
+
// This lock is advisory-only — it never blocks or refuses to load the model
|
|
97
|
+
// (a legitimate second store/project running its own memory-server on the
|
|
98
|
+
// same machine is a real, supported scenario, not a bug). It exists purely
|
|
99
|
+
// so a FUTURE occurrence of this contention class is a loud, immediately
|
|
100
|
+
// greppable stderr line at model-load time instead of a silent 25-50x
|
|
101
|
+
// slowdown that takes an agent an afternoon of `ps`/`vm_stat` archaeology to
|
|
102
|
+
// diagnose.
|
|
103
|
+
// BL-471: `resolveFastembedLockPath()` and `FastembedLockInfo` now live in
|
|
104
|
+
// `./fastembedLock.ts` — the single shared definition imported by both this
|
|
105
|
+
// writer and `sharedFastembedProcess.ts`'s reader. See that module's doc
|
|
106
|
+
// comment for why it isn't just this file.
|
|
107
|
+
/** True if a process with this pid is alive (best-effort; ESRCH => dead). */
|
|
108
|
+
export function isPidAlive(pid) {
|
|
109
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
110
|
+
return false;
|
|
111
|
+
try {
|
|
112
|
+
process.kill(pid, 0);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check for (and then claim) the advisory single-host lock. Logs a loud,
|
|
121
|
+
* greppable warning to stderr — naming the conflicting pid — if another LIVE
|
|
122
|
+
* process already holds it. Always overwrites the lock with our own pid
|
|
123
|
+
* afterward (last writer wins; this is advisory, not exclusive).
|
|
124
|
+
*/
|
|
125
|
+
export function checkAndClaimFastembedLock() {
|
|
126
|
+
const lockPath = resolveFastembedLockPath();
|
|
127
|
+
try {
|
|
128
|
+
if (fs.existsSync(lockPath)) {
|
|
129
|
+
const raw = fs.readFileSync(lockPath, 'utf8');
|
|
130
|
+
const prev = JSON.parse(raw);
|
|
131
|
+
if (typeof prev.pid === 'number' &&
|
|
132
|
+
prev.pid !== process.pid &&
|
|
133
|
+
isPidAlive(prev.pid)) {
|
|
134
|
+
console.error(`[fastembed] WARNING (BL-331): another fastembed host process (pid ${prev.pid}, ` +
|
|
135
|
+
`started ${prev.startedAt ?? 'unknown'}) is ALREADY RUNNING on this machine. ` +
|
|
136
|
+
`Concurrent onnxruntime-node CoreML/ANE execution across separate OS processes has ` +
|
|
137
|
+
`been observed to cause severe (25-50x) embed latency due to Neural Engine/hardware ` +
|
|
138
|
+
`queue contention, even though each process's own CPU usage looks low (it is waiting, ` +
|
|
139
|
+
`not computing). If pid ${prev.pid} is a leaked/orphaned process (check with ` +
|
|
140
|
+
`\`ps -p ${prev.pid}\`), terminate it. Lock file: ${lockPath}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Never let a malformed/unreadable lock file block real startup — this
|
|
146
|
+
// is a pure observability aid, not a correctness mechanism.
|
|
147
|
+
console.error(`[fastembed] BL-331 lock check failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const info = { pid: process.pid, startedAt: new Date().toISOString() };
|
|
151
|
+
fs.writeFileSync(lockPath, JSON.stringify(info));
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Non-fatal: if /tmp isn't writable for some reason, just skip claiming.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
60
157
|
// ── Model management ──────────────────────────────────────────────────────────
|
|
61
158
|
const MODEL_MAP = {
|
|
62
159
|
'bge-small-en-v1.5': 'fast-bge-small-en-v1.5',
|
|
@@ -68,28 +165,48 @@ const MODEL_MAP = {
|
|
|
68
165
|
let _embedder = null;
|
|
69
166
|
let _currentModel = '';
|
|
70
167
|
let _currentCacheDir = '';
|
|
168
|
+
function resolveExecutionProviders() {
|
|
169
|
+
const forced = process.env.SOX_EMBED_EXECUTION_PROVIDER;
|
|
170
|
+
if (forced) {
|
|
171
|
+
console.error(`[fastembed] Using forced execution provider: ${forced}`);
|
|
172
|
+
return [forced, 'cpu'];
|
|
173
|
+
}
|
|
174
|
+
if (process.platform === 'darwin')
|
|
175
|
+
return ['coreml', 'cpu'];
|
|
176
|
+
if (process.platform === 'linux' && process.arch === 'x64')
|
|
177
|
+
return ['cuda', 'cpu'];
|
|
178
|
+
if (process.platform === 'win32')
|
|
179
|
+
return ['dml', 'cpu'];
|
|
180
|
+
return ['cpu'];
|
|
181
|
+
}
|
|
71
182
|
async function loadModel(model, cacheDir) {
|
|
72
183
|
if (_embedder && _currentModel === model && _currentCacheDir === cacheDir) {
|
|
73
184
|
const models = _embedder.listSupportedModels();
|
|
74
185
|
const info = models.find((m) => m.model === model);
|
|
75
|
-
return { dim: info?.dim ?? 0 };
|
|
186
|
+
return { dim: info?.dim ?? 0, execution_provider: 'cpu' };
|
|
76
187
|
}
|
|
188
|
+
// BL-331: advisory contention check, once, right before the real (expensive,
|
|
189
|
+
// ANE-contending) model load — see the lock helpers above for the full
|
|
190
|
+
// root-cause writeup.
|
|
191
|
+
checkAndClaimFastembedLock();
|
|
77
192
|
const { FlagEmbedding, EmbeddingModel: EM } = await import('fastembed');
|
|
78
193
|
const fastModel = MODEL_MAP[model] ?? model;
|
|
79
194
|
const modelKeys = Object.keys(EM);
|
|
80
195
|
const foundKey = modelKeys.find((k) => EM[k] === fastModel);
|
|
81
196
|
const modelEnum = foundKey ? EM[foundKey] : fastModel;
|
|
82
197
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
198
|
+
const executionProviders = resolveExecutionProviders();
|
|
83
199
|
_embedder = (await FlagEmbedding.init({
|
|
84
200
|
model: modelEnum,
|
|
85
201
|
cacheDir,
|
|
202
|
+
executionProviders,
|
|
86
203
|
showDownloadProgress: false,
|
|
87
204
|
}));
|
|
88
205
|
_currentModel = model;
|
|
89
206
|
_currentCacheDir = cacheDir;
|
|
90
207
|
const models = _embedder.listSupportedModels();
|
|
91
208
|
const info = models.find((m) => m.model === model);
|
|
92
|
-
return { dim: info?.dim ?? 0 };
|
|
209
|
+
return { dim: info?.dim ?? 0, execution_provider: executionProviders[0] };
|
|
93
210
|
}
|
|
94
211
|
async function collectEmbeddings(embedder, texts) {
|
|
95
212
|
const results = [];
|
|
@@ -110,14 +227,37 @@ let _queue = Promise.resolve();
|
|
|
110
227
|
function enqueue(task) {
|
|
111
228
|
_queue = _queue.then(task, task);
|
|
112
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* (BL-405) Reply to the parent, guarded against the parent already being gone.
|
|
232
|
+
*
|
|
233
|
+
* Without this guard, `process.send()` with no error callback throws an
|
|
234
|
+
* uncaught 'error' event (EPIPE) the instant the parent's IPC channel
|
|
235
|
+
* disappears — which fatally crashes THIS child (`libc++abi: terminating due
|
|
236
|
+
* to uncaught exception of type std::__1::system_error`), reproduced on
|
|
237
|
+
* every SIGTERM sent to the parent during BL-405 diagnosis, including with
|
|
238
|
+
* no embed work in flight at the moment of the signal. Nothing previously
|
|
239
|
+
* called `getSharedFastembedProcess().terminate()` on parent shutdown, so
|
|
240
|
+
* this child simply outlived its own IPC pipe.
|
|
241
|
+
*
|
|
242
|
+
* `process.connected` short-circuits the common case (parent already fully
|
|
243
|
+
* torn down); the error-callback form of `process.send()` catches the
|
|
244
|
+
* narrower race where the channel closes mid-call. Either way the reply is
|
|
245
|
+
* moot once the parent is gone — log and drop it instead of crashing.
|
|
246
|
+
*/
|
|
113
247
|
function send(msg) {
|
|
114
|
-
process.
|
|
248
|
+
if (!process.connected)
|
|
249
|
+
return;
|
|
250
|
+
process.send?.(msg, (err) => {
|
|
251
|
+
if (err) {
|
|
252
|
+
process.stderr.write(`[fastembed-host] send failed (parent likely exited): ${err.message}\n`);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
115
255
|
}
|
|
116
256
|
async function handleRequest(msg) {
|
|
117
257
|
if (msg.type === 'init') {
|
|
118
258
|
try {
|
|
119
|
-
const { dim } = await loadModel(msg.model, msg.cacheDir);
|
|
120
|
-
send({ id: msg.id, initOk: true, dim });
|
|
259
|
+
const { dim, execution_provider } = await loadModel(msg.model, msg.cacheDir);
|
|
260
|
+
send({ id: msg.id, initOk: true, dim, execution_provider });
|
|
121
261
|
}
|
|
122
262
|
catch (e) {
|
|
123
263
|
send({ id: msg.id, error: String(e instanceof Error ? e.message : e) });
|
|
@@ -153,9 +293,41 @@ async function handleRequest(msg) {
|
|
|
153
293
|
return;
|
|
154
294
|
}
|
|
155
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* (BL-426) On `__shutdown`, let any in-flight/queued request settle and then
|
|
298
|
+
* `process.disconnect()` instead of calling `process.exit(0)`.
|
|
299
|
+
*
|
|
300
|
+
* `process.exit()` forces an ABRUPT teardown: it skips draining the event
|
|
301
|
+
* loop and runs native `atexit`/static-destructor unwinding immediately,
|
|
302
|
+
* regardless of what else is still alive in the process. Once an
|
|
303
|
+
* onnxruntime-node `InferenceSession` has been created in this process
|
|
304
|
+
* (`loadModel()` above — even a bare `init` with no `embed` call is
|
|
305
|
+
* sufficient), onnxruntime's own native background thread pool is still
|
|
306
|
+
* alive/tearing down when that abrupt unwind runs, and the two race on a
|
|
307
|
+
* native mutex. Reproduced in total isolation (no memory-server, no
|
|
308
|
+
* backend.ts, no Turso, no ONNX rerank worker — this file forked directly
|
|
309
|
+
* and driven with a real `init`/`embed`/`__shutdown` sequence), on BOTH the
|
|
310
|
+
* `coreml` and `cpu` execution providers, and even with `init` alone (no
|
|
311
|
+
* `embed` call ever made):
|
|
312
|
+
*
|
|
313
|
+
* libc++abi: terminating due to uncaught exception of type
|
|
314
|
+
* std::__1::system_error: mutex lock failed: Invalid argument
|
|
315
|
+
* (child exits via SIGABRT, not code 0)
|
|
316
|
+
*
|
|
317
|
+
* `process.disconnect()` closes the IPC channel and lets Node run its
|
|
318
|
+
* NORMAL exit sequence once the event loop is otherwise empty — no forced
|
|
319
|
+
* unwind mid-native-teardown. Verified the same isolated repro exits code 0
|
|
320
|
+
* with no crash text once `process.exit(0)` is replaced with this. See
|
|
321
|
+
* BL-426 in BACKLOG.md / `fastembedProcessHost-bl426-shutdown.spec.ts`.
|
|
322
|
+
*
|
|
323
|
+
* Waiting for `_queue` first (rather than disconnecting immediately) avoids
|
|
324
|
+
* dropping a request that was still in flight when `__shutdown` arrived.
|
|
325
|
+
*/
|
|
156
326
|
process.on('message', (msg) => {
|
|
157
327
|
if ('__shutdown' in msg) {
|
|
158
|
-
|
|
328
|
+
void _queue.finally(() => {
|
|
329
|
+
process.disconnect();
|
|
330
|
+
});
|
|
159
331
|
return;
|
|
160
332
|
}
|
|
161
333
|
enqueue(() => handleRequest(msg));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastembedProcessHost.js","sourceRoot":"","sources":["../src/fastembedProcessHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"fastembedProcessHost.js","sourceRoot":"","sources":["../src/fastembedProcessHost.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface EmbeddingHealth {
|
|
|
13
13
|
state: 'uninitialized' | 'warming' | 'real' | 'error';
|
|
14
14
|
dimensions: number | null;
|
|
15
15
|
last_error: string | null;
|
|
16
|
+
execution_provider?: string;
|
|
16
17
|
}
|
|
17
18
|
export interface EmbeddingProvider {
|
|
18
19
|
readonly metadata: EmbeddingProviderMetadata;
|
|
@@ -140,6 +141,28 @@ export declare function createEmbeddingProvider(config: EmbeddingProviderConfig)
|
|
|
140
141
|
* (fastembed.ts) that bounds the actual ONNX model load inside the worker
|
|
141
142
|
* thread. Both now read this one function/env var so a cold ONNX model
|
|
142
143
|
* download is bounded consistently end-to-end.
|
|
144
|
+
*
|
|
145
|
+
* BL-376: one budget used to cover both a cold network download (legitimately
|
|
146
|
+
* slow, ~180s) and a cached local load (measured ~650ms-12s depending on OS
|
|
147
|
+
* scheduling QoS). Sizing for the worst case meant a hung cache-hit load was
|
|
148
|
+
* indistinguishable from a slow download for the full 180s. The budget is now
|
|
149
|
+
* split by `cacheHit`, which callers determine up front via
|
|
150
|
+
* `isModelCached()` before choosing which number to ask for — this is NOT a
|
|
151
|
+
* renamed single constant, the caller genuinely branches on cache state.
|
|
152
|
+
*
|
|
153
|
+
* - `cacheHit === true` → tight, single-digit-second budget (default 8s,
|
|
154
|
+
* override via `SOX_EMBED_WARMUP_CACHED_TIMEOUT_MS`). The model binary is
|
|
155
|
+
* already on disk; loading it should be near-instant, so a hang here must
|
|
156
|
+
* surface fast instead of silently eating three minutes.
|
|
157
|
+
* - `cacheHit === false` → the original generous download budget (default
|
|
158
|
+
* 180s, override via `SOX_EMBED_WARMUP_TIMEOUT_MS`), unchanged.
|
|
159
|
+
*/
|
|
160
|
+
export declare function warmupTimeoutMs(cacheHit: boolean): number;
|
|
161
|
+
/**
|
|
162
|
+
* BL-376: determine cache-hit vs. cache-miss up front, synchronously, before
|
|
163
|
+
* either warmup timeout budget is chosen. fastembed lays the ONNX binary out
|
|
164
|
+
* at `<cacheDir>/<hfRepoId>/model_optimized.onnx` (verified against a live
|
|
165
|
+
* `~/.cache/sox/models/` tree) — its presence is the cache-hit signal.
|
|
143
166
|
*/
|
|
144
|
-
export declare function
|
|
167
|
+
export declare function isModelCached(cacheDir: string, hfRepoId: string): boolean;
|
|
145
168
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @adhd/sox-embedding-provider — pluggable text→vector embedding
|
|
2
2
|
// Authoritative interface spec: docs/plan/memory-refactor/COMPILED_INTERFACES.md
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
// ── Error taxonomy — three tiers, no silent degradation ──────────────────────
|
|
@@ -78,7 +79,8 @@ async function createFastembedProvider(config) {
|
|
|
78
79
|
joinDefaultCacheDir();
|
|
79
80
|
try {
|
|
80
81
|
const provider = new FastembedProvider(modelId, cfg.dim, cacheDir);
|
|
81
|
-
|
|
82
|
+
const cacheHit = isModelCached(cacheDir, cfg.hfRepoId);
|
|
83
|
+
await withTimeout(provider.embedSingle('warmup'), warmupTimeoutMs(cacheHit), 'fastembed warmup');
|
|
82
84
|
return provider;
|
|
83
85
|
}
|
|
84
86
|
catch (err) {
|
|
@@ -110,11 +112,39 @@ async function createRemoteProvider(config) {
|
|
|
110
112
|
* (fastembed.ts) that bounds the actual ONNX model load inside the worker
|
|
111
113
|
* thread. Both now read this one function/env var so a cold ONNX model
|
|
112
114
|
* download is bounded consistently end-to-end.
|
|
115
|
+
*
|
|
116
|
+
* BL-376: one budget used to cover both a cold network download (legitimately
|
|
117
|
+
* slow, ~180s) and a cached local load (measured ~650ms-12s depending on OS
|
|
118
|
+
* scheduling QoS). Sizing for the worst case meant a hung cache-hit load was
|
|
119
|
+
* indistinguishable from a slow download for the full 180s. The budget is now
|
|
120
|
+
* split by `cacheHit`, which callers determine up front via
|
|
121
|
+
* `isModelCached()` before choosing which number to ask for — this is NOT a
|
|
122
|
+
* renamed single constant, the caller genuinely branches on cache state.
|
|
123
|
+
*
|
|
124
|
+
* - `cacheHit === true` → tight, single-digit-second budget (default 8s,
|
|
125
|
+
* override via `SOX_EMBED_WARMUP_CACHED_TIMEOUT_MS`). The model binary is
|
|
126
|
+
* already on disk; loading it should be near-instant, so a hang here must
|
|
127
|
+
* surface fast instead of silently eating three minutes.
|
|
128
|
+
* - `cacheHit === false` → the original generous download budget (default
|
|
129
|
+
* 180s, override via `SOX_EMBED_WARMUP_TIMEOUT_MS`), unchanged.
|
|
113
130
|
*/
|
|
114
|
-
export function warmupTimeoutMs() {
|
|
131
|
+
export function warmupTimeoutMs(cacheHit) {
|
|
132
|
+
if (cacheHit) {
|
|
133
|
+
const raw = Number(process.env['SOX_EMBED_WARMUP_CACHED_TIMEOUT_MS']);
|
|
134
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 8_000;
|
|
135
|
+
}
|
|
115
136
|
const raw = Number(process.env['SOX_EMBED_WARMUP_TIMEOUT_MS']);
|
|
116
137
|
return Number.isFinite(raw) && raw > 0 ? raw : 180_000;
|
|
117
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* BL-376: determine cache-hit vs. cache-miss up front, synchronously, before
|
|
141
|
+
* either warmup timeout budget is chosen. fastembed lays the ONNX binary out
|
|
142
|
+
* at `<cacheDir>/<hfRepoId>/model_optimized.onnx` (verified against a live
|
|
143
|
+
* `~/.cache/sox/models/` tree) — its presence is the cache-hit signal.
|
|
144
|
+
*/
|
|
145
|
+
export function isModelCached(cacheDir, hfRepoId) {
|
|
146
|
+
return existsSync(join(cacheDir, hfRepoId, 'model_optimized.onnx'));
|
|
147
|
+
}
|
|
118
148
|
function withTimeout(p, ms, label) {
|
|
119
149
|
return new Promise((resolve, reject) => {
|
|
120
150
|
const to = setTimeout(() => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,iFAAiF;AAEjF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhd/sox-embedding-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -17,18 +17,29 @@
|
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"default": "./dist/index.js"
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
24
|
"dist"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
27
|
+
"@adhd/sox-telemetry": "workspace:*",
|
|
26
28
|
"@huggingface/transformers": "^4.2.0",
|
|
27
29
|
"fastembed": "^2.1.0"
|
|
28
30
|
},
|
|
29
31
|
"sox": {
|
|
30
32
|
"area": "data",
|
|
31
33
|
"group": "embed",
|
|
34
|
+
"sidecars": [
|
|
35
|
+
"src/embedWorker.ts",
|
|
36
|
+
"src/fastembedProcessHost.ts",
|
|
37
|
+
"src/sharedOnnxWorker.ts"
|
|
38
|
+
],
|
|
39
|
+
"sidecarExternals": [
|
|
40
|
+
"fastembed",
|
|
41
|
+
"onnxruntime-node"
|
|
42
|
+
],
|
|
32
43
|
"concerns": [
|
|
33
44
|
"text→vector (EmbeddingProvider interface)",
|
|
34
45
|
"config-driven model resolution (createEmbeddingProvider factory)",
|
|
@@ -20,20 +20,78 @@ export declare class SharedFastembedProcessClient {
|
|
|
20
20
|
private startingPromise;
|
|
21
21
|
private nextId;
|
|
22
22
|
private pending;
|
|
23
|
+
private readonly hostPath;
|
|
24
|
+
/**
|
|
25
|
+
* @param hostPathOverride Test-only injection point (BL-410): points the
|
|
26
|
+
* fork target at a lightweight fixture host instead of the real
|
|
27
|
+
* `fastembedProcessHost.js`, so a test can exercise the fork/ref/unref
|
|
28
|
+
* contract without loading fastembed or downloading a model. Production
|
|
29
|
+
* code (`getSharedFastembedProcess()`) never passes this — it always
|
|
30
|
+
* resolves the real host path.
|
|
31
|
+
*/
|
|
32
|
+
constructor(hostPathOverride?: string);
|
|
23
33
|
/** True once the underlying child process has been forked. */
|
|
24
34
|
get started(): boolean;
|
|
25
35
|
/** Lazily fork (exactly once) and return the single shared child process. */
|
|
26
36
|
private ensureProcess;
|
|
37
|
+
/**
|
|
38
|
+
* (BL-410) Ref the child process and its IPC channel while at least one
|
|
39
|
+
* request is in flight. `ensureProcess()` unrefs both immediately after
|
|
40
|
+
* forking so a long-lived *service* (memory-server) can exit cleanly
|
|
41
|
+
* without the embed child pinning it open forever (BL-370). But a
|
|
42
|
+
* **standalone script** whose only pending work is an in-flight
|
|
43
|
+
* `request()` has nothing else ref'd — Node can decide the event loop is
|
|
44
|
+
* empty and tear the process down mid-model-load, abandoning the pending
|
|
45
|
+
* promise before the child's reply ever arrives. Re-ref-ing for the
|
|
46
|
+
* duration of each in-flight request keeps such a script alive exactly
|
|
47
|
+
* long enough to receive its answer, and `unrefIfIdle()` (called from
|
|
48
|
+
* every settle path) immediately releases that ref again once nothing is
|
|
49
|
+
* outstanding — so a script with no other work still exits promptly, it
|
|
50
|
+
* just doesn't exit *before its own request completes*.
|
|
51
|
+
*/
|
|
52
|
+
private refForPending;
|
|
53
|
+
/** Counterpart to `refForPending()` — release the ref once `pending` drains. */
|
|
54
|
+
private unrefIfIdle;
|
|
27
55
|
/**
|
|
28
56
|
* Send a request to the shared fastembed process and await its correlated
|
|
29
57
|
* response. Assigns a globally-unique `id` — the caller must NOT set its
|
|
30
58
|
* own `id` (any `id` field on `payload` is ignored/overwritten).
|
|
59
|
+
*
|
|
60
|
+
* BL-432: this is where BL-331's head-of-line-blocking question actually
|
|
61
|
+
* lives — NOT in memory-core's `embed.ts` `wait`/`work` split, which was
|
|
62
|
+
* measured (n=570) to be structurally incapable of observing it, because
|
|
63
|
+
* `wait` only covers acquiring the already-memoised provider promise.
|
|
64
|
+
* Every request beyond the first in a process contends here, on this one
|
|
65
|
+
* shared child. Three signals are emitted with every
|
|
66
|
+
* `fastembed_process.request.*` record via the `@adhd/sox-telemetry`
|
|
67
|
+
* substrate (no second telemetry mechanism):
|
|
68
|
+
* 1. `queue_depth` — `this.pending.size` measured BEFORE this request is
|
|
69
|
+
* added, i.e. how many requests are already admitted and awaiting a
|
|
70
|
+
* reply ahead of this one. The direct head-of-line-blocking signal.
|
|
71
|
+
* 2. `response_ms` — elapsed time from immediately after `child.send()`
|
|
72
|
+
* to this request's own settle, so "sat behind N others" (visible via
|
|
73
|
+
* `queue_depth` > 0 alongside a `response_ms` that scales with it) is
|
|
74
|
+
* distinguishable from "the child itself was slow on a solo request"
|
|
75
|
+
* (`queue_depth` === 0 with a large `response_ms`).
|
|
76
|
+
* 3. `competing_host_pid` — present only when a second, still-live
|
|
77
|
+
* `fastembedProcessHost` process is detected (BL-331's advisory lock),
|
|
78
|
+
* since that changes embed latency 25-50x independent of queueing.
|
|
31
79
|
*/
|
|
32
80
|
request<T = Record<string, unknown>>(payload: Record<string, unknown>, timeoutMs?: number): Promise<T>;
|
|
33
81
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
82
|
+
* Terminate the shared fastembed process. Intended ONLY for full process
|
|
83
|
+
* shutdown or test teardown that genuinely owns the whole process's
|
|
84
|
+
* fastembed lifecycle.
|
|
85
|
+
*
|
|
86
|
+
* (BL-405) Prefers the host protocol's own `{ __shutdown: true }` message
|
|
87
|
+
* (`fastembedProcessHost.ts`'s `process.on('message', ...)` already
|
|
88
|
+
* handles it via a clean `process.exit(0)`) over a raw `kill()` — a signal
|
|
89
|
+
* landing while the child is mid-`process.send()` for an unrelated reply
|
|
90
|
+
* is what produced the uncaught EPIPE crash this bug is named for. Bounded
|
|
91
|
+
* to `TERMINATE_GRACE_MS`: if the child doesn't exit on its own (wedged,
|
|
92
|
+
* or the IPC channel was already gone so the message silently no-op'd),
|
|
93
|
+
* `kill()` is still the fallback — this must never hang the CALLER's own
|
|
94
|
+
* shutdown sequence waiting on a child that will never exit gracefully.
|
|
37
95
|
*/
|
|
38
96
|
terminate(): Promise<void>;
|
|
39
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedFastembedProcess.d.ts","sourceRoot":"","sources":["../src/sharedFastembedProcess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"sharedFastembedProcess.d.ts","sourceRoot":"","sources":["../src/sharedFastembedProcess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAsFH,qBAAa,4BAA4B;IACvC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,eAAe,CAAsC;IAC7D,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C;;;;;;;OAOG;gBACS,gBAAgB,CAAC,EAAE,MAAM;IAIrC,8DAA8D;IAC9D,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,6EAA6E;IAC7E,OAAO,CAAC,aAAa;IAyErB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,aAAa;IAKrB,gFAAgF;IAChF,OAAO,CAAC,WAAW;IAOnB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC;IA6Db;;;;;;;;;;;;;;OAcG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CA2BjC;AAOD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,4BAA4B,CAGxE;AAED;;;;;;GAMG;AACH,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D"}
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
import { fork } from 'node:child_process';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
20
|
import { dirname, join } from 'node:path';
|
|
21
|
-
import { existsSync } from 'node:fs';
|
|
21
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
22
|
+
import { performance } from 'node:perf_hooks';
|
|
23
|
+
import { log } from '@adhd/sox-telemetry';
|
|
24
|
+
import { resolveFastembedLockPath } from './fastembedLock.js';
|
|
22
25
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
26
|
/**
|
|
24
27
|
* Resolve `fastembedProcessHost.js` regardless of whether this module is
|
|
@@ -40,11 +43,72 @@ function resolveFastembedHostPath() {
|
|
|
40
43
|
// the path that was actually attempted.
|
|
41
44
|
return sibling;
|
|
42
45
|
}
|
|
46
|
+
/** True if a process with this pid is alive (best-effort; ESRCH => dead). Not
|
|
47
|
+
* imported from `fastembedProcessHost.ts` — see `detectCompetingFastembedHost`. */
|
|
48
|
+
function isPidAlive(pid) {
|
|
49
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
50
|
+
return false;
|
|
51
|
+
try {
|
|
52
|
+
process.kill(pid, 0);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* BL-432 (signal 3, best-effort): read the BL-331 advisory lock file that
|
|
61
|
+
* `fastembedProcessHost.ts`'s `checkAndClaimFastembedLock()` writes on every
|
|
62
|
+
* host startup, and report whether it names a DIFFERENT, still-live pid from
|
|
63
|
+
* `ownPid` — i.e. a second fastembed host currently exists on this machine. A
|
|
64
|
+
* second host changes embed latency 25-50x (cross-process CoreML/ANE
|
|
65
|
+
* contention, BL-331) and nothing previously recorded whether one was
|
|
66
|
+
* present for any given embed-latency number (BL-432/BL-433's "unlabelled
|
|
67
|
+
* measurement" class).
|
|
68
|
+
*
|
|
69
|
+
* Deliberately does NOT `import` `fastembedProcessHost.ts` — that module
|
|
70
|
+
* calls `checkAndClaimFastembedLock()` and registers `process.on('message')`
|
|
71
|
+
* at module scope, both meant for the forked CHILD process, never the
|
|
72
|
+
* parent that owns this client. The lock path convention and payload shape
|
|
73
|
+
* ARE shared with that module, via the side-effect-free `./fastembedLock.js`
|
|
74
|
+
* (BL-471) — only the tiny pid-liveness check below is duplicated, since it
|
|
75
|
+
* has nothing to do with the lock file's format. Advisory only: a
|
|
76
|
+
* missing/unreadable/stale lock file is silently treated as "no competing
|
|
77
|
+
* host", never thrown — this must never be able to break or slow a real
|
|
78
|
+
* embed call.
|
|
79
|
+
*/
|
|
80
|
+
function detectCompetingFastembedHost(ownPid) {
|
|
81
|
+
try {
|
|
82
|
+
const lockPath = resolveFastembedLockPath();
|
|
83
|
+
if (!existsSync(lockPath))
|
|
84
|
+
return null;
|
|
85
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
|
|
86
|
+
const pid = typeof raw.pid === 'number' ? raw.pid : null;
|
|
87
|
+
if (pid === null || pid === ownPid || !isPidAlive(pid))
|
|
88
|
+
return null;
|
|
89
|
+
return { pid, startedAt: typeof raw.startedAt === 'string' ? raw.startedAt : 'unknown' };
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
43
95
|
export class SharedFastembedProcessClient {
|
|
44
96
|
child = null;
|
|
45
97
|
startingPromise = null;
|
|
46
98
|
nextId = 1;
|
|
47
99
|
pending = new Map();
|
|
100
|
+
hostPath;
|
|
101
|
+
/**
|
|
102
|
+
* @param hostPathOverride Test-only injection point (BL-410): points the
|
|
103
|
+
* fork target at a lightweight fixture host instead of the real
|
|
104
|
+
* `fastembedProcessHost.js`, so a test can exercise the fork/ref/unref
|
|
105
|
+
* contract without loading fastembed or downloading a model. Production
|
|
106
|
+
* code (`getSharedFastembedProcess()`) never passes this — it always
|
|
107
|
+
* resolves the real host path.
|
|
108
|
+
*/
|
|
109
|
+
constructor(hostPathOverride) {
|
|
110
|
+
this.hostPath = hostPathOverride;
|
|
111
|
+
}
|
|
48
112
|
/** True once the underlying child process has been forked. */
|
|
49
113
|
get started() {
|
|
50
114
|
return this.child !== null;
|
|
@@ -56,7 +120,7 @@ export class SharedFastembedProcessClient {
|
|
|
56
120
|
if (this.startingPromise)
|
|
57
121
|
return this.startingPromise;
|
|
58
122
|
this.startingPromise = new Promise((resolveStart) => {
|
|
59
|
-
const hostPath = resolveFastembedHostPath();
|
|
123
|
+
const hostPath = this.hostPath ?? resolveFastembedHostPath();
|
|
60
124
|
const c = fork(hostPath, [], {
|
|
61
125
|
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
|
62
126
|
// Real inference is CPU-bound in native code; no need to keep the
|
|
@@ -69,6 +133,7 @@ export class SharedFastembedProcessClient {
|
|
|
69
133
|
if (!pending)
|
|
70
134
|
return;
|
|
71
135
|
this.pending.delete(msg.id);
|
|
136
|
+
this.unrefIfIdle();
|
|
72
137
|
if ('error' in msg && typeof msg['error'] === 'string') {
|
|
73
138
|
pending.reject(new Error(msg['error']));
|
|
74
139
|
}
|
|
@@ -98,24 +163,103 @@ export class SharedFastembedProcessClient {
|
|
|
98
163
|
// listener is attached so a real process can exit when its own work is
|
|
99
164
|
// done instead of hanging on this child forever.
|
|
100
165
|
c.unref();
|
|
166
|
+
// BL-370: `ChildProcess.unref()` is NOT sufficient here, and for two
|
|
167
|
+
// years the comment above described an outcome this code did not achieve.
|
|
168
|
+
// `fork()` with `'ipc'` in `stdio` creates a SEPARATE libuv handle for the
|
|
169
|
+
// IPC channel, and unref-ing the ChildProcess does not unref that channel.
|
|
170
|
+
// The result: every process that embedded even once stayed alive forever.
|
|
171
|
+
// Observed in the wild — two probe processes still running 40+ minutes
|
|
172
|
+
// after writing their final output, each holding a resident ONNX model.
|
|
173
|
+
//
|
|
174
|
+
// That leak was not merely wasteful, it CORRUPTED DIAGNOSIS: the
|
|
175
|
+
// "[fastembed] WARNING … another fastembed host process (pid N) is ALREADY
|
|
176
|
+
// RUNNING … severe (25-50x) embed latency due to Neural Engine contention"
|
|
177
|
+
// message was repeatedly cited as evidence of real ANE contention, and at
|
|
178
|
+
// least one such warning named a leaked orphan this very defect created,
|
|
179
|
+
// idle at 0% CPU. Cross-process ANE contention remains unproven; the
|
|
180
|
+
// measured cause of the live slowdown was scheduling QoS (BL-331).
|
|
181
|
+
c.channel?.unref();
|
|
101
182
|
this.child = c;
|
|
102
183
|
resolveStart(c);
|
|
103
184
|
});
|
|
104
185
|
return this.startingPromise;
|
|
105
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* (BL-410) Ref the child process and its IPC channel while at least one
|
|
189
|
+
* request is in flight. `ensureProcess()` unrefs both immediately after
|
|
190
|
+
* forking so a long-lived *service* (memory-server) can exit cleanly
|
|
191
|
+
* without the embed child pinning it open forever (BL-370). But a
|
|
192
|
+
* **standalone script** whose only pending work is an in-flight
|
|
193
|
+
* `request()` has nothing else ref'd — Node can decide the event loop is
|
|
194
|
+
* empty and tear the process down mid-model-load, abandoning the pending
|
|
195
|
+
* promise before the child's reply ever arrives. Re-ref-ing for the
|
|
196
|
+
* duration of each in-flight request keeps such a script alive exactly
|
|
197
|
+
* long enough to receive its answer, and `unrefIfIdle()` (called from
|
|
198
|
+
* every settle path) immediately releases that ref again once nothing is
|
|
199
|
+
* outstanding — so a script with no other work still exits promptly, it
|
|
200
|
+
* just doesn't exit *before its own request completes*.
|
|
201
|
+
*/
|
|
202
|
+
refForPending() {
|
|
203
|
+
this.child?.ref();
|
|
204
|
+
this.child?.channel?.ref();
|
|
205
|
+
}
|
|
206
|
+
/** Counterpart to `refForPending()` — release the ref once `pending` drains. */
|
|
207
|
+
unrefIfIdle() {
|
|
208
|
+
if (this.pending.size === 0) {
|
|
209
|
+
this.child?.unref();
|
|
210
|
+
this.child?.channel?.unref();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
106
213
|
/**
|
|
107
214
|
* Send a request to the shared fastembed process and await its correlated
|
|
108
215
|
* response. Assigns a globally-unique `id` — the caller must NOT set its
|
|
109
216
|
* own `id` (any `id` field on `payload` is ignored/overwritten).
|
|
217
|
+
*
|
|
218
|
+
* BL-432: this is where BL-331's head-of-line-blocking question actually
|
|
219
|
+
* lives — NOT in memory-core's `embed.ts` `wait`/`work` split, which was
|
|
220
|
+
* measured (n=570) to be structurally incapable of observing it, because
|
|
221
|
+
* `wait` only covers acquiring the already-memoised provider promise.
|
|
222
|
+
* Every request beyond the first in a process contends here, on this one
|
|
223
|
+
* shared child. Three signals are emitted with every
|
|
224
|
+
* `fastembed_process.request.*` record via the `@adhd/sox-telemetry`
|
|
225
|
+
* substrate (no second telemetry mechanism):
|
|
226
|
+
* 1. `queue_depth` — `this.pending.size` measured BEFORE this request is
|
|
227
|
+
* added, i.e. how many requests are already admitted and awaiting a
|
|
228
|
+
* reply ahead of this one. The direct head-of-line-blocking signal.
|
|
229
|
+
* 2. `response_ms` — elapsed time from immediately after `child.send()`
|
|
230
|
+
* to this request's own settle, so "sat behind N others" (visible via
|
|
231
|
+
* `queue_depth` > 0 alongside a `response_ms` that scales with it) is
|
|
232
|
+
* distinguishable from "the child itself was slow on a solo request"
|
|
233
|
+
* (`queue_depth` === 0 with a large `response_ms`).
|
|
234
|
+
* 3. `competing_host_pid` — present only when a second, still-live
|
|
235
|
+
* `fastembedProcessHost` process is detected (BL-331's advisory lock),
|
|
236
|
+
* since that changes embed latency 25-50x independent of queueing.
|
|
110
237
|
*/
|
|
111
238
|
async request(payload, timeoutMs) {
|
|
112
239
|
const child = await this.ensureProcess();
|
|
113
240
|
const id = this.nextId++;
|
|
241
|
+
const queueDepth = this.pending.size;
|
|
242
|
+
const competing = detectCompetingFastembedHost(child.pid);
|
|
243
|
+
const baseFields = {
|
|
244
|
+
queue_depth: queueDepth,
|
|
245
|
+
...(competing ? { competing_host_pid: competing.pid } : {}),
|
|
246
|
+
};
|
|
114
247
|
return new Promise((resolve, reject) => {
|
|
115
248
|
let to;
|
|
249
|
+
// Reassigned synchronously (below, before `child.send()` returns) so
|
|
250
|
+
// every settle path — including the ones triggered from a totally
|
|
251
|
+
// different call site (`c.on('error')`/`c.on('exit')` above, iterating
|
|
252
|
+
// `this.pending.values()`) — closes over the correct value.
|
|
253
|
+
let sentAt = performance.now();
|
|
116
254
|
if (timeoutMs && timeoutMs > 0) {
|
|
117
255
|
to = setTimeout(() => {
|
|
118
256
|
this.pending.delete(id);
|
|
257
|
+
this.unrefIfIdle();
|
|
258
|
+
log.warn('fastembed_process.request.error', {
|
|
259
|
+
...baseFields,
|
|
260
|
+
response_ms: Math.round(performance.now() - sentAt),
|
|
261
|
+
error: `timed out after ${timeoutMs}ms`,
|
|
262
|
+
});
|
|
119
263
|
reject(new Error(`shared fastembed process request timed out after ${timeoutMs}ms`));
|
|
120
264
|
}, timeoutMs);
|
|
121
265
|
if (typeof to.unref === 'function')
|
|
@@ -125,21 +269,44 @@ export class SharedFastembedProcessClient {
|
|
|
125
269
|
resolve: (v) => {
|
|
126
270
|
if (to)
|
|
127
271
|
clearTimeout(to);
|
|
272
|
+
log.info('fastembed_process.request.finish', {
|
|
273
|
+
...baseFields,
|
|
274
|
+
response_ms: Math.round(performance.now() - sentAt),
|
|
275
|
+
});
|
|
128
276
|
resolve(v);
|
|
129
277
|
},
|
|
130
278
|
reject: (e) => {
|
|
131
279
|
if (to)
|
|
132
280
|
clearTimeout(to);
|
|
281
|
+
log.warn('fastembed_process.request.error', {
|
|
282
|
+
...baseFields,
|
|
283
|
+
response_ms: Math.round(performance.now() - sentAt),
|
|
284
|
+
error: e.message,
|
|
285
|
+
});
|
|
133
286
|
reject(e);
|
|
134
287
|
},
|
|
135
288
|
});
|
|
289
|
+
// BL-410: keep the parent's event loop ref'd until this request settles.
|
|
290
|
+
this.refForPending();
|
|
291
|
+
log.info('fastembed_process.request.admitted', baseFields);
|
|
292
|
+
sentAt = performance.now();
|
|
136
293
|
child.send({ ...payload, id });
|
|
137
294
|
});
|
|
138
295
|
}
|
|
139
296
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
297
|
+
* Terminate the shared fastembed process. Intended ONLY for full process
|
|
298
|
+
* shutdown or test teardown that genuinely owns the whole process's
|
|
299
|
+
* fastembed lifecycle.
|
|
300
|
+
*
|
|
301
|
+
* (BL-405) Prefers the host protocol's own `{ __shutdown: true }` message
|
|
302
|
+
* (`fastembedProcessHost.ts`'s `process.on('message', ...)` already
|
|
303
|
+
* handles it via a clean `process.exit(0)`) over a raw `kill()` — a signal
|
|
304
|
+
* landing while the child is mid-`process.send()` for an unrelated reply
|
|
305
|
+
* is what produced the uncaught EPIPE crash this bug is named for. Bounded
|
|
306
|
+
* to `TERMINATE_GRACE_MS`: if the child doesn't exit on its own (wedged,
|
|
307
|
+
* or the IPC channel was already gone so the message silently no-op'd),
|
|
308
|
+
* `kill()` is still the fallback — this must never hang the CALLER's own
|
|
309
|
+
* shutdown sequence waiting on a child that will never exit gracefully.
|
|
143
310
|
*/
|
|
144
311
|
async terminate() {
|
|
145
312
|
const c = this.child;
|
|
@@ -149,11 +316,32 @@ export class SharedFastembedProcessClient {
|
|
|
149
316
|
reject(new Error('shared fastembed process terminated'));
|
|
150
317
|
}
|
|
151
318
|
this.pending.clear();
|
|
152
|
-
if (c)
|
|
153
|
-
|
|
319
|
+
if (!c)
|
|
320
|
+
return;
|
|
321
|
+
const exited = new Promise((resolve) => {
|
|
322
|
+
c.once('exit', () => resolve());
|
|
323
|
+
});
|
|
324
|
+
try {
|
|
325
|
+
if (c.connected)
|
|
326
|
+
c.send({ __shutdown: true });
|
|
154
327
|
}
|
|
328
|
+
catch {
|
|
329
|
+
// IPC already gone — kill() below is the only path left.
|
|
330
|
+
}
|
|
331
|
+
const timedOut = await Promise.race([
|
|
332
|
+
exited.then(() => false),
|
|
333
|
+
new Promise((resolve) => {
|
|
334
|
+
const t = setTimeout(() => resolve(true), TERMINATE_GRACE_MS);
|
|
335
|
+
if (typeof t.unref === 'function')
|
|
336
|
+
t.unref();
|
|
337
|
+
}),
|
|
338
|
+
]);
|
|
339
|
+
if (timedOut)
|
|
340
|
+
c.kill();
|
|
155
341
|
}
|
|
156
342
|
}
|
|
343
|
+
/** (BL-405) How long `terminate()` waits for the graceful `__shutdown` message before falling back to `kill()`. */
|
|
344
|
+
const TERMINATE_GRACE_MS = 1000;
|
|
157
345
|
let _singleton = null;
|
|
158
346
|
/**
|
|
159
347
|
* Process-wide singleton accessor — the ONLY sanctioned place a fastembed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedFastembedProcess.js","sourceRoot":"","sources":["../src/sharedFastembedProcess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,IAAI,EAAqB,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"sharedFastembedProcess.js","sourceRoot":"","sources":["../src/sharedFastembedProcess.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhd/sox-embedding-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -17,18 +17,29 @@
|
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"default": "./dist/index.js"
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
24
|
"dist"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@huggingface/transformers": "^4.2.0",
|
|
27
|
-
"fastembed": "^2.1.0"
|
|
28
|
+
"fastembed": "^2.1.0",
|
|
29
|
+
"@adhd/sox-telemetry": "0.2.0"
|
|
28
30
|
},
|
|
29
31
|
"sox": {
|
|
30
32
|
"area": "data",
|
|
31
33
|
"group": "embed",
|
|
34
|
+
"sidecars": [
|
|
35
|
+
"src/embedWorker.ts",
|
|
36
|
+
"src/fastembedProcessHost.ts",
|
|
37
|
+
"src/sharedOnnxWorker.ts"
|
|
38
|
+
],
|
|
39
|
+
"sidecarExternals": [
|
|
40
|
+
"fastembed",
|
|
41
|
+
"onnxruntime-node"
|
|
42
|
+
],
|
|
32
43
|
"concerns": [
|
|
33
44
|
"text→vector (EmbeddingProvider interface)",
|
|
34
45
|
"config-driven model resolution (createEmbeddingProvider factory)",
|