any2md 1.0.0__tar.gz → 1.0.1__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.
- {any2md-1.0.0 → any2md-1.0.1}/CHANGELOG.md +22 -0
- {any2md-1.0.0 → any2md-1.0.1}/PKG-INFO +2 -2
- {any2md-1.0.0 → any2md-1.0.1}/README.md +1 -1
- {any2md-1.0.0 → any2md-1.0.1}/any2md/__init__.py +1 -1
- {any2md-1.0.0 → any2md-1.0.1}/any2md/cli.py +18 -2
- {any2md-1.0.0 → any2md-1.0.1}/any2md/converters/docx.py +18 -1
- {any2md-1.0.0 → any2md-1.0.1}/any2md/converters/pdf.py +24 -2
- {any2md-1.0.0 → any2md-1.0.1}/any2md/pipeline/__init__.py +7 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/cli-reference.md +36 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/superpowers/plans/2026-04-26-any2md-v1-phase1-foundation.md +10 -2
- any2md-1.0.1/docs/superpowers/plans/2026-04-26-any2md-v1-phase2-docling-backend.md +1426 -0
- any2md-1.0.1/docs/superpowers/plans/2026-04-26-any2md-v1-phase3-figures-ocr-text-lane.md +1053 -0
- any2md-1.0.1/docs/superpowers/plans/2026-04-26-any2md-v1-phase4-polish-config-docs.md +936 -0
- any2md-1.0.1/tests/cli/test_cli_backend.py +45 -0
- any2md-1.0.1/tests/integration/test_docx_backend_override.py +57 -0
- any2md-1.0.1/tests/integration/test_pdf_backend_override.py +57 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_runner.py +10 -0
- {any2md-1.0.0 → any2md-1.0.1}/.gitignore +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/CONTRIBUTING.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/LICENSE +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/__main__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/_docling.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/config.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/converters/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/converters/html.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/converters/txt.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/frontmatter.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/pipeline/cleanup.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/pipeline/structured.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/pipeline/text.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/utils.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/any2md/validators.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/architecture.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/output-format.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/superpowers/specs/2026-04-26-any2md-v1-design.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/troubleshooting.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/docs/upgrading-from-0.7.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/pyproject.toml +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/requirements.txt +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/scripts/validate-phase1.sh +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_existing_flags.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_exit_codes.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_high_fidelity.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_meta.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_ocr_save_images.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_quiet_verbose.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/cli/test_cli_strict.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/conftest.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/docs/ligatures_and_softhyphens.txt +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/docs/multi_column.pdf +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/docs/table_heavy.docx +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/docs/web_page.html +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/make_fixtures.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/ligatures_and_softhyphens.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/multi_column.docling.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/multi_column.fallback.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/table_heavy.docling.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/table_heavy.fallback.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/fixtures/snapshots/web_page.md +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_docx_docling.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_docx_mammoth_fallback.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_html_trafilatura.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_pdf_docling.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_pdf_pymupdf_fallback.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_save_images.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_snapshots.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/integration/test_txt.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/__init__.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_footnote_markers.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_ligatures.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_nfc.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_quotes_dashes.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_soft_hyphens.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_validate.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_cleanup_whitespace.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_structured_compact_tables.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_structured_heading_hierarchy.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_structured_lift_figures.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_structured_normalize_cites.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_dedupe_paragraphs.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_dedupe_toc.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_dehyphenate.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_repair_line_wraps.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_restore_lists_code.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/pipeline/test_text_strip_headers_footers.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_auto_id.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_config.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_content_hash.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_docling_helper.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_frontmatter_compose.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_frontmatter_helpers.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_meta_overrides.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_pdf_complexity.py +0 -0
- {any2md-1.0.0 → any2md-1.0.1}/tests/unit/test_validators.py +0 -0
|
@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.0.1] — 2026-04-26
|
|
8
|
+
|
|
9
|
+
Patch release. Adds an explicit backend-selection CLI flag.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `--backend {docling,pymupdf4llm,mammoth}` CLI flag and corresponding
|
|
13
|
+
`PipelineOptions.backend` field. Lets users override the automatic
|
|
14
|
+
backend selection. Useful when Docling extracts incorrectly on a
|
|
15
|
+
specific input and the user wants to fall back to pymupdf4llm
|
|
16
|
+
without uninstalling Docling. Mismatched format/backend combinations
|
|
17
|
+
(e.g., `--backend pymupdf4llm` on a DOCX) error out per file.
|
|
18
|
+
|
|
19
|
+
### Fixes (workaround)
|
|
20
|
+
- Refs #13 — Docling drops list items on certain academic PDFs with
|
|
21
|
+
the "Parent element of the list item is not a ListGroup" warning.
|
|
22
|
+
No upstream fix yet; users hitting this can now `any2md --backend
|
|
23
|
+
pymupdf4llm doc.pdf` to use the lightweight backend instead.
|
|
24
|
+
|
|
25
|
+
### Note
|
|
26
|
+
- `--backend docling` is equivalent to the existing `--high-fidelity`
|
|
27
|
+
/ `-H` flag (both kept). Future releases may consolidate.
|
|
28
|
+
|
|
7
29
|
## [1.0.0] — 2026-04-26
|
|
8
30
|
|
|
9
31
|
First stable release of any2md v1.0. Validated end-to-end against
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: any2md
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Convert PDF, DOCX, HTML, and TXT files — or web pages by URL — to clean, LLM-optimized Markdown with YAML frontmatter.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rocklambros/any2md
|
|
6
6
|
Project-URL: Issues, https://github.com/rocklambros/any2md/issues
|
|
@@ -226,7 +226,7 @@ You'd run this when your corpus has multi-column PDFs, complex tables, or scanne
|
|
|
226
226
|
pip install "any2md[high-fidelity]"
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
After install, the CLI auto-detects Docling and uses it for PDF and DOCX. To force it explicitly (and fail fast when it isn't installed), pass `-H` / `--high-fidelity`. To require it (treat fallback as an error), combine with `--strict`.
|
|
229
|
+
After install, the CLI auto-detects Docling and uses it for PDF and DOCX. To force it explicitly (and fail fast when it isn't installed), pass `-H` / `--high-fidelity`. To require it (treat fallback as an error), combine with `--strict`. Use `--backend pymupdf4llm` to force the lightweight backend even when Docling is installed (useful when Docling produces extraction artifacts on a specific input).
|
|
230
230
|
|
|
231
231
|
### When do I need high-fidelity?
|
|
232
232
|
|
|
@@ -196,7 +196,7 @@ You'd run this when your corpus has multi-column PDFs, complex tables, or scanne
|
|
|
196
196
|
pip install "any2md[high-fidelity]"
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
After install, the CLI auto-detects Docling and uses it for PDF and DOCX. To force it explicitly (and fail fast when it isn't installed), pass `-H` / `--high-fidelity`. To require it (treat fallback as an error), combine with `--strict`.
|
|
199
|
+
After install, the CLI auto-detects Docling and uses it for PDF and DOCX. To force it explicitly (and fail fast when it isn't installed), pass `-H` / `--high-fidelity`. To require it (treat fallback as an error), combine with `--strict`. Use `--backend pymupdf4llm` to force the lightweight backend even when Docling is installed (useful when Docling produces extraction artifacts on a specific input).
|
|
200
200
|
|
|
201
201
|
### When do I need high-fidelity?
|
|
202
202
|
|
|
@@ -132,6 +132,16 @@ def main():
|
|
|
132
132
|
action="store_true",
|
|
133
133
|
help="Force the Docling backend (PDF/DOCX). Exit 1 if not installed.",
|
|
134
134
|
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"--backend",
|
|
137
|
+
choices=("docling", "pymupdf4llm", "mammoth"),
|
|
138
|
+
default=None,
|
|
139
|
+
help="Force a specific extraction backend. By default any2md auto-selects "
|
|
140
|
+
"(prefers Docling when installed). Use 'docling' (equivalent to --high-fidelity), "
|
|
141
|
+
"'pymupdf4llm' to force the lightweight PDF fallback even when Docling is installed, "
|
|
142
|
+
"or 'mammoth' to force the lightweight DOCX fallback. Mismatched format/backend "
|
|
143
|
+
"combinations (e.g., --backend pymupdf4llm on a DOCX) error out per file.",
|
|
144
|
+
)
|
|
135
145
|
parser.add_argument(
|
|
136
146
|
"--ocr-figures",
|
|
137
147
|
action="store_true",
|
|
@@ -228,11 +238,16 @@ def main():
|
|
|
228
238
|
sys.exit(1)
|
|
229
239
|
overrides = _deep_merge_overrides(overrides, cli_meta)
|
|
230
240
|
|
|
231
|
-
if
|
|
241
|
+
if (
|
|
242
|
+
args.high_fidelity
|
|
243
|
+
or args.ocr_figures
|
|
244
|
+
or args.save_images
|
|
245
|
+
or args.backend == "docling"
|
|
246
|
+
):
|
|
232
247
|
from any2md._docling import has_docling, INSTALL_HINT_MSG
|
|
233
248
|
if not has_docling():
|
|
234
249
|
print(
|
|
235
|
-
f" ERROR: Docling required for --high-fidelity / --ocr-figures / --save-images.\n"
|
|
250
|
+
f" ERROR: Docling required for --high-fidelity / --ocr-figures / --save-images / --backend docling.\n"
|
|
236
251
|
f" {INSTALL_HINT_MSG}",
|
|
237
252
|
file=sys.stderr,
|
|
238
253
|
)
|
|
@@ -252,6 +267,7 @@ def main():
|
|
|
252
267
|
auto_id_prefix=auto_id_prefix,
|
|
253
268
|
auto_id_type_code=auto_id_type_code,
|
|
254
269
|
frontmatter_overrides=overrides or None,
|
|
270
|
+
backend=args.backend,
|
|
255
271
|
)
|
|
256
272
|
|
|
257
273
|
# CLI-only output controls (not part of PipelineOptions).
|
|
@@ -114,7 +114,24 @@ def convert_docx(
|
|
|
114
114
|
return True
|
|
115
115
|
|
|
116
116
|
try:
|
|
117
|
-
|
|
117
|
+
# Backend override (PipelineOptions.backend). When set, honor the
|
|
118
|
+
# caller's choice instead of the auto-select. ``pymupdf4llm`` is
|
|
119
|
+
# invalid for DOCX — fail the file with a clear error.
|
|
120
|
+
if options.backend == "pymupdf4llm":
|
|
121
|
+
print(
|
|
122
|
+
f" FAIL: {docx_path.name} -- backend 'pymupdf4llm' is not "
|
|
123
|
+
f"valid for DOCX input (pymupdf4llm processes PDF only).",
|
|
124
|
+
file=sys.stderr,
|
|
125
|
+
)
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
if options.backend == "mammoth":
|
|
129
|
+
md_text, extracted_via = _extract_via_mammoth(docx_path, options)
|
|
130
|
+
lane = "text"
|
|
131
|
+
elif options.backend == "docling":
|
|
132
|
+
md_text, extracted_via = _extract_via_docling(docx_path)
|
|
133
|
+
lane = "structured"
|
|
134
|
+
elif has_docling():
|
|
118
135
|
try:
|
|
119
136
|
md_text, extracted_via = _extract_via_docling(docx_path)
|
|
120
137
|
lane = "structured"
|
|
@@ -182,9 +182,31 @@ def convert_pdf(
|
|
|
182
182
|
return True
|
|
183
183
|
|
|
184
184
|
try:
|
|
185
|
-
|
|
185
|
+
# Backend override (PipelineOptions.backend). When set, honor the
|
|
186
|
+
# caller's choice instead of the auto-select. ``mammoth`` is
|
|
187
|
+
# invalid for PDFs — fail the file with a clear error.
|
|
188
|
+
if options.backend == "mammoth":
|
|
189
|
+
print(
|
|
190
|
+
f" FAIL: {pdf_path.name} -- backend 'mammoth' is not "
|
|
191
|
+
f"valid for PDF input (mammoth processes DOCX only).",
|
|
192
|
+
file=sys.stderr,
|
|
193
|
+
)
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
if options.backend == "pymupdf4llm":
|
|
197
|
+
use_docling = False
|
|
198
|
+
elif options.backend == "docling":
|
|
199
|
+
# Same install-required behavior as --high-fidelity. If
|
|
200
|
+
# Docling isn't importable, fall through to the existing
|
|
201
|
+
# auto-select path which will error via _extract_via_docling
|
|
202
|
+
# below if Docling truly isn't usable. The CLI layer
|
|
203
|
+
# pre-checks install for both --high-fidelity and
|
|
204
|
+
# --backend docling, so this branch normally has Docling.
|
|
205
|
+
use_docling = has_docling()
|
|
206
|
+
else:
|
|
207
|
+
use_docling = has_docling()
|
|
186
208
|
|
|
187
|
-
if not use_docling and pdf_looks_complex(pdf_path):
|
|
209
|
+
if not use_docling and options.backend is None and pdf_looks_complex(pdf_path):
|
|
188
210
|
install_hint()
|
|
189
211
|
|
|
190
212
|
# Always extract metadata via PyMuPDF — independent of which
|
|
@@ -23,6 +23,13 @@ class PipelineOptions:
|
|
|
23
23
|
strip_links: bool = False
|
|
24
24
|
strict: bool = False
|
|
25
25
|
high_fidelity: bool = False # force Docling backend
|
|
26
|
+
# Explicit backend selection. ``None`` preserves the existing
|
|
27
|
+
# auto-select behavior (Docling when installed, else format-specific
|
|
28
|
+
# fallback). When set, the converter MUST honor the choice or fail
|
|
29
|
+
# the file with a clear error if the backend is incompatible with
|
|
30
|
+
# the input format (e.g. ``"mammoth"`` on a PDF, ``"pymupdf4llm"``
|
|
31
|
+
# on a DOCX).
|
|
32
|
+
backend: Literal["docling", "pymupdf4llm", "mammoth"] | None = None
|
|
26
33
|
# --auto-id wiring. When ``auto_id`` is True, ``frontmatter.compose``
|
|
27
34
|
# emits a generated ``document_id``. The prefix and type_code are
|
|
28
35
|
# used as-is; Phase 4 hardcodes "LOCAL"/"DOC" and Task 5 wires
|
|
@@ -145,6 +145,42 @@ You'll see `extracted_via: "docling"` in the frontmatter of every PDF and DOCX
|
|
|
145
145
|
output. Without `-H`, any2md uses Docling when it's importable and falls back
|
|
146
146
|
silently otherwise.
|
|
147
147
|
|
|
148
|
+
### `--backend {docling,pymupdf4llm,mammoth}`
|
|
149
|
+
|
|
150
|
+
Force a specific extraction backend, overriding the automatic Docling-when-
|
|
151
|
+
installed selection. `docling` is equivalent to `--high-fidelity` (and shares
|
|
152
|
+
the same install pre-flight). `pymupdf4llm` forces the lightweight PDF
|
|
153
|
+
fallback even when Docling is installed. `mammoth` forces the lightweight
|
|
154
|
+
DOCX fallback even when Docling is installed.
|
|
155
|
+
|
|
156
|
+
The backend must match the input format. Mismatches fail per file rather
|
|
157
|
+
than the whole run:
|
|
158
|
+
|
|
159
|
+
- `--backend pymupdf4llm` on a DOCX → that file fails with a clear message;
|
|
160
|
+
PDF inputs in the same run still convert.
|
|
161
|
+
- `--backend mammoth` on a PDF → that file fails with a clear message; DOCX
|
|
162
|
+
inputs in the same run still convert.
|
|
163
|
+
|
|
164
|
+
**Use this when** Docling extracts incorrectly on a specific input (e.g.
|
|
165
|
+
[#13](https://github.com/rocklambros/any2md/issues/13) — Docling drops list
|
|
166
|
+
items on certain academic PDFs) and you want to fall back to the lightweight
|
|
167
|
+
backend without uninstalling Docling.
|
|
168
|
+
|
|
169
|
+
**Don't use this when** the auto-detection is doing the right thing — the
|
|
170
|
+
default behavior already prefers Docling when installed and falls back when
|
|
171
|
+
it isn't.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Skip Docling for one specific PDF that triggers a known bug
|
|
175
|
+
any2md --backend pymupdf4llm research_paper.pdf
|
|
176
|
+
|
|
177
|
+
# Force Docling explicitly (same as --high-fidelity)
|
|
178
|
+
any2md --backend docling ./corpus
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
You'll see `extracted_via: "pymupdf4llm"` (or `"mammoth+markdownify"`, or
|
|
182
|
+
`"docling"`) in the frontmatter, matching the backend you forced.
|
|
183
|
+
|
|
148
184
|
### `--ocr-figures`
|
|
149
185
|
|
|
150
186
|
Run OCR on figures embedded in PDFs (Docling path). Implies `--high-fidelity`.
|
{any2md-1.0.0 → any2md-1.0.1}/docs/superpowers/plans/2026-04-26-any2md-v1-phase1-foundation.md
RENAMED
|
@@ -721,10 +721,11 @@ def test_source_meta_defaults_are_safe():
|
|
|
721
721
|
title_hint=None, authors=[], organization=None, date=None,
|
|
722
722
|
keywords=[], pages=None, word_count=None,
|
|
723
723
|
source_file="x.txt", source_url=None,
|
|
724
|
-
extracted_via="heuristic", lane="text",
|
|
724
|
+
doc_type="txt", extracted_via="heuristic", lane="text",
|
|
725
725
|
)
|
|
726
726
|
assert meta.lane == "text"
|
|
727
727
|
assert meta.extracted_via == "heuristic"
|
|
728
|
+
assert meta.doc_type == "txt"
|
|
728
729
|
```
|
|
729
730
|
|
|
730
731
|
- [ ] **Step 2: Run test to verify it fails**
|
|
@@ -1918,11 +1919,18 @@ from any2md.pipeline import PipelineOptions
|
|
|
1918
1919
|
|
|
1919
1920
|
|
|
1920
1921
|
def _split_frontmatter(text: str) -> tuple[dict, str]:
|
|
1921
|
-
"""Helper: split a frontmatter+body string into (yaml_dict, body_str).
|
|
1922
|
+
"""Helper: split a frontmatter+body string into (yaml_dict, body_str).
|
|
1923
|
+
|
|
1924
|
+
compose() emits a blank-line separator after the closing ---, so we
|
|
1925
|
+
strip a single leading newline from the body to recover the body that
|
|
1926
|
+
was passed into compose().
|
|
1927
|
+
"""
|
|
1922
1928
|
assert text.startswith("---\n")
|
|
1923
1929
|
end = text.index("\n---\n", 4)
|
|
1924
1930
|
fm = yaml.safe_load(text[4:end])
|
|
1925
1931
|
body = text[end + 5 :]
|
|
1932
|
+
if body.startswith("\n"):
|
|
1933
|
+
body = body[1:]
|
|
1926
1934
|
return fm, body
|
|
1927
1935
|
|
|
1928
1936
|
|