modlens

2.8kStar76Fork6Issue2Watching

为 DeepSeek Harness 纯文本模型补上视觉能力的官方插件:注册 modlens_read_image 工具并自动包装 DeepSeek/GLM 模型,直接粘贴图片即可识别。

语言
TypeScript
License
MIT
分支
main
agent-skillsclaude-codeclaude-skillscodexcordisdeepseekdshdsh-plugin

安装

$ dsh plugin --profile web add github:liustack/modlens

在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程

一句话定位

modlens 是 DeepSeek Harness 的官方视觉插件,给原生不支持图片的 DeepSeek/GLM 文本模型补上一双眼睛:装好之后 dsh 里直接粘贴截图、丢个图片路径或加一张拖拽的图片,模型就能"看到"内容并据此回答。

核心能力

  • 给文本模型注册 modlens_read_image 工具,传入本地路径或 http(s) URL 即可转成结构化 JSON 证据(全文转录、版面区块、实体关系、不确定项)
  • 自动包装所有承载 DeepSeek/GLM 纯文本模型的 provider 路由,每条路由各生成一组带 (modlens vision) 后缀的模型变体;两家自带的视觉型号自动排除
  • 浏览器侧拦截粘贴的截图,先 POST 到本地路由、再把图片作为路径插进输入框(与 OpenCode、Pi 相同的"路径触发"交互)
  • 内置六个视觉引擎(Gemini API、Anthropic API、任意 OpenAI 兼容端点、Antigravity CLI、Claude Code CLI、Kimi Code CLI),未指定时组成故障转移链
  • recover-paste 子命令从 Claude Code / Pi / OpenCode 的本地会话存储里捞出粘贴过的图片字节,落到一个 0700 私有目录
  • doctor 子命令纯本地体检:Node 版本、引擎就绪情况、当前选择、宿主、guard 判定,不消耗额度不发网络请求

技术实现

  • 语言: TypeScript(Node.js ESM),少量用于浏览器侧的 hand-written CJS(dsh/client.js
  • 关键依赖: commander(CLI 路由)、undici(远程图片下载 + DNS 钉死)、@biomejs/biome(lint,仅 devDep);宿主侧只用 Node 内建模块
  • 架构模式: 双形态包 —— 同一个 npm 包既是 CLI(bin: modlensdist/main.js),也是 dsh cordis 插件(dsh.bundle.patchcordis.patch.yml@liustack/modlens 注入 dsh cordis 注册表,由 dsh/index.js 导出 apply(ctx, config))。DSH 内 browser 侧另有一个零依赖 lazy-CJS 客户端(dsh/client.js)拦截粘贴
  • 入口文件: CLI 入口 src/main.ts;DSH 插件入口 dsh/index.js;浏览器客户端 dsh/client.js

适用场景

日常用 dsh Web/桌面 App 跟 DeepSeek 或 GLM 文本模型对话、经常需要贴截图或丢图片路径来问问题的用户 —— 安装后不用记任何命令,正常聊天粘贴即可。也适合想压榨单一文本模型但不想换模型、又不想自己写 OCR/视觉脚本的开发者。

前置依赖与兼容性

依赖最低版本说明
Node.js>=22.19package.json engines.node 声明;OpenCode 粘贴恢复进一步依赖 node:sqlite(Node 22.13+ 起 unflagged)
dsh未声明具体版本通过 dsh.bundle.patch(cordis.patch.yml)注入;按 dsh plugin --profile web add github:liustack/modlens 安装;package.json 未声明 peerDependencies
平台macOS / Windows / Linuxsrc/util/winExec.ts 处理 Windows .cmd shim;src/auto/discover.ts 跨平台探测 PATH 上的 harness CLI
原生模块未引入任何 node-gyp 依赖;远程图片下载用 undici 自带的 Agent;OpenCode 恢复用 Node 内建 node:sqlite
外部账号至少一个视觉源默认走 Antigravity CLI(免 key);也可配置 Gemini / Anthropic API key 或任意 OpenAI 兼容端点;可借用本机其他 harness CLI 的登录

安装方式

dsh plugin --profile web add github:liustack/modlens

配置项

配置文件在 ~/.modlens/config.json(0600 权限,modlens config show 渲染时 key 自动打码)。常见可写键:

配置类型说明默认值
provider字符串视觉引擎偏好名(不指定时所有就绪引擎组故障转移链)antigravity-cli(Antigravity CLI)
providers.<name>.apiKey字符串该引擎的 API key;<name>gemini-api / openai / anthropicmodlens config set <name>.apiKey 无值时进入隐藏输入
providers.<name>.baseUrl字符串自定义 API 端点;openai 必须显式配置(无默认以免误投到 OpenAI 官方)引擎各自默认
providers.<name>.model字符串该引擎默认使用的模型名引擎各自默认
providers.<name>.proxy字符串该引擎专用代理 URL,回退到顶层 proxy,再回退到 HTTPS_PROXY / HTTP_PROXY
providers.<name>.extraBody对象合并进 API 请求体的 JSON(常用于关闭 thinking,例如 {"thinking":{"type":"disabled"}});保留字段:contents / messages / model / schema 等不可覆盖
providers.<name>.structuredOutput布尔让 OpenAI 兼容端点自己用 response_format: json_schema 强约束输出;默认 false(部分网关会 400)false
proxy字符串所有 API provider 的兜底代理 URL;远程图片下载(SSRF-guarded)不受其影响
guards.denyModels字符串数组glob 模式列表;当前模型命中则拒绝启动视觉引擎(给原生视觉模型让路)[]
guards.allowModels字符串数组非空时进入"白名单模式":只有命中的模型才会调引擎,其他都被拒[]
guards.denyWhenUnknown布尔当前模型无法识别时是否拒读(默认放行,避免误锁文本模型)false
reuse.claude / codex / opencode / pi / grok布尔是否允许借用本机对应 harness 的登录做读图;claude 缺省视为 true(向后兼容)claude=true / 其他未询问
MODLENS_MODEL / MODLENS_HARNESS / MODLENS_DSH_CLI环境变量直接告诉 modlens 当前模型 / 宿主 / CLI 路径,覆盖探测

常见问题

Q: modlens 是给谁用的?为什么普通 DeepSeek/GLM 模型也需要它?

A: dsh 默认宿主下的 DeepSeek-V4、GLM 等主力对话模型是纯文本的,无法直接读取粘贴的图片。modlens 通过一个外部视觉引擎帮你把这些图片转成结构化的文字证据再喂回文本模型,让"截图 + 提问"变成一次完整的对话。

Q: 安装后默认能用吗,还需要自己配 API key 吗?

A: 默认走 Antigravity CLI(免 key、浏览器登录一次即可),零配置就能用。若想更快,配一个免费 Gemini key 通常 5-10 秒一次;其他 OpenAI 兼容端点(DashScope、SiliconFlow、OpenRouter、自建 vLLM/Ollama 等)也可直接对接。

Q: 安装后会看到什么变化?需要选哪个模型?

A: 模型选择器会多出若干 "(modlens vision)" 后缀的条目,比如 DeepSeek-V4-Flash (modlens vision)、DeepSeek-V4-Pro (modlens vision)。切到这些条目粘贴图片即可,缩略图直接可见,体验接近 Codex App;不切也能用,图片会作为路径进输入框。

Q: 一张图片会被哪个引擎处理?会扣哪个账号的额度?

A: 未指定时,所有已配引擎组成一条故障转移链:API 类快车道先试,agent CLI 兜底;每次结果里 meta.attempts 列出尝试顺序,meta.warnings 标明复用了哪个 harness 的登录、扣的是谁的额度,永远不会无声扣费。

Q: modlens recover-paste 是做什么的?

A: Claude Code、Pi、OpenCode 这些终端默认把粘贴的图片直接存进会话存储而不会落到普通临时文件。recover-paste 会从这些 harness 的本地存储里把粘贴的字节读出来另存到安全目录(0700)。从 OpenCode 恢复需要 Node 22.13+ 提供 node:sqlite。

Q: 出错了怎么排查?

A: 跑 modlens doctor,它会汇报 Node 版本、哪个 provider 已就绪、当前会选哪个、检测到了哪个 harness,全在本地查,不消耗额度也不联网。报错信息通常会自带修复命令,例如缺 key 时直接告诉你 modlens config set <provider>.apiKey

Q: 卸载会留下什么?

A: 删除本插件包即可卸载。会留在机器上的只有 ~/.modlens/config.json(你显式配置过的 key、端点、复用授权等)和 ~/.modlens/ 下的回收目录;不放进 dsh 的 hook 也不动任何 harness 配置,宿主本身可以无痕回退到原状。

上手难度

入门 — 一条 dsh 安装命令加一个默认引擎(Antigravity CLI 免 key 浏览器登录)即可跑通;想做"高级配置"(代理、自定义 OpenAI 端点、借用其它 harness 凭据)才需要读配置手册。

已知问题与限制

  • Kimi Code CLI 没有 --json-schema 等服务端强约束手段,只能靠 prompt 模板 + 容错解析;并且因为 kimi 会扫共享 skill 目录,存在被 modlens 自身递归触发的可能(已用 MODLENS_INSIDE_KIMI_CLI--skills-dir 指向空目录缓解,但模型是否走那条路是模型自己决定的,偶发难复现)
  • openai 引擎默认不开启 response_format: json_schema,因为部分兼容网关不认识这个字段会 400;需要服务端约束时显式 modlens config set openai.structuredOutput true
  • 远端图片下载有 25 MB 上限(src/imageInput.ts MAX_REMOTE_IMAGE_BYTES),覆盖一张密集截图或高分辨率照片,但大体积原图会被拒绝
  • 粘贴文件支持 png/jpeg/gif/webp/heic/heif(其它如 bmp / svg / raw 不收);通过文件 magic bytes 校验,不信任扩展名
  • Kimi / Codex / OpenCode / Claude CLI 路由只能读本地路径文件;远程 URL 必须用 API 类引擎(gemini-api / openai / anthropic)
  • Antigravity CLI 的免费额度是按周共享的桶(桌面 App / CLI / SDK 共用),并发子代理会很快耗尽(issue 文档明确说明,需等重置或换 gemini-api