any2md 1.0.6__tar.gz → 1.1.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.
- {any2md-1.0.6 → any2md-1.1.0}/CHANGELOG.md +108 -6
- {any2md-1.0.6 → any2md-1.1.0}/PKG-INFO +18 -8
- {any2md-1.0.6 → any2md-1.1.0}/README.md +16 -7
- any2md-1.1.0/any2md/__init__.py +11 -0
- any2md-1.1.0/any2md/_docling_cache.py +434 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/cli.py +1 -1
- {any2md-1.0.6 → any2md-1.1.0}/any2md/converters/docx.py +15 -3
- {any2md-1.0.6 → any2md-1.1.0}/any2md/converters/pdf.py +16 -8
- {any2md-1.0.6 → any2md-1.1.0}/any2md/converters/txt.py +1 -1
- {any2md-1.0.6 → any2md-1.1.0}/any2md/frontmatter.py +6 -6
- {any2md-1.0.6 → any2md-1.1.0}/any2md/pipeline/cleanup.py +1 -1
- {any2md-1.0.6 → any2md-1.1.0}/docs/architecture.md +13 -10
- {any2md-1.0.6 → any2md-1.1.0}/docs/cli-reference.md +1 -1
- {any2md-1.0.6 → any2md-1.1.0}/docs/output-format.md +19 -19
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-any2md-v1-phase1-foundation.md +19 -19
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-any2md-v1-phase4-polish-config-docs.md +11 -11
- any2md-1.1.0/docs/superpowers/plans/2026-05-04-v1.1.0-persistent-converter.md +2243 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/specs/2026-04-26-any2md-v1-design.md +25 -25
- any2md-1.1.0/docs/superpowers/specs/2026-05-04-v1.1.0-persistent-converter-design.md +951 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/troubleshooting.md +76 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/upgrading-from-0.7.md +9 -9
- {any2md-1.0.6 → any2md-1.1.0}/pyproject.toml +1 -0
- any2md-1.1.0/scripts/bench_docling_cache.py +87 -0
- {any2md-1.0.6 → any2md-1.1.0}/scripts/validate-phase1.sh +1 -1
- any2md-1.1.0/tests/conftest.py +43 -0
- any2md-1.1.0/tests/integration/test_docling_persistence.py +105 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_txt.py +1 -1
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_content_hash.py +1 -1
- any2md-1.1.0/tests/unit/test_docling_cache.py +703 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_frontmatter_compose.py +2 -2
- any2md-1.0.6/any2md/__init__.py +0 -3
- any2md-1.0.6/tests/conftest.py +0 -25
- {any2md-1.0.6 → any2md-1.1.0}/.gitignore +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/CODE_OF_CONDUCT.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/CONTRIBUTING.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/LICENSE +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/SECURITY.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/__main__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/_docling.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/_http.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/config.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/converters/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/converters/html.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/heuristics.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/pipeline/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/pipeline/structured.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/pipeline/text.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/utils.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/any2md/validators.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/assets/logo-original.jpeg +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/assets/logo.png +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/assets/social-preview.jpg +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-any2md-v1-phase2-docling-backend.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-any2md-v1-phase3-figures-ocr-text-lane.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-any2md-v1.0.2-implementation.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-26-issue-17-toc-followups.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/plans/2026-04-27-v1.0.6-security-hardening.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/specs/2026-04-26-any2md-v1.0.2-design.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/specs/2026-04-26-issue-17-toc-followups-design.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/specs/2026-04-26-v1.0.3-empty-title-orphan-punct-design.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/docs/superpowers/specs/2026-04-27-v1.0.6-security-hardening-design.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/requirements.txt +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/scripts/audit-outputs.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_backend.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_existing_flags.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_exit_codes.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_high_fidelity.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_meta.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_no_arxiv_lookup.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_ocr_save_images.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_quiet_verbose.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/cli/test_cli_strict.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/docs/ligatures_and_softhyphens.txt +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/docs/multi_column.pdf +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/docs/table_heavy.docx +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/docs/web_page.html +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/make_fixtures.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/ligatures_and_softhyphens.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/multi_column.docling.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/multi_column.fallback.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/table_heavy.docling.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/table_heavy.fallback.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/fixtures/snapshots/web_page.md +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_docx_backend_override.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_docx_docling.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_docx_fallback_on_warn.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_docx_mammoth_fallback.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_html_trafilatura.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_pdf_backend_override.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_pdf_docling.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_pdf_pymupdf_fallback.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_save_images.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/integration/test_snapshots.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_footnote_markers.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_html_entities.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_html_entities_double_encoded.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_ligatures.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_nfc.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_quotes_dashes.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_soft_hyphens.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_validate.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_cleanup_whitespace.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_runner.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_strip_orphan_punctuation.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_structured_body_cleanup_stages.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_structured_compact_tables.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_structured_heading_hierarchy.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_structured_lift_figures.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_structured_normalize_cites.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_dedupe_paragraphs.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_dedupe_toc.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_dedupe_toc_table.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_dehyphenate.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_repair_line_wraps.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_restore_lists_code.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_strip_cover_artifacts.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_strip_headers_footers.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_strip_repeated_byline.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/pipeline/test_text_strip_web_fragments.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/scripts/__init__.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/scripts/test_audit_outputs.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_atomic_write.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_auto_id.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_config.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_config_walkup.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_docling_helper.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_frontmatter_helpers.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_heuristics.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_http_safe.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_log_sanitizer.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_meta_overrides.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_meta_reserved_keys.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_pdf_complexity.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_safe_dir_name.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_validators.py +0 -0
- {any2md-1.0.6 → any2md-1.1.0}/tests/unit/test_zipbomb_guard.py +0 -0
|
@@ -4,6 +4,108 @@ 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.1.0] — 2026-05-04
|
|
8
|
+
|
|
9
|
+
Performance and library-mode hygiene release.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `any2md.release_models()` — imperative escape hatch to free
|
|
14
|
+
cached Docling `DocumentConverter` instances. Stability:
|
|
15
|
+
experimental for v1.1.0.
|
|
16
|
+
- `any2md.docling_session()` — contextmanager (preferred shape
|
|
17
|
+
for library use). Stability: experimental for v1.1.0.
|
|
18
|
+
- `ANY2MD_DOCLING_CACHE=0` env-var to disable the persistent
|
|
19
|
+
converter cache (per-call construction, equivalent to v1.0.x
|
|
20
|
+
behavior).
|
|
21
|
+
- `__all__` declared on `any2md/__init__.py` (formalizes the
|
|
22
|
+
public surface).
|
|
23
|
+
- New benchmark `scripts/bench_docling_cache.py` for maintainer
|
|
24
|
+
pre-release verification.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- `DocumentConverter` is now constructed once per process and
|
|
29
|
+
reused across all files in a batch (was: once per file). New
|
|
30
|
+
one-time stderr line `Loading Docling models (one-time)...`
|
|
31
|
+
printed on first PDF/DOCX in non-quiet TTY runs.
|
|
32
|
+
- `.github/workflows/ci.yml` now runs `pytest tests/unit` on each
|
|
33
|
+
push and PR (was: lint + import smoke + pip-audit only).
|
|
34
|
+
|
|
35
|
+
### Performance
|
|
36
|
+
|
|
37
|
+
- Measured: CPU first-call ~2.47s (model load), warm calls ~0.87s
|
|
38
|
+
(vs prior ~2.47s per file in v1.0.x). Mac MPS first-call ~2.77s,
|
|
39
|
+
warm ~0.14s. RSS floor +535 MB sustained for the process lifetime
|
|
40
|
+
(see `docs/troubleshooting.md` for `release_models()` mitigation).
|
|
41
|
+
|
|
42
|
+
### Migration
|
|
43
|
+
|
|
44
|
+
- None required for CLI users. The `Loading Docling models...`
|
|
45
|
+
stderr line is the only user-visible default-output change.
|
|
46
|
+
- Library/embedder users holding any2md across long-running
|
|
47
|
+
processes should call `any2md.release_models()` or use
|
|
48
|
+
`with any2md.docling_session():` to free model state between
|
|
49
|
+
workloads.
|
|
50
|
+
- Test/instrumentation code that monkey-patches
|
|
51
|
+
`docling.document_converter.DocumentConverter` AFTER any2md's
|
|
52
|
+
first call will see no effect on subsequent calls (cache returns
|
|
53
|
+
the pre-patch instance). Workaround: call `release_models()`
|
|
54
|
+
after applying the patch, or set `ANY2MD_DOCLING_CACHE=0` in the
|
|
55
|
+
test environment.
|
|
56
|
+
|
|
57
|
+
### Stability notice
|
|
58
|
+
|
|
59
|
+
The new public APIs (`release_models`, `docling_session`) are
|
|
60
|
+
marked experimental for v1.1.0; their shape may change in
|
|
61
|
+
subsequent v1.x releases without a major bump until promoted to
|
|
62
|
+
stable in a later release.
|
|
63
|
+
|
|
64
|
+
### Known limitations
|
|
65
|
+
|
|
66
|
+
- HF Hub outages during cold-start now cause N retries instead of
|
|
67
|
+
one silent fallback (the v1.0.x failure-cache behavior was
|
|
68
|
+
rejected on quality grounds). Bounded retry-with-backoff
|
|
69
|
+
deferred to a future hardening PR.
|
|
70
|
+
- Forking a process (`multiprocessing` with `fork` start method)
|
|
71
|
+
after the first `convert()` call inherits torch/CUDA state
|
|
72
|
+
unsafely regardless of cache. Use
|
|
73
|
+
`multiprocessing.set_start_method("spawn")` or call
|
|
74
|
+
`release_models()` before fork.
|
|
75
|
+
- `os.register_at_fork` is POSIX-only; on Windows the fork-reset
|
|
76
|
+
path is dead code (no fork on Windows). Cache works normally
|
|
77
|
+
on Windows.
|
|
78
|
+
|
|
79
|
+
## [1.0.7] — 2026-04-29
|
|
80
|
+
|
|
81
|
+
Branding/rebrand release. The frontmatter contract that any2md emits is
|
|
82
|
+
now described as "SAGE-compatible" (Security Analysis and Guidance
|
|
83
|
+
Exchange) — the upstream specification was renamed from SSRM (Structured
|
|
84
|
+
Security Reasoning Markdown) to SAGE. No functional or schema changes:
|
|
85
|
+
field names, ordering, controlled vocabularies, and `content_hash`
|
|
86
|
+
derivation are byte-identical to v1.0.6. Existing v1.0.6 outputs remain
|
|
87
|
+
valid SAGE-compatible documents without modification.
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- README, CHANGELOG, and all of `docs/` updated to reference SAGE /
|
|
92
|
+
Security Analysis and Guidance Exchange.
|
|
93
|
+
- `--auto-id` CLI help text now reads "SAGE-conformant document_id"
|
|
94
|
+
(previously "SSRM-conformant"). Behavior unchanged.
|
|
95
|
+
- Module/function docstrings in `any2md/frontmatter.py`,
|
|
96
|
+
`any2md/converters/txt.py`, and `any2md/pipeline/cleanup.py` updated
|
|
97
|
+
to reference SAGE §3 / §5.1 (same section numbers as the upstream
|
|
98
|
+
SSRM v1.0-RC1 spec).
|
|
99
|
+
- Two test functions renamed for naming consistency
|
|
100
|
+
(`test_txt_end_to_end_writes_sage_compat_output`,
|
|
101
|
+
`test_compose_emits_required_sage_fields`). Pytest auto-discovery is
|
|
102
|
+
unaffected; assertions unchanged.
|
|
103
|
+
|
|
104
|
+
### Migration
|
|
105
|
+
|
|
106
|
+
None required. Public API, CLI flags, frontmatter shape, and
|
|
107
|
+
`content_hash` outputs are all preserved bit-for-bit.
|
|
108
|
+
|
|
7
109
|
## [1.0.6] — 2026-04-27
|
|
8
110
|
|
|
9
111
|
Security hardening release. Closes 8 actionable findings from the
|
|
@@ -318,7 +420,7 @@ Patch release. Adds an explicit backend-selection CLI flag.
|
|
|
318
420
|
First stable release of any2md v1.0. Validated end-to-end against
|
|
319
421
|
real-world documents (a 164-page PDF technical standard via Docling,
|
|
320
422
|
a multi-page academic DOCX, and a Wikipedia article via trafilatura).
|
|
321
|
-
The output contract —
|
|
423
|
+
The output contract — SAGE-compatible frontmatter, deterministic
|
|
322
424
|
`content_hash`, NFC + LF body normalization — is now stable. Downstream
|
|
323
425
|
consumers can rely on this shape.
|
|
324
426
|
|
|
@@ -340,7 +442,7 @@ Phase 4: configuration, polish, and the v1.0 documentation set.
|
|
|
340
442
|
- `--meta-file PATH` flag plus auto-discovery of `.any2md.toml` (walks up
|
|
341
443
|
from cwd). The `[meta]` table supplies frontmatter overrides; the
|
|
342
444
|
`[document_id]` table supplies `--auto-id` prefix and type code.
|
|
343
|
-
- `--auto-id` flag — generates an
|
|
445
|
+
- `--auto-id` flag — generates an SAGE-conformant `document_id` as
|
|
344
446
|
`{PREFIX}-{YYYY}-{TYPE}-{SHA8}`. Defaults to `LOCAL`/`DOC`; override via
|
|
345
447
|
the `[document_id]` table in `.any2md.toml`.
|
|
346
448
|
- `--strict` flag — promotes pipeline validation warnings (heading
|
|
@@ -359,7 +461,7 @@ Phase 4: configuration, polish, and the v1.0 documentation set.
|
|
|
359
461
|
- Comprehensive documentation set:
|
|
360
462
|
- `README.md` rewritten with educational tone — every command preceded
|
|
361
463
|
by why you'd run it and followed by what you'll see.
|
|
362
|
-
- `docs/output-format.md` —
|
|
464
|
+
- `docs/output-format.md` — SAGE-compatible field reference and the
|
|
363
465
|
`content_hash` recomputation recipe.
|
|
364
466
|
- `docs/cli-reference.md` — flag-by-flag reference with use cases and a
|
|
365
467
|
worked-example matrix.
|
|
@@ -462,14 +564,14 @@ any2md transparently falls back to pymupdf4llm (PDF) and mammoth (DOCX).
|
|
|
462
564
|
|
|
463
565
|
First prerelease of any2md v1.0. Phase 1 of 5: foundation only — no Docling
|
|
464
566
|
backend yet, no new CLI flags. Output frontmatter has been rewritten to be
|
|
465
|
-
|
|
567
|
+
SAGE-compatible (Security Analysis and Guidance Exchange) — this is a
|
|
466
568
|
breaking change for downstream consumers parsing v0.7 output. See
|
|
467
569
|
`docs/superpowers/specs/2026-04-26-any2md-v1-design.md` for the full
|
|
468
570
|
v1.0 design and `docs/superpowers/plans/2026-04-26-any2md-v1-phase1-foundation.md`
|
|
469
571
|
for this phase's task plan.
|
|
470
572
|
|
|
471
573
|
### Added
|
|
472
|
-
- New `any2md/frontmatter.py` module —
|
|
574
|
+
- New `any2md/frontmatter.py` module — SAGE-compatible YAML emitter with the
|
|
473
575
|
`SourceMeta` dataclass, deterministic `compute_content_hash`,
|
|
474
576
|
`estimate_tokens`, `recommend_chunk_level`, `extract_abstract`, and
|
|
475
577
|
`derive_title` helpers, plus the central `compose()` function (the only
|
|
@@ -495,7 +597,7 @@ for this phase's task plan.
|
|
|
495
597
|
`reportlab`, `ruff`.
|
|
496
598
|
|
|
497
599
|
### Changed
|
|
498
|
-
- **BREAKING:** Output frontmatter shape is now
|
|
600
|
+
- **BREAKING:** Output frontmatter shape is now SAGE-compatible. New required
|
|
499
601
|
fields: `document_id` (empty string by default — opt-in via future
|
|
500
602
|
`--auto-id`), `version`, `date`, `status: "draft"`, `document_type` (empty
|
|
501
603
|
for non-security documents), `content_domain` (empty array),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: any2md
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
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
|
|
@@ -25,6 +25,7 @@ Requires-Dist: urllib3<3,>=2.6.3
|
|
|
25
25
|
Provides-Extra: dev
|
|
26
26
|
Requires-Dist: pytest-snapshot>=0.9.0; extra == 'dev'
|
|
27
27
|
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
28
29
|
Requires-Dist: reportlab>=4.0.0; extra == 'dev'
|
|
29
30
|
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
30
31
|
Provides-Extra: high-fidelity
|
|
@@ -52,7 +53,7 @@ The full design rationale lives in [docs/superpowers/specs/2026-04-26-v1.0.3-emp
|
|
|
52
53
|
|
|
53
54
|
## What this is
|
|
54
55
|
|
|
55
|
-
any2md ingests heterogeneous source documents and emits Markdown with a fixed YAML frontmatter contract. The frontmatter shape is
|
|
56
|
+
any2md ingests heterogeneous source documents and emits Markdown with a fixed YAML frontmatter contract. The frontmatter shape is SAGE-compatible (Security Analysis and Guidance Exchange — a documented schema for LLM-consumable documents), so retrieval pipelines, embeddings jobs, and chunking utilities downstream can rely on field names, types, and a deterministic `content_hash` for cache invalidation. The body is NFC-normalized with LF line endings, and the heading hierarchy is guaranteed to start at H1 and not skip levels. The goal is one stable shape, regardless of whether a document started life as a scanned PDF, a Word doc, a Wikipedia page, or a plain text dump.
|
|
56
57
|
|
|
57
58
|
## Quick start
|
|
58
59
|
|
|
@@ -144,8 +145,8 @@ document_id: "" # empty unless you pass --aut
|
|
|
144
145
|
version: "1" # "1" for source files without an embedded version
|
|
145
146
|
date: "2026-03-15" # docx core props "modified" → ISO-8601
|
|
146
147
|
status: "draft" # always "draft" for converted documents
|
|
147
|
-
document_type: "" #
|
|
148
|
-
content_domain: [] #
|
|
148
|
+
document_type: "" # SAGE controlled vocab — empty for non-security docs
|
|
149
|
+
content_domain: [] # SAGE controlled vocab — empty array when unknown
|
|
149
150
|
authors: # extracted from docProps/core.xml dc:creator
|
|
150
151
|
- "Rock Lambros"
|
|
151
152
|
organization: "" # docProps/app.xml Company; empty when absent
|
|
@@ -166,7 +167,7 @@ produced_by: "Microsoft® Word for Microsoft 365" # software that produced the
|
|
|
166
167
|
---
|
|
167
168
|
```
|
|
168
169
|
|
|
169
|
-
Fields under `# any2md extension field` are retained from v0.7 for traceability and observability — they're not part of the
|
|
170
|
+
Fields under `# any2md extension field` are retained from v0.7 for traceability and observability — they're not part of the SAGE contract proper, but they live in the same frontmatter block. See [docs/output-format.md](docs/output-format.md) for the full field reference.
|
|
170
171
|
|
|
171
172
|
### Before / after
|
|
172
173
|
|
|
@@ -360,9 +361,18 @@ any2md -f -r ./corpus/
|
|
|
360
361
|
|
|
361
362
|
You'll see the OK lines as if the conversion were fresh; previously-written outputs are overwritten.
|
|
362
363
|
|
|
364
|
+
> **v1.1.0+:** when processing PDFs or DOCX files via the Docling
|
|
365
|
+
> backend, the converter (and its loaded model weights) is constructed
|
|
366
|
+
> once on the first PDF/DOCX in an invocation and reused for all
|
|
367
|
+
> subsequent files; subsequent files extract in ~0.5–1s rather than
|
|
368
|
+
> re-paying the ~2.5s cold-load tax. Library users embedding any2md
|
|
369
|
+
> in long-running processes should call `any2md.release_models()` or
|
|
370
|
+
> use `with any2md.docling_session():` — see
|
|
371
|
+
> `docs/troubleshooting.md`.
|
|
372
|
+
|
|
363
373
|
## The output format
|
|
364
374
|
|
|
365
|
-
Every file converted by any2md has the same frontmatter shape, regardless of source format. The shape is **
|
|
375
|
+
Every file converted by any2md has the same frontmatter shape, regardless of source format. The shape is **SAGE-compatible** — it matches the field names, types, and ordering of the Security Analysis and Guidance Exchange specification, but values aren't required to match SAGE's controlled vocabularies (most converted documents aren't security research). Fields that require a controlled vocabulary (`document_type`, `content_domain`, `tlp`) are emitted empty unless you supply them via `--meta` or `.any2md.toml`.
|
|
366
376
|
|
|
367
377
|
### Field auto-fill summary
|
|
368
378
|
|
|
@@ -407,7 +417,7 @@ You'll see the OK line as usual. The frontmatter of `Text/paper.md` will have `o
|
|
|
407
417
|
|
|
408
418
|
### `.any2md.toml`
|
|
409
419
|
|
|
410
|
-
You'd use this when you have a stable set of frontmatter defaults that should apply to every conversion in a project. The file is auto-discovered by walking up from the current working directory, so you can drop one at the root of your corpus repo and forget about it. Worked example for a security-research org producing
|
|
420
|
+
You'd use this when you have a stable set of frontmatter defaults that should apply to every conversion in a project. The file is auto-discovered by walking up from the current working directory, so you can drop one at the root of your corpus repo and forget about it. Worked example for a security-research org producing SAGE-conforming outputs:
|
|
411
421
|
|
|
412
422
|
```toml
|
|
413
423
|
[meta]
|
|
@@ -561,7 +571,7 @@ The two-lane design exists because Docling output is layout-correct (running agg
|
|
|
561
571
|
|
|
562
572
|
## Migrating from v0.7
|
|
563
573
|
|
|
564
|
-
v1.0 emits
|
|
574
|
+
v1.0 emits SAGE-compatible frontmatter — that's the one intended breaking change. v0.7's small frontmatter (`title`, `source_file`, `pages`, `type`, `word_count`) becomes a fuller block with `document_id`, `version`, `date`, `status`, `content_hash`, `token_estimate`, `authors`, and several other fields. The v0.7 fields are retained as any2md extension fields in the same block, so traceability is preserved, but downstream parsers that expected the old fixed shape will need to update. The body is now NFC-normalized with LF line endings, which means `content_hash` is reproducible. If you need bit-for-bit v0.7 output, pin `any2md==0.7.0`. The full field-by-field migration guide and behavior-change list lives in [docs/upgrading-from-0.7.md](docs/upgrading-from-0.7.md).
|
|
565
575
|
|
|
566
576
|
## Security
|
|
567
577
|
|
|
@@ -19,7 +19,7 @@ The full design rationale lives in [docs/superpowers/specs/2026-04-26-v1.0.3-emp
|
|
|
19
19
|
|
|
20
20
|
## What this is
|
|
21
21
|
|
|
22
|
-
any2md ingests heterogeneous source documents and emits Markdown with a fixed YAML frontmatter contract. The frontmatter shape is
|
|
22
|
+
any2md ingests heterogeneous source documents and emits Markdown with a fixed YAML frontmatter contract. The frontmatter shape is SAGE-compatible (Security Analysis and Guidance Exchange — a documented schema for LLM-consumable documents), so retrieval pipelines, embeddings jobs, and chunking utilities downstream can rely on field names, types, and a deterministic `content_hash` for cache invalidation. The body is NFC-normalized with LF line endings, and the heading hierarchy is guaranteed to start at H1 and not skip levels. The goal is one stable shape, regardless of whether a document started life as a scanned PDF, a Word doc, a Wikipedia page, or a plain text dump.
|
|
23
23
|
|
|
24
24
|
## Quick start
|
|
25
25
|
|
|
@@ -111,8 +111,8 @@ document_id: "" # empty unless you pass --aut
|
|
|
111
111
|
version: "1" # "1" for source files without an embedded version
|
|
112
112
|
date: "2026-03-15" # docx core props "modified" → ISO-8601
|
|
113
113
|
status: "draft" # always "draft" for converted documents
|
|
114
|
-
document_type: "" #
|
|
115
|
-
content_domain: [] #
|
|
114
|
+
document_type: "" # SAGE controlled vocab — empty for non-security docs
|
|
115
|
+
content_domain: [] # SAGE controlled vocab — empty array when unknown
|
|
116
116
|
authors: # extracted from docProps/core.xml dc:creator
|
|
117
117
|
- "Rock Lambros"
|
|
118
118
|
organization: "" # docProps/app.xml Company; empty when absent
|
|
@@ -133,7 +133,7 @@ produced_by: "Microsoft® Word for Microsoft 365" # software that produced the
|
|
|
133
133
|
---
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
Fields under `# any2md extension field` are retained from v0.7 for traceability and observability — they're not part of the
|
|
136
|
+
Fields under `# any2md extension field` are retained from v0.7 for traceability and observability — they're not part of the SAGE contract proper, but they live in the same frontmatter block. See [docs/output-format.md](docs/output-format.md) for the full field reference.
|
|
137
137
|
|
|
138
138
|
### Before / after
|
|
139
139
|
|
|
@@ -327,9 +327,18 @@ any2md -f -r ./corpus/
|
|
|
327
327
|
|
|
328
328
|
You'll see the OK lines as if the conversion were fresh; previously-written outputs are overwritten.
|
|
329
329
|
|
|
330
|
+
> **v1.1.0+:** when processing PDFs or DOCX files via the Docling
|
|
331
|
+
> backend, the converter (and its loaded model weights) is constructed
|
|
332
|
+
> once on the first PDF/DOCX in an invocation and reused for all
|
|
333
|
+
> subsequent files; subsequent files extract in ~0.5–1s rather than
|
|
334
|
+
> re-paying the ~2.5s cold-load tax. Library users embedding any2md
|
|
335
|
+
> in long-running processes should call `any2md.release_models()` or
|
|
336
|
+
> use `with any2md.docling_session():` — see
|
|
337
|
+
> `docs/troubleshooting.md`.
|
|
338
|
+
|
|
330
339
|
## The output format
|
|
331
340
|
|
|
332
|
-
Every file converted by any2md has the same frontmatter shape, regardless of source format. The shape is **
|
|
341
|
+
Every file converted by any2md has the same frontmatter shape, regardless of source format. The shape is **SAGE-compatible** — it matches the field names, types, and ordering of the Security Analysis and Guidance Exchange specification, but values aren't required to match SAGE's controlled vocabularies (most converted documents aren't security research). Fields that require a controlled vocabulary (`document_type`, `content_domain`, `tlp`) are emitted empty unless you supply them via `--meta` or `.any2md.toml`.
|
|
333
342
|
|
|
334
343
|
### Field auto-fill summary
|
|
335
344
|
|
|
@@ -374,7 +383,7 @@ You'll see the OK line as usual. The frontmatter of `Text/paper.md` will have `o
|
|
|
374
383
|
|
|
375
384
|
### `.any2md.toml`
|
|
376
385
|
|
|
377
|
-
You'd use this when you have a stable set of frontmatter defaults that should apply to every conversion in a project. The file is auto-discovered by walking up from the current working directory, so you can drop one at the root of your corpus repo and forget about it. Worked example for a security-research org producing
|
|
386
|
+
You'd use this when you have a stable set of frontmatter defaults that should apply to every conversion in a project. The file is auto-discovered by walking up from the current working directory, so you can drop one at the root of your corpus repo and forget about it. Worked example for a security-research org producing SAGE-conforming outputs:
|
|
378
387
|
|
|
379
388
|
```toml
|
|
380
389
|
[meta]
|
|
@@ -528,7 +537,7 @@ The two-lane design exists because Docling output is layout-correct (running agg
|
|
|
528
537
|
|
|
529
538
|
## Migrating from v0.7
|
|
530
539
|
|
|
531
|
-
v1.0 emits
|
|
540
|
+
v1.0 emits SAGE-compatible frontmatter — that's the one intended breaking change. v0.7's small frontmatter (`title`, `source_file`, `pages`, `type`, `word_count`) becomes a fuller block with `document_id`, `version`, `date`, `status`, `content_hash`, `token_estimate`, `authors`, and several other fields. The v0.7 fields are retained as any2md extension fields in the same block, so traceability is preserved, but downstream parsers that expected the old fixed shape will need to update. The body is now NFC-normalized with LF line endings, which means `content_hash` is reproducible. If you need bit-for-bit v0.7 output, pin `any2md==0.7.0`. The full field-by-field migration guide and behavior-change list lives in [docs/upgrading-from-0.7.md](docs/upgrading-from-0.7.md).
|
|
532
541
|
|
|
533
542
|
## Security
|
|
534
543
|
|