A content-aware PDF reading plugin for vision models: profiles each page for figures (vector and raster), tables, formula risk and double-column layout, then applies content-aware hybrid extraction, rendering figure/table/formula pages as high-DPI region crops. Provides a low-resolution preview to understand the page layout, and renders a specified region at high resolution. Packaged as multiple tools for agents.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:AngelosZou/dsh-pdf-reader
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
DeepSeek Harness plugin for content-aware PDF reading by vision models.
The plugin reads a PDF the way its content demands: text-heavy pages are extracted as text, while figures, tables, formula-heavy or two-column pages are rendered as high-DPI region crops fed to the vision model — so a paper's vector figures and structured tables are never lost to the per-image token ceiling.
Backed by PyMuPDF. When Python or a dependency is missing, the tools return a clear, actionable warning (with the exact install command) for the agent to resolve, instead of failing hard.
Install
dsh plugin --profile web add dsh-pdf-reader
Requires a Python interpreter with pymupdf.
After installing Python, you can install the
dsh-python-env plugin in DeepSeek
Harness so the agent handles the dependency automatically — it will create a
project venv and install pymupdf itself, with no manual steps:
dsh plugin --profile web add dsh-python-env
To set it up by hand instead:
python -m venv .venv
.venv\Scripts\python.exe -m pip install pymupdf
Tools
| Tool | What it does |
|---|---|
pdf_scan |
Per-page content profile — columns, figures (vector regions), raster images, tables, text characters, hasGraphics, formulaRisk, hasTextLayer. Use first to decide how each page should be read. |
pdf_read_page |
Read one page. mode=mixed is the one-shot: a low-res fullPage preview + the page text + every auto-detected figure/table region as high-DPI PNGs (paths, cached in .dsh-pdf-reader). mode=auto/text/render force a single path. |
pdf_render_region |
Targeted high-res render of a [x0,y0,x1,y1] region at budget-filling DPI (or an explicit dpi), returning its path for read_image. |
Recommended workflow
The tools are built around a preview → content → refine loop so large PDFs stay cheap and no layout is lost to flattening:
- Preview (whole page, low-res). Call
pdf_read_page --mode mixedfor a page. It returns a low-resfullPagerender (the whole layout — formulas, table gridlines, figure placement, two-column order) plus the page's text. Look at the preview to see what is on the page before spending high-res budget on it. - Content (auto, cached). The same
mixedcall auto-detects every figure and table region and renders each as a high-DPI PNG. All PNGs are written to<cwd>/.dsh-pdf-readerand returned only as paths — heavy/long content lives in the cache, never inlined into your context. Feed the paths toread_image. - Refine (on demand). If a specific area is still too small, or was not
auto-cropped (an uncropped formula, a crowded table cell, a sub-figure), zoom it
with
pdf_render_regionon the exact[x0,y0,x1,y1]you read off thefullPagepreview.
Start any document with pdf_scan (whole-document overview) to plan which pages
are text-only vs figure/table/formula-rich, then apply the loop per page.
Why this design
A two-column paper's figures are usually vector (only page rasterization can see them), tables lose structure under text extraction, and formulas garble in some text layers. Meanwhile DeepSeek caps each image at ~800×800-equivalent / 384 tokens — so reading a whole two-column page at that budget makes each column ~350px and loses small text, sub/superscripts and figure detail. The fix:
- Text pages → extraction (cheap, precise, preserves prose + inline math that PyMuPDF decodes well).
- Figure/table/math pages → render the region and scale it to fill the
~640k-pixel budget via
dpi = 72 × sqrt(640000 / region_pt_area). Content is complete (nothing legible is lost inside the budget) and token-optimal (rendered AT the budget, not beyond it).
Region detection is heuristic (not a perfect classifier) and deliberately biased
toward rendering — vector clusters from get_drawings(), raster rects from
get_image_rects(), the same clustering for tables, and math from fonts + a LaTeX
producer. It over-flags (a ruled table or a logo may be treated as a figure) rather
than under-flagging, because rendering is cheap and safe.
When dependencies are missing
Each tool resolves a Python interpreter in priority order — the activated venv
($VIRTUAL_ENV) first, then PATH python/python3/py, then the project
.venv/venv/env — probes it for pymupdf (and optionally pymupdf4llm), and
picks the first one that can import it. If none can, it returns a warning naming
what is missing and how to fix it, so the agent can install the dependency,
switch interpreters, or fall back.
Limitations
page.find_tables()false-positives on plot grids and diagrams, so tables are primarily read by rendering (the reliable path); Markdown tables frompymupdf4llmare a best-effort extra.- Formula detection is heuristic (fonts + LaTeX producer). PyMuPDF decodes inline
math well, but stacked fractions can still be imperfect — use
pdf_render_regionon an equation when exact structure is needed. - A whole page renders to only ~83 DPI-equivalent at the budget; the tools never do that for a two-column page — they crop regions instead, and body text uses extraction.
- Large PDFs are parsed into memory.
Requirements
- Node ≥ 20,
@deepseek-ai/cordis^4,@deepseek-ai/dsh-tools(peer deps, provided by the harness). - Python 3 +
pymupdf(optionalpymupdf4llm).
License
MIT
Links
More in this category
liustack/modlens★ 3789
Vision bridge for text-only models: paste an image, get structured JSON evidence (OCR, layout, semantics).
ysr666/dsh-vision-router★ 1034
Free vision for text-only agents: built-in keyless vision chain plus pixel tools (Q&A, grounding, crop, pixel diff, colors, OCR, SVG trace, cutout, screenshots); paste an image to use it.
Anionex/dsh-vision-toolkit★ 846
Vision for text-only models: paste an image and the model switches to a Vision Toolkit variant for image Q&A, multi-image comparison, long-screenshot OCR, screenshot-to-UI reproduction, element grounding, and pixel diff. No API key by default — images are processed by the author-hosted free service, 100 per machine per day; configurable to your own provider.
dickpy/dsh-imagegen★ 42
AI image generation for the DSH Web GUI: text-to-image and image-to-image through a configurable OpenAI-compatible endpoint (gpt-image-2 / gpt-image-1 / dall-e-3), with an api_url/api_key settings card and a sidebar split-pane generation studio.
fandc520/dsh-comfyui★ 39
Drive a local or remote ComfyUI server from DeepSeek Harness: comfyui_run / comfyui_object_info / comfyui_workflow tools generate and edit images and videos, with a workflow library (graph extraction: per component / main flow / all), a load area with resolution auto-match, a live queue, SDXL and Wan 2.1 templates, a companion skill, and a same-origin media proxy.
jing-hy/picturereader★ 35
Image "reading" for text-only models: downscale + reduce color depth + structure/color fingerprints into text grids fed back to the conversation, letting the model zoom, sample and OCR autonomously like a multimodal model; fully local with zero external model dependency, ships an image-reading methodology skill and optional PaddleOCR.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.