@avantf/dsh-mem 0.3.1 → 0.4.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/README.md +89 -28
- package/cordis.patch.yml +17 -0
- package/lib/client.js +155 -14
- package/lib/engine/mem/config/config_files.d.ts +26 -0
- package/lib/engine/mem/config/domains.d.ts +20 -0
- package/lib/engine/mem/config/loader.d.ts +35 -0
- package/lib/engine/mem/config/paths.d.ts +53 -0
- package/lib/engine/mem/db/chunk.d.ts +26 -0
- package/lib/engine/mem/db/conn.d.ts +13 -0
- package/lib/engine/mem/db/dao/chunks.d.ts +150 -0
- package/lib/engine/mem/db/dao/contradictions.d.ts +98 -0
- package/lib/engine/mem/db/dao/documents.d.ts +51 -0
- package/lib/engine/mem/db/dao/entities.d.ts +67 -0
- package/lib/engine/mem/db/dao/facts.d.ts +472 -0
- package/lib/engine/mem/db/dao/stats.d.ts +14 -0
- package/lib/engine/mem/db/dao/triples.d.ts +80 -0
- package/lib/engine/mem/db/hash.d.ts +2 -0
- package/lib/engine/mem/db/knowledge.d.ts +17 -0
- package/lib/engine/mem/db/migrations.d.ts +71 -0
- package/lib/engine/mem/db/port.d.ts +57 -0
- package/lib/engine/mem/db/schema.d.ts +26 -0
- package/lib/engine/mem/db/sqlite.d.ts +89 -0
- package/lib/engine/mem/db/store.d.ts +35 -0
- package/lib/engine/mem/db/tokenizer.d.ts +61 -0
- package/lib/engine/mem/db/vectors.d.ts +50 -0
- package/lib/engine/mem/dispatch.d.ts +51 -0
- package/lib/engine/mem/entities/extract.d.ts +84 -0
- package/lib/engine/mem/eval/loader.d.ts +19 -0
- package/lib/engine/mem/eval/metrics.d.ts +25 -0
- package/lib/engine/mem/eval/runner.d.ts +10 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +88 -0
- package/lib/engine/mem/git.d.ts +73 -0
- package/lib/engine/mem/hrr/algebra.d.ts +30 -0
- package/lib/engine/mem/hrr/atoms.d.ts +22 -0
- package/lib/engine/mem/hrr/encode.d.ts +32 -0
- package/lib/engine/mem/hrr/index.d.ts +3 -0
- package/lib/engine/mem/index.d.ts +27 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +156 -0
- package/lib/engine/mem/lifecycle/index.d.ts +5 -0
- package/lib/engine/mem/lifecycle/maintenance.d.ts +48 -0
- package/lib/engine/mem/lifecycle/presence.d.ts +29 -0
- package/lib/engine/mem/lifecycle/tick.d.ts +43 -0
- package/lib/engine/mem/lifecycle/trust.d.ts +90 -0
- package/lib/engine/mem/modelBootstrap.d.ts +207 -0
- package/lib/engine/mem/router.d.ts +51 -0
- package/lib/engine/mem/runtime.d.ts +232 -0
- package/lib/engine/mem/store/common.d.ts +82 -0
- package/lib/engine/mem/store/doc_files.d.ts +125 -0
- package/lib/engine/mem/store/document_text.d.ts +47 -0
- package/lib/engine/mem/store/floors.d.ts +191 -0
- package/lib/engine/mem/store/hybrid.d.ts +232 -0
- package/lib/engine/mem/store/ingest_guard.d.ts +25 -0
- package/lib/engine/mem/store/knowledge.d.ts +483 -0
- package/lib/engine/mem/store/lexical.d.ts +67 -0
- package/lib/engine/mem/store/memory.d.ts +543 -0
- package/lib/engine/mem/store/source_picker.d.ts +43 -0
- package/lib/engine/mem-contract/config.d.ts +129 -0
- package/lib/engine/mem-contract/env.d.ts +31 -0
- package/lib/engine/mem-contract/family.d.ts +25 -0
- package/lib/engine/mem-contract/index.d.ts +8 -0
- package/lib/engine/mem-contract/log.d.ts +29 -0
- package/lib/engine/mem-contract/remote.d.ts +80 -0
- package/lib/engine/mem-contract/text.d.ts +92 -0
- package/lib/engine/mem-contract/tools.d.ts +284 -0
- package/lib/engine/mem-contract/types.d.ts +615 -0
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +47 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +174 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +121 -0
- package/lib/engine/mem-retrieval/adapters/local_numpy.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +52 -0
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/warm_gate.d.ts +46 -0
- package/lib/engine/mem-retrieval/budget.d.ts +30 -0
- package/lib/engine/mem-retrieval/fusion.d.ts +53 -0
- package/lib/engine/mem-retrieval/index.d.ts +14 -0
- package/lib/engine/mem-retrieval/interfaces.d.ts +82 -0
- package/lib/engine/mem-retrieval/log.d.ts +11 -0
- package/lib/engine/mem-retrieval/registry.d.ts +23 -0
- package/lib/engine/mem-retrieval/rerank.d.ts +13 -0
- package/lib/engine/mem-retrieval/stats.d.ts +64 -0
- package/lib/engine/mem-retrieval/text_budget.d.ts +52 -0
- package/lib/index.js +1773 -473
- package/lib/interface-version.json +2 -2
- package/lib/types/client/domains.d.ts +5 -4
- package/lib/types/client/domains.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/ingest.d.ts +1 -1
- package/lib/types/client/wire.d.ts +36 -0
- package/lib/types/client/wire.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +3 -2
- package/lib/types/envinit.d.ts +2 -19
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +1 -1
- package/lib/types/index.d.ts +8 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/open.d.ts +61 -11
- package/lib/types/open.d.ts.map +1 -1
- package/lib/types/prompt.d.ts +19 -1
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +1 -1
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/reconcile.d.ts +64 -0
- package/lib/types/reconcile.d.ts.map +1 -0
- package/lib/types/remote.d.ts +53 -0
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts +35 -0
- package/lib/types/render.d.ts.map +1 -0
- package/lib/types/tool_schema.d.ts +3 -13
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/package.json +24 -15
- package/lib/client.js.map +0 -1
- package/lib/types/client/domains.js +0 -25
- package/lib/types/client/domains.js.map +0 -1
- package/lib/types/client/index.js +0 -1371
- package/lib/types/client/index.js.map +0 -1
- package/lib/types/client/ingest.js +0 -30
- package/lib/types/client/ingest.js.map +0 -1
- package/lib/types/client/paging.js +0 -36
- package/lib/types/client/paging.js.map +0 -1
- package/lib/types/client/summarize.js +0 -79
- package/lib/types/client/summarize.js.map +0 -1
- package/lib/types/data_home.js +0 -26
- package/lib/types/data_home.js.map +0 -1
- package/lib/types/envinit-bootstrap.js +0 -333
- package/lib/types/envinit.js +0 -402
- package/lib/types/envinit.js.map +0 -1
- package/lib/types/hints.js +0 -53
- package/lib/types/hints.js.map +0 -1
- package/lib/types/index.js +0 -755
- package/lib/types/index.js.map +0 -1
- package/lib/types/inject.js +0 -14
- package/lib/types/inject.js.map +0 -1
- package/lib/types/interface_gate.js +0 -84
- package/lib/types/interface_gate.js.map +0 -1
- package/lib/types/open.js +0 -133
- package/lib/types/open.js.map +0 -1
- package/lib/types/prompt.js +0 -148
- package/lib/types/prompt.js.map +0 -1
- package/lib/types/provision.js +0 -177
- package/lib/types/provision.js.map +0 -1
- package/lib/types/remote.js +0 -247
- package/lib/types/remote.js.map +0 -1
- package/lib/types/tool_schema.js +0 -149
- package/lib/types/tool_schema.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @avantf/dsh-mem
|
|
2
2
|
|
|
3
3
|
DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的**记忆**和一个**文档知识库**,
|
|
4
|
-
以进程内 Cordis 插件的形式提供 8
|
|
4
|
+
以进程内 Cordis 插件的形式提供 8 个模型工具、三段用法提示和两个主窗口标签页。
|
|
5
5
|
|
|
6
6
|
## 主要功能
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的
|
|
|
12
12
|
矛盾补扫与裁决 / 维护
|
|
13
13
|
- `kb_add` / `kb_list` / `kb_remove` / `kb_reindex`:新增文档(文本 / 文件 / URL,或批量导入目录,
|
|
14
14
|
同名拒绝)、列表(带出受管 `.md` 的绝对路径与切片详情)、删除、重建索引;
|
|
15
|
-
**改一篇既有文档就是改那份 `.md
|
|
15
|
+
**改一篇既有文档就是改那份 `.md`**(用文件工具改,索引自动跟随)
|
|
16
16
|
- `kb_query`:跨「记忆事实 + 文档切片」的统一检索,结果带来源标注
|
|
17
17
|
- **混合检索 + 联合融合**:库内三路(语义 / FTS bm25 / 实体 Jaccard,另有 HRR 实体探查腿),
|
|
18
18
|
跨库在合并候选池上做联合归一化——记忆与文档切片因此处于同一分数刻度。
|
|
@@ -27,8 +27,9 @@ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的
|
|
|
27
27
|
`.doc`/`.xls`/`.ppt` / 可执行文件)会被拒绝并说明检测到的类型;GBK/GB18030 等中文旧编码会自动解码
|
|
28
28
|
并在结果里标出 `encoding`。
|
|
29
29
|
- **知识可维护**:每篇文档在 `~/.avantf/knowledge/docs/<domain>/<source>/<title>.md` 留一份可编辑副本
|
|
30
|
-
(frontmatter
|
|
31
|
-
|
|
30
|
+
(frontmatter 记着它属于哪篇文档)——用你自己的编辑器改,插件会在**其后任一工具调用结束时**自动把改动同步
|
|
31
|
+
回索引(先一次 `stat` 判漂移、再用正文哈希裁决,只重摄入真正改过的),「知识」页的「重新摄入」是手动兜底;
|
|
32
|
+
删除文档会连同这份副本一起删除,`source_uri` 指向的原文件始终不被改动。
|
|
32
33
|
- **可插拔、可降级**:语义后端 / 重排 / 向量库都走内部注册表;模型、hnswlib、分词器任一缺失都自动降级
|
|
33
34
|
(FTS + 实体 / numpy 向量库 / 正则抽取),不会因此退出。
|
|
34
35
|
- **两个主窗口标签页**(排在 对话 / 轨迹 之后):**记忆**(浏览、编辑、归档恢复、反馈、矛盾裁决)与
|
|
@@ -40,20 +41,34 @@ DSH(DeepSeek Harness)原生记忆插件:给 agent 一份可长期检索的
|
|
|
40
41
|
## 接入 dsh
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
|
-
# 1)
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
dsh plugin --profile <PROFILE> add @avantf/dsh-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#
|
|
44
|
+
# 1) 装这两个包:插件本身,以及家族底座。底座是插件的 peer;不开 autoInstallPeers 的包管理器
|
|
45
|
+
# (pnpm / yarn)不会跟着装上,所以要显式装。
|
|
46
|
+
# 版本号建议写死:pnpm ≥ 10 的 minimumReleaseAge 会避开刚发布的版本,解析到的旧版本可能不认识
|
|
47
|
+
# 当前宿主(它的 peer 区间不含这个 dsh),那一单会被宿主的安装门禁直接拒绝并把 profile 回滚。
|
|
48
|
+
dsh plugin --profile <PROFILE> add @avantf/dsh-plugin-base@<version>
|
|
49
|
+
dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
|
|
50
|
+
|
|
51
|
+
# 2) 原生依赖只剩**可选**的加速件(分词 / 向量索引 / 嵌入模型)。它们缺失只会降级检索质量,
|
|
52
|
+
# 不影响记忆库本身——存储层用的是运行时自带的 node:sqlite,没有需要编译的数据库模块。
|
|
51
53
|
cd ~/.dsh/profiles/<PROFILE>
|
|
52
|
-
pnpm approve-builds --all #
|
|
54
|
+
pnpm approve-builds --all # 勾选 nodejieba / hnswlib-node / onnxruntime,可按需
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
```yaml
|
|
56
|
-
# 3)
|
|
58
|
+
# 3) 挂载插件:装完通常什么都不用做。
|
|
59
|
+
#
|
|
60
|
+
# 本包声明了组合包(dsh.bundle.patch → 随包的 cordis.patch.yml),所以 `dsh plugin add` 装它时会
|
|
61
|
+
# 顺手把这个包选进 profile 的 dsh.profile.bundles(实测),挂载行由随包的 patch 插入;DSH Desktop
|
|
62
|
+
# 的「插件」页走的也是这条。只有两种情况才需要自己写那一行:
|
|
63
|
+
# · 用 npm / yarn 装(它们不认识 dsh.profile.bundles);
|
|
64
|
+
# · 手工编辑 profile 的包清单,而没有把本包选进 bundles。
|
|
65
|
+
# 两条路都做也不会挂两次:当前 dsh(0.1.7 / 0.2.0 线)的 loader 按条目 id 去重,只挂载一次(实测);
|
|
66
|
+
# 但 0.1.5 线(仍在本包 peer 区间内)上重复的行 id 会让 profile 树加载失败——所以始终二选一。
|
|
67
|
+
# · 前提:自动选中只在 `dsh plugin add` **跑完**那一刻发生。若它中途被构建提示(pnpm 的
|
|
68
|
+
# approve-builds)拦下(ERR_PNPM_IGNORED_BUILDS、非零退出),依赖装上了但组合包**不会被选中**,
|
|
69
|
+
# 事后再 add / install 也不会补选——先答完提示再装,或到 DSH Desktop 的「插件」页启用这个组合包。
|
|
70
|
+
#
|
|
71
|
+
# 手写的那一行,放在 ~/.dsh/profiles/<PROFILE>/cordis.patch.yml(不存在就新建):
|
|
57
72
|
- insert:
|
|
58
73
|
- id: avantf-mem
|
|
59
74
|
name: '@avantf/dsh-mem'
|
|
@@ -67,9 +82,27 @@ pnpm approve-builds --all # 至少勾选 better-sqlite3;nodejieba / hnswli
|
|
|
67
82
|
dsh web
|
|
68
83
|
```
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
### DSH Desktop
|
|
86
|
+
|
|
87
|
+
Desktop 用的是同一套 profile(profile 名 `desktop`),差别只有三点:
|
|
88
|
+
|
|
89
|
+
- **界面上只能装组合包**。Desktop 的「插件」页与 `plugin_manager` 只接受声明了 `dsh.bundle.patch`
|
|
90
|
+
的包(本包已声明):在页面上装它,它会把这个包选进 `dsh.profile.bundles`,并应用随包的
|
|
91
|
+
`cordis.patch.yml`——不用手写挂载行。
|
|
92
|
+
- **用它自带的 dsh**:`<安装目录>/resources/runtime/cli/bin/dsh.cmd`(或在设置里把 `dsh` 装进 PATH)。
|
|
93
|
+
- **重启应用**才生效:Desktop 的宿主半边与浏览器半边一起启动,不像 Web 那样热更。
|
|
94
|
+
|
|
95
|
+
Desktop 的宿主进程是 **Electron**(`NODE_MODULE_VERSION` 与同版本 Node 不同)。这正是本插件不依赖任何
|
|
96
|
+
SQLite 绑定的原因:存储层用运行时自带的 `node:sqlite`,它就在跑插件的那个进程里,没有预编译产物要匹配、
|
|
97
|
+
也不需要用户装 C++ 工具链。启动日志里 `sqlite=node:sqlite <版本>` 就是它;两个宿主写的是同一个 SQLite
|
|
98
|
+
文件格式,换宿主不需要迁移数据。
|
|
99
|
+
|
|
100
|
+
- 插件的 `@deepseek-ai/*` peer 由宿主提供(profile 上层的运行时解析表),`dsh plugin add` 不会另装一份。
|
|
101
|
+
**不要在 profile 里再装** `cordis` / `schemastery` / `dsh-tools`——两份对象身份会让工具与 typert
|
|
102
|
+
注册表对不上。
|
|
103
|
+
- `zod` 同样是**宿主提供的 required peer**(区间 `>=4.4.3 <5`,宿主的 dsh 自带):工具的 wire 与参数
|
|
104
|
+
schema 都用**这一份**。**不要在 profile 里单独装一份 `zod`**——两个副本的 schema 类型身份不同,
|
|
105
|
+
跨副本传 schema 对象会在运行期对不上。
|
|
73
106
|
|
|
74
107
|
## 怎么用
|
|
75
108
|
|
|
@@ -91,8 +124,8 @@ dsh web
|
|
|
91
124
|
看看知识库里关于「内存保护」的原文。(→ kb_query,结果带 source_ref,可据此引用)
|
|
92
125
|
```
|
|
93
126
|
|
|
94
|
-
改一篇已经入库的文档:先用 `kb_list` 拿到那份 `.md`
|
|
95
|
-
|
|
127
|
+
改一篇已经入库的文档:先用 `kb_list` 拿到那份 `.md` 的路径,再用**任意文件工具**改它——改完不必手动回填,
|
|
128
|
+
插件会在其后任一工具调用结束时自动同步(「重新摄入」是手动兜底);或直接对模型说"更新这篇文档"。
|
|
96
129
|
|
|
97
130
|
常见维护动作:
|
|
98
131
|
|
|
@@ -101,6 +134,24 @@ dsh web
|
|
|
101
134
|
把矛盾检测的积压扫一遍。(→ mem_admin contradict_check,再对每条判 true_positive / false_positive)
|
|
102
135
|
```
|
|
103
136
|
|
|
137
|
+
### 相关性门槛(严格 / 宽松)
|
|
138
|
+
|
|
139
|
+
检索对每条腿都有一条**绝对**相关性门槛(打在每条腿的原始分上、融合之前):语义余弦
|
|
140
|
+
`min_semantic_similarity`(默认 `0.5`)、FTS 腿逐行命中的**不同查询词元数** `min_fts_terms`(默认 `2`)、
|
|
141
|
+
实体 Jaccard `min_jaccard`(默认 `0.2`)——分数等于门槛保留,`0` = 关闭该腿(改在
|
|
142
|
+
`~/.avantf/configs/common.yaml` 的 `retriever` 节)。
|
|
143
|
+
|
|
144
|
+
- **两个档位(UI 选择器 / `floors` 参数)**:「知识」「记忆」页的查询面板有「严格 / 宽松」选择,`mem_recall.search`
|
|
145
|
+
与 `kb_query` 也接受 `floors: 'strict' | 'loose'`。`strict` = 用配置值、**不**自动放宽(面板默认它,好让空结果
|
|
146
|
+
有明确说法);`loose` = 直接宽松档,换成一组仍有绝对底线的值(`0.40 / ⌈词元数/2⌉ / 0.15`)——所以宽松档
|
|
147
|
+
一样答得出"确实没有相关结果",不是"无门槛"。
|
|
148
|
+
- **不传 `floors` 时的默认策略:严格档为空就自动放宽一次**:先按严格档跑;**仅当**它一条都没命中、且确实有
|
|
149
|
+
条目被门槛丢掉时,才用宽松档补跑一遍。只有宽松档真的产出结果才替换答案,结果里置 `relaxed: true`,面板显示
|
|
150
|
+
「(已自动放宽门槛)」;放宽也救不回时**原样返回严格档结果**(含"被门槛丢掉 N 条"的报告)。跨库查询只对
|
|
151
|
+
**合并后**的结果做这个决定,免得一条空库把放宽后的尾部混进另一条库的严格命中。
|
|
152
|
+
- **可解释性**:结果带生效门槛 `floors` 与每腿丢弃数 `dropped_by_floor`,所以「门槛挡掉的空」与「本来就没有」
|
|
153
|
+
能分开说——面板因此分别给出「严格门槛丢弃了 N 条,可切「宽松」重试」与「(宽松档下)确实没有相关结果」。
|
|
154
|
+
|
|
104
155
|
## 配置、数据与提示词
|
|
105
156
|
|
|
106
157
|
- **数据目录**:默认 `~/.avantf`。记忆库、知识库(含 `knowledge/docs` 下的受管文档副本)与模型缓存
|
|
@@ -109,7 +160,12 @@ dsh web
|
|
|
109
160
|
`memory.yaml`、`knowledge.yaml`;只写你要改的项,其余取内置默认。`dataHome` 也可以写在 profile 的
|
|
110
161
|
config 里,但环境变量 `AVANTF_HOME` 会压过它。
|
|
111
162
|
- **提示词文件**:`~/.avantf/prompts/mem-memory-usage.md`、`mem-knowledge-usage.md`、`mem-kb-edit.md`。
|
|
112
|
-
|
|
163
|
+
缺失或为空时插件写入默认内容,**只在启动时读一次**,改完重启生效。正文可以**按领域改写**——内置默认
|
|
164
|
+
刻意不出现任何领域名词(它同时服务开发、投研、日常等场景),只给判据与"事实型 / 规则型"两种形状:
|
|
165
|
+
- 事实型:`用户不吃香菜;订餐默认备注不要香菜。`(谁 / 什么范围 / 偏好或要求或术语是什么)
|
|
166
|
+
- 规则型:`只有「注销式回购」减少总股本,库存股回购不减少——比较 EPS 影响时要区分。`(当……时,应……,附条件与例外)
|
|
167
|
+
- 反例(不该写成事实):一次调查的过程叙述、数据表、命令与链接——依据属于知识库文档,记忆里只留**结论 + 位置**
|
|
168
|
+
(路径 / 来源 / 名称);敏感信息同理,只记存放位置,不记明文。
|
|
113
169
|
- **语义模型**:默认从 `hf-mirror.com` 下载并缓存,也可指向本地模型目录离线运行。
|
|
114
170
|
- **升级前先备份 `~/.avantf`**:数据库迁移是单向的,用旧版本打开新库会被明确拒绝。
|
|
115
171
|
|
|
@@ -119,21 +175,26 @@ dsh web
|
|
|
119
175
|
# 升级(或重装)到某个版本:同一条命令,换版本号即可
|
|
120
176
|
dsh plugin --profile <PROFILE> add @avantf/dsh-mem@<version>
|
|
121
177
|
|
|
122
|
-
# 卸载:先删掉 profile
|
|
178
|
+
# 卸载:先删掉 profile 依赖(这一步同时会取消该组合包的选中)
|
|
123
179
|
dsh plugin --profile <PROFILE> remove @avantf/dsh-mem
|
|
124
|
-
#
|
|
125
|
-
|
|
180
|
+
# 再清掉挂载项:用组合包装的,包名已随上一步从 profile 的 `dsh.profile.bundles` 里去掉,无需再动;
|
|
181
|
+
# 自己手写过那一行的,删掉 ~/.dsh/profiles/<PROFILE>/cordis.patch.yml 里那段 `- id: avantf-mem`
|
|
182
|
+
#(该文件顶层必须是 YAML 数组:删空就写 `[]`,否则 dsh 启动会拒绝加载)。
|
|
183
|
+
# DSH Desktop 的「插件」页上卸载会连着取消选中与依赖一起做。
|
|
126
184
|
```
|
|
127
185
|
|
|
128
186
|
两者都需要重启 dsh 才生效。卸载**不动 `~/.avantf`**:记忆库、知识库、模型缓存全部保留。
|
|
129
187
|
|
|
130
188
|
## 环境要求
|
|
131
189
|
|
|
132
|
-
- **Node
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
190
|
+
- **Node `>=22.15.0 <23 || >=23.11.0`**、**pnpm**。记忆与知识两个库都建在运行时自带的 `node:sqlite`
|
|
191
|
+
上,这个区间是它的两个下限的交集:模块免 flag(22.13 / 23.4)与"忽略语句未用到的命名参数"
|
|
192
|
+
(22.15 / 23.11)。低于该下限(或该构建缺 FTS5)时插件照常挂载,但 8 个工具都回
|
|
193
|
+
`memory unavailable` 并给出原因,不会拖垮宿主启动
|
|
194
|
+
- `nodejieba` / `hnswlib-node` / `@huggingface/transformers` 都可选:缺失时分别降级为正则抽取 /
|
|
195
|
+
numpy 向量库 / 纯词法检索
|
|
196
|
+
- 插件自包含:检索与知识引擎在构建时已内联进产物,安装不需要额外的家族包。宿主提供的 peer 有两个:
|
|
197
|
+
底座 `@avantf/dsh-plugin-base`(安装时随插件一起装)与 `zod`(宿主的 dsh 自带那份)
|
|
137
198
|
|
|
138
199
|
## 从源码构建
|
|
139
200
|
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# The BUNDLE layer of @avantf/dsh-mem: one row, inserted over whatever the profile root holds.
|
|
2
|
+
#
|
|
3
|
+
# A bundle is how a DSH host installs a plugin without hand-editing the profile: selecting this
|
|
4
|
+
# package in `dsh.profile.bundles` (the plugin manager does that when it installs it, and DSH Desktop's
|
|
5
|
+
# 插件 page only installs packages that declare `dsh.bundle.patch`) applies THIS file as its own patch
|
|
6
|
+
# layer. Installing the package as a plain dependency and adding the same row to the profile's own
|
|
7
|
+
# `cordis.patch.yml` by hand is the other, equally supported route — the two are alternatives; do
|
|
8
|
+
# exactly one. Doing both is never a double mount, but it is not harmless either: on the 0.1.5 line
|
|
9
|
+
# (still inside this package's peer range) the duplicate row id makes the profile tree fail to load,
|
|
10
|
+
# and on the 0.1.7/0.2.0 lines the loader keys entries by id and merely leaves a redundant row.
|
|
11
|
+
- insert:
|
|
12
|
+
- id: avantf-mem
|
|
13
|
+
name: '@avantf/dsh-mem'
|
|
14
|
+
config:
|
|
15
|
+
# The in-process Cordis plugin (the only mode a DSH host can mount). `dataHome` is deliberately
|
|
16
|
+
# left out: its default is `~/.avantf`, and `$AVANTF_HOME` outranks a configured value anyway.
|
|
17
|
+
mode: cordis
|
package/lib/client.js
CHANGED
|
@@ -5328,7 +5328,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5328
5328
|
second_pred: optionalText.optional(),
|
|
5329
5329
|
category: optionalText.optional(),
|
|
5330
5330
|
limit: count.optional(),
|
|
5331
|
-
max_tokens: count.optional()
|
|
5331
|
+
max_tokens: count.optional(),
|
|
5332
|
+
floors: optionalText.optional()
|
|
5332
5333
|
})),
|
|
5333
5334
|
direct("admin", object({
|
|
5334
5335
|
action: optionalText.optional(),
|
|
@@ -5364,7 +5365,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5364
5365
|
domain: optionalText.optional(),
|
|
5365
5366
|
source: optionalText.optional(),
|
|
5366
5367
|
limit: count.optional(),
|
|
5367
|
-
max_tokens: count.optional()
|
|
5368
|
+
max_tokens: count.optional(),
|
|
5369
|
+
floors: optionalText.optional()
|
|
5368
5370
|
})),
|
|
5369
5371
|
direct("classifySource", object({ text: optionalText.optional() })),
|
|
5370
5372
|
direct("browseDir", object({ path: optionalText.optional() })),
|
|
@@ -5417,6 +5419,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5417
5419
|
* `unwrapRemoteEnvelope` peels both, in order, and also tolerates a bare payload
|
|
5418
5420
|
* (a gateway that returns raw values, or a future protocol change), so the pages
|
|
5419
5421
|
* never render an envelope as if it were data.
|
|
5422
|
+
*
|
|
5423
|
+
* The application envelope also carries the **wire revision** (`wire`, optional): the host stamps
|
|
5424
|
+
* the revision of the Remote FACE it publishes onto every answer (see `@avantf/dsh-mem`'s
|
|
5425
|
+
* `remote.ts` `WIRE_VERSION`), and the client half reads it back to tell a rebuilt browser bundle
|
|
5426
|
+
* from the host process that `dsh web` loaded before it. It rides the envelope rather than one
|
|
5427
|
+
* method's payload because this plugin has no single entry point — each tab makes its own first
|
|
5428
|
+
* call — while the envelope is the one object every answer shares. Absent means "a host older
|
|
5429
|
+
* than the marker": still readable, but the client may not assume a method it knows about exists.
|
|
5420
5430
|
*/
|
|
5421
5431
|
function isRecord(value) {
|
|
5422
5432
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5450,17 +5460,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5450
5460
|
};
|
|
5451
5461
|
const inner = result["value"];
|
|
5452
5462
|
if (isRecord(inner) && typeof inner["ok"] === "boolean") {
|
|
5463
|
+
const wire = typeof inner["wire"] === "number" ? inner["wire"] : void 0;
|
|
5453
5464
|
if (inner["ok"] === false) {
|
|
5454
5465
|
const violations = Array.isArray(inner["violations"]) ? inner["violations"].map(String) : void 0;
|
|
5455
5466
|
return {
|
|
5456
5467
|
ok: false,
|
|
5457
5468
|
error: remoteErrorText(inner["error"]),
|
|
5458
|
-
...violations && violations.length > 0 ? { violations } : {}
|
|
5469
|
+
...violations && violations.length > 0 ? { violations } : {},
|
|
5470
|
+
...wire === void 0 ? {} : { wire }
|
|
5459
5471
|
};
|
|
5460
5472
|
}
|
|
5461
5473
|
return {
|
|
5462
5474
|
ok: true,
|
|
5463
|
-
value: inner["value"]
|
|
5475
|
+
value: inner["value"],
|
|
5476
|
+
...wire === void 0 ? {} : { wire }
|
|
5464
5477
|
};
|
|
5465
5478
|
}
|
|
5466
5479
|
return {
|
|
@@ -5631,6 +5644,71 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5631
5644
|
` + lines.join("\n") + (rest > 0 ? `\n… 另有 ${String(rest)} 篇` : "") + "\n\n选择「取消」不会写入任何内容。";
|
|
5632
5645
|
}
|
|
5633
5646
|
//#endregion
|
|
5647
|
+
//#region src/client/wire.ts
|
|
5648
|
+
/**
|
|
5649
|
+
* The client half's wire-revision diagnostics.
|
|
5650
|
+
*
|
|
5651
|
+
* WHY THE TWO HALVES DRIFT. The browser bundle is re-read on every page load; the host half is loaded
|
|
5652
|
+
* once, when `dsh web` starts. So a rebuilt client routinely talks to an older host, and the ONE
|
|
5653
|
+
* failure this must never produce is a bare gateway 404 — it reads exactly like "the record or the
|
|
5654
|
+
* file is gone", which it does not mean. The host stamps `WIRE_VERSION` (see `src/remote.ts`) onto
|
|
5655
|
+
* every answer; `unwrapRemoteEnvelope` carries it back; these functions turn "the numbers disagree"
|
|
5656
|
+
* into a sentence that names the remedy, and are also the gate a call added AFTER the marker uses to
|
|
5657
|
+
* refuse itself against a host that cannot have registered it.
|
|
5658
|
+
*
|
|
5659
|
+
* Kept free of React (this package's tests stay free of the DSH client runtime): the transport
|
|
5660
|
+
* edge is plain data in, plain text out.
|
|
5661
|
+
*
|
|
5662
|
+
* @module @avantf/dsh-mem/client/wire
|
|
5663
|
+
*/
|
|
5664
|
+
/**
|
|
5665
|
+
* The version-skew note for a host's reported revision, or `undefined` when the two halves agree.
|
|
5666
|
+
*
|
|
5667
|
+
* A NOTE, not a throw: an unrecognized revision usually still carries the fields the panels render,
|
|
5668
|
+
* and blanking them is exactly the degradation the marker exists to avoid. `hostWire` is read off
|
|
5669
|
+
* the DECODED envelope (`unwrapRemoteEnvelope`), so `undefined` means "an older host that predates
|
|
5670
|
+
* the marker", never a decode failure.
|
|
5671
|
+
*/
|
|
5672
|
+
function hostSkew(hostWire) {
|
|
5673
|
+
if (hostWire === 1) return void 0;
|
|
5674
|
+
if (hostWire === void 0) return `宿主没有回报 wire 版本(它比本客户端旧):面板可能缺少新字段,重启 dsh web 让两半对齐。(本客户端说 wire=${String(1)})`;
|
|
5675
|
+
return `宿主回报的 wire 版本是 ${String(hostWire)},本客户端只认识 ${String(1)}:面板继续显示,但两半可能已经错位——重启 dsh web 后再试。`;
|
|
5676
|
+
}
|
|
5677
|
+
/**
|
|
5678
|
+
* Whether a host reporting `hostWire` can have registered a method introduced at `required`.
|
|
5679
|
+
*
|
|
5680
|
+
* `undefined` is "no revision observed yet", and is reported as NOT supported by this predicate —
|
|
5681
|
+
* the caller decides whether "unknown" means "refuse" (see the gate in `client/index.ts`, which only
|
|
5682
|
+
* refuses when it has actually SEEN an older revision).
|
|
5683
|
+
*/
|
|
5684
|
+
function hostSupports(hostWire, required) {
|
|
5685
|
+
return hostWire !== void 0 && hostWire >= required;
|
|
5686
|
+
}
|
|
5687
|
+
/**
|
|
5688
|
+
* The sentence for a call this client refuses to SEND because the host's REPORTED revision predates
|
|
5689
|
+
* the method.
|
|
5690
|
+
*
|
|
5691
|
+
* The call is never sent, so this must not read as a transport failure: the host's own revision says
|
|
5692
|
+
* the method does not exist, and the remedy (restart dsh web) is specific. Only called with a known
|
|
5693
|
+
* revision — an unobserved one is sent and diagnosed by {@link transportHint} instead.
|
|
5694
|
+
*/
|
|
5695
|
+
function staleHostText(hostWire, required, what) {
|
|
5696
|
+
return `宿主仍在运行旧版本(wire ${String(hostWire)} < ${String(required)}):它还没有注册 ${what} 这条调用,所以这次请求没有发出去。重启 dsh web 后再试。`;
|
|
5697
|
+
}
|
|
5698
|
+
/**
|
|
5699
|
+
* Name a transport failure that is really a VERSION SKEW.
|
|
5700
|
+
*
|
|
5701
|
+
* A gateway answers a method the host never published with an HTTP 404, which reads exactly like
|
|
5702
|
+
* "the record or the file is gone". The sentence therefore names the missing REGISTRATION (an older
|
|
5703
|
+
* host, or one never restarted) rather than only repeating the transport text; anything that is not
|
|
5704
|
+
* a 404 is returned verbatim, because then the transport's own words are the useful ones.
|
|
5705
|
+
*/
|
|
5706
|
+
function transportHint(cause) {
|
|
5707
|
+
const text = cause instanceof Error ? cause.message : String(cause);
|
|
5708
|
+
if (!/404|not found/iu.test(text)) return text;
|
|
5709
|
+
return `宿主可能没有注册这个接口(旧版本 / 未重启):客户端会随页面刷新,宿主只在 dsh web 启动时加载一次,所以 dsh web 很可能还在跑旧的宿主代码。重启 dsh web 后再试。(原始错误:${text})`;
|
|
5710
|
+
}
|
|
5711
|
+
//#endregion
|
|
5634
5712
|
//#region src/client/index.ts
|
|
5635
5713
|
/**
|
|
5636
5714
|
* @avantf/dsh-mem — DSH client half.
|
|
@@ -5688,18 +5766,48 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5688
5766
|
/** One page of the 知识 tab's document list; the 记忆 tab pages at 50 for the same reason. */
|
|
5689
5767
|
const DOC_PAGE_SIZE = 50;
|
|
5690
5768
|
/**
|
|
5769
|
+
* The wire revision the host last reported, and whether its absence/mismatch was logged.
|
|
5770
|
+
*
|
|
5771
|
+
* Module state, not React state, on purpose: EVERY call goes through {@link callRemote}, so the
|
|
5772
|
+
* first answer teaches the client which host process it is talking to, whichever panel asked. The
|
|
5773
|
+
* host is loaded once, so this cannot change under a session — one write is the whole story.
|
|
5774
|
+
*/
|
|
5775
|
+
let hostWire;
|
|
5776
|
+
let skewLogged = false;
|
|
5777
|
+
/**
|
|
5691
5778
|
* Run one Remote call and normalize every shape it can take: the transport's
|
|
5692
5779
|
* `{ok,value}` envelope wrapping the gateway's own `{ok,value}` /
|
|
5693
5780
|
* `{ok,error,violations}` envelope, a bare value, or a thrown carrier failure.
|
|
5694
5781
|
* Both layers are peeled (see `unwrapRemoteEnvelope`), so `value` is always the
|
|
5695
5782
|
* payload and `error` is always the host's message. Failures are returned, never
|
|
5696
5783
|
* swallowed.
|
|
5784
|
+
*
|
|
5785
|
+
* `requires` is the wire revision that FIRST carried this method, and is how the version marker
|
|
5786
|
+
* becomes a gate: a host whose REPORTED revision predates the method is never asked (the call is not
|
|
5787
|
+
* sent), and the failure names the remedy instead of surfacing the gateway's 404. No current method
|
|
5788
|
+
* needs it — all ten predate the marker — but a method added later MUST pass its revision here; see
|
|
5789
|
+
* `src/remote.ts` `WIRE_VERSION`'s bump rule.
|
|
5790
|
+
*
|
|
5791
|
+
* An UNKNOWN revision (no answer observed yet: this is the first call) does not block — refusing
|
|
5792
|
+
* there would deadlock a gated method that happens to be first, and would refuse every host during
|
|
5793
|
+
* an upgrade. It is sent, and a host that really is too old answers with the 404 that
|
|
5794
|
+
* {@link transportHint} explains.
|
|
5697
5795
|
*/
|
|
5698
|
-
async function callRemote(label, pending) {
|
|
5796
|
+
async function callRemote(label, pending, requires) {
|
|
5797
|
+
if (requires !== void 0 && hostWire !== void 0 && !hostSupports(hostWire, requires)) return {
|
|
5798
|
+
ok: false,
|
|
5799
|
+
error: staleHostText(hostWire, requires, label)
|
|
5800
|
+
};
|
|
5699
5801
|
try {
|
|
5700
5802
|
const result = await pending;
|
|
5701
5803
|
console.log(`[avantf-mem] ${label} ->`, result);
|
|
5702
5804
|
const decoded = unwrapRemoteEnvelope(result);
|
|
5805
|
+
if (typeof decoded.wire === "number") hostWire = decoded.wire;
|
|
5806
|
+
const skew = decoded.ok || decoded.wire !== void 0 ? hostSkew(decoded.wire) : void 0;
|
|
5807
|
+
if (skew !== void 0 && !skewLogged) {
|
|
5808
|
+
skewLogged = true;
|
|
5809
|
+
console.warn(`[avantf-mem] ${skew}`);
|
|
5810
|
+
}
|
|
5703
5811
|
if (decoded.ok) return {
|
|
5704
5812
|
ok: true,
|
|
5705
5813
|
value: decoded.value
|
|
@@ -5713,7 +5821,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5713
5821
|
console.error(`[avantf-mem] ${label} failed`, error);
|
|
5714
5822
|
return {
|
|
5715
5823
|
ok: false,
|
|
5716
|
-
error:
|
|
5824
|
+
error: transportHint(error)
|
|
5717
5825
|
};
|
|
5718
5826
|
}
|
|
5719
5827
|
}
|
|
@@ -6368,13 +6476,36 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6368
6476
|
if (domain !== "" && source !== "") return `${domain} → ${source}`;
|
|
6369
6477
|
return domain || source || "文档切片";
|
|
6370
6478
|
}
|
|
6479
|
+
/**
|
|
6480
|
+
* How many candidates one reply's relevance floors removed, across every leg.
|
|
6481
|
+
*
|
|
6482
|
+
* This is the difference between the two readings of an empty result ("the floors removed N" vs
|
|
6483
|
+
* "there was nothing to remove"), and it is what makes the panel's 宽松 hint actionable rather than
|
|
6484
|
+
* a suggestion to retry blindly.
|
|
6485
|
+
*/
|
|
6486
|
+
function droppedByFloor(value) {
|
|
6487
|
+
const drops = value.dropped_by_floor;
|
|
6488
|
+
if (drops === void 0) return 0;
|
|
6489
|
+
return drops.semantic + drops.fts + drops.jaccard + drops.hrr;
|
|
6490
|
+
}
|
|
6371
6491
|
function KnowledgePanel(props) {
|
|
6372
6492
|
const [showQuery, setShowQuery] = react.useState(false);
|
|
6373
6493
|
const [query, setQuery] = react.useState("");
|
|
6374
6494
|
const [kind, setKind] = react.useState("all");
|
|
6495
|
+
/**
|
|
6496
|
+
* The relevance-floor profile (contract `FLOOR_PROFILES`): 严格 = the configured floors with NO
|
|
6497
|
+
* automatic fallback, 宽松 = the relaxed floors. Default 严格 — the panel must not silently trade
|
|
6498
|
+
* precision for recall, and sending `strict` explicitly is what makes the empty-result hint
|
|
6499
|
+
* actionable ("N were dropped, switch to 宽松") instead of the retry having already happened.
|
|
6500
|
+
* The value is always sent; an omitted profile is the engine's default policy (auto-relax once),
|
|
6501
|
+
* which the 严格/宽松 labels would misdescribe.
|
|
6502
|
+
*/
|
|
6503
|
+
const [floors, setFloors] = react.useState("strict");
|
|
6375
6504
|
const [domain, setDomain] = react.useState("");
|
|
6376
6505
|
const [source, setSource] = react.useState("");
|
|
6377
6506
|
const [hits, setHits] = react.useState([]);
|
|
6507
|
+
/** Candidates the last reply's floors removed; drives the 宽松 hint on an empty result. */
|
|
6508
|
+
const [floorDropped, setFloorDropped] = react.useState(0);
|
|
6378
6509
|
const [queryStatus, setQueryStatus] = react.useState("输入关键词后按回车检索");
|
|
6379
6510
|
const [error, setError] = react.useState();
|
|
6380
6511
|
const [notice, setNotice] = react.useState();
|
|
@@ -6430,6 +6561,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6430
6561
|
if (text.length === 0) {
|
|
6431
6562
|
setQueryStatus("请输入查询关键词");
|
|
6432
6563
|
setHits([]);
|
|
6564
|
+
setFloorDropped(0);
|
|
6433
6565
|
setError(void 0);
|
|
6434
6566
|
return;
|
|
6435
6567
|
}
|
|
@@ -6440,17 +6572,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6440
6572
|
kind,
|
|
6441
6573
|
domain: domain.trim() || void 0,
|
|
6442
6574
|
source: source.trim() || void 0,
|
|
6443
|
-
limit: 10
|
|
6575
|
+
limit: 10,
|
|
6576
|
+
floors
|
|
6444
6577
|
})).then((outcome) => {
|
|
6445
6578
|
if (!queryGuard.isCurrent(seq)) return;
|
|
6446
6579
|
if (outcome.ok) {
|
|
6447
|
-
const
|
|
6580
|
+
const value = outcome.value;
|
|
6581
|
+
const found = value?.hits ?? [];
|
|
6448
6582
|
setHits(found);
|
|
6449
|
-
|
|
6450
|
-
|
|
6583
|
+
setFloorDropped(value === void 0 ? 0 : droppedByFloor(value));
|
|
6584
|
+
const degraded = value?.degraded === true ? "(语义降级,仅 FTS+实体)" : "";
|
|
6585
|
+
const profile = value?.relaxed === true ? "(已自动放宽门槛)" : floors === "loose" ? "(宽松门槛)" : "";
|
|
6586
|
+
setQueryStatus(`命中 ${String(found.length)} 条${profile}${degraded}`);
|
|
6451
6587
|
setError(void 0);
|
|
6452
6588
|
} else {
|
|
6453
6589
|
setHits([]);
|
|
6590
|
+
setFloorDropped(0);
|
|
6454
6591
|
setQueryStatus("查询失败");
|
|
6455
6592
|
setError(outcome.error);
|
|
6456
6593
|
}
|
|
@@ -6880,7 +7017,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6880
7017
|
onChange: (e) => {
|
|
6881
7018
|
setKind(e.target.value);
|
|
6882
7019
|
}
|
|
6883
|
-
}, h("option", { value: "all" }, "全部"), h("option", { value: "fact" }, "仅记忆"), h("option", { value: "doc_chunk" }, "仅文档"))), h(
|
|
7020
|
+
}, h("option", { value: "all" }, "全部"), h("option", { value: "fact" }, "仅记忆"), h("option", { value: "doc_chunk" }, "仅文档"))), h("label", { className: pages_module_css_default.field }, "门槛", h("select", {
|
|
7021
|
+
className: pages_module_css_default.input,
|
|
7022
|
+
value: floors,
|
|
7023
|
+
onChange: (e) => {
|
|
7024
|
+
setFloors(e.target.value);
|
|
7025
|
+
}
|
|
7026
|
+
}, h("option", { value: "strict" }, "严格"), h("option", { value: "loose" }, "宽松"))), h(DomainField, {
|
|
6884
7027
|
label: "知识域(domain)",
|
|
6885
7028
|
value: domain,
|
|
6886
7029
|
onChange: setDomain,
|
|
@@ -6896,7 +7039,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6896
7039
|
label: "检索",
|
|
6897
7040
|
disabled: queryLoading,
|
|
6898
7041
|
onClick: run
|
|
6899
|
-
})), props.remote === void 0 ? null : h("div", { className: pages_module_css_default.status }, queryLoading ? "查询中…" : queryStatus), props.remote !== void 0 && !queryLoading && error === void 0 && hits.length === 0 && queryStatus.startsWith("命中") ? h("div", { className: pages_module_css_default.status }, "没有匹配项。知识库为空时先用「入库/导入」添加文档;记忆里也只包含已写入的事实。") : null, groups.map(([groupName, groupHits]) => h("div", { key: groupName }, h("div", { className: pages_module_css_default.groupHead }, groupName), h("ul", { className: pages_module_css_default.list }, groupHits.map((hit) => h("li", {
|
|
7042
|
+
})), props.remote === void 0 ? null : h("div", { className: pages_module_css_default.status }, queryLoading ? "查询中…" : queryStatus), props.remote !== void 0 && !queryLoading && error === void 0 && hits.length === 0 && queryStatus.startsWith("命中") && floors === "strict" && floorDropped > 0 ? h("div", { className: pages_module_css_default.status }, `没有达到相关性门槛的结果(严格门槛丢弃了 ${String(floorDropped)} 条)——可切「宽松」重试。`) : null, props.remote !== void 0 && !queryLoading && error === void 0 && hits.length === 0 && queryStatus.startsWith("命中") && floors === "loose" && floorDropped > 0 ? h("div", { className: pages_module_css_default.status }, `确实没有相关结果(宽松门槛下仍丢弃了 ${String(floorDropped)} 条)。`) : null, props.remote !== void 0 && !queryLoading && error === void 0 && hits.length === 0 && queryStatus.startsWith("命中") && floorDropped === 0 ? h("div", { className: pages_module_css_default.status }, "没有匹配项。知识库为空时先用「入库/导入」添加文档;记忆里也只包含已写入的事实。") : null, groups.map(([groupName, groupHits]) => h("div", { key: groupName }, h("div", { className: pages_module_css_default.groupHead }, groupName), h("ul", { className: pages_module_css_default.list }, groupHits.map((hit) => h("li", {
|
|
6900
7043
|
key: `${hit.source_ref}:${String(hit.ref_id)}`,
|
|
6901
7044
|
className: pages_module_css_default.item
|
|
6902
7045
|
}, hit.text, h("span", { className: pages_module_css_default.meta }, `${hit.kind} · score ${hit.score.toFixed(3)} · ${hit.source_ref}` + (hit.entities && hit.entities.length > 0 ? ` · 实体:${hit.entities.join("、")}` : "") + ` · 创建于 ${hit.created_at}` + (hit.updated_at !== null && hit.updated_at !== hit.created_at ? ` · 更新于 ${hit.updated_at}` : "")))))))) : null, showIngest ? h("div", { className: `${pages_module_css_default.item} ${pages_module_css_default.form}` }, h("div", { className: pages_module_css_default.toolbar }, h(DomainField, {
|
|
@@ -7082,5 +7225,3 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7082
7225
|
return module.exports;
|
|
7083
7226
|
}
|
|
7084
7227
|
});
|
|
7085
|
-
|
|
7086
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AvantfLogger } from '../../mem-contract/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The default `configs/common.yaml`, written when the file is missing or blank.
|
|
4
|
+
*
|
|
5
|
+
* It is **comments only**, on purpose: the built-in defaults are already in force, so an all-comment
|
|
6
|
+
* file changes NOTHING about how the engine behaves — it only puts the knobs, their defaults and the
|
|
7
|
+
* two managed keys' escape hatches where a reader will find them. Uncommenting a line is the edit;
|
|
8
|
+
* deleting the file brings the defaults straight back.
|
|
9
|
+
*
|
|
10
|
+
* The rule mirrors the prompt files (the plugins' `src/prompt_files.ts`): missing or blank is an
|
|
11
|
+
* unfinished edit, not an empty configuration, so it is filled in. A file with ANY content is the
|
|
12
|
+
* user's and is never rewritten.
|
|
13
|
+
*
|
|
14
|
+
* Keys must match the schema in `@avantf/mem-contract` (`ConfigSchema`): an unknown key in an
|
|
15
|
+
* UNCOMMENTED line is warned about by the loader, so a wrong name here would be a trap rather than a
|
|
16
|
+
* hint. `config_files.spec.ts` pins the ones this template names.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml / knowledge.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u6216 CLI --data-home\u3001\u63D2\u4EF6 profile \u7684 dataHome\uFF09\u51B3\u5B9A \u2014\u2014\n# \u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u7684\u65F6\u5019\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-small-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 512\n# auto_download: true\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# rerank: # \u91CD\u6392\u7684\u552F\u4E00\u5F00\u5173\uFF1Bnone = \u4E0D\u52A0\u8F7D\u4EFB\u4F55\u91CD\u6392\u6A21\u578B\n# backend: none\n# local_model: Xenova/bge-reranker-base\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u7EDD\u5BF9\u95E8\u69DB\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF08\u878D\u5408\u5206\u53EA\u5728\u4E00\u6B21\u67E5\u8BE2\u5185\u53EF\u6BD4\uFF0C\u4E0D\u80FD\u5F53\u95E8\u69DB\uFF09\u3002\n# # 0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF1B\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-small-zh-v1.5/512 \u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F Jaccard \u6BD4\u503C\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
|
|
19
|
+
/**
|
|
20
|
+
* Ensure the common config exists with content, and hand back the path either way.
|
|
21
|
+
*
|
|
22
|
+
* Never throws: an unwritable data home must not keep the engine (or the plugin mount) from working
|
|
23
|
+
* — the built-in defaults are always available, and a missing file is the same thing to the loader
|
|
24
|
+
* as an empty one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ensureCommonConfigFile(path: string, logger?: AvantfLogger): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append one 知识域 to the STORE config's top-level `domains` allowlist
|
|
3
|
+
* (`~/.avantf/configs/knowledge.yaml`, layer ③ of `loader.ts`) and return the new list.
|
|
4
|
+
*
|
|
5
|
+
* This is the write half of the UI-only `kbAddDomain` path, so the user adds a domain once in the
|
|
6
|
+
* 知识 tab instead of hand-editing YAML. It writes to the STORE config rather than the common one:
|
|
7
|
+
* `knowledge.domains` is resolved from layer ③ (the file the loader parses with
|
|
8
|
+
* `KnowledgeConfigSchema`), and a second source of truth would make "where do I change it?" a
|
|
9
|
+
* question again.
|
|
10
|
+
*
|
|
11
|
+
* `parseDocument` + `toString` (never `parse`/`stringify`): the comments in that file are the
|
|
12
|
+
* documentation for the keys around them, and a round-trip that drops them quietly turns a
|
|
13
|
+
* documented config into a bare list. When `domains` is already a sequence the node is mutated IN
|
|
14
|
+
* PLACE (`seq.add`), which keeps the comments attached to the key and its items; a missing key is
|
|
15
|
+
* created.
|
|
16
|
+
*
|
|
17
|
+
* The write is temp file + atomic rename, the same shape as the managed document copy
|
|
18
|
+
* (`doc_files.ts`): a crash mid-write must not leave a truncated config the next boot cannot parse.
|
|
19
|
+
*/
|
|
20
|
+
export declare function addKnowledgeDomain(configPath: string, domain: string): string[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type AvantfLogger, type Config, type MemoryConfig, type KnowledgeConfig } from '../../mem-contract/index.js';
|
|
2
|
+
export interface LoadedConfig {
|
|
3
|
+
home: string;
|
|
4
|
+
common: Config;
|
|
5
|
+
memory: MemoryConfig;
|
|
6
|
+
knowledge: KnowledgeConfig;
|
|
7
|
+
}
|
|
8
|
+
export interface LoadConfigOptions {
|
|
9
|
+
dataHome?: string;
|
|
10
|
+
/** Sink for load-time warnings (unknown keys). Defaults to the console logger. */
|
|
11
|
+
logger?: AvantfLogger;
|
|
12
|
+
/**
|
|
13
|
+
* The MANAGED ROOTS the caller wants as this process's built-in defaults.
|
|
14
|
+
*
|
|
15
|
+
* This is layer ① (built-in defaults), not a new precedence level: the DSH plugin replaces the
|
|
16
|
+
* default locations with the family framework's root (`<home>/tools`, `<home>/models`) because
|
|
17
|
+
* `@avantf/dsh-plugin-base` — not this engine — now fetches pandoc and the embedding model.
|
|
18
|
+
*
|
|
19
|
+
* `tools.dir` is still an ordinary configurable location, so a `config.yaml` entry or an
|
|
20
|
+
* `AVANTF_*` escape hatch keeps winning. The MODEL locations are different: they are managed, so
|
|
21
|
+
* {@link MANAGED_MODEL_KEYS} are stripped from `config.yaml` before it is merged (the environment
|
|
22
|
+
* escape hatch stays). When the framework is unavailable the plugin passes nothing and the schema
|
|
23
|
+
* defaults (the legacy directories) apply unchanged.
|
|
24
|
+
*/
|
|
25
|
+
managedRoots?: {
|
|
26
|
+
readonly tools?: string;
|
|
27
|
+
readonly models?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load config with layering: ① built-in defaults → ② `~/.avantf/configs/common.yaml`
|
|
32
|
+
* (common) → ③ `~/.avantf/{memory,knowledge}/config.yaml` (store) → ④ env →
|
|
33
|
+
* ⑤ explicit. Returns the resolved per-store config + data home.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadConfig(opts?: LoadConfigOptions): LoadedConfig;
|