sofhiaazzhr Claude Opus 5 commited on
Commit
f0cf168
Β·
1 Parent(s): f132cc1

[NOTICKET] docs(knowledge-parsing): module README + refresh the parsing/seam statuses

Browse files

README follows the per-module convention already used by src/catalog, src/query,
src/retrieval and src/security, and records the three things that are easy to get
wrong and expensive to discover: text stays verbatim, page_idx is 0-based, and
markup never goes in text (with the recall numbers behind that last one).

KNOWLEDGE_PIPELINE_TODO.md β€” parsing (B) and seam (S) rows only; P/X/D/E/F and
S7/S8 are untouched:

- B6, B8, S2, S3, S4 -> done. Contract landed, envelope carries parser identity
read back from MinerU's _middle.json, section-structure claim corrected against
MinerU's source, source_wording added.
- B4, B5, S1, S5, S6 -> in progress, deliberately NOT marked done:
B5's acceptance test needs the Open Pit textbook (S7) before the NSR
cross-page case can be claimed; S5 is satisfied on the parsing side but is
Rifqi's to close; B4's language question is answered but text quality is not
yet judged. Marking unproven work done is what makes a checklist useless.
- S6: (c) chunk size, (d) chapters-as-list and (e) pydantic are closed. (b) page
indexing stays open as a joint call. (a) is open with its premise corrected β€”
text_format is emitted by both backends and is not a formatting signal, and
MinerU carries no emphasis information anywhere, so that ranker signal has no
source short of a separate PDF pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

KNOWLEDGE_PIPELINE_TODO.md CHANGED
@@ -75,7 +75,12 @@ either port scaffolding you didn't want or discard measurements you can't recrea
75
 
76
  ## 3. The seam β€” shared with Sofhia
77
 
78
- **Status: under active discussion.** Sofhia circulated a draft `contracts.py` on 2026-08-19,
 
 
 
 
 
79
  derived from real MinerU output rather than designed on paper, and it is adopted as the base. Three
80
  of its choices improve on the prototype: a `kind` discriminator (text/table/chart/equation),
81
  `source_items` index back-references into MinerU's `content_list.json`, and an explicit
@@ -84,12 +89,12 @@ verbatim-text warning that independently identifies the same failure mode as the
84
 
85
  | # | Task | Owner | Status | Description |
86
  |---|---|---|---|---|
87
- | **S1** | Agree the parsed-artifact schema | Sofhia + Rifqi | πŸ”„ | Under discussion. Everything else in this section is an open item against her draft |
88
- | **S2** | Add a document-level envelope | Sofhia + Rifqi | πŸ”„ | The draft is `list[Chunk]` with no wrapper, so there is nowhere for `content_hash`, `n_pages`, `source_path`, or **`version`** β€” and a versioned artifact is a plan requirement. Proposed: `ParsedDocument{doc_id, source_path, content_hash, version, n_pages, parser_name, parser_version, chunks}`. Parser identity matters *because* the seam exists to make parsers swappable: when extraction output shifts, we must be able to tell a parser change from a prompt change |
89
- | **S3** | Correct the section-structure claim | Rifqi β†’ Sofhia | πŸ”„ | The draft states MinerU does not supply section structure (`text_level` on 1 of 84 items). **On the BUMA standard it does:** the full 9-page parse carries `text_level` on **29 of 118 items**, cleanly numbered `1.` β†’ `2.2.5`, including `2.1.3. Physical of Availability (PA)`. Her sample appears to be the handbook (its `doc_id` is `handbook`, and it has item types `page_number`/`header` absent from the BUMA parse), which genuinely has no numbered headings. Keeping both fields Optional is correct; the stated *reason* is not, and acting on it would starve section-aware chunking on exactly the documents that carry numbering β€” i.e. every client standard |
90
- | **S4** | Add `source_wording` while the contract is open | Rifqi β†’ Sofhia | πŸ”„ | Cheap moment to fix the defect in Β§7. With `heading` carried verbatim, a `source_wording` field span-checked against it makes *"Physical **of** Availability"* survive instead of being silently normalised |
91
- | **S5** | Keep the seam off the file path | Sofhia + Rifqi | ⬜ | The prototype's boundary was `Parser.parse(path) -> list[Page]` β€” the one shape the plan forbids. v2 must not reproduce it. Must be settled **before** the MinerU parser is built against it, or it becomes a rewrite of Sofhia's work |
92
- | **S6** | Minor contract items (deferred to review) | Rifqi β†’ Sofhia | ⏸️ | Raised but not blocking: **(a)** the formatting evidence signal β€” ranking uses six signals including formatting, read from `bold_spans`; the draft rightly excludes it as backend-specific, so either derive it from markdown emphasis or drop it deliberately, not by silence. **(b)** page indexing β€” draft is 0-based "as MinerU reports", prototype is 1-based, and page numbers are shown to the expert; name it `page_idx` if 0-based. **(c)** chunk-size policy β€” the sample chunk spans 32 source items across 2 pages; the prototype capped ~1,500 tokens. **(d)** `chapter` is lossy when a chunk crosses a chapter boundary. **(e)** pydantic over dataclass, matching this repo. **(f)** point `TermRecord` at the prototype's richer entry rather than re-deriving it |
93
  | **S7** | Commit fixture artifacts | Rifqi | ⬜ | Both source PDFs exist (BUMA standard + Open Pit textbook). Promote a parsed artifact of each into the target repo as committed fixtures, so the extraction half is testable **with MinerU not installed at all** β€” the seam doing its job |
94
  | **S8** | Write the seam rule into the repo docs | Rifqi | ⬜ | Extraction never takes a file path; neither half imports the other's internals; parsing stays swappable behind the artifact. The prototype drifted from this, so the rule needs to outlive our memory of it |
95
 
@@ -105,11 +110,11 @@ detection with type labels. Presented to the execs 13 Aug as the prerequisite fo
105
  | **B1** | MinerU cost justification (written) | Sofhia | ⬜ | Owed for the budget proposal, from the 13 Aug exec review |
106
  | **B2** | MinerU install + run on the sample docs | Sofhia | βœ… | Complete 9-page parse of the BUMA standard delivered 2026-08-19: **118 items** (90 text, 14 table, 11 equation, 3 chart), pages 0–8. Includes the rule-of-thumb grounding content (Other Activity, Controllable/Uncontrollable, Standby), so the fixture exercises all four extraction branches. Supersedes an earlier 5-page run |
107
  | **B3** | Evaluate MinerU quality vs. the current path | Sofhia | ⬜ | Compare on tables, the p.2 legend block, formulas and figures β€” against the PyMuPDF baseline the prototype used, which has real numbers rather than a guess |
108
- | **B4** | Indonesian check on **parsing** | Sofhia | ⬜ | Distinct from E1, which measured GLiNER, not the parser. Confirm MinerU's text and layout quality hold on Bahasa Indonesia |
109
- | **B5** | Section-aware chunking | Sofhia | ⬜ | Split on numbered headings rather than fixed token windows. Acceptance test: the NSR definition in the Open Pit textbook (intro p.92, formula p.93, remark p.94) stays inside one semantic unit. The prototype's chunker solved this for the PyMuPDF path and is worth reading before rebuilding β€” it already handles the breadcrumb problem below |
110
- | **B6** | Emit the agreed artifact | Sofhia | ⬜ | The parser writes our schema, not MinerU's native shape. This is what keeps MinerU swappable. Gated on §3 |
111
  | **B7** | Fallback path behind the same artifact | Sofhia | ⬜ | Tesseract / Azure Document Intelligence emits the *same* artifact, so extraction never knows which parser ran |
112
- | **B8** | Keep the parsing subsystem additive + gated | Sofhia | ⬜ | The existing unstructured path (`src/knowledge/`, Tesseract OCR β†’ chunk β†’ pgvector) stays untouched. MinerU is a new flag-gated path beside it |
113
 
114
  **Findings from the real MinerU output that affect B5/B6:**
115
 
 
75
 
76
  ## 3. The seam β€” shared with Sofhia
77
 
78
+ **Status update 2026-08-19 (Sofhia).** The contract has landed in
79
+ `src/knowledge_parsing/contracts.py` and every open item raised against the draft is closed except
80
+ the two that need a joint decision (S6a formatting, S6b page indexing). Rows below updated
81
+ accordingly; the extraction-side rows (S7, S8) are left as Rifqi has them.
82
+
83
+ **Original note β€” under active discussion.** Sofhia circulated a draft `contracts.py` on 2026-08-19,
84
  derived from real MinerU output rather than designed on paper, and it is adopted as the base. Three
85
  of its choices improve on the prototype: a `kind` discriminator (text/table/chart/equation),
86
  `source_items` index back-references into MinerU's `content_list.json`, and an explicit
 
89
 
90
  | # | Task | Owner | Status | Description |
91
  |---|---|---|---|---|
92
+ | **S1** | Agree the parsed-artifact schema | Sofhia + Rifqi | πŸ”„ | **Adopted as the base and landed** in `src/knowledge_parsing/contracts.py` (pydantic, per S6e). Everything raised against the draft is closed except the two joint decisions in S6 (a: formatting signal, b: page indexing). |
93
+ | **S2** | Add a document-level envelope | Sofhia + Rifqi | βœ… | **Done 2026-08-19.** `ParsedDocument{doc_id, source_path, content_hash, version, n_pages, parser_name, parser_version, chunks}` as proposed, plus three fields worth having: **`parser_backend`** read back from MinerU's `_middle.json` so the artifact records what *actually* ran rather than what was configured; `parser_config` (fingerprint of the settings that affect output); and `raw_output_dir`, pointing at the untouched MinerU output kept in the parse cache. |
94
+ | **S3** | Correct the section-structure claim | Rifqi β†’ Sofhia | βœ… | **Corrected 2026-08-19.** Checked against MinerU's source: `pipeline` and `vlm` run *identical* `text_level` logic and both emit `bbox`, so heading availability is document-dependent, not backend-dependent β€” the original claim came from sampling the handbook, which has no numbered headings. The normalizer now **reads** the hierarchy into a `heading_path` breadcrumb instead of deriving it, with numbering as fallback. Fields stay Optional. |
95
+ | **S4** | Add `source_wording` while the contract is open | Rifqi β†’ Sofhia | βœ… | **Done 2026-08-19.** `source_wording` added to `TermRecord`, and `heading` is carried verbatim β€” so "Physical **of** Availability" survives instead of being silently normalised (R1). |
96
+ | **S5** | Keep the seam off the file path | Sofhia + Rifqi | πŸ”„ | **Satisfied on the parsing side.** `ParsedDocument` never carries a file handle; `source_path` is provenance only, and extraction receives the artifact. Left open for Rifqi to confirm from his side β€” the prototype's `Parser.parse(path)` boundary is the shape this must not reproduce. |
97
+ | **S6** | Minor contract items (deferred to review) | Rifqi β†’ Sofhia | πŸ”„ | **Four of six closed 2026-08-19; two need a joint call.** **(b) page indexing β€” OPEN:** the artifact uses `page_idx`/`page_idxs`, **0-based**, no conversion anywhere, so it always matches the raw MinerU output kept beside it; converting to 1-based is the UI's job, done once at display. Say if you want a 1-based `page_no` added instead, since this number reaches Mas Beta. **(a) formatting signal β€” OPEN, but the premise was wrong:** `text_format` is emitted by *both* backends and is not a formatting signal at all β€” it is the constant `'latex'` on equation blocks. Checked further: MinerU carries **no emphasis information anywhere** (zero `**` in its markdown, no style/font/span keys in `middle.json`). So the signal has no source in MinerU; it would need a separate pass reading the PDF directly (PyMuPDF exposes per-span bold flags). Recommend dropping it from the ranker for now β€” one of six signals, and adding a PDF pass buys nothing measured yet. Your ranker, your call. **(c) chunk size β€” done:** ~1500-token cap, matching calibration Β§4; splits at item boundaries so text stays verbatim. **(d) `chapter` lossy β€” done:** now `chapters: list[str]`, one per page the chunk spans. **(e) pydantic β€” done.** **(f) `TermRecord`** β€” left to you; the declaration in `contracts.py` is a placeholder, not a claim. |
98
  | **S7** | Commit fixture artifacts | Rifqi | ⬜ | Both source PDFs exist (BUMA standard + Open Pit textbook). Promote a parsed artifact of each into the target repo as committed fixtures, so the extraction half is testable **with MinerU not installed at all** β€” the seam doing its job |
99
  | **S8** | Write the seam rule into the repo docs | Rifqi | ⬜ | Extraction never takes a file path; neither half imports the other's internals; parsing stays swappable behind the artifact. The prototype drifted from this, so the rule needs to outlive our memory of it |
100
 
 
110
  | **B1** | MinerU cost justification (written) | Sofhia | ⬜ | Owed for the budget proposal, from the 13 Aug exec review |
111
  | **B2** | MinerU install + run on the sample docs | Sofhia | βœ… | Complete 9-page parse of the BUMA standard delivered 2026-08-19: **118 items** (90 text, 14 table, 11 equation, 3 chart), pages 0–8. Includes the rule-of-thumb grounding content (Other Activity, Controllable/Uncontrollable, Standby), so the fixture exercises all four extraction branches. Supersedes an earlier 5-page run |
112
  | **B3** | Evaluate MinerU quality vs. the current path | Sofhia | ⬜ | Compare on tables, the p.2 legend block, formulas and figures β€” against the PyMuPDF baseline the prototype used, which has real numbers rather than a guess |
113
+ | **B4** | Indonesian check on **parsing** | Sofhia | πŸ”„ | Distinct from E1, which measured GLiNER, not the parser. Partly answered 2026-08-19: MinerU has **no `id` language code** β€” Indonesian documents use `ch`, whose model covers Latin script (`config.periksa()` now rejects a wrong code before models load). `lang` only applies to the `pipeline` backend and only when OCR actually runs, so it is moot under `vlm`. Text/layout **quality** on Bahasa Indonesia still to be judged against B3 |
114
+ | **B5** | Section-aware chunking | Sofhia | πŸ”„ | **Built 2026-08-19** in `normalize.py`. Splits on MinerU's `text_level` hierarchy into a `heading_path` breadcrumb, with numbering as fallback. Three real-document traps handled, two of them from Β§4 of the calibration doc: repeated breadcrumbs no longer reopen a section, **only numbered headings** may open one (so `Keterangan:` keeps a legend with its figure), and a ~1500-token cap guards documents with no detected headings. **Acceptance test still open** β€” needs the Open Pit textbook (S7) to confirm the NSR definition survives pp. 92–94 intact |
115
+ | **B6** | Emit the agreed artifact | Sofhia | βœ… | **Done 2026-08-19** (`5840d201`). `src/knowledge_parsing/` writes `ParsedDocument`, pydantic, not MinerU's native shape. `parse.py` keeps a content-addressed cache of the untouched MinerU output beside it, and `parser_backend` is read back from `_middle.json` so the artifact records what actually ran |
116
  | **B7** | Fallback path behind the same artifact | Sofhia | ⬜ | Tesseract / Azure Document Intelligence emits the *same* artifact, so extraction never knows which parser ran |
117
+ | **B8** | Keep the parsing subsystem additive + gated | Sofhia | βœ… | **Done 2026-08-19.** `src/knowledge/` is untouched; `src/knowledge_parsing/` sits beside it. MinerU is an **optional extra** (`knowledge-parsing`), not a main dependency, and is imported inside `parse.py` at call time β€” verified that importing the package leaves `mineru` and `torch` out of `sys.modules`, so the deployed Space neither builds nor ships them |
118
 
119
  **Findings from the real MinerU output that affect B5/B6:**
120
 
src/knowledge_parsing/README.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # knowledge_parsing
2
+
3
+ Document parsing β€” the first half of the knowledge pipeline. PDF/DOCX in, a
4
+ versioned `ParsedDocument` out.
5
+
6
+ The second half (`src/knowledge_extraction/`) consumes that artifact and never
7
+ the parser itself, which is what keeps MinerU swappable: a Tesseract or Azure
8
+ Document Intelligence path emits the same artifact and extraction never learns
9
+ which one ran.
10
+
11
+ Additive and flag-gated. The existing unstructured path (`src/knowledge/`,
12
+ Tesseract OCR β†’ chunk β†’ pgvector) is untouched and keeps running as-is.
13
+
14
+ ## Files
15
+
16
+ - `contracts.py` β€” **the seam.** `Chunk`, `ParsedDocument`, and the declared
17
+ `Mention` / `TermRecord` handoff shapes. The one file both halves must agree on
18
+ - `config.py` β€” all settings, and the single place the GPU backend flips
19
+ - `parse.py` β€” MinerU wrapper with a content-addressed parse cache
20
+ - `normalize.py` β€” MinerU's flat item list β†’ meaningful chunks
21
+ - `render.py` β€” LaTeX/HTML β†’ readable prose for `Chunk.text`
22
+ - `checks.py` β€” quality warnings, including silent-corruption detection
23
+ - `manifest.py` / `report.py` β€” per-run record and throughput extrapolation
24
+ - `run.py` β€” batch CLI: one folder in, artifacts + manifest out
25
+
26
+ ## Use
27
+
28
+ ```bash
29
+ python -m src.knowledge_parsing.run --input data/knowledge_docs/
30
+ python -m src.knowledge_parsing.report --scale 6800
31
+ ```
32
+
33
+ MinerU is an optional extra, not a main dependency β€” the agent service never
34
+ parses documents at request time, so the deployed Space does not ship it:
35
+
36
+ ```bash
37
+ uv sync --extra knowledge-parsing
38
+ ```
39
+
40
+ Importing this package does not import MinerU or torch; only `parse.py` does, at
41
+ call time.
42
+
43
+ ## Three things that are easy to get wrong
44
+
45
+ **`Chunk.text` must stay verbatim.** Extraction's guardrail locates quoted spans
46
+ literally inside it. Reflowing or normalising the text makes the lookup fail and
47
+ the field goes silently null β€” which presents as a bad model, not as a parser
48
+ bug.
49
+
50
+ **`page_idx` is 0-based**, exactly as MinerU reports it, with no conversion
51
+ anywhere in the pipeline. Converting to 1-based is the UI's job, done once at
52
+ display time, so the artifact always matches the raw MinerU output kept beside
53
+ it in the cache.
54
+
55
+ **Markup never goes in `text`.** The term filter is an NER model reading prose;
56
+ MinerU writes formulas character-spaced (`P u r c h a s i n g ~ c o s t s`) and
57
+ tables as HTML, and neither yields a single mention. Measured on one document and
58
+ gold set, changing only the parse: raw markup scored recall 0.7561 against 0.8537
59
+ for plain text; rendering it back recovered 0.8293. The markup is preserved in
60
+ `Chunk.latex` and `Chunk.table_html`, because the formula branch needs that form.
61
+
62
+ ## Output layout
63
+
64
+ ```
65
+ data/knowledge_cache/parse/<hash>-<config>-<version>/ untouched MinerU output
66
+ data/knowledge_runs/<run_id>/
67
+ β”œβ”€β”€ manifest.json backend, versions, timings, quality warnings
68
+ β”œβ”€β”€ failures.jsonl documents that failed, with traces
69
+ └── <doc>/chunks.json the artifact
70
+ ```
71
+
72
+ Both are gitignored β€” they are data, not code.
73
+
74
+ See `KNOWLEDGE_PIPELINE_TODO.md` (root) for the task breakdown and the seam
75
+ discussion, and `KNOWLEDGE_PIPELINE_CALIBRATION.md` Β§4 for the chunking
76
+ constants this module follows.