dsh-plugin
DayuanJiang/next-ai-draw-iodsh 的 draw.io 绘图插件:通过 next-ai-drawio MCP 服务器作图,点回复里的链接在右侧栏打开实时预览,可以手动修改图,AI 的每次修改都是可以恢复的版本。需 Node.js 18+,首次使用由 npx 下载 MCP 服务器。
DSH Web 界面里的化学工作台面板。在 Ketcher 画板上画结构(构建由脚本下载,浏览器本地渲染、不经后端),然后对本机 RDKit 直接计算,不调用模型——分子式、分子量、精确质量、logP、TPSA、氢键供体与受体、环数、Fsp3、立体中心,Lipinski 与 Veber 逐条判定、QED、PAINS/BRENK 结构警报与 Murcko 骨架,另有 SMARTS 子结构匹配、指纹相似度、格式互转、标准化与批量清洗。逆合成页签通过「JSON 进 JSON 出」的单进程协议对接可插拔后端(默认 Retro*),后端没装好时如实说明缺什么,不编造路线。实际计算由本仓库的 Python 包(rdkit)完成,需另外 pip 安装。
给 DeepSeek Harness 的化学工作台:聊天照常,输入框旁多一个「工作台」按钮, 点开一个可拖动、可最大化的浮层,里面三个页签:
输入框左侧 [🧪 工作台] → 浮层(不占主区域,聊天不被顶掉)
├─ 画板 Ketcher 全功能画板(浏览器本地 wasm 渲染,不经后端)
├─ 分析 本地 RDKit 现算:性质 / 类药性 / 结构警报 / 骨架 / 子结构 / 相似度 / 批量
└─ 逆合成 多步路线规划(后端可插拔,默认 Retro*;没装好就如实说没装好)
↓ 「插入到对话」/「交给小深」
结构 + 任务写进**当前会话**的输入框 → 你补一句就发

第一版是错的:把工作台注册成 main 面板 → 点开就占掉整个主区域 →
聊天看不见了,结构被插进一个"你看不到的会话"里。用户原话:"有点不太合理"。
第二版也不对:面板底部做了个"结构预览条",在画板里再画一张小图。 用户原话:"预览是在画布里的,这本身没什么用"。
现在:聊天保持原样,工作台在旁边;图交给 agent 在回复里画。
这两件事不一样,别混:
generateImage() 在浏览器本地渲(不联网、不经后端)read_image 内联画出,旁边是它对这分子的解释实测:DSH 的内联图卡硬门控于 read_image(源码写死
if (call?.name !== "read_image") return null),MCP 工具返回的图片内容块拿不到图卡,
present 也只产出交付卡片。所以"配图"这件事只能在 agent 侧做,插件不掺和。
写进输入框的只有结构和任务,例如:
CCO 请算一下这个分子的性质(分子式、MW、logP、TPSA、HBD、HBA、环数):CCO
不夹带任何内部提示(曾经塞过 [产物目录:…],用户明确要求去掉)。
两个理由,缺一不可:
<iframe> 嵌它,然后读
iframe.contentWindow.ketcher.getSmiles()。跨源会被同源策略挡掉 ——
不能让用户另起一个静态服务,必须由插件在 DSH 这个源上提供python3 -m chemcore.cli)
就够了 —— 实测冷启动含 RDKit 导入约 0.2 秒踩过:早先用 var(--dsh-surface) + color: inherit,深色主题下变成"浅色字 + 白底",
文字直接看不见。Ketcher 本身就是浅色画布,所以整窗固定浅色 + 显式色值,
两种主题下都清晰。
Ketcher 的独立构建有 116 MB,不能进 git。一条命令搞定(约 35 MB 下载):
node scripts/fetch-ketcher.mjs # 默认解到 <包目录>/ketcher,版本 3.18.0
宿主半边按这个顺序找它,找到一个就够,多数情况什么都不用配:
配置 ketcherDir → 环境变量 CHEM_KETCHER_DIR → <包目录>/ketcher
→ <仓库根>/ketcher → ~/.cache/dsh-chem-ui/ketcher → ~/.dsh/ketcher
dsh plugin --profile web add 'github:Yijian-quiet/dsh-mol#path:dsh-ui'
或者在本机 profile 的 cordis.patch.yml 里手写:
- insert:
- id: chem-ui
name: 'dsh-chem-ui'
config:
# ketcherDir 不写也行,会按上面的顺序自动找
retroHome: /path/to/retro_star-master # 逆合成后端(可选)
# python: /path/to/python3 # 默认 python3,需能 import rdkit
# retroCommand: 'python3 /path/to/your_bridge.py' # 换后端(可选)
客户端半边改动后要刷新页面才生效(第三方插件没有 HMR)。
| 路由 | 作用 |
|---|---|
GET /chem/ketcher/* | Ketcher 静态构建(带目录穿越防护) |
POST /chem/tool | {op, ...} → python3 -m chemcore.cli → RDKit 结果 |
POST /chem/retro | 逆合成(默认转发给 retro/retro_star_bridge.py) |
GET /chem/status | 能力自述:RDKit 通不通、逆合成配没配、Ketcher 挂没挂 |
刻意避开 /api 前缀 —— 那一段是 DSH 自己的 RPC 与浏览器信任栅栏的地盘。
领域错误(比如 SMILES 画错了)用 HTTP 200 + {ok:false, code} 返回:
它是业务答案,不是 HTTP 故障。
dsh web --port 3081 --no-open > /tmp/chemver.log 2>&1 & # 另起实例,别碰用户正在用的端口
NODE_PATH=<playwright 的 node_modules> \
CHEM_BASE=http://127.0.0.1:3081 \
CHEM_TOKEN=$(grep -o 'token=[^ ]*' /tmp/chemver.log | cut -d= -f2) \
CHEM_SHOTS=/tmp/chem-ui-shots \
node tests/verify-ui.cjs
26 条断言,覆盖的是交互质量而不是"元素存不存在":
两个踩过的坑写在脚本注释里:① 全新 browser context 会弹首次配置向导, 它拦截所有点击 → 必须先点掉;② 三个页签都常驻 DOM(只切
display), 选择器必须挑:visible的那个,否则.first()会命中隐藏页签里的输入框。
MIT