harper-fabric-onnx 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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/child.d.ts +16 -0
- package/dist/child.d.ts.map +1 -0
- package/dist/child.js +241 -0
- package/dist/child.js.map +1 -0
- package/dist/download.d.ts +15 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +61 -0
- package/dist/download.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +305 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +61 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +75 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 HarperDB, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# harper-fabric-onnx
|
|
2
|
+
|
|
3
|
+
ONNX Runtime embedding wrapper for Harper. Runs inference in a dedicated child process — one model instance, one thread pool, no global state races across Harper workers.
|
|
4
|
+
|
|
5
|
+
Same public API as `harper-fabric-embeddings` so `harper-kb` can swap backends by changing one import.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install harper-fabric-onnx
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js 22+.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import {
|
|
19
|
+
downloadModel,
|
|
20
|
+
init,
|
|
21
|
+
embed,
|
|
22
|
+
embedBatch,
|
|
23
|
+
dimensions,
|
|
24
|
+
dispose,
|
|
25
|
+
} from "harper-fabric-onnx";
|
|
26
|
+
|
|
27
|
+
// Download model files (one-time)
|
|
28
|
+
await downloadModel(".models");
|
|
29
|
+
|
|
30
|
+
// Initialize — spawns child process, loads ONNX model
|
|
31
|
+
await init({ modelsDir: ".models" });
|
|
32
|
+
|
|
33
|
+
// Single embedding (768-dim, L2-normalized)
|
|
34
|
+
const vec = await embed("Hello world");
|
|
35
|
+
|
|
36
|
+
// Query-optimized embedding (uses "search_query:" prefix)
|
|
37
|
+
const queryVec = await embed("What is Harper?", "query");
|
|
38
|
+
|
|
39
|
+
// Batch embedding
|
|
40
|
+
const vecs = await embedBatch(["First text", "Second text"]);
|
|
41
|
+
|
|
42
|
+
// Get model dimensions
|
|
43
|
+
dimensions(); // 768
|
|
44
|
+
|
|
45
|
+
// Cleanup
|
|
46
|
+
await dispose();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
ONNX Runtime has a global singleton and process-wide thread pool, so it can't safely run per-worker in Harper's multi-worker architecture. This package runs ONNX in a dedicated child process and routes all worker calls to it via a Unix domain socket.
|
|
52
|
+
|
|
53
|
+
- `init()` — spawns a child process (or connects to an existing one), loads the ONNX model and tokenizer
|
|
54
|
+
- `embed()` / `embedBatch()` — sends text over the socket, child tokenizes + runs inference, returns L2-normalized vectors
|
|
55
|
+
- One child process is shared across all Harper worker threads
|
|
56
|
+
- Stale process detection via PID files — auto-recovers from crashes
|
|
57
|
+
|
|
58
|
+
## Supported models
|
|
59
|
+
|
|
60
|
+
| Model | Repo | Dimensions |
|
|
61
|
+
| ---------------------------- | -------------------------------- | ---------- |
|
|
62
|
+
| `nomic-embed-text` (default) | nomic-ai/nomic-embed-text-v1.5 | 768 |
|
|
63
|
+
| `nomic-embed-text-v2-moe` | nomic-ai/nomic-embed-text-v2-moe | 768 |
|
|
64
|
+
|
|
65
|
+
## API
|
|
66
|
+
|
|
67
|
+
### `downloadModel(dir: string, modelName?: string): Promise<string>`
|
|
68
|
+
|
|
69
|
+
Downloads model and tokenizer files from HuggingFace. Returns the model directory path.
|
|
70
|
+
|
|
71
|
+
### `init(options: InitOptions): Promise<void>`
|
|
72
|
+
|
|
73
|
+
Spawns the child process and loads the model. Options:
|
|
74
|
+
|
|
75
|
+
- `modelsDir` — directory containing model subdirectories (e.g., `.models`)
|
|
76
|
+
- `modelPath` — direct path to a specific model directory
|
|
77
|
+
- `modelName` — model name from the registry (default: `nomic-embed-text`)
|
|
78
|
+
|
|
79
|
+
### `embed(text: string, type?: 'document' | 'query'): Promise<number[]>`
|
|
80
|
+
|
|
81
|
+
Returns an L2-normalized embedding vector. Default type is `'document'`.
|
|
82
|
+
|
|
83
|
+
### `embedBatch(texts: string[], type?: 'document' | 'query'): Promise<number[][]>`
|
|
84
|
+
|
|
85
|
+
Returns an array of L2-normalized embedding vectors.
|
|
86
|
+
|
|
87
|
+
### `dimensions(): number`
|
|
88
|
+
|
|
89
|
+
Returns the dimensionality of the loaded model (e.g., 768).
|
|
90
|
+
|
|
91
|
+
### `dispose(): Promise<void>`
|
|
92
|
+
|
|
93
|
+
Shuts down the child process and cleans up socket/PID files.
|
|
94
|
+
|
|
95
|
+
## Harper component usage
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// resources.js
|
|
99
|
+
const { Resource } = globalThis;
|
|
100
|
+
|
|
101
|
+
export class Embed extends Resource {
|
|
102
|
+
static loadAsInstance = false;
|
|
103
|
+
|
|
104
|
+
async post(_query, data) {
|
|
105
|
+
const { init, embed, embedBatch, dimensions } =
|
|
106
|
+
await import("harper-fabric-onnx");
|
|
107
|
+
await init({ modelsDir: process.env.ONNX_MODELS_DIR });
|
|
108
|
+
|
|
109
|
+
if (data.texts) {
|
|
110
|
+
const vecs = await embedBatch(data.texts);
|
|
111
|
+
return { dimensions: dimensions(), vectors: vecs };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const vec = await embed(data.text, data.type);
|
|
115
|
+
return { dimensions: dimensions(), vector: vec };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
package/dist/child.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child process entry point — owns the ONNX Runtime session and tokenizer.
|
|
3
|
+
*
|
|
4
|
+
* Spawned via fork() from the proxy (index.ts). Runs outside Harper's sandbox
|
|
5
|
+
* so it can require() native modules normally.
|
|
6
|
+
*
|
|
7
|
+
* Protocol:
|
|
8
|
+
* 1. Parent sends IPC message with { socketPath, pidPath } to configure.
|
|
9
|
+
* 2. Child creates a Unix domain socket server.
|
|
10
|
+
* 3. Child writes PID file and sends { ready: true } back via IPC.
|
|
11
|
+
* 4. All subsequent communication happens over the socket using
|
|
12
|
+
* length-prefixed JSON frames (see protocol.ts).
|
|
13
|
+
* 5. Requests are processed serially — ONNX session is not thread-safe.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=child.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child.d.ts","sourceRoot":"","sources":["../src/child.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
package/dist/child.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child process entry point — owns the ONNX Runtime session and tokenizer.
|
|
3
|
+
*
|
|
4
|
+
* Spawned via fork() from the proxy (index.ts). Runs outside Harper's sandbox
|
|
5
|
+
* so it can require() native modules normally.
|
|
6
|
+
*
|
|
7
|
+
* Protocol:
|
|
8
|
+
* 1. Parent sends IPC message with { socketPath, pidPath } to configure.
|
|
9
|
+
* 2. Child creates a Unix domain socket server.
|
|
10
|
+
* 3. Child writes PID file and sends { ready: true } back via IPC.
|
|
11
|
+
* 4. All subsequent communication happens over the socket using
|
|
12
|
+
* length-prefixed JSON frames (see protocol.ts).
|
|
13
|
+
* 5. Requests are processed serially — ONNX session is not thread-safe.
|
|
14
|
+
*/
|
|
15
|
+
import { createServer } from "node:net";
|
|
16
|
+
import { writeFileSync, unlinkSync } from "node:fs";
|
|
17
|
+
import ort from "onnxruntime-node";
|
|
18
|
+
import { AutoTokenizer, } from "@huggingface/transformers";
|
|
19
|
+
import { MODELS, DEFAULT_MODEL, encodeMessage, FrameDecoder, } from "./protocol.js";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// State
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
let session = null;
|
|
25
|
+
let tokenizer = null;
|
|
26
|
+
let modelName = DEFAULT_MODEL;
|
|
27
|
+
let cachedDimensions = 0;
|
|
28
|
+
let sockPath = "";
|
|
29
|
+
let pidPath = "";
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Inference helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
function normalize(vec) {
|
|
34
|
+
let sumSq = 0;
|
|
35
|
+
for (let i = 0; i < vec.length; i++) {
|
|
36
|
+
sumSq += vec[i] * vec[i];
|
|
37
|
+
}
|
|
38
|
+
const norm = Math.sqrt(sumSq);
|
|
39
|
+
if (norm === 0)
|
|
40
|
+
return Array.from(vec);
|
|
41
|
+
const out = Array.from({ length: vec.length });
|
|
42
|
+
for (let i = 0; i < vec.length; i++) {
|
|
43
|
+
out[i] = vec[i] / norm;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function meanPool(hidden, mask, seqLen, hiddenSize) {
|
|
48
|
+
const accum = new Float64Array(hiddenSize);
|
|
49
|
+
let tokenCount = 0;
|
|
50
|
+
for (let t = 0; t < seqLen; t++) {
|
|
51
|
+
if (mask[t] === 0n)
|
|
52
|
+
continue;
|
|
53
|
+
tokenCount++;
|
|
54
|
+
const offset = t * hiddenSize;
|
|
55
|
+
for (let d = 0; d < hiddenSize; d++) {
|
|
56
|
+
accum[d] += hidden[offset + d];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (tokenCount === 0)
|
|
60
|
+
return Array.from({ length: hiddenSize }).fill(0);
|
|
61
|
+
const result = Array.from({ length: hiddenSize });
|
|
62
|
+
for (let d = 0; d < hiddenSize; d++) {
|
|
63
|
+
result[d] = accum[d] / tokenCount;
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
async function embedOne(text, embedType = "document") {
|
|
68
|
+
if (!session || !tokenizer)
|
|
69
|
+
throw new Error("Not initialized");
|
|
70
|
+
const config = MODELS[modelName];
|
|
71
|
+
const prefix = embedType === "query" ? config.prefix.query : config.prefix.document;
|
|
72
|
+
const prefixed = prefix + text;
|
|
73
|
+
const encoded = tokenizer(prefixed, {
|
|
74
|
+
padding: false,
|
|
75
|
+
truncation: true,
|
|
76
|
+
return_tensor: false,
|
|
77
|
+
});
|
|
78
|
+
const inputIds = encoded.input_ids;
|
|
79
|
+
const attentionMask = encoded.attention_mask;
|
|
80
|
+
const seqLen = inputIds.length;
|
|
81
|
+
const idsBigInt = BigInt64Array.from(inputIds, (v) => BigInt(v));
|
|
82
|
+
const maskBigInt = BigInt64Array.from(attentionMask, (v) => BigInt(v));
|
|
83
|
+
const feeds = {
|
|
84
|
+
input_ids: new ort.Tensor("int64", idsBigInt, [1, seqLen]),
|
|
85
|
+
attention_mask: new ort.Tensor("int64", maskBigInt, [1, seqLen]),
|
|
86
|
+
};
|
|
87
|
+
// Some models also expect token_type_ids
|
|
88
|
+
if (session.inputNames.includes("token_type_ids")) {
|
|
89
|
+
feeds.token_type_ids = new ort.Tensor("int64", new BigInt64Array(seqLen), [
|
|
90
|
+
1,
|
|
91
|
+
seqLen,
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
const output = await session.run(feeds);
|
|
95
|
+
// Try dedicated sentence embedding output first, fall back to mean pooling
|
|
96
|
+
if (output.sentence_embedding) {
|
|
97
|
+
return normalize(output.sentence_embedding.data);
|
|
98
|
+
}
|
|
99
|
+
const hidden = output.last_hidden_state;
|
|
100
|
+
if (!hidden)
|
|
101
|
+
throw new Error("Model output has no last_hidden_state");
|
|
102
|
+
const hiddenSize = hidden.dims[2];
|
|
103
|
+
return normalize(meanPool(hidden.data, Array.from(maskBigInt), seqLen, hiddenSize));
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Request handler
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
async function handleRequest(req) {
|
|
109
|
+
try {
|
|
110
|
+
switch (req.type) {
|
|
111
|
+
case "init": {
|
|
112
|
+
if (!session) {
|
|
113
|
+
modelName = req.modelName || DEFAULT_MODEL;
|
|
114
|
+
const config = MODELS[modelName];
|
|
115
|
+
if (!config) {
|
|
116
|
+
return {
|
|
117
|
+
id: req.id,
|
|
118
|
+
ok: false,
|
|
119
|
+
error: `Unknown model: ${modelName}`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const modelPath = join(req.modelDir, "model.onnx");
|
|
123
|
+
session = await ort.InferenceSession.create(modelPath, {
|
|
124
|
+
executionProviders: ["cpu"],
|
|
125
|
+
// 'basic' avoids fusion passes that fail on fp16 models in ORT 1.24+
|
|
126
|
+
graphOptimizationLevel: "basic",
|
|
127
|
+
});
|
|
128
|
+
tokenizer = await AutoTokenizer.from_pretrained(req.modelDir, {
|
|
129
|
+
local_files_only: true,
|
|
130
|
+
});
|
|
131
|
+
cachedDimensions = config.dimensions;
|
|
132
|
+
}
|
|
133
|
+
return { id: req.id, ok: true, result: cachedDimensions };
|
|
134
|
+
}
|
|
135
|
+
case "embed": {
|
|
136
|
+
const vec = await embedOne(req.text, req.embedType);
|
|
137
|
+
return { id: req.id, ok: true, result: vec };
|
|
138
|
+
}
|
|
139
|
+
case "batch": {
|
|
140
|
+
const results = [];
|
|
141
|
+
for (const text of req.texts) {
|
|
142
|
+
results.push(await embedOne(text, req.embedType));
|
|
143
|
+
}
|
|
144
|
+
return { id: req.id, ok: true, result: results };
|
|
145
|
+
}
|
|
146
|
+
case "dimensions": {
|
|
147
|
+
return { id: req.id, ok: true, result: cachedDimensions };
|
|
148
|
+
}
|
|
149
|
+
case "dispose": {
|
|
150
|
+
await cleanup();
|
|
151
|
+
return { id: req.id, ok: true, result: null };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return {
|
|
157
|
+
id: req.id,
|
|
158
|
+
ok: false,
|
|
159
|
+
error: err instanceof Error ? err.message : String(err),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Request queue — processes requests serially
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
let queue = Promise.resolve();
|
|
167
|
+
function enqueue(socket, req) {
|
|
168
|
+
queue = queue.then(async () => {
|
|
169
|
+
const res = await handleRequest(req);
|
|
170
|
+
if (!socket.destroyed) {
|
|
171
|
+
socket.write(encodeMessage(res));
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Socket server
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
function startServer() {
|
|
179
|
+
const server = createServer((conn) => {
|
|
180
|
+
const decoder = new FrameDecoder();
|
|
181
|
+
conn.on("data", (chunk) => {
|
|
182
|
+
decoder.push(chunk);
|
|
183
|
+
for (const msg of decoder.drain()) {
|
|
184
|
+
enqueue(conn, msg);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
// Clean up stale socket from a previous run
|
|
189
|
+
try {
|
|
190
|
+
unlinkSync(sockPath);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// not present — fine
|
|
194
|
+
}
|
|
195
|
+
server.listen(sockPath, () => {
|
|
196
|
+
writeFileSync(pidPath, String(process.pid));
|
|
197
|
+
process.send?.({ ready: true });
|
|
198
|
+
});
|
|
199
|
+
server.on("error", (err) => {
|
|
200
|
+
console.error("[harper-fabric-onnx child] server error:", err.message);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
process.on("SIGTERM", () => {
|
|
204
|
+
server.close(() => cleanup().then(() => process.exit(0)));
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Cleanup
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
async function cleanup() {
|
|
211
|
+
try {
|
|
212
|
+
session?.release();
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// ignore
|
|
216
|
+
}
|
|
217
|
+
session = null;
|
|
218
|
+
tokenizer = null;
|
|
219
|
+
try {
|
|
220
|
+
unlinkSync(sockPath);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// may already be gone
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
unlinkSync(pidPath);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// may already be gone
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Entry point — wait for config from parent via IPC
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
process.once("message", (msg) => {
|
|
236
|
+
const config = msg;
|
|
237
|
+
sockPath = config.socketPath;
|
|
238
|
+
pidPath = config.pidPath;
|
|
239
|
+
startServer();
|
|
240
|
+
});
|
|
241
|
+
//# sourceMappingURL=child.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child.js","sourceRoot":"","sources":["../src/child.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and tokenizer download from HuggingFace.
|
|
3
|
+
*
|
|
4
|
+
* Downloads ONNX model + tokenizer files to a model-specific subdirectory.
|
|
5
|
+
* Uses streaming download with atomic rename to prevent partial files.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Download an ONNX model and its tokenizer files from HuggingFace.
|
|
9
|
+
*
|
|
10
|
+
* @param dir - Parent directory for model storage
|
|
11
|
+
* @param modelName - Model name from the built-in registry
|
|
12
|
+
* @returns Path to the model directory containing model.onnx and tokenizer files
|
|
13
|
+
*/
|
|
14
|
+
export declare function downloadModel(dir: string, modelName?: string): Promise<string>;
|
|
15
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,SAAS,SAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CA4BjB"}
|
package/dist/download.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and tokenizer download from HuggingFace.
|
|
3
|
+
*
|
|
4
|
+
* Downloads ONNX model + tokenizer files to a model-specific subdirectory.
|
|
5
|
+
* Uses streaming download with atomic rename to prevent partial files.
|
|
6
|
+
*/
|
|
7
|
+
import { createWriteStream, existsSync, mkdirSync } from "node:fs";
|
|
8
|
+
import { rename, unlink } from "node:fs/promises";
|
|
9
|
+
import { pipeline } from "node:stream/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { MODELS, DEFAULT_MODEL } from "./protocol.js";
|
|
12
|
+
/**
|
|
13
|
+
* Download an ONNX model and its tokenizer files from HuggingFace.
|
|
14
|
+
*
|
|
15
|
+
* @param dir - Parent directory for model storage
|
|
16
|
+
* @param modelName - Model name from the built-in registry
|
|
17
|
+
* @returns Path to the model directory containing model.onnx and tokenizer files
|
|
18
|
+
*/
|
|
19
|
+
export async function downloadModel(dir, modelName = DEFAULT_MODEL) {
|
|
20
|
+
const config = MODELS[modelName];
|
|
21
|
+
if (!config) {
|
|
22
|
+
throw new Error(`Unknown model: ${modelName}. Available: ${Object.keys(MODELS).join(", ")}`);
|
|
23
|
+
}
|
|
24
|
+
const modelDir = join(dir, modelName);
|
|
25
|
+
mkdirSync(modelDir, { recursive: true });
|
|
26
|
+
// Download ONNX model file
|
|
27
|
+
const onnxDest = join(modelDir, "model.onnx");
|
|
28
|
+
if (!existsSync(onnxDest)) {
|
|
29
|
+
const onnxUrl = `https://huggingface.co/${config.repo}/resolve/main/${config.onnxFile}`;
|
|
30
|
+
await downloadFile(onnxUrl, onnxDest, config.onnxFile);
|
|
31
|
+
}
|
|
32
|
+
// Download tokenizer files
|
|
33
|
+
for (const file of config.tokenizerFiles) {
|
|
34
|
+
const dest = join(modelDir, file);
|
|
35
|
+
if (!existsSync(dest)) {
|
|
36
|
+
const url = `https://huggingface.co/${config.repo}/resolve/main/${file}`;
|
|
37
|
+
await downloadFile(url, dest, file);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return modelDir;
|
|
41
|
+
}
|
|
42
|
+
async function downloadFile(url, destPath, label) {
|
|
43
|
+
const tmpPath = destPath + ".downloading";
|
|
44
|
+
console.log(`[harper-fabric-onnx] Downloading ${label}...`);
|
|
45
|
+
const response = await fetch(url, { redirect: "follow" });
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText} — ${url}`);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const fileStream = createWriteStream(tmpPath);
|
|
51
|
+
await pipeline(response.body, fileStream);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
await unlink(tmpPath).catch(() => { });
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
// Atomic rename on same filesystem
|
|
58
|
+
await rename(tmpPath, destPath);
|
|
59
|
+
console.log(`[harper-fabric-onnx] Downloaded ${label} to ${destPath}`);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,SAAS,GAAG,aAAa;IAEzB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,kBAAkB,SAAS,gBAAgB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACtC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzC,2BAA2B;IAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,0BAA0B,MAAM,CAAC,IAAI,iBAAiB,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxF,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED,2BAA2B;IAC3B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,0BAA0B,MAAM,CAAC,IAAI,iBAAiB,IAAI,EAAE,CAAC;YACzE,MAAM,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,GAAW,EACX,QAAgB,EAChB,KAAa;IAEb,MAAM,OAAO,GAAG,QAAQ,GAAG,cAAc,CAAC;IAE1C,OAAO,CAAC,GAAG,CAAC,oCAAoC,KAAK,KAAK,CAAC,CAAC;IAE5D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,oBAAoB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,GAAG,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAK,EAAE,UAAU,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,mCAAmC;IACnC,MAAM,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,mCAAmC,KAAK,OAAO,QAAQ,EAAE,CAAC,CAAC;AACzE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* harper-fabric-onnx
|
|
3
|
+
*
|
|
4
|
+
* ONNX Runtime embedding wrapper for Harper Fabric.
|
|
5
|
+
* Runs inference in a dedicated child process — one model instance,
|
|
6
|
+
* one thread pool, no global state races across Harper workers.
|
|
7
|
+
*
|
|
8
|
+
* Same public API as harper-fabric-embeddings so harper-kb can swap
|
|
9
|
+
* backends by changing one import.
|
|
10
|
+
*/
|
|
11
|
+
import { type InitOptions } from "./protocol.js";
|
|
12
|
+
export type { InitOptions } from "./protocol.js";
|
|
13
|
+
export { downloadModel } from "./download.js";
|
|
14
|
+
export { MODELS, DEFAULT_MODEL } from "./protocol.js";
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the embedding engine.
|
|
17
|
+
*
|
|
18
|
+
* Provide either `modelPath` (absolute path to an ONNX model directory) or
|
|
19
|
+
* `modelsDir` (directory to search/download into) + optional `modelName`.
|
|
20
|
+
*
|
|
21
|
+
* Safe to call concurrently — concurrent callers share the same initialization.
|
|
22
|
+
*/
|
|
23
|
+
export declare function init(options: InitOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Generate an embedding vector for the given text.
|
|
26
|
+
*
|
|
27
|
+
* Returns an L2-normalized vector as number[].
|
|
28
|
+
*/
|
|
29
|
+
export declare function embed(text: string, type?: "document" | "query"): Promise<number[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Generate embedding vectors for multiple texts.
|
|
32
|
+
*/
|
|
33
|
+
export declare function embedBatch(texts: string[], type?: "document" | "query"): Promise<number[][]>;
|
|
34
|
+
/**
|
|
35
|
+
* Get the embedding vector dimensionality.
|
|
36
|
+
*/
|
|
37
|
+
export declare function dimensions(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Shut down the child process and clean up resources.
|
|
40
|
+
*/
|
|
41
|
+
export declare function dispose(): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,EAGL,KAAK,WAAW,EAOjB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAuBtD;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9D;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,UAAU,GAAG,OAAoB,GACtC,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE,UAAU,GAAG,OAAoB,GACtC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAQrB;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAqC7C"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* harper-fabric-onnx
|
|
3
|
+
*
|
|
4
|
+
* ONNX Runtime embedding wrapper for Harper Fabric.
|
|
5
|
+
* Runs inference in a dedicated child process — one model instance,
|
|
6
|
+
* one thread pool, no global state races across Harper workers.
|
|
7
|
+
*
|
|
8
|
+
* Same public API as harper-fabric-embeddings so harper-kb can swap
|
|
9
|
+
* backends by changing one import.
|
|
10
|
+
*/
|
|
11
|
+
import { connect } from "node:net";
|
|
12
|
+
import { fork } from "node:child_process";
|
|
13
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
14
|
+
import { dirname, join } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { MODELS, DEFAULT_MODEL, socketPath, pidFilePath, encodeMessage, FrameDecoder, } from "./protocol.js";
|
|
17
|
+
export { downloadModel } from "./download.js";
|
|
18
|
+
export { MODELS, DEFAULT_MODEL } from "./protocol.js";
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// State
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
let socket = null;
|
|
23
|
+
let child = null;
|
|
24
|
+
let decoder = null;
|
|
25
|
+
let nextId = 1;
|
|
26
|
+
let cachedDimensions = 0;
|
|
27
|
+
let disposed = false;
|
|
28
|
+
let initPromise = null;
|
|
29
|
+
let modelsDir = "";
|
|
30
|
+
const pending = new Map();
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Public API
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the embedding engine.
|
|
36
|
+
*
|
|
37
|
+
* Provide either `modelPath` (absolute path to an ONNX model directory) or
|
|
38
|
+
* `modelsDir` (directory to search/download into) + optional `modelName`.
|
|
39
|
+
*
|
|
40
|
+
* Safe to call concurrently — concurrent callers share the same initialization.
|
|
41
|
+
*/
|
|
42
|
+
export async function init(options) {
|
|
43
|
+
if (initPromise)
|
|
44
|
+
return initPromise;
|
|
45
|
+
initPromise = doInit(options);
|
|
46
|
+
try {
|
|
47
|
+
await initPromise;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
initPromise = null;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generate an embedding vector for the given text.
|
|
56
|
+
*
|
|
57
|
+
* Returns an L2-normalized vector as number[].
|
|
58
|
+
*/
|
|
59
|
+
export async function embed(text, type = "document") {
|
|
60
|
+
assertReady();
|
|
61
|
+
return sendRequest({
|
|
62
|
+
id: nextId++,
|
|
63
|
+
type: "embed",
|
|
64
|
+
text,
|
|
65
|
+
embedType: type,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate embedding vectors for multiple texts.
|
|
70
|
+
*/
|
|
71
|
+
export async function embedBatch(texts, type = "document") {
|
|
72
|
+
assertReady();
|
|
73
|
+
return sendRequest({
|
|
74
|
+
id: nextId++,
|
|
75
|
+
type: "batch",
|
|
76
|
+
texts,
|
|
77
|
+
embedType: type,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the embedding vector dimensionality.
|
|
82
|
+
*/
|
|
83
|
+
export function dimensions() {
|
|
84
|
+
if (!cachedDimensions) {
|
|
85
|
+
throw new Error("Not initialized. Call init() first.");
|
|
86
|
+
}
|
|
87
|
+
return cachedDimensions;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Shut down the child process and clean up resources.
|
|
91
|
+
*/
|
|
92
|
+
export async function dispose() {
|
|
93
|
+
disposed = true;
|
|
94
|
+
initPromise = null;
|
|
95
|
+
if (socket && !socket.destroyed) {
|
|
96
|
+
try {
|
|
97
|
+
await sendRequest({ id: nextId++, type: "dispose" });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// child may already be gone
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
socket?.destroy();
|
|
104
|
+
socket = null;
|
|
105
|
+
decoder = null;
|
|
106
|
+
if (child) {
|
|
107
|
+
child.kill("SIGTERM");
|
|
108
|
+
child = null;
|
|
109
|
+
}
|
|
110
|
+
// Clean up files
|
|
111
|
+
if (modelsDir) {
|
|
112
|
+
try {
|
|
113
|
+
unlinkSync(socketPath(modelsDir));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// ignore
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(pidFilePath(modelsDir));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// ignore
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
cachedDimensions = 0;
|
|
126
|
+
pending.clear();
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Internals
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
function assertReady() {
|
|
132
|
+
if (!socket || socket.destroyed) {
|
|
133
|
+
throw new Error("Not initialized. Call init() first.");
|
|
134
|
+
}
|
|
135
|
+
if (disposed) {
|
|
136
|
+
throw new Error("Engine has been disposed.");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function doInit(options) {
|
|
140
|
+
if (socket && !socket.destroyed)
|
|
141
|
+
return;
|
|
142
|
+
const { modelPath, modelsDir: dir, modelName = DEFAULT_MODEL } = options;
|
|
143
|
+
if (!MODELS[modelName]) {
|
|
144
|
+
throw new Error(`Unknown model: ${modelName}. Available: ${Object.keys(MODELS).join(", ")}`);
|
|
145
|
+
}
|
|
146
|
+
// Resolve model directory
|
|
147
|
+
let modelDir;
|
|
148
|
+
if (modelPath) {
|
|
149
|
+
modelDir = modelPath;
|
|
150
|
+
}
|
|
151
|
+
else if (dir) {
|
|
152
|
+
modelDir = join(dir, modelName);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw new Error("Either modelPath or modelsDir is required");
|
|
156
|
+
}
|
|
157
|
+
modelsDir = dir || modelDir;
|
|
158
|
+
const sock = socketPath(modelsDir);
|
|
159
|
+
const pid = pidFilePath(modelsDir);
|
|
160
|
+
// Try connecting to an existing child process
|
|
161
|
+
const connected = await tryConnect(sock);
|
|
162
|
+
if (!connected) {
|
|
163
|
+
// Check for stale PID file
|
|
164
|
+
if (existsSync(pid)) {
|
|
165
|
+
const oldPid = parseInt(readFileSync(pid, "utf-8").trim(), 10);
|
|
166
|
+
if (!isProcessAlive(oldPid)) {
|
|
167
|
+
try {
|
|
168
|
+
unlinkSync(pid);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// ignore
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
unlinkSync(sock);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// ignore
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Spawn child process
|
|
182
|
+
const childScript = join(dirname(fileURLToPath(import.meta.url)), "child.js");
|
|
183
|
+
child = fork(childScript, [], {
|
|
184
|
+
stdio: ["pipe", "inherit", "inherit", "ipc"],
|
|
185
|
+
// PR #210 (Spawn Wisely): Harper's sandboxed child_process requires
|
|
186
|
+
// a name option for all spawns. fork() is always allowed but still
|
|
187
|
+
// needs a name for singleton PID-file tracking.
|
|
188
|
+
name: "harper-fabric-onnx",
|
|
189
|
+
});
|
|
190
|
+
// Wait for child to signal ready
|
|
191
|
+
await new Promise((resolve, reject) => {
|
|
192
|
+
const timeout = setTimeout(() => {
|
|
193
|
+
reject(new Error("Child process startup timed out"));
|
|
194
|
+
}, 30_000);
|
|
195
|
+
child.once("message", (msg) => {
|
|
196
|
+
clearTimeout(timeout);
|
|
197
|
+
if (msg.ready) {
|
|
198
|
+
resolve();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
reject(new Error("Unexpected message from child"));
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
child.once("error", (err) => {
|
|
205
|
+
clearTimeout(timeout);
|
|
206
|
+
reject(err);
|
|
207
|
+
});
|
|
208
|
+
child.once("exit", (code) => {
|
|
209
|
+
clearTimeout(timeout);
|
|
210
|
+
reject(new Error(`Child process exited with code ${code}`));
|
|
211
|
+
});
|
|
212
|
+
// Send config to child
|
|
213
|
+
child.send({ socketPath: sock, pidPath: pid });
|
|
214
|
+
});
|
|
215
|
+
// Connect to the socket
|
|
216
|
+
const retryConnected = await tryConnect(sock);
|
|
217
|
+
if (!retryConnected) {
|
|
218
|
+
throw new Error("Failed to connect to child process socket");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Send init request
|
|
222
|
+
const result = await sendRequest({
|
|
223
|
+
id: nextId++,
|
|
224
|
+
type: "init",
|
|
225
|
+
modelDir,
|
|
226
|
+
modelName,
|
|
227
|
+
});
|
|
228
|
+
cachedDimensions = result;
|
|
229
|
+
disposed = false;
|
|
230
|
+
}
|
|
231
|
+
function tryConnect(sock) {
|
|
232
|
+
return new Promise((resolve) => {
|
|
233
|
+
if (!existsSync(sock)) {
|
|
234
|
+
resolve(false);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const conn = connect(sock);
|
|
238
|
+
const timeout = setTimeout(() => {
|
|
239
|
+
conn.destroy();
|
|
240
|
+
resolve(false);
|
|
241
|
+
}, 2_000);
|
|
242
|
+
conn.once("connect", () => {
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
setupSocket(conn);
|
|
245
|
+
resolve(true);
|
|
246
|
+
});
|
|
247
|
+
conn.once("error", () => {
|
|
248
|
+
clearTimeout(timeout);
|
|
249
|
+
conn.destroy();
|
|
250
|
+
resolve(false);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function setupSocket(conn) {
|
|
255
|
+
socket = conn;
|
|
256
|
+
decoder = new FrameDecoder();
|
|
257
|
+
conn.on("data", (chunk) => {
|
|
258
|
+
decoder.push(chunk);
|
|
259
|
+
for (const msg of decoder.drain()) {
|
|
260
|
+
const res = msg;
|
|
261
|
+
const entry = pending.get(res.id);
|
|
262
|
+
if (!entry)
|
|
263
|
+
continue;
|
|
264
|
+
pending.delete(res.id);
|
|
265
|
+
if (res.ok) {
|
|
266
|
+
entry.resolve(res.result);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
entry.reject(new Error(res.error));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
conn.on("close", () => {
|
|
274
|
+
for (const [id, entry] of pending) {
|
|
275
|
+
entry.reject(new Error("Connection closed"));
|
|
276
|
+
pending.delete(id);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
conn.on("error", (err) => {
|
|
280
|
+
for (const [id, entry] of pending) {
|
|
281
|
+
entry.reject(err);
|
|
282
|
+
pending.delete(id);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function sendRequest(req) {
|
|
287
|
+
return new Promise((resolve, reject) => {
|
|
288
|
+
if (!socket || socket.destroyed) {
|
|
289
|
+
reject(new Error("Not connected"));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
pending.set(req.id, { resolve, reject });
|
|
293
|
+
socket.write(encodeMessage(req));
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function isProcessAlive(pid) {
|
|
297
|
+
try {
|
|
298
|
+
process.kill(pid, 0);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface InitOptions {
|
|
2
|
+
modelPath?: string;
|
|
3
|
+
modelsDir?: string;
|
|
4
|
+
modelName?: string;
|
|
5
|
+
}
|
|
6
|
+
export type Request = {
|
|
7
|
+
id: number;
|
|
8
|
+
type: "init";
|
|
9
|
+
modelDir: string;
|
|
10
|
+
modelName: string;
|
|
11
|
+
} | {
|
|
12
|
+
id: number;
|
|
13
|
+
type: "embed";
|
|
14
|
+
text: string;
|
|
15
|
+
embedType?: "document" | "query";
|
|
16
|
+
} | {
|
|
17
|
+
id: number;
|
|
18
|
+
type: "batch";
|
|
19
|
+
texts: string[];
|
|
20
|
+
embedType?: "document" | "query";
|
|
21
|
+
} | {
|
|
22
|
+
id: number;
|
|
23
|
+
type: "dimensions";
|
|
24
|
+
} | {
|
|
25
|
+
id: number;
|
|
26
|
+
type: "dispose";
|
|
27
|
+
};
|
|
28
|
+
export type Response = {
|
|
29
|
+
id: number;
|
|
30
|
+
ok: true;
|
|
31
|
+
result: number[] | number[][] | number | null;
|
|
32
|
+
} | {
|
|
33
|
+
id: number;
|
|
34
|
+
ok: false;
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
|
37
|
+
export interface ModelConfig {
|
|
38
|
+
repo: string;
|
|
39
|
+
onnxFile: string;
|
|
40
|
+
tokenizerFiles: string[];
|
|
41
|
+
dimensions: number;
|
|
42
|
+
prefix: {
|
|
43
|
+
document: string;
|
|
44
|
+
query: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export declare const MODELS: Record<string, ModelConfig>;
|
|
48
|
+
export declare const DEFAULT_MODEL = "nomic-embed-text";
|
|
49
|
+
export declare function socketPath(modelsDir: string): string;
|
|
50
|
+
export declare function pidFilePath(modelsDir: string): string;
|
|
51
|
+
export declare function encodeMessage(msg: Request | Response): Buffer;
|
|
52
|
+
/**
|
|
53
|
+
* Streaming decoder: feed it chunks via `push()`, pull complete messages
|
|
54
|
+
* from `drain()`. Handles partial reads across TCP boundaries.
|
|
55
|
+
*/
|
|
56
|
+
export declare class FrameDecoder {
|
|
57
|
+
private buffer;
|
|
58
|
+
push(chunk: Buffer): void;
|
|
59
|
+
drain(): (Request | Response)[];
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,OAAO,GACf;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACjE;IACE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;CAClC,GACD;IACE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;CAClC,GACD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAClC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEpC,MAAM,MAAM,QAAQ,GAChB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAM7C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAuB9C,CAAC;AAEF,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAWhD,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErD;AAOD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAM7D;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAmB;IAEjC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,EAAE;CAWhC"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export const MODELS = {
|
|
5
|
+
"nomic-embed-text": {
|
|
6
|
+
repo: "nomic-ai/nomic-embed-text-v1.5",
|
|
7
|
+
onnxFile: "onnx/model_fp16.onnx",
|
|
8
|
+
tokenizerFiles: [
|
|
9
|
+
"tokenizer.json",
|
|
10
|
+
"tokenizer_config.json",
|
|
11
|
+
"special_tokens_map.json",
|
|
12
|
+
],
|
|
13
|
+
dimensions: 768,
|
|
14
|
+
prefix: { document: "search_document: ", query: "search_query: " },
|
|
15
|
+
},
|
|
16
|
+
"nomic-embed-text-v2-moe": {
|
|
17
|
+
repo: "nomic-ai/nomic-embed-text-v2-moe",
|
|
18
|
+
onnxFile: "onnx/model_fp16.onnx",
|
|
19
|
+
tokenizerFiles: [
|
|
20
|
+
"tokenizer.json",
|
|
21
|
+
"tokenizer_config.json",
|
|
22
|
+
"special_tokens_map.json",
|
|
23
|
+
],
|
|
24
|
+
dimensions: 768,
|
|
25
|
+
prefix: { document: "search_document: ", query: "search_query: " },
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export const DEFAULT_MODEL = "nomic-embed-text";
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Socket / PID path helpers — deterministic from modelsDir so multiple
|
|
31
|
+
// instances with different model directories don't collide.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
function pathHash(input) {
|
|
34
|
+
return createHash("md5").update(input).digest("hex").slice(0, 8);
|
|
35
|
+
}
|
|
36
|
+
export function socketPath(modelsDir) {
|
|
37
|
+
return join(tmpdir(), `harper-onnx-${pathHash(modelsDir)}.sock`);
|
|
38
|
+
}
|
|
39
|
+
export function pidFilePath(modelsDir) {
|
|
40
|
+
return join(tmpdir(), `harper-onnx-${pathHash(modelsDir)}.pid`);
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Length-prefixed framing — 4-byte uint32 big-endian length + JSON payload.
|
|
44
|
+
// Handles TCP stream fragmentation so we always get complete messages.
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
export function encodeMessage(msg) {
|
|
47
|
+
const json = Buffer.from(JSON.stringify(msg));
|
|
48
|
+
const frame = Buffer.allocUnsafe(4 + json.length);
|
|
49
|
+
frame.writeUInt32BE(json.length, 0);
|
|
50
|
+
json.copy(frame, 4);
|
|
51
|
+
return frame;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Streaming decoder: feed it chunks via `push()`, pull complete messages
|
|
55
|
+
* from `drain()`. Handles partial reads across TCP boundaries.
|
|
56
|
+
*/
|
|
57
|
+
export class FrameDecoder {
|
|
58
|
+
buffer = Buffer.alloc(0);
|
|
59
|
+
push(chunk) {
|
|
60
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
61
|
+
}
|
|
62
|
+
drain() {
|
|
63
|
+
const messages = [];
|
|
64
|
+
while (this.buffer.length >= 4) {
|
|
65
|
+
const len = this.buffer.readUInt32BE(0);
|
|
66
|
+
if (this.buffer.length < 4 + len)
|
|
67
|
+
break;
|
|
68
|
+
const json = this.buffer.subarray(4, 4 + len);
|
|
69
|
+
this.buffer = this.buffer.subarray(4 + len);
|
|
70
|
+
messages.push(JSON.parse(json.toString()));
|
|
71
|
+
}
|
|
72
|
+
return messages;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "harper-fabric-onnx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ONNX Runtime embedding wrapper for Harper Fabric. Runs inference in a dedicated child process — one model instance, one thread pool, no global state races.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "npm run build && node --test test/index.test.js",
|
|
17
|
+
"test:integration": "npm run build && node scripts/test-integration.js",
|
|
18
|
+
"test:harper": "npm run build && node --experimental-strip-types --test test/harper.test.mts",
|
|
19
|
+
"lint": "oxlint --deny-warnings .",
|
|
20
|
+
"lint:fix": "oxlint --deny-warnings --fix .",
|
|
21
|
+
"format": "prettier --write .",
|
|
22
|
+
"format:check": "prettier --check .",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"onnx",
|
|
27
|
+
"embeddings",
|
|
28
|
+
"harper",
|
|
29
|
+
"fabric",
|
|
30
|
+
"vector"
|
|
31
|
+
],
|
|
32
|
+
"author": "Nathan Heskew <nathan@heskew.dev> (https://heskew.dev)",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/heskew/harper-fabric-onnx"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@huggingface/transformers": "^3.4.0",
|
|
40
|
+
"onnxruntime-node": "^1.21.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@harperdb/code-guidelines": "^0.0.6",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"harper": "file:./harper-5.0.0-unreleased.tgz",
|
|
46
|
+
"oxlint": "^1.31.0",
|
|
47
|
+
"prettier": "^3.7.0",
|
|
48
|
+
"typescript": "^5.7.0"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"README.md",
|
|
56
|
+
"LICENSE"
|
|
57
|
+
]
|
|
58
|
+
}
|