feat: local MinerU mode via synchronous /file_parse - #233
Open
gdccyuen wants to merge 1 commit into
Open
Conversation
Add MINERU_LOCAL_MODE and supporting config (lang_list, backend,
timeout) to dispatch PDF parsing to a local MinerU instance's
synchronous /file_parse endpoint, avoiding the cloud-only batch APIs
that return 404 against local deployments.
parse_via_full now checks settings.MINERU_LOCAL_MODE first and routes
to a new parse_via_local that POSTs the PDF as multipart form data,
gets a ZIP back, extracts it, then flattens the nested {stem}/auto/
layout into the flat {output_dir}/full.md + images/* shape downstream
code expects.
Local MinerU is single-concurrency by default, so a separate requests
session with Retry(read=0) prevents ReadTimeout-driven urllib3 retries
from pushing requests to the back of the same queue. 429s surface as
UnavailableException without going through the cloud quota manager
(local mode has no API key).
Excerpted from ADR 0001 in knowhere-self-hosted; retires the
forked-image stopgap there once a knowhere release ships this natively.
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.
What
Adds first-class support for parsing PDFs against a local (self-hosted) MinerU instance — the synchronous
/file_parseendpoint — as an alternative to the cloud batch APIs. Self-hosted deployments that run MinerU on their own network (or fully offline) currently cannot use/file-urls/batch,/extract/task/batch, or/extract-results/batch(they 404 against local MinerU). This PR closes that gap.Why
Design rationale lives in ADR 0001 of
knowhere-self-hosted. Until this PR lands, self-hosted deployments have to ship a forked Docker image that patchespdf_service.pyat build time. Once this merges and a release ships, that stopgap can be retired.Changes
Config (
packages/shared-python/shared/core/config/mineru.py)Four new typed
MineruConfigfields:MINERU_LOCAL_MODEfalseparse_via_fulldispatches toparse_via_localbefore any cloud-mode logic.MINERU_LOCAL_LANG_LIST"ch"/file_parse'slang_listparam. Local MinerU does not accept"auto";"ch"covers Chinese, English, Japanese, Traditional Chinese, and Latin.MINERU_LOCAL_BACKEND"pipeline"pipelinefor CPU,vlm-enginefor GPU).MINERU_LOCAL_TIMEOUT3600/file_parsecall. Includes queue wait whenMINERU_SHARD_CONCURRENCY > 1.Implementation (
apps/worker/app/services/document_parser/providers/mineru/pdf_service.py)parse_via_local— POSTs the PDF as multipart form data to{MINERU_URL}/file_parsewithlang_list,backend, andreturn_images=true. Streams remote URLs through a temp file first. Receives a ZIP, extracts it, flattens it._flatten_extracted_zip— Local MinerU's ZIP has a nested{stem}/auto/{stem}.md+{stem}/auto/images/*layout. Downstream code expectsfull.mdand animages/directory at the output root. This lifts{stem}/auto/'s kept-ext contents to the output root (preserving theimages/subdir), excludescontent_list/middle.json/model.json(parity with cloud'sdownload_and_extract_zipexclude list), and renames the single.mdtofull.md. Hard-fails on zero or multiple.mdfiles, and on zero or multiple{stem}/auto/dirs, so we never silently pick the wrong one._get_local_mineru_session— separaterequests.SessionwithRetry(read=0, ...). Rationale: local MinerU is single-concurrency (max_concurrent_requests=1) by default; aReadTimeoutfrom queue wait must not cascade into a urllib3 retry that pushes the request to the back of the same queue. Cloud'sbuild_mineru_sessionretries on[429, 502, 503, 504]; local mode drops429fromstatus_forcelistand surfaces it asUnavailableExceptiondirectly (no cloud quota manager — local has no API key).parse_via_full— top-of-function dispatch:if settings.MINERU_LOCAL_MODE: parse_via_local(...); return. Existing cloud flow untouched whenMINERU_LOCAL_MODE=false.Tests (
apps/worker/tests/unit/test_pdf_service_flatten.py)7 unit tests for
_flatten_extracted_zipagainst synthetic ZIPs constructed in-memory:{stem}/auto/{stem}.md+images/*→full.md+images/*{stem}/auto/dir (layout drift){stem}/auto/dirs.mdfiles.mdfilescontent_list,middle.json,model.jsondropped;keep.jsonretained)Environment variables
Documented in self-hosted
docs/configuration.mdalready (added in commit history ofknowhere-self-hosted). Once merged here, theos.environ.getreads used by the stopgap patch in the self-hosted repo become proper typedpydantic_settingsfields — the original ADR's "Future work" called this out explicitly.Verification
Smoke-test fixtures for actual end-to-end local MinerU testing live in
knowhere-self-hosted's smoke test (which already covers local mode once this is shipped in a release).Backward compatibility
MINERU_LOCAL_MODEdefaults tofalse. Existing cloud deployments are entirely unaffected. Self-hosted deployments that want local mode set three env vars:MINERU_LOCAL_MODE=true MINERU_URL=http://host.docker.internal:8000 # MINERU_API_KEYS may be left as placeholder; local mode never reads itOut of scope (future PRs)
/tasksAPI for very large PDFs (sync/file_parsetimeout becomes the bottleneck on multi-shard large PDFs).