@echofiles/echo-pdf 0.5.0 → 0.6.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/LICENSE +201 -0
- package/README.md +85 -562
- package/bin/echo-pdf.js +130 -525
- package/dist/file-utils.d.ts +0 -3
- package/dist/file-utils.js +0 -18
- package/dist/local/document.d.ts +10 -0
- package/dist/local/document.js +133 -0
- package/dist/local/index.d.ts +3 -135
- package/dist/local/index.js +2 -555
- package/dist/local/semantic.d.ts +2 -0
- package/dist/local/semantic.js +231 -0
- package/dist/local/shared.d.ts +50 -0
- package/dist/local/shared.js +173 -0
- package/dist/local/types.d.ts +183 -0
- package/dist/local/types.js +2 -0
- package/dist/node/pdfium-local.js +30 -6
- package/dist/pdf-config.js +2 -65
- package/dist/pdf-types.d.ts +1 -58
- package/dist/types.d.ts +1 -87
- package/echo-pdf.config.json +1 -21
- package/package.json +25 -22
- package/bin/lib/http.js +0 -97
- package/bin/lib/mcp-stdio.js +0 -99
- package/dist/auth.d.ts +0 -18
- package/dist/auth.js +0 -36
- package/dist/core/index.d.ts +0 -50
- package/dist/core/index.js +0 -7
- package/dist/file-ops.d.ts +0 -11
- package/dist/file-ops.js +0 -36
- package/dist/file-store-do.d.ts +0 -36
- package/dist/file-store-do.js +0 -298
- package/dist/http-error.d.ts +0 -9
- package/dist/http-error.js +0 -14
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/mcp-server.d.ts +0 -3
- package/dist/mcp-server.js +0 -124
- package/dist/node/semantic-local.d.ts +0 -16
- package/dist/node/semantic-local.js +0 -113
- package/dist/pdf-agent.d.ts +0 -18
- package/dist/pdf-agent.js +0 -217
- package/dist/pdf-storage.d.ts +0 -8
- package/dist/pdf-storage.js +0 -86
- package/dist/pdfium-engine.d.ts +0 -9
- package/dist/pdfium-engine.js +0 -180
- package/dist/r2-file-store.d.ts +0 -20
- package/dist/r2-file-store.js +0 -176
- package/dist/response-schema.d.ts +0 -15
- package/dist/response-schema.js +0 -159
- package/dist/tool-registry.d.ts +0 -16
- package/dist/tool-registry.js +0 -175
- package/dist/worker.d.ts +0 -7
- package/dist/worker.js +0 -386
- package/scripts/export-fixtures.sh +0 -204
- package/wrangler.toml +0 -19
package/README.md
CHANGED
|
@@ -1,86 +1,82 @@
|
|
|
1
1
|
# echo-pdf
|
|
2
2
|
|
|
3
|
-
`echo-pdf`
|
|
3
|
+
`echo-pdf` is a local-first, vision-language-first PDF context engine for AI agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It turns a local PDF into reusable CLI outputs, Node/Bun library primitives, and inspectable workspace artifacts for page rendering, page understanding, semantic structure, and downstream local reuse.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## What It Is
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Primary product surfaces:
|
|
10
10
|
|
|
11
|
-
- npm package
|
|
12
|
-
- CLI
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- npm package: `@echofiles/echo-pdf`
|
|
12
|
+
- local CLI: `echo-pdf ...`
|
|
13
|
+
- local workspace artifacts: `.echo-pdf-workspace/...`
|
|
14
|
+
- docs site: [pdf.echofile.ai](https://pdf.echofile.ai/)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Current focus:
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
18
|
+
- local-first workflows
|
|
19
|
+
- VL-first page understanding
|
|
20
|
+
- stable page-level document primitives
|
|
21
|
+
- reusable workspace artifacts
|
|
22
|
+
- clean package entrypoints for downstream consumers
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
Non-goals for this phase:
|
|
23
25
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
- 语义结构层:在 page index 之上产出可缓存的 heading / section 结构
|
|
29
|
-
- 页面渲染与 OCR artifacts:把 page render/image 与 OCR 结果缓存到本地 workspace
|
|
26
|
+
- hosted SaaS
|
|
27
|
+
- treating MCP as the primary product entrypoint
|
|
28
|
+
- turning the docs site into an online PDF processing service
|
|
29
|
+
- domain-specific logic such as datasheet- or EDA-specific extraction behavior
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
## Install
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
- 本地 library/client API
|
|
35
|
-
- 本地 workspace artifacts
|
|
36
|
-
- clean-consumer npm package
|
|
33
|
+
Requirements:
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
- Node.js `>=20`
|
|
36
|
+
- ESM-capable runtime
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
- Hosted SaaS / multi-tenant 平台能力
|
|
42
|
-
- 把网站做成在线 PDF 服务
|
|
43
|
-
- datasheet / EDA 等领域特化逻辑
|
|
38
|
+
Install globally for CLI use:
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
```bash
|
|
41
|
+
npm i -g @echofiles/echo-pdf
|
|
42
|
+
```
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
- [`docs/PACKAGING.md`](./docs/PACKAGING.md)
|
|
49
|
-
- [`docs/WORKSPACE_CONTRACT.md`](./docs/WORKSPACE_CONTRACT.md)
|
|
50
|
-
- [`docs/DEVELOPMENT.md`](./docs/DEVELOPMENT.md)
|
|
44
|
+
Or install as a dependency:
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
```bash
|
|
47
|
+
npm i @echofiles/echo-pdf
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
Index a local PDF and inspect its page-level context:
|
|
55
53
|
|
|
56
54
|
```bash
|
|
57
|
-
npm i -g @echofiles/echo-pdf
|
|
58
55
|
echo-pdf document ./sample.pdf
|
|
59
56
|
echo-pdf structure ./sample.pdf
|
|
60
|
-
echo-pdf semantic ./sample.pdf
|
|
61
57
|
echo-pdf page ./sample.pdf --page 1
|
|
62
|
-
echo-pdf render ./sample.pdf --page 1
|
|
63
|
-
echo-pdf ocr ./sample.pdf --page 1 --model gpt-4.1-mini
|
|
58
|
+
echo-pdf render ./sample.pdf --page 1 --scale 2
|
|
64
59
|
```
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
To run the VL-first semantic path locally, configure a provider key and model once, then run `semantic`:
|
|
67
62
|
|
|
68
63
|
```bash
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
npm run document:dev -- semantic ./fixtures/smoke.pdf
|
|
73
|
-
npm run document:dev -- page ./fixtures/smoke.pdf --page 1
|
|
64
|
+
echo-pdf provider set --provider openai --api-key "$OPENAI_API_KEY"
|
|
65
|
+
echo-pdf model set --provider openai --model gpt-4.1-mini
|
|
66
|
+
echo-pdf semantic ./sample.pdf
|
|
74
67
|
```
|
|
75
68
|
|
|
76
|
-
|
|
69
|
+
`echo-pdf semantic` now uses the CLI profile's provider/model/api-key settings. If the selected provider or model is missing, it fails early with a clear setup error instead of quietly dropping back to a weaker path.
|
|
70
|
+
|
|
71
|
+
What these commands map to:
|
|
77
72
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
- `
|
|
81
|
-
-
|
|
73
|
+
- `document` -> `get_document`
|
|
74
|
+
- `structure` -> `get_document_structure`
|
|
75
|
+
- `semantic` -> `get_semantic_document_structure`
|
|
76
|
+
- `page` -> `get_page_content`
|
|
77
|
+
- `render` -> `get_page_render`
|
|
82
78
|
|
|
83
|
-
|
|
79
|
+
By default, `echo-pdf` writes reusable artifacts into a local workspace:
|
|
84
80
|
|
|
85
81
|
```text
|
|
86
82
|
.echo-pdf-workspace/
|
|
@@ -95,26 +91,12 @@ npm run document:dev -- page ./fixtures/smoke.pdf --page 1
|
|
|
95
91
|
renders/
|
|
96
92
|
0001.scale-2.json
|
|
97
93
|
0001.scale-2.png
|
|
98
|
-
ocr/
|
|
99
|
-
0001.scale-2.provider-openai.model-gpt-4o.prompt-<hash>.json
|
|
100
94
|
```
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
正式的 workspace layout、cache/invalidation、detector/strategy metadata、以及下游可依赖边界,见 [`docs/WORKSPACE_CONTRACT.md`](./docs/WORKSPACE_CONTRACT.md)。
|
|
105
|
-
|
|
106
|
-
## Local library/client API
|
|
107
|
-
|
|
108
|
-
当前阶段优先提供本地可组合 primitives,让下游产品直接围绕 PDF 建立 document metadata / page-level artifacts,而不是先依赖远端 MCP/SaaS。
|
|
96
|
+
These artifacts are meant to be inspected, cached, and reused by downstream local tools.
|
|
97
|
+
## Library Usage
|
|
109
98
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- `@echofiles/echo-pdf`:core API
|
|
113
|
-
- `@echofiles/echo-pdf/core`:与根入口等价的 core API
|
|
114
|
-
- `@echofiles/echo-pdf/local`:本地 document primitives
|
|
115
|
-
- `@echofiles/echo-pdf/worker`:兼容保留的 Worker 路由入口,不是本阶段重点
|
|
116
|
-
|
|
117
|
-
### Local document primitives
|
|
99
|
+
Use the local document primitives directly from Node/Bun:
|
|
118
100
|
|
|
119
101
|
```ts
|
|
120
102
|
import {
|
|
@@ -123,523 +105,64 @@ import {
|
|
|
123
105
|
get_semantic_document_structure,
|
|
124
106
|
get_page_content,
|
|
125
107
|
get_page_render,
|
|
126
|
-
get_page_ocr,
|
|
127
108
|
} from "@echofiles/echo-pdf/local"
|
|
128
109
|
|
|
129
|
-
const
|
|
130
|
-
const
|
|
110
|
+
const document = await get_document({ pdfPath: "./sample.pdf" })
|
|
111
|
+
const structure = await get_document_structure({ pdfPath: "./sample.pdf" })
|
|
131
112
|
const semantic = await get_semantic_document_structure({ pdfPath: "./sample.pdf" })
|
|
132
113
|
const page1 = await get_page_content({ pdfPath: "./sample.pdf", pageNumber: 1 })
|
|
133
|
-
const render1 = await get_page_render({ pdfPath: "./sample.pdf", pageNumber: 1 })
|
|
134
|
-
const ocr1 = await get_page_ocr({ pdfPath: "./sample.pdf", pageNumber: 1, model: "gpt-4.1-mini" })
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
这些调用会把 artifacts 写入本地 workspace,并在 PDF 未变化时尽量复用已有页面结果。
|
|
138
|
-
`get_document_structure()` 继续返回最小 page index:`document -> pages[]`。
|
|
139
|
-
`get_semantic_document_structure()` 单独返回 heading / section 语义层,并写入 `semantic-structure.json`。
|
|
140
|
-
`get_page_render()` 会生成可复用的 PNG + metadata。
|
|
141
|
-
`get_page_ocr()` 会把 OCR 结果写入独立 artifact;它需要本地 provider key / model,不依赖 MCP 或远端服务入口。
|
|
142
|
-
|
|
143
|
-
### Page Index vs Semantic Structure
|
|
144
|
-
|
|
145
|
-
- `get_document_structure()`
|
|
146
|
-
- 契约:稳定的 `document -> pages[]`
|
|
147
|
-
- artifact:`structure.json`
|
|
148
|
-
- 目的:给下游做页级遍历、page artifact 定位、增量读取
|
|
149
|
-
- `get_semantic_document_structure()`
|
|
150
|
-
- 契约:显式的 heading / section 语义层,优先走本地 provider/model 的 agent 抽取;未配置或失败时退回保守 heuristic
|
|
151
|
-
- artifact:`semantic-structure.json`
|
|
152
|
-
- 目的:给下游做章节导航、语义分段;它不替代 page index,也不改变 `pages[]` 输出
|
|
153
|
-
|
|
154
|
-
当前 semantic 结构会把 detector 明确写入 artifact:
|
|
155
|
-
|
|
156
|
-
- `agent-structured-v1`:使用本地配置的 provider/model 对 page text 进行结构化抽取
|
|
157
|
-
- `heading-heuristic-v1`:当本地未配置模型,或 agent 抽取失败时使用的保守回退
|
|
158
|
-
|
|
159
|
-
两种模式都遵循同一输出契约;检测不到时会返回空结构,而不是伪造 section tree。
|
|
160
|
-
|
|
161
|
-
## Tool library compatibility
|
|
162
|
-
|
|
163
|
-
除了 local-first primitives 之外,`@echofiles/echo-pdf` 仍保留现有 `pdf_extract_pages / pdf_ocr_pages / pdf_tables_to_latex / file_ops` 工具实现的复用入口,用于兼容已有集成。
|
|
164
|
-
|
|
165
|
-
### Public entrypoints(semver 稳定)
|
|
166
|
-
|
|
167
|
-
- `@echofiles/echo-pdf`:core API
|
|
168
|
-
- `@echofiles/echo-pdf/core`:与根入口等价的 core API
|
|
169
|
-
- `@echofiles/echo-pdf/local`:本地 document primitives
|
|
170
|
-
- `@echofiles/echo-pdf/worker`:Worker 路由入口(兼容保留)
|
|
171
|
-
|
|
172
|
-
仅以上 `exports` 子路径视为公开 API。`src/*`、`dist/*` 等深路径导入不受兼容性承诺保护,可能在次版本中变动。
|
|
173
|
-
|
|
174
|
-
完整的 package entrypoint、runtime、semver、以及 clean-consumer import 保证,见 [`docs/PACKAGING.md`](./docs/PACKAGING.md)。
|
|
175
|
-
|
|
176
|
-
### Runtime expectations
|
|
177
|
-
|
|
178
|
-
- Node.js: `>=20`(与 `package.json#engines` 一致)
|
|
179
|
-
- 需要 ESM `import` 能力与标准 `fetch`(Node 20+ 原生支持)
|
|
180
|
-
- `@echofiles/echo-pdf/local` 面向本地 Node/Bun CLI 或 app runtime
|
|
181
|
-
- 建议使用支持 package `exports` 的现代 bundler/runtime(Vite、Webpack 5、Rspack、esbuild、Wrangler 等)
|
|
182
|
-
- TypeScript 消费方建议:`module=NodeNext` + `moduleResolution=NodeNext`
|
|
183
|
-
|
|
184
|
-
### Clean project import smoke
|
|
185
|
-
|
|
186
|
-
下面这段命令与仓库中的集成测试保持一致,可在全新目录验证 npm 包“可直接 import”:
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
tmpdir="$(mktemp -d)"
|
|
190
|
-
cd "$tmpdir"
|
|
191
|
-
npm init -y
|
|
192
|
-
npm i /path/to/echofiles-echo-pdf-<version>.tgz
|
|
193
|
-
node --input-type=module -e "await import('@echofiles/echo-pdf'); await import('@echofiles/echo-pdf/core'); await import('@echofiles/echo-pdf/local'); await import('@echofiles/echo-pdf/worker'); console.log('ok')"
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Example
|
|
197
|
-
|
|
198
|
-
```ts
|
|
199
|
-
import { callTool, listToolSchemas } from "@echofiles/echo-pdf"
|
|
200
|
-
import configJson from "./echo-pdf.config.json" with { type: "json" }
|
|
201
|
-
|
|
202
|
-
const fileStore = {
|
|
203
|
-
async put(input) {
|
|
204
|
-
const id = crypto.randomUUID()
|
|
205
|
-
const record = { ...input, id, sizeBytes: input.bytes.byteLength, createdAt: new Date().toISOString() }
|
|
206
|
-
memory.set(id, record)
|
|
207
|
-
return record
|
|
208
|
-
},
|
|
209
|
-
async get(id) {
|
|
210
|
-
return memory.get(id) ?? null
|
|
211
|
-
},
|
|
212
|
-
async list() {
|
|
213
|
-
return [...memory.values()]
|
|
214
|
-
},
|
|
215
|
-
async delete(id) {
|
|
216
|
-
return memory.delete(id)
|
|
217
|
-
},
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const memory = new Map()
|
|
221
|
-
const env = {}
|
|
222
|
-
|
|
223
|
-
console.log(listToolSchemas().map((tool) => tool.name))
|
|
224
|
-
|
|
225
|
-
const result = await callTool(
|
|
226
|
-
"pdf_extract_pages",
|
|
227
|
-
{ fileId: "<FILE_ID>", pages: [1], returnMode: "inline" },
|
|
228
|
-
{ config: configJson, env, fileStore }
|
|
229
|
-
)
|
|
230
|
-
console.log(result)
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
版本策略:
|
|
234
|
-
|
|
235
|
-
- `exports` 列出的入口及其导出符号按 semver 管理
|
|
236
|
-
- 对公开 API 的破坏性变更只会在 major 版本发布
|
|
237
|
-
- 新增导出、参数扩展(向后兼容)会在 minor/patch 发布
|
|
238
|
-
|
|
239
|
-
## 1. Compatibility surfaces(deferred / not primary)
|
|
240
|
-
|
|
241
|
-
以下内容是当前仓库中兼容保留的入口,不是本阶段主线产品形态。
|
|
242
|
-
主线仍然是 npm package + CLI + local workspace artifacts;网站只是文档站,不是在线服务。
|
|
243
|
-
|
|
244
|
-
请先确定你的线上地址(Worker 域名)。文档里用:
|
|
245
|
-
|
|
246
|
-
- `https://echo-pdf.echofilesai.workers.dev`
|
|
247
|
-
|
|
248
|
-
你自己的地址如果不同,把下面命令里的域名全部替换掉。
|
|
249
|
-
|
|
250
|
-
主要端点:
|
|
251
|
-
|
|
252
|
-
- Web UI: `https://echo-pdf.echofilesai.workers.dev/`
|
|
253
|
-
- MCP: `https://echo-pdf.echofilesai.workers.dev/mcp`
|
|
254
|
-
- HTTP API 根路径: `https://echo-pdf.echofilesai.workers.dev`
|
|
255
|
-
|
|
256
|
-
## 1.1 API 兼容性说明
|
|
257
|
-
|
|
258
|
-
- 从 `v0.3.0` 开始,`POST /tools/call` 返回结构改为:
|
|
259
|
-
- `{"ok": true, "data": ..., "artifacts": [...]}`
|
|
260
|
-
- 老格式 `{"name":"...","output":...}` 已移除。
|
|
261
|
-
- MCP `tools/call` 仍保留 `type:"text"`,并新增 `type:"resource_link"` 供下载二进制结果。
|
|
262
|
-
|
|
263
|
-
## 2. 快速开始(CLI)
|
|
264
|
-
|
|
265
|
-
安装:
|
|
266
|
-
|
|
267
|
-
```bash
|
|
268
|
-
npm i -g @echofiles/echo-pdf
|
|
114
|
+
const render1 = await get_page_render({ pdfPath: "./sample.pdf", pageNumber: 1, scale: 2 })
|
|
269
115
|
```
|
|
270
116
|
|
|
271
|
-
|
|
117
|
+
Notes:
|
|
272
118
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
119
|
+
- `get_document_structure()` returns the stable page index: `document -> pages[]`
|
|
120
|
+
- `get_semantic_document_structure()` returns a separate semantic structure layer; it does not replace `pages[]`
|
|
121
|
+
- `get_page_render()` materializes a reusable PNG plus render metadata and is the mainline visual input path
|
|
276
122
|
|
|
277
|
-
|
|
123
|
+
Migration note:
|
|
278
124
|
|
|
279
|
-
|
|
280
|
-
echo-pdf dev --port 8788
|
|
281
|
-
echo-pdf init --service-url http://127.0.0.1:8788
|
|
282
|
-
```
|
|
125
|
+
- older workspaces may still contain `ocr/*` artifacts from pre-VL-first builds, but they are no longer part of the supported first-class contract
|
|
283
126
|
|
|
284
|
-
|
|
127
|
+
## Public Package Entrypoints
|
|
285
128
|
|
|
286
|
-
|
|
287
|
-
echo-pdf provider set --provider openai --api-key <OPENAI_API_KEY>
|
|
288
|
-
echo-pdf provider set --provider openrouter --api-key <OPENROUTER_KEY>
|
|
289
|
-
echo-pdf provider set --provider vercel-ai-gateway --api-key <VERCEL_AI_GATEWAY_API_KEY>
|
|
290
|
-
```
|
|
129
|
+
The semver-stable public entrypoints are:
|
|
291
130
|
|
|
292
|
-
|
|
131
|
+
- `@echofiles/echo-pdf`
|
|
132
|
+
- `@echofiles/echo-pdf/local`
|
|
293
133
|
|
|
294
|
-
|
|
295
|
-
echo-pdf provider use --provider vercel_gateway
|
|
296
|
-
echo-pdf model set --provider vercel_gateway --model google/gemini-3-flash
|
|
297
|
-
echo-pdf model list
|
|
298
|
-
```
|
|
134
|
+
`@echofiles/echo-pdf` and `@echofiles/echo-pdf/local` expose the same supported local-first library surface.
|
|
299
135
|
|
|
300
|
-
|
|
136
|
+
Everything else, including deep imports such as `src/*` or `dist/*`, is private implementation detail.
|
|
301
137
|
|
|
302
|
-
|
|
303
|
-
echo-pdf models --provider vercel_gateway
|
|
304
|
-
```
|
|
138
|
+
## Docs
|
|
305
139
|
|
|
306
|
-
|
|
140
|
+
Contract and product docs:
|
|
307
141
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
## 2.1 六个核心 primitives
|
|
315
|
-
|
|
316
|
-
本地 CLI 主命令面与 `@echofiles/echo-pdf/local` 的六个 primitives 一一对应:
|
|
317
|
-
|
|
318
|
-
- `document <file.pdf>` -> `get_document`
|
|
319
|
-
- `structure <file.pdf>` -> `get_document_structure`
|
|
320
|
-
- `semantic <file.pdf>` -> `get_semantic_document_structure`
|
|
321
|
-
- `page <file.pdf> --page <N>` -> `get_page_content`
|
|
322
|
-
- `render <file.pdf> --page <N>` -> `get_page_render`
|
|
323
|
-
- `ocr <file.pdf> --page <N>` -> `get_page_ocr`
|
|
142
|
+
- [Product positioning](./docs/PRODUCT.md)
|
|
143
|
+
- [Package entrypoints and integration guarantees](./docs/PACKAGING.md)
|
|
144
|
+
- [Workspace artifact contract](./docs/WORKSPACE_CONTRACT.md)
|
|
145
|
+
- [Development guide](./docs/DEVELOPMENT.md)
|
|
146
|
+
- [Shared sample assets](./samples/README.md)
|
|
147
|
+
- [Eval harness](./eval/README.md)
|
|
324
148
|
|
|
325
|
-
|
|
149
|
+
Published docs site:
|
|
326
150
|
|
|
327
|
-
-
|
|
328
|
-
- README 和 `--help` 现在优先展示这六个主命令,而不是旧的子命令树
|
|
151
|
+
- [pdf.echofile.ai](https://pdf.echofile.ai/)
|
|
329
152
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
```bash
|
|
333
|
-
echo-pdf document ./sample.pdf
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
读取结构树:
|
|
337
|
-
|
|
338
|
-
```bash
|
|
339
|
-
echo-pdf structure ./sample.pdf
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
读取语义结构层:
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
echo-pdf semantic ./sample.pdf
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
读取指定页面内容:
|
|
349
|
-
|
|
350
|
-
```bash
|
|
351
|
-
echo-pdf page ./sample.pdf --page 1
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
生成页面渲染 artifact:
|
|
355
|
-
|
|
356
|
-
```bash
|
|
357
|
-
echo-pdf render ./sample.pdf --page 1 --scale 2
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
生成 OCR artifact(需要本地 provider key / model):
|
|
361
|
-
|
|
362
|
-
```bash
|
|
363
|
-
echo-pdf ocr ./sample.pdf --page 1 --model gpt-4.1-mini
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
自定义 artifact workspace:
|
|
367
|
-
|
|
368
|
-
```bash
|
|
369
|
-
echo-pdf document ./sample.pdf --workspace ./.cache/echo-pdf
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
## 3. MCP 使用(兼容保留,非本阶段重点)
|
|
373
|
-
|
|
374
|
-
### 3.1 检查 MCP 服务可用
|
|
375
|
-
|
|
376
|
-
```bash
|
|
377
|
-
echo-pdf mcp initialize
|
|
378
|
-
echo-pdf mcp tools
|
|
379
|
-
echo-pdf mcp call --tool file_ops --args '{"op":"list"}'
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### 3.1.1 纯 MCP 场景推荐流程(本地 PDF)
|
|
383
|
-
|
|
384
|
-
远端 MCP server 无法直接读取你本机文件路径。推荐两步:
|
|
385
|
-
|
|
386
|
-
1. 先通过 HTTP 上传本地 PDF,拿到 `fileId`
|
|
387
|
-
2. 再用 MCP 工具传 `fileId` 调用
|
|
388
|
-
|
|
389
|
-
示例:
|
|
390
|
-
|
|
391
|
-
```bash
|
|
392
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/api/files/upload \
|
|
393
|
-
-F 'file=@./input.pdf'
|
|
394
|
-
|
|
395
|
-
echo-pdf mcp call --tool pdf_extract_pages --args '{"fileId":"<FILE_ID>","pages":[1]}'
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
### 3.1.2 不上传文件的 URL ingest 流程
|
|
399
|
-
|
|
400
|
-
如果 PDF 已经在公网可访问,直接传 `url`:
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
echo-pdf mcp call --tool pdf_extract_pages --args '{
|
|
404
|
-
"url":"https://example.com/sample.pdf",
|
|
405
|
-
"pages":[1]
|
|
406
|
-
}'
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### 3.1.3 stdio MCP(支持本地文件路径)
|
|
410
|
-
|
|
411
|
-
stdio 模式会把本地 `path/filePath` 自动上传为 `fileId` 后再调用远端工具。
|
|
412
|
-
|
|
413
|
-
```bash
|
|
414
|
-
echo-pdf mcp-stdio
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
生成 Claude Desktop/Cursor 等可用的 stdio 配置片段:
|
|
418
|
-
|
|
419
|
-
```bash
|
|
420
|
-
echo-pdf setup add claude-desktop --mode stdio
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### 3.2 给客户端生成 MCP 配置片段
|
|
424
|
-
|
|
425
|
-
```bash
|
|
426
|
-
echo-pdf setup add claude-desktop
|
|
427
|
-
echo-pdf setup add cursor
|
|
428
|
-
echo-pdf setup add cline
|
|
429
|
-
echo-pdf setup add windsurf
|
|
430
|
-
echo-pdf setup add json
|
|
431
|
-
echo-pdf setup add claude-desktop --mode stdio
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
`setup add` 输出的是配置片段,把它合并到对应客户端的 MCP 配置文件。
|
|
435
|
-
|
|
436
|
-
### 3.3 MCP 工具列表
|
|
437
|
-
|
|
438
|
-
- `pdf_extract_pages`
|
|
439
|
-
- `pdf_ocr_pages`
|
|
440
|
-
- `pdf_tables_to_latex`
|
|
441
|
-
- `file_ops`
|
|
442
|
-
|
|
443
|
-
MCP 输出策略:
|
|
444
|
-
|
|
445
|
-
- `pdf_extract_pages` 在 MCP 下默认 `returnMode=url`(不传 `returnMode` 时生效)
|
|
446
|
-
- MCP `text` 会对大字段做去二进制/截断,避免把大段 base64 塞进上下文
|
|
447
|
-
- 二进制结果请优先使用 `resource_link` 中的下载地址
|
|
448
|
-
|
|
449
|
-
## 4. Web UI 使用
|
|
450
|
-
|
|
451
|
-
打开:
|
|
452
|
-
|
|
453
|
-
- `https://echo-pdf.echofilesai.workers.dev/`
|
|
454
|
-
|
|
455
|
-
流程:
|
|
456
|
-
|
|
457
|
-
1. 选择 provider。
|
|
458
|
-
2. 点击“测试模型列表”后,选择一个模型。
|
|
459
|
-
3. 上传 PDF。
|
|
460
|
-
4. 选择工具并填写参数(例如 `pages: [1]`)。
|
|
461
|
-
5. 点击 `Run Tool` 或 `Run Stream`。
|
|
462
|
-
|
|
463
|
-
说明:
|
|
464
|
-
|
|
465
|
-
- UI 中输入的 key 属于当前会话,不落库到服务端。
|
|
466
|
-
- `returnMode` 支持 `inline`、`file_id`、`url`。
|
|
467
|
-
- `tools/call` 返回统一结构:`{ ok, data, artifacts }`,其中 `artifacts[*].url` 可直接下载。
|
|
468
|
-
- 表格工具返回值会校验并要求包含合法 `tabular`,否则报错。
|
|
469
|
-
|
|
470
|
-
## 5. HTTP API 使用
|
|
471
|
-
|
|
472
|
-
### 5.1 上传 PDF
|
|
473
|
-
|
|
474
|
-
```bash
|
|
475
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/api/files/upload \
|
|
476
|
-
-F 'file=@./sample.pdf'
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
返回中会拿到 `file.id`。
|
|
480
|
-
|
|
481
|
-
CLI 等价命令:
|
|
482
|
-
|
|
483
|
-
```bash
|
|
484
|
-
echo-pdf file upload ./sample.pdf
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
### 5.2 提取页面图片
|
|
488
|
-
|
|
489
|
-
```bash
|
|
490
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/tools/call \
|
|
491
|
-
-H 'content-type: application/json' \
|
|
492
|
-
-d '{
|
|
493
|
-
"name":"pdf_extract_pages",
|
|
494
|
-
"arguments":{"fileId":"<FILE_ID>","pages":[1],"returnMode":"inline"},
|
|
495
|
-
"provider":"vercel_gateway",
|
|
496
|
-
"model":"google/gemini-3-flash"
|
|
497
|
-
}'
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
CLI(默认不自动上传本地文件,需显式开启):
|
|
501
|
-
|
|
502
|
-
```bash
|
|
503
|
-
echo-pdf call --tool pdf_extract_pages --auto-upload --args '{"path":"./sample.pdf","pages":[1],"returnMode":"url"}'
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
说明:
|
|
507
|
-
|
|
508
|
-
- `echo-pdf call` 默认禁用本地文件自动上传,避免误上传脚枪。
|
|
509
|
-
- 需要自动上传时,显式传 `--auto-upload`,CLI 会回显上传清单(本地路径 -> fileId)。
|
|
510
|
-
- 如果是本地 agent/IDE 场景,优先使用 `echo-pdf mcp-stdio`,它会按 MCP stdio 约定处理 `path/filePath` 自动上传。
|
|
511
|
-
|
|
512
|
-
下载产物:
|
|
513
|
-
|
|
514
|
-
```bash
|
|
515
|
-
echo-pdf file get --file-id <FILE_ID> --out ./output.bin
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
### 5.3 OCR
|
|
519
|
-
|
|
520
|
-
```bash
|
|
521
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/tools/call \
|
|
522
|
-
-H 'content-type: application/json' \
|
|
523
|
-
-d '{
|
|
524
|
-
"name":"pdf_ocr_pages",
|
|
525
|
-
"arguments":{"fileId":"<FILE_ID>","pages":[1],"provider":"vercel_gateway","model":"google/gemini-3-flash"}
|
|
526
|
-
}'
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### 5.4 表格识别为 LaTeX
|
|
530
|
-
|
|
531
|
-
```bash
|
|
532
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/tools/call \
|
|
533
|
-
-H 'content-type: application/json' \
|
|
534
|
-
-d '{
|
|
535
|
-
"name":"pdf_tables_to_latex",
|
|
536
|
-
"arguments":{"fileId":"<FILE_ID>","pages":[1],"provider":"vercel_gateway","model":"google/gemini-3-flash"}
|
|
537
|
-
}'
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
## 6. 配置与环境变量
|
|
541
|
-
|
|
542
|
-
统一配置文件:`echo-pdf.config.json`
|
|
543
|
-
|
|
544
|
-
关键字段:
|
|
545
|
-
|
|
546
|
-
- `agent.defaultProvider`
|
|
547
|
-
- `agent.defaultModel`
|
|
548
|
-
- `service.publicBaseUrl`
|
|
549
|
-
- `service.fileGet.cacheTtlSeconds`
|
|
550
|
-
- `service.maxPdfBytes`
|
|
551
|
-
- `service.storage.maxFileBytes`
|
|
552
|
-
- `service.storage.maxTotalBytes`
|
|
553
|
-
- `service.storage.ttlHours`
|
|
554
|
-
|
|
555
|
-
限制关系说明:
|
|
556
|
-
|
|
557
|
-
- `service.maxPdfBytes`:允许处理的 PDF 最大字节数。
|
|
558
|
-
- `service.storage.maxFileBytes`:文件存储单文件上限(上传 PDF、`url/base64` ingest、以及 `file_id` 结果都会落到存储层)。
|
|
559
|
-
- 当前项目要求 `service.storage.maxFileBytes >= service.maxPdfBytes`,否则配置无效并在启动时报错。
|
|
560
|
-
- 当前默认配置下两者都是 `10000000`(10MB)。
|
|
561
|
-
- 当未绑定 R2、使用 DO 存储时,`service.storage.maxFileBytes` 必须 `<= 1200000`,否则启动会报错。
|
|
562
|
-
- 生产建议始终绑定 R2,并让 DO 只负责协调/元数据,不承载大文件数据。
|
|
563
|
-
|
|
564
|
-
常用环境变量:
|
|
565
|
-
|
|
566
|
-
- `OPENAI_API_KEY`
|
|
567
|
-
- `OPENROUTER_KEY` / `OPENROUTER_API_KEY`
|
|
568
|
-
- `VERCEL_AI_GATEWAY_API_KEY` / `VERCEL_AI_GATEWAY_KEY`
|
|
569
|
-
- `ECHO_PDF_DEFAULT_PROVIDER`
|
|
570
|
-
- `ECHO_PDF_DEFAULT_MODEL`
|
|
571
|
-
- `ECHO_PDF_PUBLIC_BASE_URL`(可选,强制 artifacts 生成外部可访问绝对 URL)
|
|
572
|
-
- `ECHO_PDF_FILE_GET_CACHE_TTL_SECONDS`(可选,`/api/files/get` 缓存秒数,`0` 表示 `no-store`)
|
|
573
|
-
- `ECHO_PDF_FILE_GET_AUTH_HEADER` + `ECHO_PDF_FILE_GET_AUTH_ENV`(可选,启用下载端点 header 鉴权)
|
|
574
|
-
- `ECHO_PDF_MCP_KEY`(可选,启用 MCP 鉴权)
|
|
575
|
-
- `ECHO_PDF_WORKER_NAME`(CLI 默认 URL 推导)
|
|
576
|
-
|
|
577
|
-
鉴权注意:
|
|
578
|
-
|
|
579
|
-
- 如果配置了 `authHeader/authEnv` 但未注入对应 secret,服务会返回配置错误(fail-closed),不会默认放行。
|
|
580
|
-
- 仅开发调试场景可显式设置 `ECHO_PDF_ALLOW_MISSING_AUTH_SECRET=1` 临时放行“缺 secret”的请求。
|
|
581
|
-
|
|
582
|
-
## 7. 本地开发与测试
|
|
583
|
-
|
|
584
|
-
安装与开发:
|
|
585
|
-
|
|
586
|
-
```bash
|
|
587
|
-
npm install
|
|
588
|
-
npm run dev
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
测试:
|
|
153
|
+
## Development
|
|
592
154
|
|
|
593
155
|
```bash
|
|
156
|
+
npm ci
|
|
157
|
+
npm run build
|
|
594
158
|
npm run typecheck
|
|
595
|
-
npm run test
|
|
596
|
-
npm run
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
导出真实调用结果到 `fixtures/output`(会先清空输出目录):
|
|
600
|
-
|
|
601
|
-
```bash
|
|
602
|
-
INPUT_PDF=./fixtures/input.pdf ./scripts/export-fixtures.sh
|
|
159
|
+
npm run test:unit
|
|
160
|
+
npm run test:acceptance
|
|
161
|
+
npm run test:integration
|
|
603
162
|
```
|
|
604
163
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
### 8.1 设置了模型但没生效
|
|
608
|
-
|
|
609
|
-
请确认三处一致:
|
|
610
|
-
|
|
611
|
-
- CLI 当前 profile 的 `model set` 值
|
|
612
|
-
- 请求里传入的 `model`
|
|
613
|
-
- 实际 provider 的模型列表中存在该 model
|
|
614
|
-
|
|
615
|
-
当前项目策略是“用户设置的 provider/model 即默认”,不会自动切换到其它模型。
|
|
616
|
-
|
|
617
|
-
### 8.2 `pdf_tables_to_latex` 返回失败
|
|
618
|
-
|
|
619
|
-
当前实现要求模型输出中必须包含合法 `\\begin{tabular}...\\end{tabular}`。如果模型返回解释性文本或超时,会直接报错。
|
|
620
|
-
|
|
621
|
-
### 8.3 `returnMode=url` 如何使用
|
|
622
|
-
|
|
623
|
-
`url` 模式会把结果落到存储层,并返回一个可直接 `GET` 的下载地址:
|
|
624
|
-
|
|
625
|
-
- `GET /api/files/get?fileId=<id>`
|
|
626
|
-
|
|
627
|
-
示例(提取页面并返回 URL):
|
|
628
|
-
|
|
629
|
-
```bash
|
|
630
|
-
curl -sS -X POST https://echo-pdf.echofilesai.workers.dev/tools/call \
|
|
631
|
-
-H 'content-type: application/json' \
|
|
632
|
-
-d '{
|
|
633
|
-
"name":"pdf_extract_pages",
|
|
634
|
-
"arguments":{"fileId":"<FILE_ID>","pages":[1],"returnMode":"url"}
|
|
635
|
-
}'
|
|
636
|
-
```
|
|
164
|
+
For source-checkout CLI development and repo-local workflows, see [docs/DEVELOPMENT.md](./docs/DEVELOPMENT.md).
|
|
637
165
|
|
|
638
|
-
|
|
166
|
+
## License
|
|
639
167
|
|
|
640
|
-
-
|
|
641
|
-
- `PAGES_REQUIRED`(400)
|
|
642
|
-
- `PAGE_OUT_OF_RANGE`(400)
|
|
643
|
-
- `MISSING_FILE_INPUT`(400)
|
|
644
|
-
- `FILE_NOT_FOUND`(404)
|
|
645
|
-
- 服务端故障返回 `5xx`。
|
|
168
|
+
Apache-2.0
|