dsh-prime-memory 0.26.27 → 0.26.28
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 +10 -0
- package/dist/store/local-embedding.d.ts +7 -0
- package/dist/store/local-embedding.js +21 -2
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# 更新日志(Changelog)
|
|
2
2
|
|
|
3
|
+
## [0.26.28] — 2026-11-02
|
|
4
|
+
|
|
5
|
+
> 本版只修一个错:**我在 `0.26.26`/`0.26.27` 的 CHANGELOG 与 issue 留言里说「子进程上报本批实测进模型的最大 token 数」,但那个值从来没进过日志**。它只被本地验证脚本的探针读到,插件自己的 `memory.log` 里一行都没有——而 #22 这类问题事后能核的只有日志。计划本身是要求这一行落盘的(每批 info 记 `n / 最大tokens / inferMs`,fork 时记 `pid`),是我执行时漏了两项。
|
|
6
|
+
|
|
7
|
+
- **每批推理现在真会落一行 info**(`describeEmbedBatch()`):`[memory] 本地嵌入一批完成:model=<id> n=<条数>,进模型 tokensMax=<实测值>,耗时=<ms>`。三个细节:① `tokensMax` 是子进程按库口径**实测**回来的值,不是推算;② 不可证(子进程给 -1)时印「未知」而不是 0,避开「一条也没进」的误读;③ 用 `info` 而非 `debug`(`src/util/filelog.ts` 只镜像 info+,debug 根本进不了 `memory.log`)。这样报告人按我给的清单回验时,「日志里每批 `tokensMax ≤ 预算`」这一条才真的可验。
|
|
8
|
+
- **fork 后补记子进程 `pid`**(`RealChildChannel` 新增 `onForked` 回调):#22 的归因本来就是靠「系统崩溃报告 ↔ 插件日志」对时间戳,有 pid 才能把一份 `.ips` 对上哪个子进程,`taskkill /T` 的进程树也因此可核。原先只有 `execPath/release/model/tokenBudget`,没 pid。
|
|
9
|
+
- **协议类型同步**:`EmbedWorkerReply` 的 `embedded` 分支加上可选 `tokensMax`,让这个值在主线程类型可见(不再是只存在于未声明字段的 JSON 里)。
|
|
10
|
+
- **验证**:`scripts/verify-token-guard-realmodel.mjs` 的端到端段现在**直接断言真落盘的日志行**(而不只是内存里取个值):拉起行含 `execPath`+`tokenBudget`、另有一行 `pid=\d+`、每批一行 `tokensMax` 且均 ≤ 预算。实测:`pid=37384`、两批 `tokensMax=1535,1535`(预算 1536)、混批与同文本长批均正常返回 1024 维向量。新增 CI 用例 2 例(每批日志内容断言 + 「不可证记未知」边界),本刀累计 31 例。
|
|
11
|
+
- 全量 **1935 例 / 191 文件**全绿;typecheck 0、lint 0 error、build/build:smoke/smoke/verify-catalog 均 0。无配置面变更,无破坏性变更。
|
|
12
|
+
|
|
3
13
|
## [0.26.27] — 2026-11-02
|
|
4
14
|
|
|
5
15
|
> #22 报告人在 macOS/M5 上回验了 `0.26.25`:**三个原致死档不再 SIGTRAP、护栏确实接上**(这验证了主线结论),但翻出一个**比原缺陷更贴近真实负载**的问题,本版纳入他的二分实测。
|
|
@@ -40,6 +40,7 @@ export type EmbedWorkerReply = {
|
|
|
40
40
|
ok: true;
|
|
41
41
|
type: 'embedded';
|
|
42
42
|
vectors: Float32Array[];
|
|
43
|
+
tokensMax?: number;
|
|
43
44
|
} | {
|
|
44
45
|
id: number;
|
|
45
46
|
ok: false;
|
|
@@ -60,6 +61,12 @@ export interface EmbedWorkerChannel {
|
|
|
60
61
|
* signal 同时进 crashed 串、向上传为 loadError,故一处修三处可见。
|
|
61
62
|
*/
|
|
62
63
|
export declare function describeExit(what: string, code: number | null, signal: string | null): string;
|
|
64
|
+
/**
|
|
65
|
+
* 每批推理的可见性行(info 才能进 memory.log)。`tokensMax` 是子进程**实测**上报的
|
|
66
|
+
* 「本轮真正进模型的最大 token 数」(-1 = 不可证):把它落盘才能事后回答
|
|
67
|
+
* 「护栏当时到底生效了没有」,否则它只存在于本地脚本的探针里。
|
|
68
|
+
*/
|
|
69
|
+
export declare function describeEmbedBatch(modelId: string, n: number, tokensMax: number | undefined, ms: number): string;
|
|
63
70
|
export interface LocalEmbeddingOptions {
|
|
64
71
|
/** 数据目录 runtime/(推理侧据此 createRequire 加载 transformers)。 */
|
|
65
72
|
runtimeDir: string;
|
|
@@ -108,6 +108,15 @@ class RealWorkerChannel {
|
|
|
108
108
|
export function describeExit(what, code, signal) {
|
|
109
109
|
return `${what}退出(code=${code ?? 'null'},signal=${signal ?? 'null'})`;
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* 每批推理的可见性行(info 才能进 memory.log)。`tokensMax` 是子进程**实测**上报的
|
|
113
|
+
* 「本轮真正进模型的最大 token 数」(-1 = 不可证):把它落盘才能事后回答
|
|
114
|
+
* 「护栏当时到底生效了没有」,否则它只存在于本地脚本的探针里。
|
|
115
|
+
*/
|
|
116
|
+
export function describeEmbedBatch(modelId, n, tokensMax, ms) {
|
|
117
|
+
const tok = typeof tokensMax === 'number' && tokensMax >= 0 ? String(tokensMax) : '未知';
|
|
118
|
+
return `[memory] 本地嵌入一批完成:model=${modelId} n=${n},进模型 tokensMax=${tok},耗时=${ms}ms`;
|
|
119
|
+
}
|
|
111
120
|
/**
|
|
112
121
|
* 真实子进程通道(fix #11 默认):fork 独立进程隔离 sharp/libvips DLL 冲突。
|
|
113
122
|
* 与 RealWorkerChannel 同协议语义,差异仅在传输层:
|
|
@@ -124,13 +133,16 @@ class RealChildChannel {
|
|
|
124
133
|
terminated = false;
|
|
125
134
|
crashed;
|
|
126
135
|
crashCb;
|
|
127
|
-
constructor(childPath, cfg) {
|
|
136
|
+
constructor(childPath, cfg, onForked) {
|
|
128
137
|
this.child = fork(childPath, [], {
|
|
129
138
|
// execPath 显式沿用当前二进制:宿主 Electron 自带 Node runtime,打包后 PATH 里没有 node
|
|
130
139
|
execPath: process.execPath,
|
|
131
140
|
env: { ...process.env, DSH_EMBED_CFG: JSON.stringify(cfg) },
|
|
132
141
|
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
|
133
142
|
});
|
|
143
|
+
// pid 必须落日志:#22 报告人就是靠「崩溃报告 ↔ 日志」的时间对撞才归因上的,而有 pid
|
|
144
|
+
// 才能把一份系统崩溃报告对上哪个子进程(taskkill /T 的进程树也因此可核)。
|
|
145
|
+
onForked?.(this.child.pid);
|
|
134
146
|
this.child.on('message', (msg) => {
|
|
135
147
|
if (msg && msg.type === 'fatal') {
|
|
136
148
|
this.failAll(`本地嵌入子进程致命错误: ${msg.error ?? '未知'}`);
|
|
@@ -249,7 +261,10 @@ export class LocalEmbeddingService {
|
|
|
249
261
|
opts.channel ??
|
|
250
262
|
(opts.useWorker
|
|
251
263
|
? new RealWorkerChannel(opts.workerPath ?? defaultWorkerPath(), cfgObj)
|
|
252
|
-
: new RealChildChannel(opts.childPath ?? defaultChildPath(), cfgObj)
|
|
264
|
+
: new RealChildChannel(opts.childPath ?? defaultChildPath(), cfgObj, (pid) => {
|
|
265
|
+
// pid 只能在 fork 后拿到,故补一条(与上方拉起日志同前缀,便于 grep 成对)
|
|
266
|
+
this.logger?.info(`[memory] 本地嵌入子进程 pid=${pid ?? '未知'}(model=${entry.id})`);
|
|
267
|
+
}));
|
|
253
268
|
// 崩溃不自愈(换源/重启恢复):拒绝语义沿 EmbedHelper 降级链走 FTS
|
|
254
269
|
this.channel.setOnCrash((error) => {
|
|
255
270
|
if (this.state === 'terminated')
|
|
@@ -313,6 +328,7 @@ export class LocalEmbeddingService {
|
|
|
313
328
|
if (this.state !== 'ready')
|
|
314
329
|
this.state = 'loading';
|
|
315
330
|
// 单条请求(召回 query)带优先标记:worker 侧插队,不被 reindex 批次堵队尾
|
|
331
|
+
const startedAt = Date.now();
|
|
316
332
|
const reply = await this.requestWithTimeout({ type: 'embed', texts, priority: texts.length === 1 }, callOpts?.timeoutMs);
|
|
317
333
|
if (!reply.ok) {
|
|
318
334
|
if (reply.stage === 'load')
|
|
@@ -329,6 +345,9 @@ export class LocalEmbeddingService {
|
|
|
329
345
|
throw new Error(`本地嵌入维度不匹配:期望 ${this.entry.dims},得到 ${v.length}`);
|
|
330
346
|
}
|
|
331
347
|
}
|
|
348
|
+
// 每批一行 info(#22 取证面):子进程**实测**进模型的 tokensMax 必须落盘,
|
|
349
|
+
// 否则事后无法回答「护栏当时生效了没」;耗时兼作 EMA 熔断之外的裸值参考。
|
|
350
|
+
this.logger?.info(describeEmbedBatch(this.entry.id, texts.length, reply.tokensMax, Date.now() - startedAt));
|
|
332
351
|
return reply.vectors;
|
|
333
352
|
}
|
|
334
353
|
/** 释放推理子进程与模型(嵌入源切走/关闭时调用;幂等)。terminated 后不可
|
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-prime-memory",
|
|
3
3
|
"name": "dsh-prime-memory",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.28",
|
|
5
5
|
"description": {
|
|
6
6
|
"en": "Layered distilled memory plugin (L0 capture → L1 atomic memory → L2 scenes → L3 persona) with automatic recall injection before each model step.",
|
|
7
7
|
"zh": "L0~L3 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|