dsh-ocr-local

visioninstall failsnpmMIT

Local OCR fallback for text-only routes: when the session model declares it cannot accept images, the attached image is cached locally and its path injected so the model can call ocr_image — PP-OCRv5 + ONNX Runtime on CPU, no API key, images never leave the machine. Silent when the model can see ima

下载量 / Downloads
2,169
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-ocr-local

Sandbox test results

StageResult
1 install✗
2 list—
3 mount—
4 boot—
First error
dsh: initialized profile web at /home/node/.dsh/profiles/web

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-ocr-local

[English](README.en.md) · [中文](README.md)

![license](LICENSE) ![GitHub](https://github.com/grelvan/dsh-ocr-local)

给 DeepSeek Harness(Web 端)装一个本地 OCR 兜底:当会话路由到的模型不支持图片输入时,

把图片里的文字读出来给模型;模型能看图时,插件完全静默、不插手。

识别引擎是 PP-OCRv5 + ONNX Runtime,纯 CPU、完全离线,图片不会离开你的电脑。

插件什么时候生效,什么时候静默

这是理解本插件最重要的一张表。判定基于当前会话实际路由到的模型声明的输入能力

(inputModalities),而不是猜:

会话路由到的模型 · 插件行为 · 模型实际拿到什么

明确声明不支持图片(如纯文本模型) · ✅ 生效:图片存到本地缓存,并注入路径提示 → 模型调 ocr_image 识别 · 文字。Harness 对这类模型只给一句 [image omitted because this model accepts text only; …],没有任何路径,所以没有本插件模型就完全读不到图

明确声明支持图片(多模态模型) · 🔇 静默:不存缓存、不注入提示 · 图片本身。Harness 还会在图片前附一条只读副本路径,模型想逐字核对时可以直接对那个路径调 ocr_image

无法确定(provider 未注册 / 查询失败 / 没声明模态) · 🔇 静默 · 按 Harness 原样处理。宁可不打扰,也不在一个可能能看图的模型上多塞提示

"无法确定就静默"是刻意的。如果你确实需要无条件介入,把 autoOcr 设成 'always'(见下方配置)。

关于「未登记的 model id」:DeepSeek 适配器对没写进模型目录的 id 会显式返回
inputModalities: ["text"],所以这类纯文本路由会被上表第一行正确覆盖。
其它 provider 若对未声明模型返回「无模态信息」,则落到第三行(静默)。

⚠️ 在 Web 设置页配置的模型,能力声明可能是空的

插件的判定完全依赖配置里声明的 inputModalities,而不是模型的真实能力。

而 Web 设置页不提供这个字段——这是最容易踩空的一处,单独说清楚。

声明写在 ~/.dsh/settings.yaml 的 llm-deepseek.models[] 里:

llm-deepseek:
  models:
    - id: deepseek-v4-pro
      name: DeepSeek-V4-Pro
      contextWindow: 1000000
      inputModalities:        # ← 决定插件介入还是静默的就是这一项
        - text

Web 模型编辑器只暴露 4 个字段:id / name / contextWindow / maxTokens。

inputModalities 不在其中,所以在页面上新增模型时,写回的条目里没有这一项,

而配置 schema 会给它兜底成 ['text']。

在 Web 页面上做的事 · 对该模型能力声明的影响

新增一个模型 · 条目里没有 inputModalities → 按纯文本处理

编辑已有模型的 name / 窗口 / tokens · 原有声明原样保留(页面的 patch 是「展开合并」语义,不会删掉它不认识的字段)

兜底成纯文本会怎样:

  • 该模型确实不支持图片 → 正好是插件该介入的场景,不用管。
  • 该模型其实支持图片(自建 vision 端点,或想把某个多模态模型接进来)→ 插件会多此一举地

介入,图片被 Harness 投影成 [image omitted …] 占位符,本来能看图却看不到。

这时必须手工补声明:

      inputModalities:
        - text
        - image

改完不用重启:settings.yaml 是热加载的(文件监听 + 去抖),保存即生效。

声明要满足的约束(写错会在加载时直接报错,不会静默忽略):

  • 必须是非空数组,元素只能是 text / image,且不能重复。
  • 声明了 image 才能写 imagePixelBudget(像素预算,可填 low 或正整数)与 imageMaxBytes

(单图字节上限)。纯文本模型写了这两个字段会直接报错:

text-only catalog model "xxx" cannot declare image request limits。

非 DeepSeek 的 provider 卡片有自己的字段集合,能力声明的字段名以该 adapter 的配置 schema 为准。
顺带说清一个相关语义:settings.yaml 里的 models 是整体覆盖内置目录的,
而模型选择器显示的正是这个数组(适配器的 listModels 直接映射它)。
所以在 Web 页面上动过模型列表之后,选择器里有哪些模型完全以这个数组为准——
把 deepseek-v4-flash-vision-exp 删掉了,就一个多模态模型都切不过去了
(本机默认是保留的)。

为什么插件要动模型的"能力声明"

Harness 在你按下发送的那一刻(prompt 准入阶段)就会读模型声明的图片能力:只要声明里

没有 image,它会在消息进入会话之前直接拒绝,并提示

「当前模型不支持图片,请切换支持图片的模型」。图片进不了会话,插件也就永远收不到

user/message 事件——本地 OCR 兜底在这条路径上就成了一行死代码。

所以插件会在这一个方法(llm.resolveModelInfo)上给纯文本模型补上 image 声明,

把准入放行。这不会让图片真的发给纯文本模型:请求由适配器按自己的模型目录构造,

图片在送上 wire 之前仍会被 Harness 投影成

[image omitted because this model accepts text only; attachment sha256:…] 文本。

换句话说,插件扩的是「准入」,不是「能力」。

不想要这个行为,把 autoOcr 设为 false——那时插件连这层包装都不会装。

与视觉模型的关系

两条链路互不干扰,因为它们在不同的层做事:

  • 插件只管本地 OCR:把图存到本地、提示模型去调 ocr_image。
  • 图要不要发给模型由模型能力 / 客户端配置决定,插件不干预。

所以多模态模型下你不需要为本插件做任何事:它会自己退场。想手动让它读图里的字,

随时可以直接对 agent 说「用 ocr_image 读这张图」。

快速开始

第 1 步:安装插件

DSH 的 profile 互相隔离,插件要装到你要用的那个 profile(Web 端通常叫 web)。

先确认你能调起 dsh CLI——它有几个等价入口,按你手上有什么选一个(下面的命令都以此为准):

你的情况 · 怎么调

PATH 里有 dsh 命令 · dsh plugin …

在 harness 源码仓库里(pnpm dsh 是仓库自带脚本) · cd <仓库> && pnpm dsh plugin …

同上,想走构建产物、不起 tsx · cd <仓库> && node apps/cli/lib/bin.js plugin …

直接用 npm 上发布的 CLI 包 · npx -y @deepseek-ai/dsh@alpha plugin …

⚠️ 两点容易卡住:
1. npx -y @deepseek-ai/dsh 不带版本标签可能调不起来——npm 上这个包的 latest 停在预发布
的 0.1.5-rc.1(而源码仓库已经是 0.1.6-alpha.1)。要么显式写 @alpha,要么直接用源码
仓库的入口;版本最好与你的 profile 保持一致。
2. dsh plugin 内部靠 pnpm 转发,PATH 里必须有 pnpm(缺了会报 pnpm not found on PATH)。

装 npm 上的稳定版:

dsh plugin --profile web add dsh-ocr-local

也可以直接从 GitHub 安装(想跟最新提交、或 npm 不可达时):

dsh plugin --profile web add github:grelvan/dsh-ocr-local

或本地克隆后安装:

git clone https://github.com/grelvan/dsh-ocr-local.git
dsh plugin --profile web add ./dsh-ocr-local
改代码做开发时,用 link: 装软链更快——dsh plugin --profile web add link:./dsh-ocr-local,
此后改仓库里的文件即刻生效,不用重装。

装完重启 dsh,插件才会生效。

第 2 步:准备识别引擎(只需一次)

在自己终端里跑这一条(把 web 换成你的 profile 名;直接指向 clone 下来的仓库也行):

python3 ~/.dsh/profiles/web/node_modules/dsh-ocr-local/ocr/setup.py

它会自动完成三件事。识别模型是随插件包一起发布的,不用下载 —— 所以实际只需要装

Python 依赖(onnxruntime / numpy / opencv,约 85MB,走国内镜像通常 1-3 分钟):

[setup] dsh-ocr-local —— 本地 OCR 环境准备
  共 3 步,首次约 2-6 分钟(大头是下载);每步都会打进度,不用干等。

[setup] 1/3 准备 Python 虚拟环境 ...
[setup] 2/3 安装依赖(约 85MB)...
    Collecting onnxruntime ...
[setup] 3/3 下载识别模型(约 21MB,已下过的会跳过)...
[1/3] PP-OCRv5_mobile_det.onnx
    47% (2MB/4MB) [直连]

装完会打印自检命令。之后每次识别都在本地秒级完成,不再需要网络。

网络不好怎么办(脚本会自救,剩下的靠这几个开关):

情况 · 怎么办

GitHub 拉得动但很慢 · 会按速度换源:持续低于 200KB/s 就换下一个,日志里会写明「源 X 太慢(80KB/s),换下一个」

GitHub 完全拉不动 · 会自动回退:直连 → ghproxy → gh-proxy → ghfast。想钉死某一个就设 DSH_OCR_MODELS_MIRROR=https://ghproxy.com/

整体都很慢 · 加代理最快:https_proxy=http://127.0.0.1:7892 http_proxy=http://127.0.0.1:7892 python3 …/ocr/setup.py

PyPI 慢 · 依赖默认已走清华源;要换源设 DSH_OCR_PIP_INDEX=<index-url>(设为空 = 官方源)

想调速度阈值 · DSH_OCR_MIN_SPEED_KBPS=500(默认 200)

如果所有源都慢,最后一轮会停止换源、直接把它下完 —— 不会永远在换源里打转。
中途断掉重跑即可,已下好且 sha256 校验通过的文件会跳过。
⚠️ 为什么不建议对 agent 说「用 ocr_setup 装」?
插件执行 Bash 时可能处在受限沙箱里(网络、可写目录都收紧),而装环境恰恰要下载依赖、
写 venv 和模型目录。实测在沙箱里会卡在「uv 缓存目录只读」之类的坑上,模型反复试错、
几分钟还不一定成。让它在你自己的 shell 里跑,一次就过。
ocr_setup 工具仍然可用(它调的就是同一个脚本),只是在受限环境下成功率低。

装好后把任意一张图片发给 agent,说「识别这张图片」即可。

第 3 步:开始使用

方式 A:粘贴截图(最常用)

在 Web 输入

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::6,最后更新 2026-10-21

Links

GitHub · Homepage · npm

Same category