dsh-book-reader 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +494 -2
- package/SKILL.md +97 -0
- package/cordis.patch.yml +31 -0
- package/icon.svg +13 -0
- package/lib/client.js +1794 -0
- package/lib/core/README.md +299 -0
- package/lib/core/pdf.js +962 -0
- package/lib/core/render.js +351 -0
- package/lib/core/tokens.js +116 -0
- package/lib/index.js +1936 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +83 -4
package/SKILL.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pdf-reader
|
|
3
|
+
description: 'Read a PDF (book, paper, manual) cheaply and accurately with the dsh-book-reader tools: page map/TOC, literal full-text search, then selective page reads (text first, images only when typography matters). Use whenever the user asks about specific pages, a section, a theorem, a figure, or "this page" in a PDF.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reading a PDF with `dsh-book-reader`
|
|
7
|
+
|
|
8
|
+
Four tools, three cost tiers. **The default is text.** Images are a deliberate
|
|
9
|
+
expense, not the first move.
|
|
10
|
+
|
|
11
|
+
| Tool | Cost | Use for |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `pdf_info` | ~0 (text) | page count, printed↔physical offset, outline/TOC |
|
|
14
|
+
| `pdf_search` | ~0 (text) | locating a phrase, theorem number, or figure caption |
|
|
15
|
+
| `pdf_read_pages` + `include_image:false` | ~0 (text, per page) | prose, theorem statements, exercise text |
|
|
16
|
+
| `pdf_read_pages` (images on) | **~350–1024 tokens/page** | formulas, diagrams, tables, layout |
|
|
17
|
+
| `pdf_current_page` | ~0 | which page the user is looking at |
|
|
18
|
+
|
|
19
|
+
## Default loop (text-first)
|
|
20
|
+
|
|
21
|
+
1. `pdf_info` once per document → note `printedOffset` and the outline. Never
|
|
22
|
+
hardcode an offset; it is detected (`printed = pdf − printedOffset`).
|
|
23
|
+
2. `pdf_search` for a distinctive phrase → page numbers.
|
|
24
|
+
3. `pdf_read_pages` with **`include_image:false`** to read those pages as text.
|
|
25
|
+
4. Only if the text is ambiguous or the question is about typography (a formula,
|
|
26
|
+
a diagram, a table) → call again with images on, and crop.
|
|
27
|
+
|
|
28
|
+
## When images are worth it
|
|
29
|
+
|
|
30
|
+
- The question is about a **formula's exact form**, a **matrix layout**, a
|
|
31
|
+
**diagram**, or **which theorem box** a sentence is in.
|
|
32
|
+
- Extracted text is known to garble math: `P_{n−1}(C)` becomes `𝒫 𝑛 − 1(𝐂)`,
|
|
33
|
+
subscripts flatten, fractions collapse, theorem numbers split across lines.
|
|
34
|
+
- Prefer `crop` (`"0,0,1,0.55"` = top 55%) and `image_quality:"cheap"` (1.5×,
|
|
35
|
+
~350 tokens) unless dense subscripts need `scale:3`.
|
|
36
|
+
|
|
37
|
+
## Token economics (DeepSeek vision billing)
|
|
38
|
+
|
|
39
|
+
Each image is projected onto a 14 px patch grid, downsampled 3:1, and **capped at
|
|
40
|
+
1024 tokens**. For a 6×9 in book page:
|
|
41
|
+
|
|
42
|
+
| Render | Pixels | Tokens |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| 1.5× (`image_quality:"cheap"`) | 563×954 | **347** |
|
|
45
|
+
| **2.5× (default)** | 938×1590 | **914** |
|
|
46
|
+
| 3× | 1125×1908 | 1002 (capped — no gain above ~2.7×) |
|
|
47
|
+
|
|
48
|
+
- Hard cap `maxPagesPerCall` (default **4**) is enforced before rendering.
|
|
49
|
+
- `pdf_read_pages` returns `imageTokens` and per-page `tokens`; check them.
|
|
50
|
+
- Images stay in the conversation and are re-billed on later turns. **Read once,
|
|
51
|
+
then answer in text.**
|
|
52
|
+
- Never request `scale` above 3: the provider downscales to the same grid anyway.
|
|
53
|
+
|
|
54
|
+
## Page numbering
|
|
55
|
+
|
|
56
|
+
`pdf_info` returns `printedOffset` with `physical = printed + printedOffset`.
|
|
57
|
+
Tools accept either numbering; **printed is the default when detectable**.
|
|
58
|
+
Roman-numeral front matter has no numeric printed page — use
|
|
59
|
+
`page_numbers:"pdf"`. Say which numbering you mean when it is ambiguous.
|
|
60
|
+
|
|
61
|
+
## The injected reading position
|
|
62
|
+
|
|
63
|
+
At most one short line per turn may arrive as a user message, e.g.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
[dsh-pdf-reader] 正在阅读《book》printed 132 (PDF 152) · Chapter 5 › 5B
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
It is a **position marker only**. The model decides whether to read anything.
|
|
70
|
+
Do not read a page just because the marker exists; read it when the question
|
|
71
|
+
needs it, and prefer `include_image:false` first.
|
|
72
|
+
|
|
73
|
+
## Pitfalls
|
|
74
|
+
|
|
75
|
+
- **Scanned PDFs** have no text layer: `pdf_search` finds nothing and
|
|
76
|
+
`include_image:false` returns `textUnavailable`. Only images help.
|
|
77
|
+
- Errors are coded: `PDF_NOT_FOUND`, `PDF_NOT_FILE`, `PDF_ENCRYPTED`,
|
|
78
|
+
`PDF_CORRUPT`, `PDF_READER_INVALID_PAGE`, `PDF_READER_INVALID_SCALE`,
|
|
79
|
+
`PDF_RENDER_TIMEOUT`, `PDF_PAGE_TOO_LARGE`, `PDF_OUTSIDE_WORKSPACE`.
|
|
80
|
+
- **Tools are workspace-scoped by default.** Pass a path inside the session
|
|
81
|
+
working directory; anything outside fails with `PDF_OUTSIDE_WORKSPACE` unless
|
|
82
|
+
the user set `allowOutsideWorkspace: true`.
|
|
83
|
+
- `pdf_info` also reports `title`, `author`, `fileSizeBytes`, `pageSize` and
|
|
84
|
+
`textCoverage` (`isLikelyScanned`). Check `textCoverage.isLikelyScanned`
|
|
85
|
+
*before* searching: a scanned PDF has no text layer, so go straight to images.
|
|
86
|
+
- Search is literal: it does not unfold hyphenation and is not a regex engine.
|
|
87
|
+
- Page numbers are 1-based; relative paths resolve against the session directory.
|
|
88
|
+
- No annotation/note features: this plugin reads and renders only.
|
|
89
|
+
|
|
90
|
+
## Fallback (plugin disabled)
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
node -e "import('./lib/core/render.js').then(async m=>{const r=await m.renderPageToPng('<abs pdf>',344,{scale:2.5});require('fs').writeFileSync('/tmp/p344.png',r.buffer);console.log(r.width+'x'+r.height)})"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
then `read_image('/tmp/p344.png')`. Slower, bypasses the attachment cache —
|
|
97
|
+
prefer the tools.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# dsh-book-reader bundle patch: mount the plugin into the active profile.
|
|
2
|
+
# Host half: pdf_info / pdf_search / pdf_read_pages / pdf_current_page tools,
|
|
3
|
+
# /dsh-book-reader/* routes, and a minimal per-turn reading-position
|
|
4
|
+
# line injected through agent/pre-step.
|
|
5
|
+
# Browser half: page readout, jump, search panel, and share-to-chat on top of
|
|
6
|
+
# the official PDF preview's document-header slot.
|
|
7
|
+
#
|
|
8
|
+
# All config keys are optional. The defaults shown are the shipped behavior.
|
|
9
|
+
- insert:
|
|
10
|
+
- id: pdf-reader
|
|
11
|
+
name: 'dsh-book-reader'
|
|
12
|
+
# config:
|
|
13
|
+
# enabled: true
|
|
14
|
+
# autoShare: true # false stops position injection
|
|
15
|
+
# autoShareSection: true # include the outline section in the line
|
|
16
|
+
# autoShareMinIntervalMs: 180000 # at most once every 3 minutes
|
|
17
|
+
# autoSharePageDelta: 1 # require a page change
|
|
18
|
+
# autoShareMinSteps: 1 # at most once per step
|
|
19
|
+
# autoShareMaxAgeMs: 0 # 0 = no age limit on a reported position
|
|
20
|
+
# allowOutsideWorkspace: false # tools may only read inside the session cwd
|
|
21
|
+
# routeAllowedRoots: [] # extra dirs the HTTP routes may serve; [] = any
|
|
22
|
+
# maxPagesPerCall: 4 # hard cap per pdf_read_pages call
|
|
23
|
+
# defaultScale: 2.5 # ~180 DPI; never useful above ~2.7
|
|
24
|
+
# maxTextCharsPerPage: 1200 # text kept per page (0 disables)
|
|
25
|
+
# maxSearchResults: 100
|
|
26
|
+
# maxOutlineEntries: 500
|
|
27
|
+
# maxLabels: 0 # 0 = omit page labels (cheapest)
|
|
28
|
+
# outlineDepth: 1 # 0=chapters, 1=sections, 2=subsections
|
|
29
|
+
# debugCost: false # append JSON cost lines to .debug-cost.log
|
|
30
|
+
# debug: false # include error detail in HTTP 500 bodies
|
|
31
|
+
# maxFileBytes: 268435456 # 256 MiB
|
package/icon.svg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Book Reader">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#4c6ef5"/>
|
|
5
|
+
<stop offset="1" stop-color="#364fc7"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="3" y="3" width="58" height="58" rx="14" fill="url(#bg)"/>
|
|
9
|
+
<path d="M12 21.5c6.5-3.6 13-3.6 20 0v23c-7-3.2-13.5-3.2-20 0z" fill="#f8f9fa"/>
|
|
10
|
+
<path d="M52 21.5c-6.5-3.6-13-3.6-20 0v23c7-3.2 13.5-3.2 20 0z" fill="#e9ecef"/>
|
|
11
|
+
<path d="M32 21.5v23" fill="none" stroke="#adb5bd" stroke-width="1.6" stroke-linecap="round"/>
|
|
12
|
+
<path d="M38.5 17.5h6v13l-3-2.4-3 2.4z" fill="#ffd43b"/>
|
|
13
|
+
</svg>
|