dsh-molbio-tools 0.21.0 → 0.22.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/CHANGELOG.md +163 -0
- package/README.md +20 -9
- package/benchmark/README.md +1 -1
- package/benchmark/profile.mjs +2 -1
- package/benchmark/tasks/08-records.json +5 -1
- package/benchmark/verifications.mjs +5 -0
- package/build/preset-patch.mjs +101 -41
- package/build/release-preflight.mjs +1 -1
- package/cordis.patch.yml +187 -25
- package/docs/history.md +49 -0
- package/docs/maintainer.md +6 -5
- package/docs/roadmap.md +11 -2
- package/docs/store-evidence.md +152 -0
- package/docs/store-preset-exemption-issue.md +46 -0
- package/docs/store-preset-exemption.md +235 -0
- package/docs/workflow.md +4 -4
- package/index.mjs +64 -10
- package/package.json +11 -7
- package/papers.mjs +151 -0
- package/preset/install.mjs +4 -4
- package/preset/molbio-lab/agent.cordis.yml +12 -11
- package/test/contract.mjs +18 -4
- package/test/drift-probe.mjs +1 -1
- package/test/preset-health.mjs +1 -1
- package/test/preset-rows.mjs +4 -3
- package/test/smoke.mjs +109 -2
- package/packages/molbio-panel/README.md +0 -63
- package/packages/molbio-panel/cordis.patch.yml +0 -7
- package/packages/molbio-panel/index.mjs +0 -33
- package/packages/molbio-panel/lib/client.js +0 -9113
- package/packages/molbio-panel/package.json +0 -57
- package/preset/molbio-lab/preset.patch.yml +0 -170
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,169 @@
|
|
|
10
10
|
改为按**包名**引用后模块缓存问题随之消失——没有拷贝,也就没有需要保持同步的版本目录。
|
|
11
11
|
下文历史条目里出现的 `vN` 目录保持原样,作为当时的记录。
|
|
12
12
|
|
|
13
|
+
## [0.22.0] — 2026-10-25(DSH 0.2.1-rc.3 适配;PubMed 官方 API + NCBI API key)
|
|
14
|
+
|
|
15
|
+
**工具数不变(60)。新增一条通路:`molbio_pubmed_search` / `molbio_pubmed_abstract` 可以带着
|
|
16
|
+
NCBI API key 走 PubMed 官方 E-utilities。** 没有 key 时两条工具的行为与 0.21.1 相同——检索仍走
|
|
17
|
+
harness 的 web 检索,摘要仍走 `ctx.web.fetch`。
|
|
18
|
+
|
|
19
|
+
### 1. 为什么要有官方通路
|
|
20
|
+
|
|
21
|
+
原来的检索完全依赖 harness 的 web 检索服务:它返回的是**搜索引擎索引到的页面**,不是 PubMed 的
|
|
22
|
+
题录。三个具体代价:命中的 PMID 要从 URL 里猜、拿不到期刊/年份/作者/DOI 这些结构化字段、也
|
|
23
|
+
无法用 NCBI 的 API key 提升速率上限(无 key 3 次/秒,有 key 10 次/秒)。官方 API 是两步:
|
|
24
|
+
`esearch`(term → PMID 列表)→ `esummary`(PMID → 题录),摘要另走 `efetch`。
|
|
25
|
+
|
|
26
|
+
### 2. 通路选择与 key 的来源
|
|
27
|
+
|
|
28
|
+
| 条件 | 检索通路 | 结果字段 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| 有 key(参数或环境变量) | 官方 E-utilities:esearch + esummary | PMID、标题、期刊、年份、作者、DOI,另含 `total_matches` |
|
|
31
|
+
| 无 key | 原来的 harness web 检索(行为不变) | 标题、URL、snippet、从 URL 提取的 PMID |
|
|
32
|
+
|
|
33
|
+
key 的优先级是 **`api_key` 参数 > `NCBI_API_KEY` > `MOLBIO_NCBI_API_KEY`**;空白值视为未提供
|
|
34
|
+
(不会发出 `api_key=` 这种被 NCBI 以 400 拒绝的空参数)。一次带 key 的检索是 **2 个请求**,
|
|
35
|
+
`molbio_pubmed_abstract` 是 1 个 efetch 请求。
|
|
36
|
+
|
|
37
|
+
> **内联传 key 会出现在对话记录和工具调用卡片里**,这是参数通路的固有代价;长期使用应设环境
|
|
38
|
+
> 变量。工具的输出、渲染和错误信息都不回显 key。
|
|
39
|
+
|
|
40
|
+
### 3. 实现
|
|
41
|
+
|
|
42
|
+
- `papers.mjs` 增加纯函数:`pubmedApiKey`(参数/环境变量优先级)、`pubmedSearchUrl` /
|
|
43
|
+
`pubmedSummaryUrl` / `pubmedFetchUrl`(三个 URL 构造器,统一带 `tool=dsh-molbio-tools`)、
|
|
44
|
+
`parseEsearch`(ids + count)、`parseEsummaryRecords`(按 `uids` 顺序归一化题录,
|
|
45
|
+
DOI 从 `articleids` 里取 `idtype: doi`)、`esummarySource`(转成工具返回的 source 形状)。
|
|
46
|
+
- 传输仍走 `ctx.web.fetch`:官方 API 不是一条绕开沙箱/同意机制的新网络通路。没有 `web.fetch`
|
|
47
|
+
而用户又给了 key 时,工具明确报「需要 ctx.web.fetch」,而不是悄悄退回 web 检索。
|
|
48
|
+
- 错误处理:NCBI 的 `{"error":"API key invalid"}` 会变成 `PubMed E-utilities error: API key
|
|
49
|
+
invalid`,**不会被当成「没有结果」**——「搜不到」和「key 错了」是两件事。空结果返回
|
|
50
|
+
`sources: []` 与 `total_matches: 0`。
|
|
51
|
+
- 输出 schema 扩展:`backend`(`pubmed-eutils` / `web-search`)、`total_matches`,以及 source 上的
|
|
52
|
+
`doi` / `authors` / `journal` / `year`;全部为可选字段,旧调用的输出形状不变。
|
|
53
|
+
|
|
54
|
+
### 4. rc.3 适配:这次上游什么都没动,但仍要走完流程
|
|
55
|
+
|
|
56
|
+
- `@deepseek-ai/dsh-web-app@0.2.1-rc.3` 的 `standard.patch.yml` 与 rc.2 **逐字节一致**(`git diff
|
|
57
|
+
--no-index` 验证),所以 preset 行清单、`cordis.patch.yml`、`preset-health` 的期望都没有改。
|
|
58
|
+
- 在 rc.3 上全套守卫全绿:`preset-health`(30 行)、`drift-probe`(14 条突变)、`contract`(16 项,
|
|
59
|
+
含客户端入口与 `presentationMeta` 的行为断言)、`benchmark-profile`(12 行 / 60 工具)。
|
|
60
|
+
- 声明:`engines.dsh` 仍为 `>=0.2.1-rc.2 <0.3.0-0`(rc.2 仍可用);`dshReleases` 把 rc.3 改成
|
|
61
|
+
`compatible`;CI 的 test/release 两条工作流钉到 `0.2.1-rc.3`;面板包 `0.1.4 → 0.1.5`(清单的
|
|
62
|
+
兼容声明变了,客户端产物字节未变,因此只 bump 面板版本线)。
|
|
63
|
+
- 证据用 rc.3 的 CLI 重跑:`docs/store-evidence.md`(0.22.0 tarball 安装 → 启动 → 卸载)。
|
|
64
|
+
|
|
65
|
+
### 5. benchmark 的处理(以及为什么没有重跑模型)
|
|
66
|
+
|
|
67
|
+
- 默认通路(无 key)的输出与 0.21.1 相同,`bench --offline` 的 61 题期望值逐条照跑。
|
|
68
|
+
- `literature-search` 仍是 `NETWORK_TASKS` 里唯一的一条:它断言的是模型的最终回答(PMID、标题、
|
|
69
|
+
摘要措辞),工具结果本来就是**当天的 live PubMed 数据**,没有可以钉住的值。
|
|
70
|
+
- **带 key 的新通路由 `test/smoke.mjs` 覆盖,而不是 benchmark**:benchmark 不能要求真实 key、
|
|
71
|
+
也不该把 live 记录冻结成期望值。smoke 用 mock 的 esearch/esummary/efetch 断言了四件事——
|
|
72
|
+
key 同时出现在两个请求里、题录字段解析正确、环境变量能回退、无效 key 会报错;另有一条
|
|
73
|
+
「无 key 时不碰 E-utilities 传输」的对照。
|
|
74
|
+
|
|
75
|
+
### 6. 版本与验证
|
|
76
|
+
|
|
77
|
+
- 根包 `0.21.1 → 0.22.0`;面板 `0.1.4 → 0.1.5`。
|
|
78
|
+
- `npm test`(DSH 0.2.1-rc.3)全绿:smoke、画面四套、客户端四套、contract 16、drift-probe 14 条
|
|
79
|
+
突变、preset-health 30 行、release 13 项、benchmark 四道守卫 + `bench --offline` 61 题。
|
|
80
|
+
- `node build/preset-patch.mjs --check` 与 `node build/client-bundle.mjs --check` 均报告与源一致
|
|
81
|
+
(rc.3 适配没有动任何生成物)。
|
|
82
|
+
- 一次性 Profile:tarball **115 个文件 / 约 1.0 MB**;安装 571 ms;启动 `HTTP 200`、38180 字节,
|
|
83
|
+
`__DSH_BOOT__` 含 `dsh-molbio-tools/client.js&rev=a25c3919676b`;卸载 421 ms,`molbio` 引用 0、
|
|
84
|
+
lockfile 与 `node_modules` 无残留。
|
|
85
|
+
## [0.21.1] — 2026-10-25(DSH STORE 提交前置:一个 patch、逐版本兼容声明、一个 tarball 一个清单)
|
|
86
|
+
|
|
87
|
+
**工具数不变(60),插件运行时行为零改动。** 这一版不加任何计算能力,只把"这个包以什么形状提交"
|
|
88
|
+
改成 DSH STORE 的提交预检与 DSH 自身的清单契约**都能接受、且不再互相矛盾**的那一种。三处改动都
|
|
89
|
+
来自同一次上架提交:预检里能靠改包解决的两条已经解决,剩下两条的原理与请求另写在
|
|
90
|
+
[docs/store-preset-exemption.md](docs/store-preset-exemption.md)。
|
|
91
|
+
|
|
92
|
+
### 1. 两层 patch 合并成单个 `cordis.patch.yml`
|
|
93
|
+
|
|
94
|
+
0.21.0 的 `dsh.bundle.patch` 是**有序数组**:`["./cordis.patch.yml", "./preset/molbio-lab/preset.patch.yml"]`。
|
|
95
|
+
DSH 运行时接受这种写法(`@deepseek-ai/dsh-app-boot` 的 `bundlePatchPaths`:one file, or an
|
|
96
|
+
ordered list of files),官方 `@deepseek-ai/dsh-web-app` 自己就列了 5 个文件;但 DSH STORE 的提交
|
|
97
|
+
预检(`scripts/check-plugin-submission.mjs` 的 `safePatchPath`)只接受字符串,数组直接判
|
|
98
|
+
`SUBMISSION_BUNDLE_PATCH_INVALID`。两边都自称官方契约,解法不必等预检放宽:
|
|
99
|
+
|
|
100
|
+
- `build/preset-patch.mjs` 现在直接产出**根目录的 `cordis.patch.yml`**,内容是**两个 insert**:
|
|
101
|
+
`molbio-client → ./host.mjs`(惰性宿主锚点)在前,`preset-molbio-lab`(由
|
|
102
|
+
`preset/molbio-lab/agent.cordis.yml` 生成的 preset 行)在后;顺序即应用顺序。
|
|
103
|
+
- 不再生成 `preset/molbio-lab/preset.patch.yml`(文件已删);`package.json` 的
|
|
104
|
+
`dsh.bundle.patch` 是**单字符串**。
|
|
105
|
+
- 四个读者改为跟单文件走:`test/preset-health.mjs`、`test/drift-probe.mjs`、
|
|
106
|
+
`preset/install.mjs`、`test/preset-rows.mjs`(后者在同一个文件的两个 insert 里找 preset 段,
|
|
107
|
+
不假设"preset.patch.yml 才是 preset")。
|
|
108
|
+
|
|
109
|
+
> 这一条同时守住一条更早的教训:**锚点行不能因为"文件变少了"而消失**。0.15.2 的两个侧栏 tab
|
|
110
|
+
> 就是锚点丢失后静默消失的;`test/client-mount.mjs` 仍然逐包断言"被选中的包必须有一个宿主行
|
|
111
|
+
> 能解析到它",`test/contract.mjs` 仍然断言"产物里的每一处 `register` 都在 `inject` 里"。
|
|
112
|
+
|
|
113
|
+
### 2. `engines.dsh` 与逐版本 `dshReleases`
|
|
114
|
+
|
|
115
|
+
两个 manifest 各声明一份:
|
|
116
|
+
|
|
117
|
+
```jsonc
|
|
118
|
+
"engines": { "node": ">=26", "dsh": ">=0.2.1-rc.2 <0.3.0-0" },
|
|
119
|
+
"dsh": { "compatibility": { "dshReleases": {
|
|
120
|
+
"0.2.1-rc.1": "unknown", "0.2.1-rc.2": "compatible", "0.2.1-rc.3": "unknown" } } }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
上界写 `<0.3.0-0` 而不是 `<0.3.0`:DSH 用 `semver.satisfies(..., { includePrerelease: true })`
|
|
124
|
+
判定(`@deepseek-ai/dsh-app-boot` 的 `unmetRequirements`),带 prerelease 选项时 `<0.3.0`
|
|
125
|
+
会放行 `0.3.0-alpha.1`,`-0` 不会。`rc.1` 早于本包基线、`rc.3` 未取得也未验证,两者都只能
|
|
126
|
+
标 `unknown`——**范围不等于验收**:`engines` 是安装时的粗筛,商城的 `dshReleases` 记录才是
|
|
127
|
+
逐版本的验收结论。
|
|
128
|
+
|
|
129
|
+
面板包随之 `0.1.3 → 0.1.4`:它的版本线独立于根包,但清单变了,不发这个版本号就没有能承载新
|
|
130
|
+
声明的发布物。
|
|
131
|
+
|
|
132
|
+
### 3. 根 tarball 不再内嵌面板包
|
|
133
|
+
|
|
134
|
+
`files` 白名单去掉 `packages`。此前根 tarball 里同时躺着 `package.json` 与
|
|
135
|
+
`packages/molbio-panel/package.json` 两个 DSH 插件清单,还重复携带 427 KB 相同的 `lib/client.js`;
|
|
136
|
+
商城把"一个包里出现第二个插件清单"视为提交歧义是对的。现在根 tarball **只有一个插件清单**:
|
|
137
|
+
**118 → 115 个文件、约 1.1 MB → 约 999 kB**(多出的 1 个文件是本次提交的
|
|
138
|
+
`docs/store-preset-exemption-issue.md`)。面板没有因此消失——根包的 `lib/client.js` 本来就带着它
|
|
139
|
+
(第 4 节的启动回读可以作证);面板包继续按自己的地址单独发布。
|
|
140
|
+
|
|
141
|
+
`test/contract.mjs` 的打包断言随之改向:不再要求 `files` 必须包含 `packages/`,而是断言
|
|
142
|
+
**一个 tarball 里恰好只有一个 DSH 清单**(把 `packages` 加回白名单会立刻 FAIL)。
|
|
143
|
+
|
|
144
|
+
### 4. 一次性 profile 证据(tarball 就是发布物)
|
|
145
|
+
|
|
146
|
+
证据记录在 [docs/store-evidence.md](docs/store-evidence.md),用临时 `DSH_HOME` 重跑,不碰真实
|
|
147
|
+
`~/.dsh`:`npm pack` → `--from-default-profile web` 建 profile → `plugin add` tarball →
|
|
148
|
+
`--dump-config`(`molbio-client` 解析到已安装包内,preset 行带 `tool-molbio`)→
|
|
149
|
+
`--no-open --port 0` 启动 → 带 token 回读页面(**HTTP 200**,`__DSH_BOOT__` 图里有
|
|
150
|
+
`dsh-molbio-tools/client.js`)→ `plugin remove` → 再 `--dump-config`(`molbio` 引用 **0** 处)。
|
|
151
|
+
装 tarball 而不是目录,是因为 tarball 同时证明 `files` 白名单带全了运行文件。
|
|
152
|
+
|
|
153
|
+
### 5. 未做 / 边界
|
|
154
|
+
|
|
155
|
+
- **`0.2.1-rc.3` 没有验证**,因此标 `unknown`。拿到该版本后按
|
|
156
|
+
[docs/workflow.md](docs/workflow.md) 第 4 节的升级流程重跑 `preset-health` / `drift-probe` /
|
|
157
|
+
一次性 profile,再把它改成 `compatible`。
|
|
158
|
+
- **预检的另外两条(保护命名空间、身份一致)不是改包能解决的**:本包已自行消掉
|
|
159
|
+
`SUBMISSION_BUNDLE_PATCH_INVALID`;剩下两条的证据、代码位置与三个可执行请求写在
|
|
160
|
+
[docs/store-preset-exemption.md](docs/store-preset-exemption.md),可直接贴到 issue 的短版在
|
|
161
|
+
[docs/store-preset-exemption-issue.md](docs/store-preset-exemption-issue.md)。
|
|
162
|
+
- **工具行为与 benchmark 一律不动**:60 个工具的输出与 0.21.0 相同,按规则 6 的"没变就不必跑
|
|
163
|
+
模型";`bench --offline` 仍在 `npm test` 里,期望值照样逐条校验。
|
|
164
|
+
|
|
165
|
+
### 6. 版本与验证
|
|
166
|
+
|
|
167
|
+
- 根包 `0.21.0 → 0.21.1`;面板 `0.1.4`(见 §2)。
|
|
168
|
+
- `npm test` 全绿:smoke、画面四套、客户端四套、contract 16 项、drift-probe 14 条突变、
|
|
169
|
+
preset-health 30 行、release 13 项、benchmark 四道守卫 + `bench --offline` 61 题。
|
|
170
|
+
- `node build/preset-patch.mjs --check` 与 `node build/client-bundle.mjs --check` 均报告与源一致。
|
|
171
|
+
- `node build/release-preflight.mjs --tag v0.21.1`:本地不变量全过;npm 上 `dsh-molbio-tools`
|
|
172
|
+
已有 0.21.0、本版按 `publish` 处理;面板 0.1.4 尚未发布。
|
|
173
|
+
- 文档同步:maintainer 的版本号、roadmap 的当前状态、history 的版本一览与本段、store-evidence
|
|
174
|
+
的 tarball 名与文件数。
|
|
175
|
+
|
|
13
176
|
## [0.21.0] — 2026-10-23(DSH 0.2.1-rc.2 适配;两条 benchmark 欠账清零)
|
|
14
177
|
|
|
15
178
|
**工具数不变(60),插件行为零改动。** 这是一次"只跟上游走"的版本:0.20.1 之后本机 harness
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ dsh plugin --profile web update dsh-molbio-tools
|
|
|
79
79
|
> **0.7.1 起不再需要"注册 preset 根目录"这一步。**
|
|
80
80
|
> DSH 0.1.7-alpha.1 移除了 `@deepseek-ai/dsh-agent-presets` 及其目录发现机制:preset 现在是
|
|
81
81
|
> bundle patch 里的一个 `@deepseek-ai/dsh-agent-preset` **行**(本包的是
|
|
82
|
-
> `
|
|
82
|
+
> `cordis.patch.yml` 里的第二个 insert,由 `dsh.bundle.patch` 声明)。
|
|
83
83
|
> 因此 preset 随 bundle 一起安装,**装完即出现**。
|
|
84
84
|
>
|
|
85
85
|
> 老版本遗留的两样东西可以清理(`node preset\install.mjs --profile web` 会替你查出来):
|
|
@@ -95,12 +95,12 @@ dsh plugin --profile web update dsh-molbio-tools
|
|
|
95
95
|
|
|
96
96
|
### 工具只在专属模式里出现(设计如此)
|
|
97
97
|
|
|
98
|
-
本包的 bundle patch
|
|
98
|
+
本包的 bundle patch 是**一个文件、两层 insert**,分工是固定的:
|
|
99
99
|
|
|
100
|
-
|
|
|
100
|
+
| 层(同一个 `cordis.patch.yml` 里的两段) | 作用 |
|
|
101
101
|
| --- | --- |
|
|
102
|
-
| `
|
|
103
|
-
| `preset
|
|
102
|
+
| `molbio-client` → `./host.mjs` | 只插一行**惰性宿主锚点**,不注册任何工具 |
|
|
103
|
+
| `preset-molbio-lab` → `@deepseek-ai/dsh-agent-preset` | 声明 **Molecular Biology Lab** 模式,60 个工具在这一层里 |
|
|
104
104
|
|
|
105
105
|
所以 60 个工具**只在选中该模式的会话里加载**,其它模式(standard / ptc / minimal / cordis)
|
|
106
106
|
不带它们,不占提示预算。bundle 安装 ≠ 工具全局可见:bundle 只是把"模式"和"面板"一起装进来。
|
|
@@ -224,8 +224,8 @@ dsh plugin --profile <profile> add dsh-molbio-panel
|
|
|
224
224
|
|
|
225
225
|
| 工具 | 用途 |
|
|
226
226
|
| --- | --- |
|
|
227
|
-
| `molbio_pubmed_search` |
|
|
228
|
-
| `molbio_pubmed_abstract` | 按 PMID
|
|
227
|
+
| `molbio_pubmed_search` | 搜 PubMed 文献。提供 NCBI API key(`api_key` 参数,或环境变量 `NCBI_API_KEY` / `MOLBIO_NCBI_API_KEY`)时走**官方 E-utilities**(esearch + esummary),返回 PMID/标题/期刊/年份/作者/DOI;无 key 时回退到 harness 的 web 检索 |
|
|
228
|
+
| `molbio_pubmed_abstract` | 按 PMID 拉取摘要(efetch;同样支持 NCBI API key,依赖部署提供 web fetch 能力) |
|
|
229
229
|
| `molbio_paper_add` / `molbio_paper_list` / `molbio_paper_update` / `molbio_paper_remove` | 阅读库(工作区 `papers.json`):加/列/改/删,按 PMID→URL→标题+年份去重 |
|
|
230
230
|
| `molbio_paper_export_bibtex` | `papers.json` → BibTeX `.bib`(可按 tag 过滤) |
|
|
231
231
|
| `molbio_protocol_add` / `molbio_protocol_list` / `molbio_protocol_update` | 协议库(`protocols.json`):步骤列表、自由参数、来源文献 |
|
|
@@ -556,6 +556,11 @@ mutations remain deferred")。所以插件无法在工作区里合法地落一
|
|
|
556
556
|
### 文献与记录
|
|
557
557
|
|
|
558
558
|
- 检索与摘要走 harness 的 **web** 服务(部署不提供时工具会明确报错,而不是假装搜到)。
|
|
559
|
+
- 提供 NCBI API key 时,`molbio_pubmed_search` 用官方 E-utilities:esearch 拿 PMID、esummary
|
|
560
|
+
拿题录(标题/期刊/年份/作者/DOI),`molbio_pubmed_abstract` 用 efetch 拿摘要。key 的来源优先级是
|
|
561
|
+
`api_key` 参数 > `NCBI_API_KEY` > `MOLBIO_NCBI_API_KEY`;**长期使用请用环境变量**,内联传参会让 key
|
|
562
|
+
出现在对话记录与工具卡片里。NCBI 的速率上限是**无 key 3 次/秒、有 key 10 次/秒**,一次带 key 的
|
|
563
|
+
检索是 2 个请求(esearch + esummary)。没有 key 时检索仍走原来的 web 检索通路,行为不变。
|
|
559
564
|
- 阅读库/协议库/实验日志是工作区里的 `papers.json` / `protocols.json` / `experiments.json`,
|
|
560
565
|
写入遵循会话的**沙箱政策**,与其它文件工具同权;面板(Papers tab)直接读 `papers.json`。
|
|
561
566
|
- `molbio_paper_export_bibtex` 可选按 tag 过滤导出。
|
|
@@ -649,6 +654,12 @@ node <包目录>\preset\install.mjs --profile web --check # 只报告,不组
|
|
|
649
654
|
工具会返回 `notes` 说明放宽哪个约束(Tm 窗口、GC 窗口、扩增子长度、探针长度…)。通常是把
|
|
650
655
|
`amplicon_min/max` 或 `tm_min/max` 放宽一点即可。
|
|
651
656
|
|
|
657
|
+
**PubMed 的 API key 怎么配?**
|
|
658
|
+
在启动 DSH 的环境里设 `NCBI_API_KEY`(或 `MOLBIO_NCBI_API_KEY`),插件会自动把它带进 E-utilities 请求;
|
|
659
|
+
也可以在单次调用里传 `api_key`。有 key 时 `molbio_pubmed_search` 走官方 API(PubMed 自己的题录,
|
|
660
|
+
含 DOI),没有 key 时仍走 harness 的 web 检索。注意:**内联传的 key 会出现在对话记录和工具调用卡片里**,
|
|
661
|
+
长期用请设环境变量。
|
|
662
|
+
|
|
652
663
|
**"帮我把这段序列分析一下" 它却问我要参数?**
|
|
653
664
|
把目标说具体即可:要引物还是探针、产物多长、Tm 多少、哪个区域。工具默认值都是常规实验条件
|
|
654
665
|
(例如引物 18–28 nt / Tm 55–65 °C / GC 40–60%),不特别说明时按默认来。
|
|
@@ -741,11 +752,11 @@ dsh-molbio-tools/
|
|
|
741
752
|
├── lib/client.js # 客户端产物(exports["./client"],由 npm run build:client 生成)
|
|
742
753
|
├── packages/molbio-panel/ # 面板专用包(只面板、不带工具)
|
|
743
754
|
├── preset/molbio-lab/ # 专属模式 preset:agent.cordis.yml 是行清单(手改这里)
|
|
744
|
-
│ # preset.
|
|
755
|
+
│ # preset.yml 是选择器文案;两者由生成器写进下面的 cordis.patch.yml
|
|
745
756
|
├── benchmark/ # 可用性评估:tasks/*.json + 判分器 + headless profile 推导(见其 README)
|
|
746
757
|
├── test/ # 冒烟 + 光栅化器 + 客户端/组合检查(含 preset 漂移与 benchmark profile 守卫)
|
|
747
758
|
├── docs/ # 维护者与实现文档(rules / workflow / roadmap / history 是本目录的骨架)
|
|
748
|
-
└── cordis.patch.yml # bundle
|
|
759
|
+
└── cordis.patch.yml # 唯一 bundle patch(生成物):锚点 insert + preset insert
|
|
749
760
|
```
|
|
750
761
|
|
|
751
762
|
---
|
package/benchmark/README.md
CHANGED
|
@@ -222,7 +222,7 @@ session "…" runs under agent preset "molbio-lab", which the one-shot runner do
|
|
|
222
222
|
Presets are an agent-plane feature the Web surface composes; the one-shot runner
|
|
223
223
|
drives `agents.create()` directly and never joins one. So the tools must be
|
|
224
224
|
mounted as **host rows**, which is the one shape this package otherwise refuses
|
|
225
|
-
to ship (`cordis.patch.yml`
|
|
225
|
+
to ship (`cordis.patch.yml` only inserts the inert client anchor).
|
|
226
226
|
|
|
227
227
|
`benchmark/profile.mjs` therefore generates a dedicated profile
|
|
228
228
|
(`$DSH_HOME/profiles/molbio-bench`) whose patch layer is **derived** from
|
package/benchmark/profile.mjs
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* agent-plane feature the Web surface composes; the one-shot runner drives
|
|
18
18
|
* `agents.create()` directly and never joins one. So the tools have to be
|
|
19
19
|
* mounted as HOST rows, which is the one shape this package otherwise refuses
|
|
20
|
-
* to ship (`cordis.patch.yml`
|
|
20
|
+
* to ship (`cordis.patch.yml` only inserts the inert client anchor — see the
|
|
21
|
+
* preset's header).
|
|
21
22
|
*
|
|
22
23
|
* ── What keeps the reconstruction honest ────────────────────────────────────
|
|
23
24
|
*
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
"whatever PubMed answers today and there is no recorded value to pin. Its",
|
|
14
14
|
"assertions are therefore all `where: \"answer\"` and structural — a PMID",
|
|
15
15
|
"appears, a title appears — and `benchmark/verifications.mjs` deliberately",
|
|
16
|
-
"declares no offline invocation for it. See NETWORK_TASKS there."
|
|
16
|
+
"declares no offline invocation for it. See NETWORK_TASKS there.",
|
|
17
|
+
"",
|
|
18
|
+
"The 0.22.0 NCBI-API-key path is NOT exercised here: a benchmark cannot",
|
|
19
|
+
"require a real key or pin live PubMed records. test/smoke.mjs covers it with",
|
|
20
|
+
"mocked esearch/esummary/efetch responses (including the invalid-key error)."
|
|
17
21
|
],
|
|
18
22
|
"tasks": [
|
|
19
23
|
{
|
|
@@ -402,5 +402,10 @@ export function invocationsFor(task) {
|
|
|
402
402
|
* answer (structural facts: a PMID appears, a title appears), which is why this
|
|
403
403
|
* module returns no invocations for them and why the offline gate tolerates that
|
|
404
404
|
* specific case while failing every other task that forgets to declare one.
|
|
405
|
+
*
|
|
406
|
+
* The NCBI-API-key path added in 0.22.0 is covered by `test/smoke.mjs` instead:
|
|
407
|
+
* a keyed call is two E-utilities requests (esearch + esummary) whose responses
|
|
408
|
+
* the mock returns, including the invalid-key error. A benchmark must not need a
|
|
409
|
+
* real NCBI key or pin live PubMed data, so this task stays network-only.
|
|
405
410
|
*/
|
|
406
411
|
export const NETWORK_TASKS = new Set(['literature-search']);
|
package/build/preset-patch.mjs
CHANGED
|
@@ -1,35 +1,55 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-molbio-tools/build/preset-patch.mjs
|
|
3
3
|
*
|
|
4
|
-
* Generate the
|
|
4
|
+
* Generate the ONE bundle patch this package ships: the client anchor plus the
|
|
5
|
+
* "Molecular Biology Lab" agent preset.
|
|
5
6
|
*
|
|
6
|
-
* WHY
|
|
7
|
-
*
|
|
8
|
-
* DSH
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* WHY ONE FILE
|
|
8
|
+
* ------------
|
|
9
|
+
* DSH's runtime accepts `dsh.bundle.patch` as a file path or an ordered list of
|
|
10
|
+
* file paths (`@deepseek-ai/dsh-app-boot` → `bundlePatchPaths`), and the shipped
|
|
11
|
+
* `@deepseek-ai/dsh-web-app` uses the list form. DSH STORE's submission precheck
|
|
12
|
+
* only accepts a single string (`safePatchPath`), so the two layers this package
|
|
13
|
+
* used to ship as a list are emitted as one file with two top-level `insert`
|
|
14
|
+
* operations, in the same order they were applied:
|
|
14
15
|
*
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
-
* name: '@deepseek-ai/dsh-agent-preset'
|
|
18
|
-
* config:
|
|
19
|
-
* id: molbio-lab
|
|
20
|
-
* name: Molecular Biology Lab
|
|
21
|
-
* description: ...
|
|
22
|
-
* plugins: [ ...the rows that used to be the composition... ]
|
|
16
|
+
* 1. molbio-client → './host.mjs' (inert host anchor; see below)
|
|
17
|
+
* 2. preset-molbio-lab → '@deepseek-ai/dsh-agent-preset'
|
|
23
18
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
19
|
+
* Nothing about either layer changes: a patch file is an ordered list of
|
|
20
|
+
* operations, and DSH applies them in file order exactly as it applied the two
|
|
21
|
+
* files in list order.
|
|
22
|
+
*
|
|
23
|
+
* WHY THE ANCHOR EXISTS
|
|
24
|
+
* ---------------------
|
|
25
|
+
* DSH's client-module scan walks the HOST Loader tree only. It reads each live
|
|
26
|
+
* host row's package manifest, takes `dsh.client` and `exports["./client"]` from
|
|
27
|
+
* it, and composes the browser graph that becomes `window.__DSH_BOOT__`. A
|
|
28
|
+
* preset's rows live in an isolated subtree that scan never visits.
|
|
29
|
+
*
|
|
30
|
+
* So when the tools moved into the preset, this package lost its only host-plane
|
|
31
|
+
* row, its bundle stopped reaching the page, and the two right-sidebar tabs
|
|
32
|
+
* (Molbio / Papers) silently disappeared. `host.mjs` restores exactly one inert
|
|
33
|
+
* row so the browser half is discoverable again — without putting any tool into
|
|
34
|
+
* a session that did not choose the molbio-lab mode.
|
|
35
|
+
*
|
|
36
|
+
* WHY THE PRESET IS GENERATED
|
|
37
|
+
* ---------------------------
|
|
38
|
+
* DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a
|
|
39
|
+
* preset is now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which
|
|
40
|
+
* is what the second insert below declares. Because the BUNDLE registers it,
|
|
41
|
+
* `dsh plugin --profile <p> add dsh-molbio-tools` puts the mode in the picker
|
|
42
|
+
* with no copy step and no preset-root patch — see README「安装」.
|
|
43
|
+
*
|
|
44
|
+
* Keeping the rows in ONE place and generating the container from them is what
|
|
45
|
+
* stops the two from drifting, because `test/preset-health.mjs` validates every
|
|
46
|
+
* row against the INSTALLED harness's own schemas.
|
|
28
47
|
*
|
|
29
48
|
* source : preset/molbio-lab/agent.cordis.yml (top-level row list, comments intact)
|
|
30
|
-
*
|
|
49
|
+
* source : preset/molbio-lab/preset.yml (picker name + description)
|
|
50
|
+
* output : cordis.patch.yml (GENERATED; anchor + preset)
|
|
31
51
|
*
|
|
32
|
-
* The
|
|
52
|
+
* The rows are emitted as-is: no re-serialization, so the comments that explain
|
|
33
53
|
* each row survive and the file stays diffable against the shipped `standard`
|
|
34
54
|
* patch it descends from.
|
|
35
55
|
*
|
|
@@ -55,20 +75,56 @@ export function rowBlockOf(source) {
|
|
|
55
75
|
/** Indent every line of a block by `width` spaces. */
|
|
56
76
|
const indent = (block, width) => block.split('\n').map((line) => (line === '' ? line : ' '.repeat(width) + line)).join('\n');
|
|
57
77
|
|
|
78
|
+
const HEADER = [
|
|
79
|
+
'# dsh-molbio-tools bundle patch — the one file `dsh.bundle.patch` names.',
|
|
80
|
+
'#',
|
|
81
|
+
'# GENERATED by build/preset-patch.mjs. Edit the row list in',
|
|
82
|
+
'# preset/molbio-lab/agent.cordis.yml (and the picker strings in preset.yml), then',
|
|
83
|
+
'# re-run `node build/preset-patch.mjs`; hand edits here are reverted by the next',
|
|
84
|
+
'# run, and `node build/preset-patch.mjs --check` fails while the two disagree.',
|
|
85
|
+
'#',
|
|
86
|
+
'# Two top-level inserts, applied in this order:',
|
|
87
|
+
'# 1. molbio-client — inert host anchor for the browser half (see below)',
|
|
88
|
+
'# 2. preset-molbio-lab — the "Molecular Biology Lab" agent preset',
|
|
89
|
+
].join('\n');
|
|
90
|
+
|
|
91
|
+
const ANCHOR = [
|
|
92
|
+
'# ── 1. the CLIENT-BUNDLE ANCHOR ────────────────────────────────────────────',
|
|
93
|
+
'#',
|
|
94
|
+
'# `host.mjs` registers no tool and publishes no service. Its only job is to be a',
|
|
95
|
+
"# loader row DSH's client-module scan can see: that scan walks the HOST Loader",
|
|
96
|
+
"# tree, reads the row's package manifest, and takes `dsh.client` +",
|
|
97
|
+
'# `exports["./client"]` from it to compose `window.__DSH_BOOT__`. A preset\'s rows',
|
|
98
|
+
'# live in an isolated subtree the scan never visits, so a `dsh.client` package',
|
|
99
|
+
'# referenced only from inside a preset loses its only discoverable row and its',
|
|
100
|
+
'# right-sidebar tabs silently disappear.',
|
|
101
|
+
'#',
|
|
102
|
+
'# Keep the tool in the PRESET and only the anchor here. An earlier version of',
|
|
103
|
+
"# this file inserted a host row for the tool itself (`name: dsh-molbio-tools`),",
|
|
104
|
+
"# which both registered all 60 tools globally and collided with the preset's own",
|
|
105
|
+
'# copy of that row:',
|
|
106
|
+
'#',
|
|
107
|
+
'# broken: "tool-molbio (...): never started"',
|
|
108
|
+
'- insert:',
|
|
109
|
+
' - id: molbio-client',
|
|
110
|
+
" name: './host.mjs'",
|
|
111
|
+
].join('\n');
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build the bundle patch: anchor first, preset second.
|
|
115
|
+
*
|
|
116
|
+
* @param {{rows: string, description: string, displayName?: string}} input
|
|
117
|
+
* @returns {string} the complete `cordis.patch.yml` text (single trailing newline)
|
|
118
|
+
*/
|
|
58
119
|
export function buildPatch({ rows, description, displayName = 'Molecular Biology Lab' }) {
|
|
59
|
-
const
|
|
60
|
-
'#
|
|
120
|
+
const presetHeader = [
|
|
121
|
+
'# ── 2. the MOLECULAR BIOLOGY LAB PRESET ───────────────────────────────────',
|
|
61
122
|
'#',
|
|
62
|
-
'#
|
|
63
|
-
'#
|
|
64
|
-
|
|
65
|
-
'# `
|
|
66
|
-
'#',
|
|
67
|
-
'# DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a preset is',
|
|
68
|
-
'# now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which is what this',
|
|
69
|
-
'# layer inserts. Because the BUNDLE registers it, `dsh plugin --profile <p> add',
|
|
70
|
-
'# dsh-molbio-tools` puts the mode in the picker with no copy step and no',
|
|
71
|
-
'# preset-root patch — see README「安装」.',
|
|
123
|
+
'# DSH 0.1.7-alpha.1 removed the `<dshHome>/.agent-presets` directory scan: a',
|
|
124
|
+
'# preset is now a `@deepseek-ai/dsh-agent-preset` ROW in a bundle patch, which',
|
|
125
|
+
'# is what this insert declares. Because the BUNDLE registers it,',
|
|
126
|
+
'# `dsh plugin --profile <p> add dsh-molbio-tools` puts the mode in the picker',
|
|
127
|
+
'# with no copy step and no preset-root patch — see README「安装」.',
|
|
72
128
|
'#',
|
|
73
129
|
'# `order: 5` is deliberate: the shipped presets occupy 1..4 (standard, ptc,',
|
|
74
130
|
'# minimal, cordis) and the registry sorts by `order` with an id tie-break, so a',
|
|
@@ -76,7 +132,11 @@ export function buildPatch({ rows, description, displayName = 'Molecular Biology
|
|
|
76
132
|
].join('\n');
|
|
77
133
|
|
|
78
134
|
return [
|
|
79
|
-
|
|
135
|
+
HEADER,
|
|
136
|
+
'',
|
|
137
|
+
ANCHOR,
|
|
138
|
+
'',
|
|
139
|
+
presetHeader,
|
|
80
140
|
'- insert:',
|
|
81
141
|
' - id: preset-molbio-lab',
|
|
82
142
|
" name: '@deepseek-ai/dsh-agent-preset'",
|
|
@@ -95,7 +155,7 @@ function main() {
|
|
|
95
155
|
const check = process.argv.includes('--check');
|
|
96
156
|
const sourcePath = join(repoRoot, 'preset', 'molbio-lab', 'agent.cordis.yml');
|
|
97
157
|
const metaPath = join(repoRoot, 'preset', 'molbio-lab', 'preset.yml');
|
|
98
|
-
const outPath = join(repoRoot, '
|
|
158
|
+
const outPath = join(repoRoot, 'cordis.patch.yml');
|
|
99
159
|
|
|
100
160
|
const rows = rowBlockOf(readFileSync(sourcePath, 'utf8'));
|
|
101
161
|
if (rows === undefined || !rows.includes('- id:')) {
|
|
@@ -124,16 +184,16 @@ function main() {
|
|
|
124
184
|
process.exit(1);
|
|
125
185
|
}
|
|
126
186
|
if (current !== text) {
|
|
127
|
-
console.error('preset-patch: the committed
|
|
187
|
+
console.error('preset-patch: the committed bundle patch is STALE — run `node build/preset-patch.mjs`');
|
|
128
188
|
process.exit(1);
|
|
129
189
|
}
|
|
130
|
-
console.log('
|
|
190
|
+
console.log('bundle patch is current');
|
|
131
191
|
return;
|
|
132
192
|
}
|
|
133
193
|
|
|
134
194
|
writeFileSync(outPath, text, 'utf8');
|
|
135
|
-
const
|
|
136
|
-
console.log(`
|
|
195
|
+
const rowCount = rows.split('\n').filter((line) => /^\s*- id:/.test(line)).length;
|
|
196
|
+
console.log(`bundle patch written: ${outPath} (${rowCount} preset rows + anchor)`);
|
|
137
197
|
}
|
|
138
198
|
|
|
139
199
|
if (process.argv[1] !== undefined && import.meta.url === pathToFileURLSafe(process.argv[1])) main();
|
|
@@ -201,7 +201,7 @@ export function tagVersion(tag) {
|
|
|
201
201
|
* `rc.10 < rc.2`, so a release of `rc.2` after `rc.10` would look like a
|
|
202
202
|
* DOWNGRADE and the gate would refuse a legitimate publish (or, in the other
|
|
203
203
|
* direction, wave through a stale one). This repository does cut prereleases
|
|
204
|
-
* (the harness baseline itself is `0.2.1-rc.
|
|
204
|
+
* (the harness baseline itself is `0.2.1-rc.3`), so the rule is implemented
|
|
205
205
|
* rather than approximated.
|
|
206
206
|
*
|
|
207
207
|
* Semver's rule: split on dots; numeric identifiers compare numerically and rank
|