Skip to content
dsh.fish
Bundle

dsh-vision-bridge

DeepSeek Harness 多模态视觉桥:贴图自动转文字描述(llm/stream 代理)+ view_image/ocr_image 主动视觉工具 + 原生多模态路由自动跳过(rc.7 适配),让 text-only 的 DeepSeek 模型看见图片

Source
DreamRift
License
MIT
Updated
Updated 5 hours ago

Readme

# DeepSeek 多模态视觉桥(dsh-vision-bridge)

`dsh-vision-bridge` 是 DeepSeek Harness(DSH)的多模态视觉插件。DeepSeek 官方接口是纯文本的:
在 Web UI 贴图后,`dsh-llm-deepseek` 适配器会因消息中的图片内容块直接抛 `UNSUPPORTED_CONTENT`。
本插件借鉴 [Qwen-MM-Plugins](https://github.com/QwenLM/Qwen-MM-Plugins) 的思路(VL 描述注入 +
主动视觉工具),以 DSH 原生插件实现「让纯文本的 DeepSeek 模型看见图片」:

- **贴图自动代理**:拦截 `llm/stream` 请求,把消息中的 `ImageBlock` 交给 OpenAI 兼容的视觉模型
  (默认 DashScope `qwen-vl-max`)生成结构化中文描述(含图中文字逐字转录),替换为文本块后放行。
  同图跨轮次缓存、描述文本稳定(保 KV cache 前缀);VL 失败进入失败冷却并降级为占位文本,
  不阻塞会话;会话标题请求自动跳过视觉解析。
- **主动视觉工具**:注册 `view_image`(查看本地图片 / 按问题回答)与 `ocr_image`(逐字转录),
  模型可主动查看文件系统中的截图、设计稿、图表。
- **设置页卡片**(v0.2.0 起):设置 → 插件 → 插件配置 → 「视觉模型」,可修改端点(来源)/ 模型 /
  OCR 模型并保存,**热更新生效**(无需重启);密钥与 harness 官方一致——write-only 不回显明文,
  只显示「已配置 / 未配置」徽标,留空保持。
- **模型能力桥接**(v0.4.0 重构):运行时探测原生多模态路由并自动跳过视觉桥。
  DSH rc.7 起官方 `llm-pi-ai` 适配器(pi-ai 库)**原生支持图片**,本插件在请求到达时
  经 `ctx.llm.resolveModelInfo()` 查询 `inputModalities`,含 `image` 即直通(不调 VL、
  不改写消息),让 pi-ai 原生多模态链路零开销工作;text-only 路由(`deepseek-official`)
  仍走 VL 代理。旧版「给 pi-ai 模型补 image 声明」行为已移除(rc.7 中既有害又无必要)。

零硬 npm 依赖(`dsh-settings`/`schemastery`/`dsh-tools` 动态 import,缺包仅降级对应功能)、
不改 DSH 核心源码、不写会话事件,Windows / Linux / macOS 原生可用(无 Python / uvx / WSL 要求)。

- 设计依据与决策记录:[`DeepSeek多模态视觉桥-开发计划.md`](DeepSeek多模态视觉桥-开发计划.md)
- 挂载与配置:[`docs/挂载指南.md`](docs/挂载指南.md)
- Qwen-MM-Plugins 调研:[`docs/Qwen-MM-Plugins调研报告.md`](docs/Qwen-MM-Plugins调研报告.md)
- 许可证:[`LICENSE`](LICENSE)(MIT)

## 部署

### 方式一:官方安装(推荐)

仓库已打 `dsh-plugin` topic,可被 [DSH 插件商店](https://dshpluginstore.com) 等目录自动收录。
一条命令安装:

```bash
dsh plugin --profile web add "github:DreamRift/dsh-vision-bridge"
```

### 方式二:手动安装(tgz + bundle)

1. 克隆本仓库,进入项目目录并生成可安装包:

   ```bash
   cd dsh-vision-bridge
   npm pack    # 产出 dsh-vision-bridge-0.4.0.tgz
   ```

2. 在 `$DSH_HOME/profiles/web/package.json` 声明依赖与 bundle(包自带
   `cordis.patch.yml`,无需在 profile patch 手写 insert):

   ```json
   {
     "dsh": { "profile": { "bundles": ["dsh-vision-bridge"] } },
     "dependencies": {
       "dsh-vision-bridge": "file:<本仓库绝对路径>/dsh-vision-bridge-0.4.0.tgz"
     }
   }
   ```

3. 安装 profile 依赖:

   ```bash
   cd "$DSH_HOME/profiles/web"
   pnpm install
   ```

4. 默认配置(DashScope `qwen-vl-max` 等)在包内 `cordis.patch.yml`;改配置优先走设置页
   「视觉模型」卡片(热更新),高级字段见 [`docs/挂载指南.md`](docs/挂载指南.md) §3.2。

5. 设置页自动暴露(rc.7 起无需 patch):`settings.describe()` 列出所有已注册 namespace,
   注册即暴露。**旧版 `scripts/patch-api-proxy-namespace.mjs` 已删除**(rc.7 删除了
   `WEB_SETTINGS_NAMESPACES` 白名单)。

6. **内置路由图片准入豁免**(deepseek-official 等 `inputModalities` 被 DSH 硬编码为 `["text"]`
   的路由贴图被拒时必须;DSH 升级后需重跑一次):

   ```bash
   node scripts/patch-api-proxy-image-admission.mjs
   ```

   详见 [`docs/挂载指南.md`](docs/挂载指南.md) §3.6:`deepseek-official` 无法声明 image,
   会被 api-proxy 的 `MODEL_DOES_NOT_SUPPORT_IMAGES` 准入检查拦截;本脚本对
   `providerRoutes` 内已接管的 provider 跳过该准入(图片由视觉桥代理转文字)。
   改完需**重启 DSH 后端**生效。

7. 把 VL 服务的 key 写入 `$DSH_HOME/.credentials.yaml`(明文不进任何配置文件 / 仓库;
   也可重启后直接在设置页「视觉模型」卡片里填,效果相同):

   ```yaml
   QWEN_MM_VISION_API_KEY: sk-<your-key>
   ```

8. 重启 `dsh web`,启动日志应出现 `[vision-bridge] 已启用:…` 与 `[vision-bridge] 设置页已就绪:…`。

## 工作原理

DSH 的贴图链路本身完整(拖放 → `ctx.attachments` → 消息中的 `ImageBlock`),卡在 DeepSeek 适配器
拒绝图片内容。本插件监听 `llm/stream` waterfall:由于 cordis 的 waterfall `next` 不接收替换参数,
插件采用 **veto + 重入**(DSH 官方插件 `dsh-session-checkpoint-policy` 示范的合法模式)——
不调用 `next`,而是把替换后的请求打上 `Symbol.for` 标记重新送入 `ctx.llm.stream()`。
不含图片的请求走同步直通,零开销。会话日志中的 `ImageBlock` 保持原样(替换只发生在请求侧,
聊天历史仍显示原图)。rc.7 起在重写前先探测当前路由是否原生支持图片(`ctx.llm.resolveModelInfo`
的 `inputModalities`):原生多模态(llm-pi-ai 等)直接直通、不调 VL。机制细节与源码依据见
开发计划 §3.1/§5.2 与 `src/dsh/proxy.js` 头注。

## 目录结构

```
dsh-vision-bridge/
├── DeepSeek多模态视觉桥-开发计划.md   # 设计与决策记录(含 DSH 机制调研来源)
├── docs/
│   ├── 挂载指南.md                   # 挂载到 DSH + 配置 + 验证清单 + 常见问题
│   └── Qwen-MM-Plugins调研报告.md    # 上游项目调研
├── package.json                      # dsh-vision-bridge 包(out-of-tree 插件)
├── src/
│   ├── core/                         # 核心库(纯 JS,零依赖,可单测)
│   │   ├── vision-client.js          # OpenAI 兼容 VL 客户端(超时 / 429 重试 / 错误分类)
│   │   ├── prompts.js                # describe / ocr / ask 三类中文 prompt
│   │   ├── image-utils.js            # 格式白名单、扩展名+魔数识别、base64 dataURL
│   │   ├── message-rewrite.js        # 不可变重写:ImageBlock → 文本块
│   │   └── describe-cache.js         # attachmentId → 描述 LRU(保 KV cache 前缀稳定)
│   └── dsh/                          # DSH 适配层
│       ├── index.js                  # 插件入口 apply(ctx, config)
│       ├── proxy.js                  # llm/stream 拦截(veto + 重入 + 原生多模态直通)
│       ├── tools.js                  # view_image / ocr_image 工具注册与执行
│       ├── runtime.js                # 共享运行时(日志 / fetch / 凭据解析 / VL 客户端)
│       ├── model-bridge.js           # 运行时探测原生多模态路由(llm.resolveModelInfo)并跳过
│       ├── settings.js               # 设置页桥接(schema + 热更新)
│       └── config.js                 # 配置解析(默认值 + 归一化,零依赖)
└── tests/                            # node:test,66 个用例(mock fetch,无需 VL key)
```

## 测试

```bash
node --test tests/*.test.js   # 66 个单元/集成测试全绿
```

覆盖:消息不可变重写(含冻结输入与 tool-result 内嵌)、缓存稳定性与 LRU、
VL 客户端(成功/401/429 重试/超时/取消/畸形响应/网络错误)、veto+重入链路
(直通/替换/降级/strict/失败冷却/标题占位/原生多模态探测直通)、
模型桥探测(resolveModelInfo 命中/TTL 缓存/降级/热更新清缓存/旧字段兼容)、
工具执行(正常/缺凭据指引/OCR 回退)。

## 致谢

- [QwenLM/Qwen-MM-Plugins](https://github.com/QwenLM/Qwen-MM-Plugins):核心思路
  (VL 描述注入、结构化转述、主动视觉工具)的来源;本项目未复用其代码(Python/MCP 实现改为
  DSH 原生 Node 插件)。

## 许可

[MIT](LICENSE)

Install

dsh plugin --profile web add github:DreamRift/dsh-vision-bridge#a9501e68ee8aeb7187034e78e6ae9f3fc2f4ba56

Profile: web

Source