dsh-prime-memory 0.18.5 → 0.18.6
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/CHANGELOG.md +11 -28
- package/dist/contract.d.ts +1 -109
- package/dist/hooks/recall.d.ts +1 -4
- package/dist/hooks/recall.js +1 -7
- package/dist/hooks/slot-recall.js +24 -1
- package/dist/index.js +4 -14
- package/dist/metadata-validators.d.ts +0 -5
- package/dist/metadata-validators.js +1 -11
- package/dist/pipeline/ruminate.d.ts +1 -11
- package/dist/pipeline/ruminate.js +1 -18
- package/dist/projection/slots.js +9 -14
- package/dist/stats.d.ts +2 -26
- package/dist/stats.js +13 -261
- package/dist/store/l1.d.ts +0 -34
- package/dist/store/l1.js +1 -68
- package/dist/store/sqlite.d.ts +0 -4
- package/dist/store/sqlite.js +0 -20
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/index.js +2 -224
- package/dist/types.d.ts +0 -4
- package/package.json +1 -1
- package/dist/client.js +0 -6274
- package/dist/config.d.ts +0 -1407
- package/dist/embedding-worker.cjs +0 -176
- package/dist/index.d.ts +0 -999
- package/dist/room-review.d.ts +0 -196
- package/dist/room-review.js +0 -281
- package/dist/runtime-package-lock.json +0 -982
- package/dist/store/recall-usage.d.ts +0 -43
- package/dist/store/recall-usage.js +0 -149
- package/dist/store/rooms-registry.d.ts +0 -66
- package/dist/store/rooms-registry.js +0 -250
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 本地嵌入 worker:transformers.js 的模型加载与推理在本线程执行,主线程只
|
|
3
|
-
* 投递文本、收回向量。onnxruntime-node 的 run/loadModel 是 setImmediate 回调里
|
|
4
|
-
* 的同步调用(Promise 包装不卸载计算),留在主线程会冻结宿主事件循环——
|
|
5
|
-
* dsh 页面一切交互无响应数秒的根因。
|
|
6
|
-
*
|
|
7
|
-
* 协议(与 src/store/local-embedding.ts 的 RealWorkerChannel 配对;自增 id):
|
|
8
|
-
* - 入向:{id,type:'ping'} | {id,type:'warmup'} | {id,type:'embed',texts,priority}
|
|
9
|
-
* - 出向:{id,ok:true,type:'pong'|'ready'|'embedded',vectors?}(向量经
|
|
10
|
-
* transfer 列表零拷贝回传)| {id,ok:false,stage:'load'|'infer',error}
|
|
11
|
-
* | {type:'fatal',error}
|
|
12
|
-
*
|
|
13
|
-
* 调度:单线程串行。embedBatch 拆成逐条 job、条间 setImmediate 让路——
|
|
14
|
-
* 单条请求(priority,即召回 query)unshift 插队,最坏只等"正在算的那一条",
|
|
15
|
-
* 不会被 reindex 批次(16/32 条 ≈5-10s)堵在队尾。
|
|
16
|
-
*
|
|
17
|
-
* 信任边界(沿 AGENTS.md 审查记录):runtime/ 目录在数据目录信任边界内,
|
|
18
|
-
* 本线程按其 package.json createRequire 加载 transformers——对数据目录有写
|
|
19
|
-
* 权限的进程理论上可植入恶意模块;模型文件逐文件 sha256 锁定,运行时代码
|
|
20
|
-
* 无同级防护,完整修复需安装指纹设计,暂以文档化假设为准。
|
|
21
|
-
*/
|
|
22
|
-
'use strict';
|
|
23
|
-
|
|
24
|
-
const { parentPort, workerData } = require('node:worker_threads');
|
|
25
|
-
const path = require('node:path');
|
|
26
|
-
const { createRequire } = require('node:module');
|
|
27
|
-
|
|
28
|
-
/** 错误消息归一化(Error 取 message,其余 String 化)。 */
|
|
29
|
-
function errMsg(err) {
|
|
30
|
-
return err && err.message ? err.message : String(err);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const cfg = workerData || {};
|
|
34
|
-
const RUNTIME_DIR = typeof cfg.runtimeDir === 'string' ? cfg.runtimeDir : '';
|
|
35
|
-
const MODEL_DIR = typeof cfg.modelDir === 'string' ? cfg.modelDir : '';
|
|
36
|
-
const POOLING = cfg.pooling === 'cls' ? 'cls' : 'mean';
|
|
37
|
-
const DTYPE = typeof cfg.dtype === 'string' ? cfg.dtype : 'q8';
|
|
38
|
-
const MAX_INPUT_CHARS = cfg.maxInputChars > 0 ? cfg.maxInputChars : 5000;
|
|
39
|
-
|
|
40
|
-
let extractor = null; // pipeline('feature-extraction', …)
|
|
41
|
-
let loadState = 'idle'; // idle | loading | ready | failed
|
|
42
|
-
let loadPromise = null;
|
|
43
|
-
let loadError = null;
|
|
44
|
-
|
|
45
|
-
// ── 模型懒加载(首请求触发;失败后经 warmup 可重试,embed 请求失败快返回) ──
|
|
46
|
-
function ensureLoaded() {
|
|
47
|
-
if (loadState === 'ready') return Promise.resolve();
|
|
48
|
-
if (loadState === 'loading' && loadPromise) return loadPromise;
|
|
49
|
-
loadState = 'loading';
|
|
50
|
-
loadError = null;
|
|
51
|
-
loadPromise = (async () => {
|
|
52
|
-
try {
|
|
53
|
-
if (!RUNTIME_DIR || !MODEL_DIR) throw new Error('workerData 缺少 runtimeDir/modelDir');
|
|
54
|
-
const req = createRequire(path.join(RUNTIME_DIR, 'package.json'));
|
|
55
|
-
const mod = req('@huggingface/transformers');
|
|
56
|
-
if (mod.env) {
|
|
57
|
-
mod.env.allowRemoteModels = false;
|
|
58
|
-
if (mod.env.allowLocalModels !== undefined) mod.env.allowLocalModels = true;
|
|
59
|
-
}
|
|
60
|
-
extractor = await mod.pipeline('feature-extraction', MODEL_DIR, { dtype: DTYPE });
|
|
61
|
-
loadState = 'ready';
|
|
62
|
-
} catch (err) {
|
|
63
|
-
loadState = 'failed';
|
|
64
|
-
loadError = errMsg(err);
|
|
65
|
-
loadPromise = null; // 失败后下一次 warmup 重新走加载
|
|
66
|
-
throw err;
|
|
67
|
-
}
|
|
68
|
-
})();
|
|
69
|
-
return loadPromise;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ── 推理队列(逐条 job;priority 插队;条间让路) ──
|
|
73
|
-
const queue = []; // [{ request, index, text }]
|
|
74
|
-
let pumping = false;
|
|
75
|
-
|
|
76
|
-
function postReply(msg, transfer) {
|
|
77
|
-
parentPort.postMessage(msg, transfer || []);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** 请求全部条目完成 → 回执(transfer 向量缓冲零拷贝)。 */
|
|
81
|
-
function settle(request) {
|
|
82
|
-
if (request.failed || request.remaining !== 0) return;
|
|
83
|
-
postReply(
|
|
84
|
-
{ id: request.id, ok: true, type: 'embedded', vectors: request.vectors },
|
|
85
|
-
request.vectors.map((v) => v.buffer),
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** 首个失败即回执错误,并移除该请求尚未开跑的条目(正在跑的至多 1 条,算完即弃)。 */
|
|
90
|
-
function failRequest(request, error, stage) {
|
|
91
|
-
if (request.failed) return;
|
|
92
|
-
request.failed = true;
|
|
93
|
-
for (let i = queue.length - 1; i >= 0; i--) {
|
|
94
|
-
if (queue[i].request === request) queue.splice(i, 1);
|
|
95
|
-
}
|
|
96
|
-
postReply({ id: request.id, ok: false, stage: stage, error: errMsg(error) });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function pump() {
|
|
100
|
-
if (pumping) return;
|
|
101
|
-
pumping = true;
|
|
102
|
-
try {
|
|
103
|
-
while (queue.length > 0) {
|
|
104
|
-
const job = queue.shift();
|
|
105
|
-
if (job.request.failed) continue;
|
|
106
|
-
try {
|
|
107
|
-
const result = await extractor([job.text.slice(0, MAX_INPUT_CHARS)], { pooling: POOLING, normalize: true });
|
|
108
|
-
const data = result[0] && result[0].data;
|
|
109
|
-
job.request.vectors[job.index] = data instanceof Float32Array ? data : new Float32Array(data);
|
|
110
|
-
job.request.remaining -= 1;
|
|
111
|
-
settle(job.request);
|
|
112
|
-
} catch (err) {
|
|
113
|
-
failRequest(job.request, err, 'infer');
|
|
114
|
-
}
|
|
115
|
-
// 条间让路:交还本线程事件循环,新消息(含 priority 插队)可在批间被处理
|
|
116
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
117
|
-
}
|
|
118
|
-
} finally {
|
|
119
|
-
pumping = false;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function handleEmbed(msg) {
|
|
124
|
-
const texts = Array.isArray(msg.texts) ? msg.texts : [];
|
|
125
|
-
if (texts.length === 0) {
|
|
126
|
-
postReply({ id: msg.id, ok: true, type: 'embedded', vectors: [] });
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const request = { id: msg.id, vectors: new Array(texts.length), remaining: texts.length, failed: false };
|
|
130
|
-
// 单条请求(召回 query)插队到队首;批次追加到队尾(批内条目间仍会被后来的插队)
|
|
131
|
-
const priority = msg.priority === true && texts.length === 1;
|
|
132
|
-
for (let i = 0; i < texts.length; i++) {
|
|
133
|
-
const job = { request: request, index: i, text: String(texts[i] == null ? '' : texts[i]) };
|
|
134
|
-
if (priority) queue.unshift(job);
|
|
135
|
-
else queue.push(job);
|
|
136
|
-
}
|
|
137
|
-
void pump();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
parentPort.on('message', (msg) => {
|
|
141
|
-
if (!msg || typeof msg.id !== 'number' || typeof msg.type !== 'string') return;
|
|
142
|
-
if (msg.type === 'ping') {
|
|
143
|
-
postReply({ id: msg.id, ok: true, type: 'pong' });
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (msg.type === 'warmup') {
|
|
147
|
-
ensureLoaded().then(
|
|
148
|
-
() => postReply({ id: msg.id, ok: true, type: 'ready' }),
|
|
149
|
-
() => postReply({ id: msg.id, ok: false, stage: 'load', error: loadError ?? '模型加载失败' }),
|
|
150
|
-
);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (msg.type === 'embed') {
|
|
154
|
-
void (async () => {
|
|
155
|
-
try {
|
|
156
|
-
await ensureLoaded();
|
|
157
|
-
} catch {
|
|
158
|
-
postReply({ id: msg.id, ok: false, stage: 'load', error: loadError ?? '模型加载失败' });
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
handleEmbed(msg);
|
|
162
|
-
})();
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// 未捕获异常:尽力通知主线程后强制退出(主线程的 exit 处理会拒绝全部 pending
|
|
168
|
-
// 并转入 failed 态)。不保持半死状态——半死的 worker 只会让调用方挂到超时。
|
|
169
|
-
process.on('uncaughtException', (err) => {
|
|
170
|
-
try {
|
|
171
|
-
postReply({ type: 'fatal', error: errMsg(err) });
|
|
172
|
-
} catch {
|
|
173
|
-
/* 端口已坏,exit 处理兜底 */
|
|
174
|
-
}
|
|
175
|
-
setTimeout(() => process.exit(1), 50);
|
|
176
|
-
});
|