Repository navigation
feat(documents): pluggable document conversion with converter chain - #182
Merged
Merged
Conversation
Replace the fixed PDF and Office processors with one DocumentProcessor that runs an ordered, per-format chain of DocumentConverter backends configured by DocumentConversion (formats, chain order, 25 MB document cap, remote opt-in). - Built-in converters behind features: pdf-inspector (docs-pdf, default), office (docs-office, default), Cloudflare toMarkdown (docs-cloudflare) and a generic multipart HttpConverter (docs-http). - Remote converters run only with allow_remote(true), never under hardened(); uploads go through a pluggable ConverterClient; tokens are redacted. - Fall through to the next converter on error, unsupported input, or an empty/OCR-required result; formats are confirmed by magic bytes. - Config reachable from ToolBuilder, FetchOptions, CLI/MCP flags and Python; ToolBuilder::content_processor adds custom processors ahead of built-ins. - Rejection message names the recognised format when conversion is disabled. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2vd61x8kbPtmDxze9Q4Gi
chaliy
added a commit
to everruns/markkit
that referenced
this pull request
Oct 12, 2026
…sion (#9) Before: markkit had no way to convert document bytes to Markdown. DOCX went through docx-rs with no limits, and the only office path pulled in the HTTP client stack. `cargo deny` also failed on newly published advisories. After: - `Markkit::extract_document(bytes, DocumentHint)` and `extract_document_sync` convert DOCX, XLSX/XLSM, XLS and ODS to Markdown. They return an `Extraction` with the title, the detected `Format`, warnings and stats. - The `docx` and `spreadsheet` features build without reqwest or hyper, so fetchkit can embed markkit as its Office backend. - `cargo deny check` and `cargo audit` pass. How: - **Format detection.** `Format::detect`, `sniff` and `from_file_name` use the content type first, then the file extension, then magic bytes. Detection works with no format feature enabled. - **DOCX.** Read directly with `zip` and `quick-xml`; docx-rs is removed. Every part is decompressed with caps and parsed into a depth-limited XML tree. - **Spreadsheets.** Converted with calamine 0.36. - XLSX is streamed. - XLS and ODS are pre-scanned against `max_spreadsheet_cells`, so a tiny file cannot force a huge dense-grid allocation. - Hidden sheets are skipped by default, and formulas are not evaluated. - **New `MarkkitBuilder` limits.** `max_archive_entries`, `max_archive_entry_size`, `max_archive_total_size`, `max_sheet_rows`, `max_sheet_cols`, `max_spreadsheet_cells` and `include_hidden_sheets`. - **Unsupported formats.** PDF, PPTX, legacy `.doc` and XLSB are detected and return an explicit `UnsupportedFormat` error. - **Advisories.** - Removed `pdf-extract`, which was never called. This clears lopdf RUSTSEC-2026-0187 and the unmaintained `ttf-parser`. The `pdf` feature is now a dependency-free placeholder. - Upgraded `pyo3` and `pyo3-async-runtimes` to 0.29 (RUSTSEC-2026-0176/0177). - Upgraded `tabled` in the bench crate to 0.22, which drops the unmaintained `proc-macro-error2`. - **Security tests.** They cover: - zip bombs - entry and total size caps - grid blow-ups - billion laughs - XXE - deep nesting - hidden sheets - fuzz-style proptests Fixtures are built in memory. - **CI.** A new step tests `--no-default-features --features docx,spreadsheet` and fails if an HTTP client appears in the dependency tree. - **Docs.** Specs, the threat model (T6, S8–S11), the README and the CHANGELOG are updated. Tests: - `cargo test --workspace` passes. - `cargo clippy -D warnings` passes for both the default and the all-features builds. - The doc build and the release build pass. - `cargo deny check` and `cargo audit` pass. Part of the fetchkit document conversion work (everruns/fetchkit#182). markkit will replace fetchkit's in-crate Office converter. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Y2vd61x8kbPtmDxze9Q4Gi --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4 tasks done
chaliy
added a commit
that referenced
this pull request
Oct 12, 2026
#183) ## What changed markkit (0.1.0, crates.io) becomes the default Office backend. The in-crate `office` converter from #181 is deprecated and off by default. - **New `docs-markkit` feature, on by default.** It adds `MarkkitConverter` (name `markkit`, local) for DOCX, XLSX/XLSM, XLS and ODS. - Extraction methods are `markkit_docx`, `markkit_xlsx`, `markkit_xls` and `markkit_ods`. - Limits are tuned with `MarkkitConverter::with_markkit(Markkit::builder()…)`. `Markkit` and `MarkkitBuilder` are re-exported from `fetchkit::documents`. - **Feature defaults:** `docs-pdf` + `docs-office` becomes `docs-pdf` + `docs-markkit`. `docs-all` now includes markkit and excludes office. - **`OfficeConverter` is `#[deprecated]`.** It still works behind `docs-office`, and when that feature is enabled it runs after markkit as a fallback. - **Settings, CLI and Python:** `--converter markkit` / `converters=["markkit"]` are accepted. When a feature is missing, the error says so. - markkit is built without its HTTP client, and the only reqwest in the tree is fetchkit's own. ## Why The repo owner decided that #181's in-crate Office parsing is not the implementation to keep. Office parsing moves to markkit, which fetchkit's design already planned as a pluggable backend. markkit has zip-bomb, archive and spreadsheet-grid limits, and it skips hidden sheets by default. ## Before / After Before (default build): `fetchkit fetch …/demo.docx` gave `extraction_method: "office_docx"`. After: ``` $ fetchkit fetch https://calibre-ebook.com/downloads/demos/demo.docx quality_score: 1.00 extraction_method: "markkit_docx" --- # Demonstration of DOCX support in calibre … | ITEM | NEEDED | | Books | 1 | ``` Local files: `.xlsx` gives `markkit_xlsx` (with a `hidden_sheet_skipped` warning), `.xls` gives `markkit_xls`, `.ods` gives `markkit_ods`, and a large sheet carries a `truncated_sheet` warning. Legacy `.doc` is still rejected with "DOC conversion is not enabled". ## Risk - Medium. Unreleased API change on top of #182. - PPTX and XLSB are no longer converted by default, because markkit doesn't support them yet. Enabling `docs-office` restores them. - Spreadsheet titles are no longer extracted. - markkit's default sheet caps are looser: 10,000 rows × 256 columns, where `office` used 1,000 × 50. Output is still capped by `max_body_size`. - New transitive crates: `cfb`, `uuid`, `regex`, `anyhow`, `mime`, `unicode-segmentation`. ### Checklist - [x] Unit tests are passed: `cargo test --workspace`, plus feature sets with no defaults, `docs-markkit` only, and `docs-all,docs-office`. clippy `-D warnings` passes across the same sets. - [x] Smoke tests are passed: the release CLI on a real DOCX, and local XLSX/XLS/ODS files. - [x] Documentation is updated: README, CHANGELOG (Added and Deprecated), docs/hardening.md. - [x] Knowledge is up to date and not in conflict: fetchers, tool contract, TM-DOS-008, and the log. `check_okf.py` passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Y2vd61x8kbPtmDxze9Q4Gi --- _Generated by [Claude Code](https://claude.ai/code/session_01Y2vd61x8kbPtmDxze9Q4Gi)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Михайло · project thread
What changed
PDF and Office conversion is now configurable and pluggable instead of two fixed processors.
DocumentProcessorruns an ordered chain ofDocumentConverterbackends, set per format. When a backend fails, doesn't support the input, or returns nothing usable (for example a scanned PDF), the next one runs.docs-pdf(default on):pdf-inspector.docs-office(default on): the local Office converter from feat(content): convert Office documents to Markdown locally #181.docs-cloudflare(opt-in): Cloudflare Workers AItoMarkdown.docs-http(opt-in): a generic multipartHttpConverterfor self-hosted services such as Docling or MarkItDown.allow_remote(true), and never underToolBuilder::hardened(). Uploads go through a pluggableConverterClient, so a host can route them through its own egress. Tokens are redacted from debug output and errors.max_document_bytes(25 MB default), separate frommax_body_size. Formats are confirmed by magic bytes before any converter runs.ToolBuilder::document_conversion, plusToolBuilder::content_processorto add custom processors ahead of the built-ins.--convert,--converter,--allow-remote-conversion,--max-document-bytes. The Cloudflare converter readsCLOUDFLARE_ACCOUNT_IDandCLOUDFLARE_API_TOKEN.DocumentConverter,FormatDetector,ConverterClientandContentProcessor.Why
Design: Fetchkit: PDF and Office document conversion design. Conversion should be configurable, feature-flagged and pluggable. It should allow third-party services such as Cloudflare, and leave room for markkit or other backends later.
Before / After
Before: Office conversion was a fixed
OfficeProcessor(from #181), with no way to turn it off, choose a backend or add a remote one.After, default build (unchanged output for supported formats):
Restricting formats:
Remote converter without its feature:
The Cloudflare path is covered end to end with wiremock: the tool downloads the DOCX, uploads it to a mock
toMarkdown, and returnscloudflare_tomarkdown. It has not been run against a real Cloudflare account yet.Risk
PdfProcessorandOfficeProcessorare replaced byDocumentProcessorplusPdfInspectorConverterandOfficeConverter.pdf-inspector,calamine,zipandquick-xmlare now optional dependencies. Default features keep them on.application/mswordandapplication/epub+zipare now treated as binary.Checklist
cargo test --workspace; clippy with default, no-default,docs-alland single-backend feature sets)check_okf.pypasses)🤖 Generated with Claude Code
https://claude.ai/code/session_01Y2vd61x8kbPtmDxze9Q4Gi
Generated by Claude Code