Add snapshot-ready MinerU-HTML extraction with managed Dynamo - #21
Add snapshot-ready MinerU-HTML extraction with managed Dynamo#21VibhuJawa wants to merge 7 commits into
Conversation
| if self.quantization: | ||
| kwargs["quantization"] = self.quantization | ||
| kwargs.update(self.vllm_init_kwargs) | ||
| self._llm = LLM(model=model_path, **kwargs) |
There was a problem hiding this comment.
Use VLLM utils to load the model
Benchmarking session: results, and what I removed after measuring itBenchmarked this on 8× H100 (10k Common Crawl documents, the strided sample described below). Headline: 28.5 → 62.3 docs/s (2.2×), quality unchanged. But most of what I tried made things worse, and I've deleted all of it rather than leave knobs nobody should turn. Recording the negative results here so nobody re-runs these. What shipped
What I added and then removed (all measured)
The pattern: every knob that reshuffles work inside one engine moved throughput by ≤18%. The only thing that mattered was removing the per-partition drain, which is what the server backend does. Two things worth a second opinion
Separately: four bugs found in Curator itselfNot fixed here, but reproduced and worth filing:
🤖 Generated with Claude Code |
Correction to the previous comment: the server backend is at parity, not 2.2×My earlier comment reported 28.5 → 62.3 docs/s (2.2×). That comparison was not like-for-like and I'm withdrawing the claim. Pushed as What the per-stage timings actually showNormalising the inference stage by worker count (from
The inference work costs the same either way. The 137 s end-to-end difference is almost entirely vLLM engine startup, which the in-process path pays inside its measured window: A persistent server pays that once, outside. Per document the server stage is in fact ~2× slower (120.8 ms vs 62.5 ms) from HTTP + serialization — it only keeps up because the latency is spread over twice as many workers. This also fails to confirm the motivating hypothesisI argued the per-partition drain ( Why the backend is still worth havingOperational, not throughput:
For a single run over a large corpus, the in-process backend is simpler and no slower. That's now what the module docstring says. What still stands from the previous commentThe list of measured-and-removed dead ends is unaffected — packing (−6%/−4%/−4% across fractional GPU, explicit KV budget, and CUDA MPS), DP=16 ( The 🤖 Generated with Claude Code |
Superseding the two comments above: the PR is now server-onlyThe description has been rewritten to match the code. Both earlier comments are now partly stale — this note says what changed so nobody works from them. What changed since those comments
Validated by measurement, not assumption:
Two reps of the new code agree to 0.0% and sit inside the pre-refactor band. Deleting the GPU stage changed nothing measurable. What is now stale in the earlier comments
Still accurate from those commentsThe dead-end table (packing −6%/−4%/−4%, DP=16 unstartable, ordering −5%, coarse partitions −23%, batching knobs flat, guided decoding +0.6%) and the four Curator bugs. Both are reproduced in the updated description. One correction to the second comment, though: I attributed the residual gap to the CPU stages. The per-stage timings say otherwise — inference is still the largest single stage (75.5s wall vs 11.3s simplify and 16.0s extract), so that claim was wrong. One further correction, on a test that was never validI reported that the 🤖 Generated with Claude Code |
Four review comments on PR #21, all on mineru_html.py. "We should always use the real path, remove this fallback" and "I dont think we need to support both paths" -- `pretokenize` is gone and prompts are always tokenized on the simplify workers. That removes the whole second path: the PROMPT_FIELD column, the TOKENS_FIELD-or-PROMPT_FIELD sniff in the inference stage, the `--no-pretokenize` tutorial flag, and the character-count length estimate the over-long pre-filter used when not pre-tokenizing. The filter now counts real token ids, which is also strictly more accurate -- the estimate it replaces overshot ~1.9x and diverted documents that would have fit. "Can we come up with a better `_as_text`" -- now `decode_html_cell` in mineru_utils, with a two-line docstring. The pd.NA rationale is one line at the branch rather than a five-line essay above the function. "Should this be in utils" -- the three fallback handlers are now `FallbackExtractor` in mineru_utils, alongside `decode_html_cell` which it uses. The extract stage's setup() is a single line again. Tests updated rather than deleted: the chat-template assertions used the text prompt column, so they now exercise `_chat_wrap` directly, which is the thing they were actually testing. A new test asserts the emitted token ids are exactly the tokenization of the chat-wrapped prompt, which is the property the old text-vs-tokens comparison was checking across the two paths. 125 tests pass, ruff and format clean, both CLIs still parse every flag the benchmark config passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four review comments on PR #21, all on mineru_html.py. "We should always use the real path, remove this fallback" and "I dont think we need to support both paths" -- `pretokenize` is gone and prompts are always tokenized on the simplify workers. That removes the whole second path: the PROMPT_FIELD column, the TOKENS_FIELD-or-PROMPT_FIELD sniff in the inference stage, the `--no-pretokenize` tutorial flag, and the character-count length estimate the over-long pre-filter used when not pre-tokenizing. The filter now counts real token ids, which is also strictly more accurate -- the estimate it replaces overshot ~1.9x and diverted documents that would have fit. "Can we come up with a better `_as_text`" -- now `decode_html_cell` in mineru_utils, with a two-line docstring. The pd.NA rationale is one line at the branch rather than a five-line essay above the function. "Should this be in utils" -- the three fallback handlers are now `FallbackExtractor` in mineru_utils, alongside `decode_html_cell` which it uses. The extract stage's setup() is a single line again. Tests updated rather than deleted: the chat-template assertions used the text prompt column, so they now exercise `_chat_wrap` directly, which is the thing they were actually testing. A new test asserts the emitted token ids are exactly the tokenization of the chat-wrapped prompt, which is the property the old text-vs-tokens comparison was checking across the two paths. 125 tests pass, ruff and format clean, both CLIs still parse every flag the benchmark config passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52a4ce0 to
2bdfa3d
Compare
2bdfa3d to
4940f26
Compare
4940f26 to
59dc2c6
Compare
Summary
Adds MinerU-HTML main-content extraction and a Curator-native path for processing a complete Common Crawl snapshot through
benchmarking/run.pyon Slurm.This consolidates the useful MinerU and managed-Dynamo work from the earlier PRs into one branch built on the upstream Ray 2.57 / scheduler-observability changes from NVIDIA-NeMo#2304. The implementation intentionally does not use custom async scheduling.
WARC-to-output architecture
CommonCrawlWARCManifestSourceStagereads a frozen officialwarc.pathsmanifest and emits one deterministic Curator source task per complete WARC. It performs no snapshot discovery or URL generation.CommonCrawlWARCDownloadAndReadStagedownloads and reads a WARC on the same Ray worker, supports configurable S3-compatible whole-object transport, and cleans up its temporary object after iteration.Download, simplify, inference-client, extraction, Ray object-store, Slurm-array, and GPU-node concurrency remain independent tuning controls.
MinerU serving baseline
opendatalab/MinerU-HTML-v1.1-hunyuan0.5B-compact.FULL_AND_PIECEWISECUDA graphs.guided_regexrequest extension.Recovery and verification
Pipeline.run(checkpoint_path=...)tracks completion at the source-WARC boundary. A retry preserves the logical shard geometry and replays only unfinished source parents. Existing chunks are overwritten at deterministic paths instead of being appended as duplicate files.A dependent CPU
run.pyentry verifies:url,text, and_mineru_status;Only then does it atomically write
SNAPSHOT_SUCCESS.json. This is an operational completeness and gross-quality check; model, prompt, or parser changes still require a labelled quality canary.Validation
216 passedacross MinerU extraction, Common Crawl download/read, snapshot verification, benchmark integration, Ray resource handling, and Dynamo configuration tests.uv lock --check,git diff --check, and shell syntax checks pass.Checklist