dsh-plugin-om 0.0.9 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -12
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/embedding.d.ts +19 -2
- package/dist/embedding.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +70 -16
- package/dist/model-download.d.ts +5 -1
- package/dist/model-download.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ dsh plugin --profile <profile> add dsh-plugin-om
|
|
|
41
41
|
|
|
42
42
|
### 开发插件
|
|
43
43
|
|
|
44
|
-
运行`pnpm dev`,等待`dist/index.mjs
|
|
44
|
+
运行`pnpm dev`,等待`dist/index.mjs`构筑完毕(本地嵌入模型可在运行时自动下载到跨版本共享目录;如需提前预下载用`pnpm run download:model`,已存在则跳过)
|
|
45
45
|
|
|
46
46
|
在 `cordis.patch.yml` 里加入
|
|
47
47
|
|
|
@@ -85,24 +85,25 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
|
|
|
85
85
|
|
|
86
86
|
### 依赖策略
|
|
87
87
|
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
- 模型二进制:量化 ONNX 约 113MB,超过 GitHub 单文件 100MB 限制,**不进入 git 仓库,也不随 npm 包分发**(`package.json` 的 `files` 以 `!models/*/onnx/*.onnx` 排除,见 `tests/package-pack.test.ts`)。改为**运行时按需下载**:仅当配置键 `semanticRecallEnabled` 启用且 `models/<id>/onnx/model_quantized.onnx` 缺失时,插件 apply 后台自动从 HuggingFace([Xenova 转换仓库](https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2))下载到 `models/`(不阻塞;下载失败仅记日志,下次调用自动重试;未就绪时 `recall-semantic` 工具返回文案告知模型);本地开发也可用 `pnpm run download:model` 手动预下载(已存在则跳过,`--force` 强制重下);直连 `huggingface.co` 受限时设置环境变量 `HF_ENDPOINT=https://hf-mirror.com` 走镜像
|
|
88
|
+
- dsh宿主提供的依赖,直接复用即可,如 cordis / dsh-tools / zod 等
|
|
89
|
+
- 向量模型模型二进制:量化 ONNX 约 113MB,启用语义召回时下载,位置`$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>/onnx/model_quantized.onnx`。下载失败可以设置`HF_ENDPOINT=https://hf-mirror.com`
|
|
91
90
|
|
|
92
91
|
## 插件配置项
|
|
93
92
|
|
|
94
93
|
| 键 | 默认 | 含义 |
|
|
95
94
|
| ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
-
| `thresholdRatio` | `0.
|
|
95
|
+
| `thresholdRatio` | `0.1` | 观察阈值:未压缩消息 ≥ 窗口 × 该比例触发压缩 |
|
|
97
96
|
| `historyMergeRatio` | `0.2` | 反思阈值:摘要 ≥ 窗口 × 该比例触发精简合并 |
|
|
98
|
-
| `compressMaxTokens` | `
|
|
97
|
+
| `compressMaxTokens` | `10000` | 单次摘要(观察/反思调用)生成上限 |
|
|
99
98
|
| `tailMessageCount` | `10` | 尾部保留的不压缩消息条数(不压缩、不被替换、不进摘要日志) |
|
|
100
|
-
| `modelDir` |
|
|
99
|
+
| `modelDir` | 共享目录 | recall-semantic 嵌入模型目录(默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`,跨插件版本共享;小文件随包分发并在缺失时自动补齐,onnx 缺失且启用语义召回时运行时自动下载到该目录;可指向自定义目录) |
|
|
101
100
|
| `summaryMode` | `fork` | 摘要模式:`fork`(缺省)/ `new` / `disable`(关闭自动压缩);非法值在插件加载时报错(见[摘要模式](#摘要模式)) |
|
|
102
101
|
| `debug` | dev | 压缩流程步骤级(debug)日志开关:`true` 强制开启、`false` 强制关闭;缺省按 `NODE_ENV !== 'production'` 判定(dev/test 输出,生产隐藏)。**失败日志不受此开关影响,始终输出** |
|
|
103
102
|
| `recallEnabled` | `true` | 是否注册 `recall` 工具(`false` 时禁用,不注册) |
|
|
104
103
|
| `semanticRecallEnabled` | `true` | 是否注册 `recall-semantic` 工具(`false` 时禁用,不注册、不触发模型下载) |
|
|
105
104
|
|
|
105
|
+
> 不建议将thresholdRatio设置的过高,越早OM收益越高,且当前的机制需要模型对消息计数,过多的消息会导致历史混乱
|
|
106
|
+
|
|
106
107
|
### 摘要模式
|
|
107
108
|
|
|
108
109
|
摘要调用由配置键 `summaryMode` 控制:
|
|
@@ -119,7 +120,7 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
|
|
|
119
120
|
| `pnpm typecheck` | TypeScript 类型检查 |
|
|
120
121
|
| `pnpm lint` / `pnpm format` | 代码检查 / 格式化 |
|
|
121
122
|
| `pnpm test` | vitest 单元测试 |
|
|
122
|
-
| `pnpm run download:model` | 手动预下载本地嵌入模型 ONNX
|
|
123
|
+
| `pnpm run download:model` | 手动预下载本地嵌入模型 ONNX 到共享目录(默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`;已存在跳过,`--force` 重下;运行时也会按需自动下载) |
|
|
123
124
|
| `pnpm build` | |
|
|
124
125
|
| `pnpm dev` | 自动打包 |
|
|
125
126
|
| `pnpm run release` | 先跑 check(typecheck+lint+test+build),再 CHANGELOG 归档 + 版本号更新 + 打 tag 推送 |
|
|
@@ -148,16 +149,16 @@ src/
|
|
|
148
149
|
├── config.ts # 配置默认值 / 校验(缺省、null、空串回退默认值;数值键/布尔键/summaryMode/modelDir)
|
|
149
150
|
├── utils.ts # 零依赖工具函数(配置校验 / 文本渲染 / 主会话判定 / 路由解析)
|
|
150
151
|
├── log-index.ts # 完整消息索引(index 定位 user/assistant/toolcall 三类完整消息;recall 与摘要共用)
|
|
151
|
-
├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers +
|
|
152
|
-
├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload /
|
|
152
|
+
├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers + 本地模型;共享目录解析 / 小文件补齐 / 运行时按需下载编排 / 懒加载 / 批量 / cosine)
|
|
153
|
+
├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload / 原子落盘 / 开始结束日志与失败镜像建议;运行时与 dev CLI 共用)
|
|
153
154
|
├── summarize.ts # 观察/反思 persona + 提示词 + 直连 ctx.llm.stream() 摘要(fork/new 双模式;extractSummaryLog 提取校验;流式 usage 归入主会话)
|
|
154
155
|
├── recall.ts # recall 工具
|
|
155
156
|
├── semantic-recall.ts # recall-semantic 工具(query 语义检索 + 区间限定 + 回退全量 + 匹配说明)
|
|
156
157
|
└── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / compaction/* 生命周期事件 + checkpoint 替换)
|
|
157
158
|
models/
|
|
158
|
-
└── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx
|
|
159
|
+
└── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx 二进制不随包分发、由运行时按需下载到跨版本共享目录,不进 git)
|
|
159
160
|
scripts/ # release-archive.mjs(CHANGELOG 归档)/ download-model.mjs(开发手动预下载 CLI)
|
|
160
|
-
tests/ # vitest 单元测试(
|
|
161
|
+
tests/ # vitest 单元测试(143 例)
|
|
161
162
|
.dsh/skills/ # 项目级 skill(feature-defect-workflow:需求/缺陷完成工作流)
|
|
162
163
|
```
|
|
163
164
|
|
package/dist/config.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type PluginConfig = {
|
|
|
24
24
|
recallEnabled: boolean;
|
|
25
25
|
/** 是否注册 recall-semantic 工具(缺省 true;false 时不注册、不触发模型下载)。 */
|
|
26
26
|
semanticRecallEnabled: boolean;
|
|
27
|
-
/**
|
|
27
|
+
/** 语义召回嵌入模型目录(默认跨版本共享目录;可指向自定义模型目录)。 */
|
|
28
28
|
modelDir: string;
|
|
29
29
|
};
|
|
30
30
|
/** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAUhD,CAAC;AAkCH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAK5D;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAsCnE"}
|
package/dist/embedding.d.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* 本地语义嵌入(recall-semantic 的向量引擎)。
|
|
3
3
|
*
|
|
4
4
|
* - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX,多语言,
|
|
5
|
-
* 中英 +
|
|
5
|
+
* 中英 + 代码均可处理)。模型目录默认 = $DSH_HOME/plugin-data/dsh-plugin-om/
|
|
6
|
+
* models/<模型id>/(跨插件版本共享):随包小文件(config/tokenizer 等)缺失时
|
|
7
|
+
* 从打包目录复制补齐(离线可用),onnx 二进制按需下载。
|
|
6
8
|
* - 运行时下载:模型 onnx 缺失时由 ensureModelReady 后台下载(不阻塞、单飞、
|
|
7
9
|
* 失败自动重试),下载逻辑见 model-download.ts;就绪前工具告知模型。
|
|
8
10
|
* - 懒加载:首次调用才 `import('@huggingface/transformers')` 并加载 pipeline,
|
|
@@ -17,6 +19,20 @@ import { EMBEDDING_MODEL_ID, type ModelFetch } from './model-download.ts';
|
|
|
17
19
|
/** 打包模型目录:<包根>/models/<model-id>/。 */
|
|
18
20
|
export declare const BUNDLED_MODEL_DIR: string;
|
|
19
21
|
export { EMBEDDING_MODEL_ID };
|
|
22
|
+
/** DSH 用户数据根目录:$DSH_HOME 优先(空白视为未设置),缺省 ~/.dsh(与宿主解析规则一致)。 */
|
|
23
|
+
export declare function resolveDshHome(): string;
|
|
24
|
+
/**
|
|
25
|
+
* 跨插件版本共享的默认模型目录:$DSH_HOME/plugin-data/dsh-plugin-om/models/<模型id>。
|
|
26
|
+
* 不随插件包(版本)变化,升级/重装插件后仍复用同一份已下载模型(onnx 不重复下载)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function sharedModelDir(): string;
|
|
29
|
+
/**
|
|
30
|
+
* 补齐模型目录的随包小文件(config/tokenizer 等):modelDir 与打包目录不同
|
|
31
|
+
* (如共享目录)时,把打包目录中缺失的小文件复制过去,保证模型可离线加载。
|
|
32
|
+
* 已存在的文件不覆盖(同一模型文件跨版本稳定);打包目录本身缺失则跳过。
|
|
33
|
+
* 幂等、无网络。onnx 二进制不在此列(按需下载,见 model-download.ts)。
|
|
34
|
+
*/
|
|
35
|
+
export declare function ensureModelSmallFiles(modelDir: string, bundledDir?: string): void;
|
|
20
36
|
/** 嵌入函数类型:批量文本 → 每条一个向量(Float32Array)。 */
|
|
21
37
|
export type EmbedFn = (texts: readonly string[]) => Promise<Float32Array[]>;
|
|
22
38
|
/**
|
|
@@ -34,9 +50,10 @@ export type ModelStatus = 'ready' | 'downloading';
|
|
|
34
50
|
* - 缺失 → 启动后台下载(不阻塞,单飞)并返回 'downloading';下载失败仅调用
|
|
35
51
|
* warn 记录日志并结束本次尝试,下次调用会重新触发下载(自动重试)。
|
|
36
52
|
* - warn 可选:下载失败时的日志回调(默认静默;apply 注入 ctx.logger.warn)。
|
|
53
|
+
* - log 可选:下载开始/结束日志回调(默认静默;apply 注入 console.log + ctx.logger.info 双通道)。
|
|
37
54
|
* - fetchImpl 可注入(测试传替身,默认全局 fetch)。
|
|
38
55
|
*/
|
|
39
|
-
export declare function ensureModelReady(modelDir?: string, warn?: (message: string) => void, fetchImpl?: ModelFetch): Promise<ModelStatus>;
|
|
56
|
+
export declare function ensureModelReady(modelDir?: string, warn?: (message: string) => void, fetchImpl?: ModelFetch, log?: (message: string) => void): Promise<ModelStatus>;
|
|
40
57
|
/** 重置下载状态(测试用:清空在途下载任务记录)。 */
|
|
41
58
|
export declare function resetModelDownloads(): void;
|
|
42
59
|
/**
|
package/dist/embedding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../src/embedding.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../src/embedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,EAEL,kBAAkB,EAElB,KAAK,UAAU,EAGhB,MAAM,qBAAqB,CAAC;AAK7B,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,QAAsD,CAAC;AAErF,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,8DAA8D;AAC9D,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAA0B,GACrC,IAAI,CASN;AAED,0CAA0C;AAC1C,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;AAQ5E;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,GAAE,MAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,CAoClF;AAED,mCAAmC;AACnC,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,aAAa,CAAC;AAKlD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,GAAE,MAA0B,EACpC,IAAI,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,EAC1C,SAAS,CAAC,EAAE,UAAU,EACtB,GAAG,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GACxC,OAAO,CAAC,WAAW,CAAC,CAqBtB;AAED,8BAA8B;AAC9B,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,EACnC,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,GAClC,MAAM,CAkBR"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,6BAA6B;AAC7B,eAAO,MAAM,IAAI,kBAAkB,CAAC;AAEpC,wDAAwD;AACxD,eAAO,MAAM,MAAM,UAA6C,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,6BAA6B;AAC7B,eAAO,MAAM,IAAI,kBAAkB,CAAC;AAEpC,wDAAwD;AACxD,eAAO,MAAM,MAAM,UAA6C,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAwD1D"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
1
3
|
import path from "node:path";
|
|
2
4
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { mkdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
//#region src/constants.ts
|
|
6
7
|
/**
|
|
@@ -962,10 +963,13 @@ async function maybeCompress(ctx, agent, config, signal) {
|
|
|
962
963
|
*
|
|
963
964
|
* - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX ~113MB,超过
|
|
964
965
|
* GitHub 单文件 100MB 限制,不能进入 git 仓库;仅小文件随仓库提交)。
|
|
965
|
-
* - 下载目标:<modelDir>/onnx/model_quantized.onnx(modelDir 默认 =
|
|
966
|
-
* models/<id
|
|
966
|
+
* - 下载目标:<modelDir>/onnx/model_quantized.onnx(modelDir 默认 = 跨版本共享目录
|
|
967
|
+
* $DSH_HOME/plugin-data/dsh-plugin-om/models/<id>/,见 embedding.ts 的 sharedModelDir;
|
|
968
|
+
* 自定义 modelDir 同样生效)。
|
|
967
969
|
* - 直连 huggingface.co 受限时设置环境变量 HF_ENDPOINT=https://hf-mirror.com 走镜像。
|
|
968
970
|
* - 原子落盘:先写 .tmp 再改名,避免半截文件被当成完整模型加载。
|
|
971
|
+
* - 日志:下载开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时
|
|
972
|
+
* 错误消息附带 HF_ENDPOINT=https://hf-mirror.com 镜像建议。
|
|
969
973
|
*
|
|
970
974
|
* 运行时编排见 embedding.ts 的 ensureModelReady(下载单飞、未就绪不阻塞);
|
|
971
975
|
* 本模块只提供纯下载原语(供 src 与 scripts/download-model.mjs CLI 复用)。
|
|
@@ -974,6 +978,13 @@ async function maybeCompress(ctx, agent, config, signal) {
|
|
|
974
978
|
const EMBEDDING_MODEL_ID = "paraphrase-multilingual-MiniLM-L12-v2";
|
|
975
979
|
/** transformers.js 转换仓库(Xenova org)中量化 ONNX 文件的相对路径(相对模型目录)。 */
|
|
976
980
|
const ONNX_REL = "onnx/model_quantized.onnx";
|
|
981
|
+
/** 随 npm 包分发的小模型文件(相对模型目录;onnx 二进制不随包分发,见 tests/package-pack.test.ts)。 */
|
|
982
|
+
const MODEL_SMALL_FILES = [
|
|
983
|
+
"config.json",
|
|
984
|
+
"special_tokens_map.json",
|
|
985
|
+
"tokenizer.json",
|
|
986
|
+
"tokenizer_config.json"
|
|
987
|
+
];
|
|
977
988
|
/**
|
|
978
989
|
* HuggingFace 原始文件 URL(resolve 会 302 到 CDN,fetch 自动跟随重定向)。
|
|
979
990
|
* base 默认 huggingface.co,可用环境变量 HF_ENDPOINT 覆盖(直连受限时改用镜像,如 https://hf-mirror.com)。
|
|
@@ -1000,12 +1011,14 @@ function needsDownload(target, { force = false } = {}) {
|
|
|
1000
1011
|
}
|
|
1001
1012
|
/**
|
|
1002
1013
|
* 下载模型到 <modelDir>/onnx/model_quantized.onnx。
|
|
1003
|
-
*
|
|
1014
|
+
* 开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时清理临时文件,
|
|
1015
|
+
* 并在错误消息中附带镜像建议(直连受限时设置 HF_ENDPOINT=https://hf-mirror.com)。
|
|
1016
|
+
* 可注入 fetchImpl / log(供测试)。
|
|
1004
1017
|
*/
|
|
1005
1018
|
async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log = console.log } = {}) {
|
|
1006
1019
|
const target = modelTargetPath(modelDir);
|
|
1007
1020
|
if (!needsDownload(target, { force })) {
|
|
1008
|
-
log(`[download-model]
|
|
1021
|
+
log(`[download-model] 模型已存在,跳过下载:${target}`);
|
|
1009
1022
|
return {
|
|
1010
1023
|
downloaded: false,
|
|
1011
1024
|
bytes: 0,
|
|
@@ -1015,13 +1028,14 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
|
|
|
1015
1028
|
const url = modelSourceUrl();
|
|
1016
1029
|
mkdirSync(path.dirname(target), { recursive: true });
|
|
1017
1030
|
const tmp = `${target}.tmp`;
|
|
1031
|
+
log(`[download-model] 开始下载:${url}`);
|
|
1018
1032
|
try {
|
|
1019
1033
|
const resp = await fetchImpl(url);
|
|
1020
1034
|
if (!resp.ok) throw new Error(`下载失败:HTTP ${resp.status} ${resp.statusText}(${url})`);
|
|
1021
1035
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
1022
1036
|
writeFileSync(tmp, buf);
|
|
1023
1037
|
renameSync(tmp, target);
|
|
1024
|
-
log(`[download-model]
|
|
1038
|
+
log(`[download-model] 下载完成:${buf.length} 字节 → ${target}`);
|
|
1025
1039
|
return {
|
|
1026
1040
|
downloaded: true,
|
|
1027
1041
|
bytes: buf.length,
|
|
@@ -1029,7 +1043,8 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
|
|
|
1029
1043
|
};
|
|
1030
1044
|
} catch (err) {
|
|
1031
1045
|
rmSync(tmp, { force: true });
|
|
1032
|
-
|
|
1046
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1047
|
+
throw new Error(`${detail};如直连 HuggingFace 受限,请设置环境变量 HF_ENDPOINT=https://hf-mirror.com 后重试`);
|
|
1033
1048
|
}
|
|
1034
1049
|
}
|
|
1035
1050
|
//#endregion
|
|
@@ -1038,7 +1053,9 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
|
|
|
1038
1053
|
* 本地语义嵌入(recall-semantic 的向量引擎)。
|
|
1039
1054
|
*
|
|
1040
1055
|
* - 模型:Xenova/paraphrase-multilingual-MiniLM-L12-v2(量化 ONNX,多语言,
|
|
1041
|
-
* 中英 +
|
|
1056
|
+
* 中英 + 代码均可处理)。模型目录默认 = $DSH_HOME/plugin-data/dsh-plugin-om/
|
|
1057
|
+
* models/<模型id>/(跨插件版本共享):随包小文件(config/tokenizer 等)缺失时
|
|
1058
|
+
* 从打包目录复制补齐(离线可用),onnx 二进制按需下载。
|
|
1042
1059
|
* - 运行时下载:模型 onnx 缺失时由 ensureModelReady 后台下载(不阻塞、单飞、
|
|
1043
1060
|
* 失败自动重试),下载逻辑见 model-download.ts;就绪前工具告知模型。
|
|
1044
1061
|
* - 懒加载:首次调用才 `import('@huggingface/transformers')` 并加载 pipeline,
|
|
@@ -1053,6 +1070,35 @@ async function downloadModel(modelDir, { force = false, fetchImpl = fetch, log =
|
|
|
1053
1070
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
1054
1071
|
/** 打包模型目录:<包根>/models/<model-id>/。 */
|
|
1055
1072
|
const BUNDLED_MODEL_DIR = path.join(here, "..", "models", EMBEDDING_MODEL_ID);
|
|
1073
|
+
/** DSH 用户数据根目录:$DSH_HOME 优先(空白视为未设置),缺省 ~/.dsh(与宿主解析规则一致)。 */
|
|
1074
|
+
function resolveDshHome() {
|
|
1075
|
+
const fromEnv = process.env.DSH_HOME;
|
|
1076
|
+
if (fromEnv !== void 0 && fromEnv.trim().length > 0) return path.resolve(fromEnv);
|
|
1077
|
+
return path.join(homedir(), ".dsh");
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* 跨插件版本共享的默认模型目录:$DSH_HOME/plugin-data/dsh-plugin-om/models/<模型id>。
|
|
1081
|
+
* 不随插件包(版本)变化,升级/重装插件后仍复用同一份已下载模型(onnx 不重复下载)。
|
|
1082
|
+
*/
|
|
1083
|
+
function sharedModelDir() {
|
|
1084
|
+
return path.join(resolveDshHome(), "plugin-data", "dsh-plugin-om", "models", EMBEDDING_MODEL_ID);
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* 补齐模型目录的随包小文件(config/tokenizer 等):modelDir 与打包目录不同
|
|
1088
|
+
* (如共享目录)时,把打包目录中缺失的小文件复制过去,保证模型可离线加载。
|
|
1089
|
+
* 已存在的文件不覆盖(同一模型文件跨版本稳定);打包目录本身缺失则跳过。
|
|
1090
|
+
* 幂等、无网络。onnx 二进制不在此列(按需下载,见 model-download.ts)。
|
|
1091
|
+
*/
|
|
1092
|
+
function ensureModelSmallFiles(modelDir, bundledDir = BUNDLED_MODEL_DIR) {
|
|
1093
|
+
if (path.resolve(modelDir) === path.resolve(bundledDir)) return;
|
|
1094
|
+
for (const rel of MODEL_SMALL_FILES) {
|
|
1095
|
+
const src = path.join(bundledDir, rel);
|
|
1096
|
+
const dest = path.join(modelDir, rel);
|
|
1097
|
+
if (existsSync(dest) || !existsSync(src)) continue;
|
|
1098
|
+
mkdirSync(path.dirname(dest), { recursive: true });
|
|
1099
|
+
copyFileSync(src, dest);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1056
1102
|
/** 单批最大文本数(避免单次推理过大)。 */
|
|
1057
1103
|
const BATCH_SIZE = 32;
|
|
1058
1104
|
/** 懒加载中的 pipeline 单例(null = 尚未加载/已重置)。 */
|
|
@@ -1064,6 +1110,7 @@ let pipelinePromise = null;
|
|
|
1064
1110
|
function getEmbedder(modelDir = BUNDLED_MODEL_DIR) {
|
|
1065
1111
|
if (pipelinePromise !== null) return pipelinePromise;
|
|
1066
1112
|
pipelinePromise = (async () => {
|
|
1113
|
+
ensureModelSmallFiles(modelDir);
|
|
1067
1114
|
const { env, pipeline } = await import("@huggingface/transformers");
|
|
1068
1115
|
env.localModelPath = `${path.dirname(modelDir)}${path.sep}`;
|
|
1069
1116
|
env.allowLocalModels = true;
|
|
@@ -1099,14 +1146,16 @@ const inflightDownloads = /* @__PURE__ */ new Map();
|
|
|
1099
1146
|
* - 缺失 → 启动后台下载(不阻塞,单飞)并返回 'downloading';下载失败仅调用
|
|
1100
1147
|
* warn 记录日志并结束本次尝试,下次调用会重新触发下载(自动重试)。
|
|
1101
1148
|
* - warn 可选:下载失败时的日志回调(默认静默;apply 注入 ctx.logger.warn)。
|
|
1149
|
+
* - log 可选:下载开始/结束日志回调(默认静默;apply 注入 console.log + ctx.logger.info 双通道)。
|
|
1102
1150
|
* - fetchImpl 可注入(测试传替身,默认全局 fetch)。
|
|
1103
1151
|
*/
|
|
1104
|
-
function ensureModelReady(modelDir = BUNDLED_MODEL_DIR, warn = () => {}, fetchImpl) {
|
|
1152
|
+
function ensureModelReady(modelDir = BUNDLED_MODEL_DIR, warn = () => {}, fetchImpl, log = () => {}) {
|
|
1153
|
+
ensureModelSmallFiles(modelDir);
|
|
1105
1154
|
if (!needsDownload(modelTargetPath(modelDir))) return Promise.resolve("ready");
|
|
1106
1155
|
if (!inflightDownloads.has(modelDir)) {
|
|
1107
|
-
const task = downloadModel(modelDir, fetchImpl === void 0 ? { log
|
|
1156
|
+
const task = downloadModel(modelDir, fetchImpl === void 0 ? { log } : {
|
|
1108
1157
|
fetchImpl,
|
|
1109
|
-
log
|
|
1158
|
+
log
|
|
1110
1159
|
}).then(() => {}).catch((err) => {
|
|
1111
1160
|
warn(`[download-model] 下载失败:${err instanceof Error ? err.message : String(err)}`);
|
|
1112
1161
|
}).finally(() => {
|
|
@@ -1147,15 +1196,15 @@ function cosineSimilarity(a, b) {
|
|
|
1147
1196
|
*/
|
|
1148
1197
|
/** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
|
|
1149
1198
|
const DEFAULT_CONFIG = Object.freeze({
|
|
1150
|
-
thresholdRatio: .
|
|
1199
|
+
thresholdRatio: .1,
|
|
1151
1200
|
historyMergeRatio: .2,
|
|
1152
|
-
compressMaxTokens:
|
|
1201
|
+
compressMaxTokens: 1e4,
|
|
1153
1202
|
tailMessageCount: 10,
|
|
1154
1203
|
summaryMode: "fork",
|
|
1155
1204
|
debug: false,
|
|
1156
1205
|
recallEnabled: true,
|
|
1157
1206
|
semanticRecallEnabled: true,
|
|
1158
|
-
modelDir:
|
|
1207
|
+
modelDir: sharedModelDir()
|
|
1159
1208
|
});
|
|
1160
1209
|
/** 合法配置键集合(未知键直接拒绝)。 */
|
|
1161
1210
|
const CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -1609,10 +1658,15 @@ function apply(ctx, config) {
|
|
|
1609
1658
|
if (resolved.recallEnabled) ctx.tools.register(buildRecallTool(() => ctx.get("toolResultPruner")));
|
|
1610
1659
|
if (resolved.semanticRecallEnabled) {
|
|
1611
1660
|
const warnModel = (message) => ctx.logger.warn(`dsh-plugin-om: ${message}`);
|
|
1612
|
-
|
|
1661
|
+
/** 下载开始/结束日志:console + 插件日志双通道(失败经 warnModel 输出)。 */
|
|
1662
|
+
const logModel = (message) => {
|
|
1663
|
+
console.log(message);
|
|
1664
|
+
logger.info(message);
|
|
1665
|
+
};
|
|
1666
|
+
ensureModelReady(resolved.modelDir, warnModel, void 0, logModel);
|
|
1613
1667
|
ctx.tools.register(buildSemanticRecallTool({
|
|
1614
1668
|
getPruner: () => ctx.get("toolResultPruner"),
|
|
1615
|
-
modelStatus: () => ensureModelReady(resolved.modelDir, warnModel),
|
|
1669
|
+
modelStatus: () => ensureModelReady(resolved.modelDir, warnModel, void 0, logModel),
|
|
1616
1670
|
embedder: (texts) => getEmbedder(resolved.modelDir).then((embed) => embed(texts))
|
|
1617
1671
|
}));
|
|
1618
1672
|
}
|
package/dist/model-download.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export declare const EMBEDDING_MODEL_ID = "paraphrase-multilingual-MiniLM-L12-v2";
|
|
3
3
|
/** transformers.js 转换仓库(Xenova org)中量化 ONNX 文件的相对路径(相对模型目录)。 */
|
|
4
4
|
export declare const ONNX_REL = "onnx/model_quantized.onnx";
|
|
5
|
+
/** 随 npm 包分发的小模型文件(相对模型目录;onnx 二进制不随包分发,见 tests/package-pack.test.ts)。 */
|
|
6
|
+
export declare const MODEL_SMALL_FILES: string[];
|
|
5
7
|
/** 下载返回结果。 */
|
|
6
8
|
export type DownloadResult = {
|
|
7
9
|
downloaded: boolean;
|
|
@@ -31,7 +33,9 @@ export type ModelFetch = (url: string) => Promise<{
|
|
|
31
33
|
}>;
|
|
32
34
|
/**
|
|
33
35
|
* 下载模型到 <modelDir>/onnx/model_quantized.onnx。
|
|
34
|
-
*
|
|
36
|
+
* 开始/结束(含跳过)经 log 回调输出(默认 console.log);失败时清理临时文件,
|
|
37
|
+
* 并在错误消息中附带镜像建议(直连受限时设置 HF_ENDPOINT=https://hf-mirror.com)。
|
|
38
|
+
* 可注入 fetchImpl / log(供测试)。
|
|
35
39
|
*/
|
|
36
40
|
export declare function downloadModel(modelDir: string, { force, fetchImpl, log, }?: {
|
|
37
41
|
force?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../src/model-download.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../src/model-download.ts"],"names":[],"mappings":"AAmBA,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,0CAA0C,CAAC;AAE1E,gEAAgE;AAChE,eAAO,MAAM,QAAQ,8BAA8B,CAAC;AAEpD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,UAK7B,CAAC;AAEF,cAAc;AACd,MAAM,MAAM,cAAc,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,EAAE,GAAE,MAA2B,EAC/B,GAAG,GAAE,MAAiB,EACtB,IAAI,GAAE,MAA4D,GACjE,MAAM,CAER;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAiB,GAAG,MAAM,CAEhF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,EAAE,KAAa,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,OAAO,CAQT;AAED,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC,CAAC,CAAC;AAEH;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,EACE,KAAa,EACb,SAAiB,EACjB,GAAiB,GAClB,GAAE;IACD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,GACL,OAAO,CAAC,cAAc,CAAC,CA6BzB"}
|