llmPDF 0.7.0__tar.gz

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.
Files changed (102) hide show
  1. llmpdf-0.7.0/LICENSE +103 -0
  2. llmpdf-0.7.0/MANIFEST.in +1 -0
  3. llmpdf-0.7.0/PKG-INFO +488 -0
  4. llmpdf-0.7.0/README.md +455 -0
  5. llmpdf-0.7.0/SDK.md +258 -0
  6. llmpdf-0.7.0/pyproject.toml +68 -0
  7. llmpdf-0.7.0/setup.cfg +4 -0
  8. llmpdf-0.7.0/src/llmPDF.egg-info/PKG-INFO +488 -0
  9. llmpdf-0.7.0/src/llmPDF.egg-info/SOURCES.txt +100 -0
  10. llmpdf-0.7.0/src/llmPDF.egg-info/dependency_links.txt +1 -0
  11. llmpdf-0.7.0/src/llmPDF.egg-info/entry_points.txt +3 -0
  12. llmpdf-0.7.0/src/llmPDF.egg-info/requires.txt +9 -0
  13. llmpdf-0.7.0/src/llmPDF.egg-info/top_level.txt +2 -0
  14. llmpdf-0.7.0/src/llmpdf/__init__.py +36 -0
  15. llmpdf-0.7.0/src/llmpdf/__main__.py +3 -0
  16. llmpdf-0.7.0/src/llmpdf/agent_scheduler.py +221 -0
  17. llmpdf-0.7.0/src/llmpdf/assets_task.py +174 -0
  18. llmpdf-0.7.0/src/llmpdf/batch.py +418 -0
  19. llmpdf-0.7.0/src/llmpdf/cli.py +472 -0
  20. llmpdf-0.7.0/src/llmpdf/detection_task.py +543 -0
  21. llmpdf-0.7.0/src/llmpdf/docling_task.py +342 -0
  22. llmpdf-0.7.0/src/llmpdf/docling_worker.py +37 -0
  23. llmpdf-0.7.0/src/llmpdf/extraction_task.py +371 -0
  24. llmpdf-0.7.0/src/llmpdf/image_analysis_task.py +1210 -0
  25. llmpdf-0.7.0/src/llmpdf/images_task.py +122 -0
  26. llmpdf-0.7.0/src/llmpdf/io_utils.py +62 -0
  27. llmpdf-0.7.0/src/llmpdf/merge_rules/__init__.py +9 -0
  28. llmpdf-0.7.0/src/llmpdf/merge_rules/find_image_absorbed_blocks.py +29 -0
  29. llmpdf-0.7.0/src/llmpdf/merge_rules/find_table_absorbed_blocks.py +18 -0
  30. llmpdf-0.7.0/src/llmpdf/merge_rules/is_protected_block.py +6 -0
  31. llmpdf-0.7.0/src/llmpdf/merge_task.py +639 -0
  32. llmpdf-0.7.0/src/llmpdf/metrics_task.py +236 -0
  33. llmpdf-0.7.0/src/llmpdf/models.py +150 -0
  34. llmpdf-0.7.0/src/llmpdf/pages.py +69 -0
  35. llmpdf-0.7.0/src/llmpdf/pi_runtime.py +5 -0
  36. llmpdf-0.7.0/src/llmpdf/pipeline.py +164 -0
  37. llmpdf-0.7.0/src/llmpdf/preflight.py +97 -0
  38. llmpdf-0.7.0/src/llmpdf/progress.py +12 -0
  39. llmpdf-0.7.0/src/llmpdf/rerun.py +205 -0
  40. llmpdf-0.7.0/src/llmpdf/retention.py +185 -0
  41. llmpdf-0.7.0/src/llmpdf/review.py +43 -0
  42. llmpdf-0.7.0/src/llmpdf/review_discovery.py +54 -0
  43. llmpdf-0.7.0/src/llmpdf/review_project.py +42 -0
  44. llmpdf-0.7.0/src/llmpdf/review_server.py +114 -0
  45. llmpdf-0.7.0/src/llmpdf/review_source.py +431 -0
  46. llmpdf-0.7.0/src/llmpdf/review_static/assets/index-Ch2-xtmb.js +90 -0
  47. llmpdf-0.7.0/src/llmpdf/review_static/assets/index-DYQ2zsXw.css +1 -0
  48. llmpdf-0.7.0/src/llmpdf/review_static/assets/pdf.worker.min-Dswkl-cV.mjs +29 -0
  49. llmpdf-0.7.0/src/llmpdf/review_static/index.html +14 -0
  50. llmpdf-0.7.0/src/llmpdf/review_tables.py +101 -0
  51. llmpdf-0.7.0/src/llmpdf/run_status.py +206 -0
  52. llmpdf-0.7.0/src/llmpdf/screenshots_task.py +151 -0
  53. llmpdf-0.7.0/src/llmpdf/sdk.py +477 -0
  54. llmpdf-0.7.0/src/llmpdf/table/__init__.py +3 -0
  55. llmpdf-0.7.0/src/llmpdf/table/__main__.py +5 -0
  56. llmpdf-0.7.0/src/llmpdf/table/agent_runner.py +229 -0
  57. llmpdf-0.7.0/src/llmpdf/table/cli.py +176 -0
  58. llmpdf-0.7.0/src/llmpdf/table/io_utils.py +64 -0
  59. llmpdf-0.7.0/src/llmpdf/table/models.py +49 -0
  60. llmpdf-0.7.0/src/llmpdf/table/orchestration.py +436 -0
  61. llmpdf-0.7.0/src/llmpdf/table/pi_runtime.py +65 -0
  62. llmpdf-0.7.0/src/llmpdf/table/prepare.py +222 -0
  63. llmpdf-0.7.0/src/llmpdf/table/prompt.py +253 -0
  64. llmpdf-0.7.0/src/llmpdf/table/run_all.py +63 -0
  65. llmpdf-0.7.0/src/llmpdf/table/runner.py +41 -0
  66. llmpdf-0.7.0/src/llmpdf/table/runtime.py +170 -0
  67. llmpdf-0.7.0/src/llmpdf/table/table_guard.py +230 -0
  68. llmpdf-0.7.0/src/llmpdf/task.py +117 -0
  69. llmpdf-0.7.0/src/llmpdf/validate_task.py +309 -0
  70. llmpdf-0.7.0/tests/test_agent_scheduler.py +68 -0
  71. llmpdf-0.7.0/tests/test_assets.py +21 -0
  72. llmpdf-0.7.0/tests/test_batch.py +112 -0
  73. llmpdf-0.7.0/tests/test_cli.py +240 -0
  74. llmpdf-0.7.0/tests/test_detection.py +316 -0
  75. llmpdf-0.7.0/tests/test_docling.py +149 -0
  76. llmpdf-0.7.0/tests/test_executable_discovery.py +18 -0
  77. llmpdf-0.7.0/tests/test_extraction.py +110 -0
  78. llmpdf-0.7.0/tests/test_image_analysis.py +211 -0
  79. llmpdf-0.7.0/tests/test_image_optimization.py +31 -0
  80. llmpdf-0.7.0/tests/test_images.py +18 -0
  81. llmpdf-0.7.0/tests/test_io_utils.py +67 -0
  82. llmpdf-0.7.0/tests/test_merge.py +307 -0
  83. llmpdf-0.7.0/tests/test_merge_rules.py +37 -0
  84. llmpdf-0.7.0/tests/test_metrics.py +47 -0
  85. llmpdf-0.7.0/tests/test_models.py +15 -0
  86. llmpdf-0.7.0/tests/test_pages.py +26 -0
  87. llmpdf-0.7.0/tests/test_pipeline_sessions.py +89 -0
  88. llmpdf-0.7.0/tests/test_preflight.py +46 -0
  89. llmpdf-0.7.0/tests/test_render_budget.py +27 -0
  90. llmpdf-0.7.0/tests/test_rerun.py +112 -0
  91. llmpdf-0.7.0/tests/test_retention.py +113 -0
  92. llmpdf-0.7.0/tests/test_review.py +250 -0
  93. llmpdf-0.7.0/tests/test_run_status.py +94 -0
  94. llmpdf-0.7.0/tests/test_sdk.py +213 -0
  95. llmpdf-0.7.0/tests/test_table_guard.py +21 -0
  96. llmpdf-0.7.0/tests/test_table_models.py +30 -0
  97. llmpdf-0.7.0/tests/test_table_prompt.py +89 -0
  98. llmpdf-0.7.0/tests/test_table_run_all.py +41 -0
  99. llmpdf-0.7.0/tests/test_table_runner.py +323 -0
  100. llmpdf-0.7.0/tests/test_table_runtime.py +118 -0
  101. llmpdf-0.7.0/tests/test_task_cache.py +63 -0
  102. llmpdf-0.7.0/tests/test_validate.py +77 -0
llmpdf-0.7.0/LICENSE ADDED
@@ -0,0 +1,103 @@
1
+ llmPDF Limited Use License 1.0
2
+
3
+ Copyright (c) 2026 yechenyan. All rights reserved.
4
+
5
+ 1. Definitions
6
+
7
+ "Software" means the llmPDF software distributed with this license, including
8
+ its source code, object code, documentation, and original assets.
9
+
10
+ "Unmodified Copy" means a complete copy of the Software whose contents have
11
+ not been altered. Ordinary installation, execution, configuration through
12
+ documented options, creation of bytecode or caches, and use through documented
13
+ command-line or programming interfaces do not make a copy modified.
14
+
15
+ 2. License Grant
16
+
17
+ Subject to this license, the copyright holder grants you a worldwide,
18
+ royalty-free, non-exclusive license to:
19
+
20
+ a. install, execute, and use Unmodified Copies for any purpose, including
21
+ commercial purposes;
22
+
23
+ b. integrate an Unmodified Copy with other software through the Software's
24
+ documented command-line and programming interfaces; and
25
+
26
+ c. reproduce and redistribute Unmodified Copies, either separately or together
27
+ with other software, provided that you comply with Section 3.
28
+
29
+ 3. Attribution and Notices
30
+
31
+ Every redistributed copy must include this license and all copyright,
32
+ attribution, and proprietary notices supplied with the Software.
33
+
34
+ Any product, distribution, documentation, credits page, or other materials that
35
+ accompany a redistributed copy must display the following notice in a
36
+ reasonably prominent manner:
37
+
38
+ This product includes llmPDF, Copyright (c) 2026 yechenyan,
39
+ used under the llmPDF Limited Use License 1.0.
40
+
41
+ If the distribution has no documentation or credits page, the notice must be
42
+ provided in another reasonably prominent location accompanying the Software.
43
+
44
+ 4. Restrictions
45
+
46
+ Except where applicable law expressly permits and does not allow the
47
+ restriction to be waived, you may not:
48
+
49
+ a. modify, adapt, translate, transform, or create derivative works of the
50
+ Software;
51
+
52
+ b. distribute or make available a modified version or derivative work of the
53
+ Software;
54
+
55
+ c. remove, obscure, or alter any copyright, attribution, license, or
56
+ proprietary notice;
57
+
58
+ d. sublicense the Software or grant rights in it beyond those provided by this
59
+ license; or
60
+
61
+ e. use the names, logos, or trademarks of llmPDF or the copyright holder to
62
+ imply endorsement, sponsorship, or affiliation.
63
+
64
+ 5. Ownership
65
+
66
+ The Software is licensed, not sold. The copyright holder retains all rights,
67
+ title, and interest in the Software except for the limited permissions expressly
68
+ granted by this license. No patent or trademark rights are granted.
69
+
70
+ 6. Third-Party Components
71
+
72
+ Third-party software and assets included with or required by the Software are
73
+ licensed under their respective terms. This license applies only to the rights
74
+ held by the llmPDF copyright holder and does not replace or limit any
75
+ third-party license.
76
+
77
+ 7. Termination
78
+
79
+ Your rights under this license terminate automatically if you breach its terms.
80
+ Upon termination, you must stop using and redistributing the Software and
81
+ destroy copies in your possession or control, except where retention is required
82
+ by law. The copyright holder may reinstate your rights in writing.
83
+
84
+ 8. Disclaimer of Warranty
85
+
86
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
87
+ IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
88
+ PURPOSE, TITLE, AND NON-INFRINGEMENT. YOU BEAR ALL RISK ARISING FROM USE OF THE
89
+ SOFTWARE.
90
+
91
+ 9. Limitation of Liability
92
+
93
+ TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE COPYRIGHT HOLDER WILL NOT BE LIABLE
94
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL, OR
95
+ OTHER DAMAGES ARISING FROM OR RELATED TO THE SOFTWARE OR THIS LICENSE, EVEN IF
96
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
97
+
98
+ 10. Severability
99
+
100
+ If any provision of this license is held unenforceable, it will be limited or
101
+ removed only to the minimum extent necessary, and the remaining provisions will
102
+ remain in effect.
103
+
@@ -0,0 +1 @@
1
+ include SDK.md
llmpdf-0.7.0/PKG-INFO ADDED
@@ -0,0 +1,488 @@
1
+ Metadata-Version: 2.4
2
+ Name: llmPDF
3
+ Version: 0.7.0
4
+ Summary: Task-oriented PDF conversion and review toolkit
5
+ Author-email: yechenyan <ccyechenyan@gmail.com>
6
+ License-Expression: LicenseRef-llmPDF-Limited-Use-1.0
7
+ Project-URL: Homepage, https://github.com/yechenyan/llmPDF
8
+ Project-URL: Repository, https://github.com/yechenyan/llmPDF
9
+ Project-URL: Issues, https://github.com/yechenyan/llmPDF/issues
10
+ Keywords: pdf,markdown,docling,document-processing,llm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Text Processing :: Markup
21
+ Requires-Python: <3.15,>=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: docling<2.124,>=2.123
25
+ Requires-Dist: pdfplumber<0.12,>=0.11
26
+ Requires-Dist: Pillow<13,>=11
27
+ Requires-Dist: pypdf<7,>=6
28
+ Requires-Dist: PyYAML<7,>=6
29
+ Provides-Extra: review
30
+ Requires-Dist: fastapi<1,>=0.115; extra == "review"
31
+ Requires-Dist: uvicorn<1,>=0.30; extra == "review"
32
+ Dynamic: license-file
33
+
34
+ # llmPDF
35
+
36
+ Convert PDFs into Markdown that is suitable for reading, search, and downstream processing. The tool preserves document structure, extracts tables, and can analyze images and charts. A local review UI lets you compare the generated Markdown with the source PDF.
37
+
38
+ Use `llmpdf convert` for normal operation. One command runs the complete conversion.
39
+
40
+ ## Requirements
41
+
42
+ - Python 3.11–3.14
43
+ - [uv](https://docs.astral.sh/uv/)
44
+ - Poppler's `pdftoppm`
45
+ - Node.js 20.6+
46
+ - A working Codex/Pi login and network connection
47
+
48
+ Install Poppler on macOS with Homebrew:
49
+
50
+ ```bash
51
+ brew install poppler
52
+ ```
53
+
54
+ On Ubuntu or Debian, install `poppler-utils`:
55
+
56
+ ```bash
57
+ sudo apt-get install poppler-utils
58
+ ```
59
+
60
+ The first run may download document-analysis models and can take longer than later runs.
61
+
62
+ ## Installation
63
+
64
+ From the project directory, run:
65
+
66
+ ```bash
67
+ uv sync --extra review --locked
68
+ ```
69
+
70
+ This installs both the converter and the optional local review UI.
71
+
72
+ Verify the installation:
73
+
74
+ ```bash
75
+ uv run llmpdf --help
76
+ uv run llmpdf convert --help
77
+ ```
78
+
79
+ ## Quick start
80
+
81
+ ```bash
82
+ uv run llmpdf convert \
83
+ "/absolute/path/to/input.pdf" \
84
+ --output-dir "/absolute/path/to/results"
85
+ ```
86
+
87
+ On success, the command prints JSON to standard output. It includes `output_markdown`, `output_dir`, token usage, and elapsed time.
88
+
89
+ The default result directory is:
90
+
91
+ ```text
92
+ <output-dir>/<UTC batch timestamp>/<PDF filename>/
93
+ ```
94
+
95
+ Set a stable batch name when you need a predictable path or resumable retries:
96
+
97
+ ```bash
98
+ uv run llmpdf convert \
99
+ "/absolute/path/to/input.pdf" \
100
+ --output-dir "/absolute/path/to/results" \
101
+ --batch-id "edis-2024"
102
+ ```
103
+
104
+ After a failure, rerun with the same PDF, `--output-dir`, and `--batch-id`. Completed results that remain valid are reused. Use `--force` only when every stage must run again.
105
+
106
+ To process selected pages while retaining their original PDF page numbers:
107
+
108
+ ```bash
109
+ uv run llmpdf convert \
110
+ "/absolute/path/to/input.pdf" \
111
+ --output-dir "/absolute/path/to/results" \
112
+ --pages "1,3,8-12"
113
+ ```
114
+
115
+ Only selected pages are rendered, detected, analyzed, and written to `output.md`. Docling receives the smallest continuous physical-page range covering the selection, then its output is filtered to the exact selected pages. For example, `8,9` processes only pages 8–9 in Docling, while `1,3,8-12` processes pages 1–12 in Docling and retains only the requested pages downstream. Metadata records both the complete PDF page count and `selected_pages`; the review comparison displays only those physical pages.
116
+
117
+ During conversion, concise progress is written to standard error: pipeline stages, rendered-page counts, detection batches, and completed table/image Agent tasks. The final machine-readable result remains the only content written to standard output.
118
+
119
+ Docling runs in an isolated child process. If that process fails during PDF parsing, the converter keeps the main pipeline alive and retries that PDF once with one Docling parsing thread and batch sizes of one. This fallback is part of the core converter, so it applies equally to `convert`, `run-all`, SDK-driven conversions, and directory batches. User-supplied Docling options, including OCR settings, are retained during the fallback. A second failure is reported normally and can be resumed by running the same command again.
120
+
121
+ All Agent work uses one dynamic scheduler with at most five workers. Whenever a worker becomes free, it selects the first ready task type in this order: Find, cross-page table, ordinary table, image. Completed Find batches can release bounded table groups before the remaining Find batches finish. Tasks with unresolved neighboring-page dependencies remain blocked rather than being extracted prematurely.
122
+
123
+ ## `convert` parameters
124
+
125
+ Syntax:
126
+
127
+ ```text
128
+ llmpdf convert PDF --output-dir OUTPUT_DIR [OPTIONS]
129
+ ```
130
+
131
+ ### Input and output
132
+
133
+ | Parameter | Default | Description |
134
+ | --- | --- | --- |
135
+ | `PDF` | Required | Input PDF path. |
136
+ | `--output-dir PATH` | Required | Root directory for all batch results. |
137
+ | `--batch-id NAME` | UTC timestamp | Batch directory name. It must be a single path component and remain unchanged for retries. |
138
+ | `--pages PAGES` | All pages | One-based PDF pages, such as `1-25` or `1,3,8-12`. Pages are de-duplicated and processed in source order. |
139
+ | `--force` | Disabled | Ignore existing results and run the conversion again. |
140
+ | `--retain-docling-tables` / `--no-retain-docling-tables` | Enabled | Preserve original Docling table Markdown for review. |
141
+
142
+ ### Table and page detection
143
+
144
+ | Parameter | Default | Description |
145
+ | --- | --- | --- |
146
+ | `--model MODEL` | `gpt-5.6-sol` | Model used for table detection and extraction. |
147
+ | `--thinking LEVEL` | `medium` | Reasoning effort for table operations, such as `low`, `medium`, or `high`. |
148
+ | `--detection-dpi N` | `96` | Page-rendering DPI for detection. Higher values improve detail but cost more time and memory. |
149
+ | `--contact-sheet-size N` | `8` | Number of PDF pages per detection contact sheet. |
150
+ | `--agent-concurrency N` | `5` | Maximum concurrent Agent tasks across all task types. Must be between `1` and `5`. |
151
+ | `--find-concurrency N` | `5` | Maximum concurrent Find Agent tasks within the global limit. |
152
+ | `--confidence-threshold N` | `0.35` | Minimum candidate confidence, from `0` to `1`. |
153
+ | `--table-concurrency N` | `5` | Table-processing concurrency. Must be greater than `0`. |
154
+ | `--table-image-max-patches N` | `30000` | Maximum 32x32-pixel patches per table Agent image. Oversized table pages lower only their own render DPI. |
155
+
156
+ ### Images and charts
157
+
158
+ | Parameter | Default | Description |
159
+ | --- | --- | --- |
160
+ | `--analyze-images` / `--no-analyze-images` | Enabled | Analyze meaningful images and charts. |
161
+ | `--image-model MODEL` | `gpt-5.6-terra` | Model used for image analysis. |
162
+ | `--image-thinking LEVEL` | `medium` | Reasoning effort for image analysis. |
163
+ | `--image-render-dpi N` | `240` | Page-rendering DPI for image analysis. |
164
+ | `--image-max-patches N` | `10000` | Maximum 32x32-pixel patches per image-analysis model image. Oversized pages automatically use a lower DPI; normal pages keep `--image-render-dpi`. |
165
+ | `--image-concurrency N` | `5` | Image-processing concurrency. Must be greater than `0`. |
166
+
167
+ When an image is a readable chart, the tool attempts to produce structured table data. Visually estimated results are prefixed with “AI visual extraction; values may be inaccurate”. If values cannot be read reliably, the result keeps the image description and does not invent table data.
168
+
169
+ ### Runtime and advanced options
170
+
171
+ | Parameter | Default | Description |
172
+ | --- | --- | --- |
173
+ | `--agent-timeout-seconds N` | `1800` | Timeout for each model call, in seconds. |
174
+ | `--pdftoppm PATH` | `pdftoppm` | Command name or executable path for `pdftoppm`. |
175
+ | `--pi-executable PATH` | Auto-detected | Explicit Pi executable path. Normally unnecessary. |
176
+ | `--docling-options-file PATH` | None | JSON file containing additional document-analysis options. |
177
+ | `--keep-sessions` | Disabled | Keep model session files for troubleshooting. |
178
+ | `--keep-work` | Disabled | Keep all intermediate files after success. By default, reproducible work files are removed after metrics and validation are complete. |
179
+ | `--llmpdf-table-executable PATH` | None | Deprecated compatibility option; avoid it in new integrations. |
180
+
181
+ Use the built-in help as the authoritative parameter reference for the installed version:
182
+
183
+ ```bash
184
+ uv run llmpdf convert --help
185
+ ```
186
+
187
+ ## Common commands
188
+
189
+ ### Convert a directory of PDFs
190
+
191
+ Use `convert-dir` to recursively find PDFs and convert them in place:
192
+
193
+ ```bash
194
+ uv run llmpdf convert-dir "/absolute/path/to/pdf-root" --jobs 3
195
+ ```
196
+
197
+ Each PDF remains beside its generated `output.md`, `assets/`, and `work/` directories. Because these names are shared, in-place mode requires exactly one PDF in each containing directory. Directories containing multiple PDFs are reported as invalid instead of overwriting one result with another.
198
+
199
+ The directory command skips a PDF only when all of the following are true: `work/status.json` is completed, validation passed, `output.md` is non-empty, and the source SHA-256 in `assets/metadata.json` still matches the PDF. Interrupted, failed, stale, or incomplete results are resumed using valid stage caches.
200
+
201
+ All PDF workers start immediately. To process up to three PDFs concurrently, run:
202
+
203
+ ```bash
204
+ uv run llmpdf convert-dir "/absolute/path/to/pdf-root" \
205
+ --jobs 3
206
+ ```
207
+
208
+ When a running PDF finishes, its slot starts the next queued PDF immediately. Status heartbeats are printed every 30 seconds. Combined output is appended to `<pdf-root>/log.md` and synchronized to disk every 300 lines; use `--log-file PATH` to choose another file.
209
+
210
+ Preview the decisions without converting anything:
211
+
212
+ ```bash
213
+ uv run llmpdf convert-dir "/absolute/path/to/pdf-root" --dry-run
214
+ ```
215
+
216
+ Options for each individual conversion can be forwarded after `--`:
217
+
218
+ ```bash
219
+ uv run llmpdf convert-dir "/absolute/path/to/pdf-root" \
220
+ --jobs 2 -- --no-analyze-images
221
+ ```
222
+
223
+ Use `--no-recursive` to inspect only PDFs directly inside the specified directory. The process exits nonzero if any PDF fails or any directory contains multiple PDFs.
224
+
225
+ The final directory layout is:
226
+
227
+ ```text
228
+ <pdf-root>/
229
+ ├── log.md
230
+ └── company-or-document/
231
+ ├── source.pdf
232
+ ├── output.md
233
+ ├── assets/
234
+ └── work/
235
+ ```
236
+
237
+ ### Other examples
238
+
239
+ Process text and tables without analyzing images:
240
+
241
+ ```bash
242
+ uv run llmpdf convert input.pdf \
243
+ --output-dir results \
244
+ --batch-id text-and-tables \
245
+ --no-analyze-images
246
+ ```
247
+
248
+ Reduce concurrency on a resource-constrained machine:
249
+
250
+ ```bash
251
+ uv run llmpdf convert input.pdf \
252
+ --output-dir results \
253
+ --batch-id low-load \
254
+ --agent-concurrency 2 \
255
+ --find-concurrency 2 \
256
+ --table-concurrency 2 \
257
+ --image-concurrency 2
258
+ ```
259
+
260
+ For a scanned document, create `docling-options.json` to enable OCR:
261
+
262
+ ```json
263
+ {
264
+ "do_ocr": true,
265
+ "images_scale": 2.0,
266
+ "ocr_options": {
267
+ "lang": ["de", "en"]
268
+ }
269
+ }
270
+ ```
271
+
272
+ Then run:
273
+
274
+ ```bash
275
+ uv run llmpdf convert scanned.pdf \
276
+ --output-dir results \
277
+ --batch-id scanned-document \
278
+ --docling-options-file docling-options.json
279
+ ```
280
+
281
+ Line breaks inside table cells are converted to spaces. Final Markdown does not use `<br>` for cell-internal line breaks.
282
+
283
+ ## Output files
284
+
285
+ A successful conversion produces:
286
+
287
+ ```text
288
+ <output-dir>/<batch-id>/<pdf-stem>/
289
+ ├── output.md
290
+ ├── assets/
291
+ │ ├── metadata.json
292
+ │ ├── tables/
293
+ │ ├── images/
294
+ │ ├── chart-tables/
295
+ │ └── docling-tables/
296
+ └── work/
297
+ ├── status.json
298
+ ├── metrics.json
299
+ ├── run-manifest.json
300
+ ├── run-blocks.json.gz
301
+ ├── table-code/ # Agent-authored table extractors
302
+ ├── diagnostics/
303
+ └── review/ # Created only after review activity
304
+ ```
305
+
306
+ - `output.md`: final Markdown.
307
+ - `assets/tables/`: extracted table CSV files.
308
+ - `assets/images/`: images retained from the PDF.
309
+ - `assets/chart-tables/`: CSV files extracted from charts.
310
+ - `assets/metadata.json`: page, source, and artifact relationships.
311
+ - `work/status.json`: live and final run state, elapsed time, pipeline and Agent task counts, token usage, and Pi's API-price estimate.
312
+ - `work/metrics.json`: elapsed time, token usage, and status for the full run and individual model calls.
313
+ - `work/run-manifest.json`: source, model, and candidate-page information required to regenerate tables.
314
+ - `work/run-blocks.json.gz`: compressed document structure used when regenerated tables are merged back into Markdown.
315
+ - `work/table-code/`: the generated `extract.py` for every table plus a compact page-mapping index.
316
+ - `work/diagnostics/`: troubleshooting information.
317
+
318
+ ### Run status
319
+
320
+ `work/status.json` shows whether a conversion is `running`, `completed`, or `failed`. It is updated during the run and retained after cleanup. It also records elapsed time, successful and failed task counts, token usage, and Pi's estimated API cost.
321
+
322
+ Example:
323
+
324
+ ```json
325
+ {
326
+ "status": "completed",
327
+ "elapsed_seconds": 130.0,
328
+ "pipeline": {
329
+ "successful_tasks": 11,
330
+ "failed_tasks": 0
331
+ },
332
+ "agents": {
333
+ "successful_tasks": 12,
334
+ "failed_tasks": 0
335
+ },
336
+ "usage": {
337
+ "total_tokens": 1700,
338
+ "pi_api_price_estimate_usd": 0.012345
339
+ }
340
+ }
341
+ ```
342
+
343
+ `pi_api_price_estimate_usd` is an estimate for comparing runs, not the actual charge against a Codex plan. See `work/metrics.json` for detailed timing and token information.
344
+
345
+ Successful conversions use minimal retention by default. Raw Agent logs, page renders, single-page PDFs, Docling work files, and duplicate table/image intermediates are removed only after validation passes and metrics are written. Failed conversions keep their work files so the same batch can resume. Use `--keep-work` for a successful diagnostic run that must retain all intermediates. `--keep-sessions` also prevents minimal cleanup.
346
+
347
+ Artifact paths in `output.md` and metadata are POSIX paths relative to the current result directory, so the entire directory can be moved. Keep `output.md`, `assets/`, and `work/` together. The source PDF must remain available at the path recorded in `assets/metadata.json` for review and table regeneration.
348
+
349
+ Large images created from full-page vision analysis are stored as WebP at their original pixel dimensions when that representation is smaller. This changes encoding, not resolution.
350
+
351
+ ## Regenerate tables
352
+
353
+ Regenerate table extraction from a successful minimal result:
354
+
355
+ ```bash
356
+ uv run llmpdf rerun-tables \
357
+ "/absolute/path/to/results/edis-2024/input"
358
+ ```
359
+
360
+ The command verifies the source PDF against the saved SHA-256, reruns all table candidates, rebuilds table assets and `output.md`, validates the result, updates metrics, and restores minimal retention. Image analysis and table detection are not rerun. Table regeneration is rejected after review decisions have been saved, preventing accidental loss of review work.
361
+
362
+ ## Manual review
363
+
364
+ Start the local review UI:
365
+
366
+ ```bash
367
+ uv run --extra review llmpdf review \
368
+ --result "/absolute/path/to/results/edis-2024/input"
369
+ ```
370
+
371
+ The UI opens at `http://127.0.0.1:8765/` by default. Its document-comparison view displays the PDF on the left and generated Markdown on the right, with synchronized scrolling, a collapsible sidebar, and PDF zoom controls.
372
+
373
+ Load multiple results by repeating `--result`:
374
+
375
+ ```bash
376
+ uv run --extra review llmpdf review \
377
+ --result "/path/to/result-a" \
378
+ --result "/path/to/result-b"
379
+ ```
380
+
381
+ You can also load a complete batch or a review-project file:
382
+
383
+ ```bash
384
+ uv run --extra review llmpdf review --batch "/path/to/batch"
385
+ uv run --extra review llmpdf review --project review-project.json
386
+ ```
387
+
388
+ ### `review` parameters
389
+
390
+ | Parameter | Default | Description |
391
+ | --- | --- | --- |
392
+ | `--result PATH` | None | Load a result directory. May be repeated. |
393
+ | `--batch PATH` | None | Load a batch directory. May be repeated. |
394
+ | `--project PATH` | None | Load a review-project JSON file. |
395
+ | `--host HOST` | `127.0.0.1` | Listening address. |
396
+ | `--port N` | `8765` | Listening port. |
397
+ | `--no-open` | Disabled | Start the server without opening a browser. |
398
+
399
+ The review UI operates on local results and does not make model calls.
400
+
401
+ ## AI and automation usage
402
+
403
+ For non-interactive calls, always provide absolute input and output paths and a stable `--batch-id`:
404
+
405
+ ```bash
406
+ uv run llmpdf convert \
407
+ "/data/in/report.pdf" \
408
+ --output-dir "/data/out" \
409
+ --batch-id "report-2026-09-03"
410
+ ```
411
+
412
+ Calling programs should follow these conventions:
413
+
414
+ 1. Use the process exit code to determine success or failure.
415
+ 2. On success, parse JSON from standard output and read the final Markdown path from `output_markdown`.
416
+ 3. On failure, parse JSON from standard error. If it contains `batch_id` or `recovery`, retry with the original parameters.
417
+ 4. Do not copy `output.md` without its referenced resources; preserve at least `assets/` with it.
418
+ 5. Read `work/metrics.json` for cost reporting or slow-call diagnostics.
419
+
420
+ ## Python SDK
421
+
422
+ ```python
423
+ from pathlib import Path
424
+
425
+ from llmpdf import ConvertOptions, convert
426
+
427
+ result = convert(
428
+ ConvertOptions(
429
+ pdf=Path("/data/in/report.pdf"),
430
+ output_root=Path("/data/out"),
431
+ batch_id="report-2026-09-03",
432
+ pages="1-25",
433
+ analyze_images=True,
434
+ agent_concurrency=5,
435
+ find_concurrency=5,
436
+ table_concurrency=5,
437
+ image_concurrency=5,
438
+ )
439
+ )
440
+
441
+ print(result.output_markdown)
442
+ print(result.total_tokens)
443
+ print(result.elapsed_seconds)
444
+ ```
445
+
446
+ Path attributes on the SDK result are absolute `Path` objects that can be accessed directly. Artifact paths in `result.to_dict()` are relative to the result directory for portability. See [SDK.md](SDK.md) for all fields and exception types.
447
+
448
+ ## Troubleshooting
449
+
450
+ ### `pdftoppm` is not found
451
+
452
+ Install Poppler and verify the command:
453
+
454
+ ```bash
455
+ pdftoppm -v
456
+ ```
457
+
458
+ If the executable is outside `PATH`, pass `--pdftoppm /absolute/path/to/pdftoppm`.
459
+
460
+ ### Model authentication fails
461
+
462
+ Confirm that the local Codex/Pi login is valid and that network access works. Use `--pi-executable` when a custom executable path is required.
463
+
464
+ ### Conversion times out
465
+
466
+ Complex pages and high-resolution images may need more time. Increase `--agent-timeout-seconds`, or lower `--agent-concurrency` when local resources are limited.
467
+
468
+ ### Resume after a failure
469
+
470
+ Run the same command with the same `--batch-id`. A new batch name starts a new conversion.
471
+
472
+ ### Regenerate everything
473
+
474
+ Keep the same path and add `--force`:
475
+
476
+ ```bash
477
+ uv run llmpdf convert input.pdf \
478
+ --output-dir results \
479
+ --batch-id existing-batch \
480
+ --force
481
+ ```
482
+
483
+ ## License
484
+
485
+ llmPDF is distributed under the [llmPDF Limited Use License 1.0](LICENSE).
486
+ Unmodified copies may be used for any purpose, including commercial use, and
487
+ may be redistributed with other software when the required attribution is
488
+ provided. Modification, adaptation, and derivative works are not permitted.