# dsh-llm-vision-bridge [English](README.md) | 中文 [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) 让**纯文本模型(DeepSeek)也能"看"图片**:在 dsh web GUI 的聊天输入框直接粘贴图片,插件自动把图片路由到视觉辅助模型(复用你已配置的 pi-ai / llama.cpp 上的 **Qwen3-VL**),得到文字描述后交给 DeepSeek 继续生成回复——体验与原生多模态模型一致。 ## 功能特性 - **原生 LLM provider**——注册 `deepseek-vision` 到 DSH 的 `LlmAdapter` 接缝。图片准入、请求路由、会话压缩全部由 harness 原生机制驱动,无需改动 UI、无需拦截前端。 - **无图零开销**——不含图片的请求原样透传给回退 provider(默认 `deepseek-official`)。 - **视觉辅助回复**——每张图片由视觉模型解析(附带用户文本一起送入提示词),替换为 `[图片 N 描述]` 文本块后再发给 DeepSeek。 - **LRU 描述缓存**——同一张图 + 同一问题不重复解析;历史回放与会话压缩不会反复调用视觉模型。 - **503/429 自动重试**——适配台式机单 GPU 互斥调度(其他工具占用显存时视觉网关返回 503)。 - **可配置失败策略**——`placeholder`(插入失败说明继续对话)或 `error`(整轮报错)。 ## 工作原理 聊天输入栏原生支持图片附件:图片以 `{type:"image", attachment}` 内容块进入模型请求。但 DeepSeek chat-completions 适配器对 image 块显式报 `UNSUPPORTED_CONTENT`,纯文本模型无法直接处理。 本插件的桥接 provider(`deepseek-vision`)对外声明 `inputModalities: ["text", "image"]`,从而通过 host 的图片准入校验(否则消息进 agent 前就会被 `MODEL_DOES_NOT_SUPPORT_IMAGES` 拒绝)。在它的 `stream()` 中: 1. **无图** → `yield* ctx.llm.stream({ ...options, provider: fallbackProvider })`,纯透传、零开销; 2. **有图** → 对每个 image 块,通过一次嵌套的 `ctx.llm.stream()` 调用配置的视觉 provider(如 pi-ai 的 `llama` 路由,图片字节由附件服务自动读取),把 image 块替换为 `[图片 N 描述]\n<描述>` 文本块后,将改写后的消息转发给回退 provider。 会话压缩(compaction)复用最近一次请求的 provider,因此含图历史也会自动走桥接。可选的 `autoRoute`(默认关)会把 `deepseek-official` 的 agent 请求额外改写为本 provider,但**无法绕过 host 的图片准入校验**,仅作兜底——要真正发图,请把主模型配置为 `deepseek-vision`。 ## 安装 ```sh # 从 GitHub 安装(纯 JS 无需构建,也无需 allowBuilds) dsh plugin --profile web add github:Einskyle/dsh-llm-vision-bridge # 或从 npm registry 安装 dsh plugin --profile web add dsh-llm-vision-bridge # 重启 web 服务生效 pnpm dsh web ``` 无 pnpm 环境的手动安装(等效): 1. 将本包目录复制到 `%USERPROFILE%\.dsh\profiles\web\node_modules\dsh-llm-vision-bridge\` 2. 编辑 `%USERPROFILE%\.dsh\profiles\web\package.json`: - `dependencies` 增加 `"dsh-llm-vision-bridge": "file:<绝对路径>"` - `dsh.profile.bundles` 数组增加 `"dsh-llm-vision-bridge"` 3. 重启 web 服务 ## 快速开始 1. 重启后打开 **设置 → 模型**:出现新 provider **「DeepSeek(视觉桥接)」**(模型 `deepseek-v4-flash` / `deepseek-v4-pro`)。 2. **把主模型配置为桥接 provider**——`agent-default-model.provider: deepseek-vision`。这是必须的:host 的图片准入校验读的是会话选中模型的 `inputModalities`,只有桥接模型声明了 `image`。 3. 在聊天输入框直接 **粘贴/上传图片**(PNG/JPEG/WebP/GIF),可附图注文字,发送即可。图片先经视觉模型解析(约 10–40 秒,含冷加载与思考),随后 DeepSeek 基于描述回复。 4. 想用纯文本时可随时把主模型切回 `deepseek-official`(此时发图会被准入拒绝,属预期行为)。 ## 配置(设置 → 模型 → llm-vision-bridge) | 字段 | 默认 | 说明 | |---|---|---| | `enabled` | `true` | 总开关;关闭后桥接 provider 退化为纯透传 | | `autoRoute` | `false` | 额外把 `deepseek-official` 的 agent 请求改写为本 provider(无法绕过图片准入,仅兜底) | | `fallbackProvider` | `deepseek-official` | 真正生成回复的纯文本 provider | | `visionProvider` | `llama` | 视觉 provider 路由(pi-ai) | | `visionModel` | `/models/qwen3-vl-4b-thinking/Qwen3-VL-4B-Thinking-Q4_K_M.gguf` | 视觉模型 id | | `visionPrompt` | (内置中文提示词) | 视觉解析系统提示词 | | `visionMaxTokens` | `2048` | 视觉解析输出上限(建议 ≥1024,思考过程会占 token) | | `visionRetries` | `3` | 可重试错误(503/429/超时)的最大重试次数 | | `visionRetryDelayMs` | `30000` | 重试间隔 | | `onVisionFailure` | `placeholder` | 解析最终失败时:`placeholder`=插入失败说明继续对话;`error`=整轮报错 | ## 视觉模型选择 视觉调用走 pi-ai 适配器(`ctx.llm.stream` 指向 `visionProvider`/`visionModel`),因此**任意 OpenAI 兼容的视觉端点都可以用**——本地 llama.cpp 网关只是默认配置,不是硬性要求。 | 类型 | 示例 | 需要 key | 说明 | |---|---|---|---| | 本地 llama.cpp 网关(当前默认) | `Qwen3-VL-4B` via `http://:18081/v1` | 否 | 免费、隐私、仅局域网;图片字节不出你的网络 | | 云 OpenAI 兼容 API | `qwen-vl-max`(百炼)、`glm-4v-plus`(智谱)、`gpt-4o`(OpenAI)、OpenRouter/硅基流动等 | 是 | 模型更强;图片会发送到云端 | 示例——在 `settings.yaml`(或 设置 → 模型 → llm-pi-ai)新增百炼路由并让桥接指向它: ```yaml llm-pi-ai: providers: dashscope: displayName: DashScope apiKeyEnv: DASHSCOPE_API_KEY api: openai-completions baseURL: https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 models: - id: qwen-vl-max name: Qwen-VL-Max input: [ text, image ] llm-vision-bridge: visionProvider: dashscope visionModel: qwen-vl-max ``` 设置改动即时生效,无需重启。约束:端点必须 OpenAI 兼容且支持图片输入;视觉 provider 不能是桥接 provider 自身(`deepseek-vision`,防递归);云路由需要存储凭据(`apiKeyEnv` → 设置 → 模型),否则 pi-ai 报 `MISSING_CREDENTIAL`。 ## 依赖与前置 - 视觉模型走 pi-ai 适配器,需在 **设置 → 模型 → llm-pi-ai** 配置好视觉 provider(如 `llama` 路由:baseURL 指向台式机 llama.cpp `http://:18081/v1`,模型声明 `input: [text, image]`)。 - 若视觉 provider 配置了 `apiKeyEnv` 但凭据未设置,pi-ai 会报 `MISSING_CREDENTIAL`:在设置页存一个任意占位值(本地 llama.cpp 不校验 key),或删除该 `apiKeyEnv`。 - 台式机单 GPU 互斥调度:其他工具占用显存时视觉网关返回 503,插件按 `visionRetries` 自动等待重试。 ## 故障排查 | 现象 | 处理 | |---|---| | 设置里看不到「DeepSeek(视觉桥接)」 | 插件未加载成功,检查 web 服务启动日志;确认 bundle 已加入 profile | | 发图报 `attachment-error` / `MODEL_DOES_NOT_SUPPORT_IMAGES` | 会话选中模型不是桥接模型:把 `agent-default-model.provider` 配为 `deepseek-vision`,或在该会话手动选择「DeepSeek(视觉桥接)」 | | 发图报 `VISION_UNAVAILABLE` | 视觉模型不可达:检查 `llama` provider 的 baseURL、台式机是否开机、`LLAMA_API_KEY` 是否缺失 | | 发图后仍报 `UNSUPPORTED_CONTENT` | 请求没走桥接 provider:确认主模型是 `deepseek-vision`(不是 `deepseek-official`) | | 视觉解析慢 | Qwen3-VL 冷加载 10–40s 属正常;若频繁 503,等台式机其他 GPU 任务结束 | ## 许可 MIT