Closes #17384. ## Summary Drops a dead `re.I` flag from two outlier delimiter-parsing sites and adds regression tests so the inconsistency can't creep back. ## What's wrong Two of the six delimiter-parsing implementations pass `re.I` to `re.finditer`: - `rag/nlp/__init__.py::get_delimiters` (line 1633) - `deepdoc/parser/txt_parser.py::parser_txt` (line 51) The other four implementations correctly omit `re.I`: - `rag/nlp/__init__.py::naive_merge` custom-delimiter path (line 1195) - `rag/nlp/__init__.py::naive_merge_with_images` custom-delimiter path (line 1269) - `rag/nlp/__init__.py::_build_cks` (line 1389) - `rag/flow/chunker/token_chunker.py` (line 73) ## Why this matters (and why it doesn't break anything) The flag is **dead code** today. Verified empirically with a Python REPL: ```python >>> import re >>> for m in re.finditer(r"`([^`]+)`", "`end`", re.I): ... print(repr(m.group(1))) 'end' # plain string, no flag attached >>> re.split("(a)", "Class A is a Sample") ['Cl', 'a', '', 's', ' A i', 's', ' a Sample'] # Case-sensitive: only lowercase 'a' splits. Uppercase 'A' is preserved. ``` `re.I` does not propagate from `re.finditer` to `m.group(1)` or to downstream `re.split` / `re.match` calls (which all omit `re.I`). So the actual splitting behavior has always been case-sensitive — removing the flag is a **defensive cleanup**, not a behavioral fix. So why bother? 1. **Consistency** — the two sites were the only outliers in a six-way implementation cluster. The three sibling sites in `rag/nlp/__init__.py` already omit `re.I`, which strongly suggests the flag was accidental. 2. **Future-proofing** — a refactor could easily propagate the flag to a downstream `re.split` call where it *would* change behavior. The tests added here pin the case-sensitive semantics so that regression fails loudly. 3. **Reader clarity** — the flag is misleading. Anyone reading `re.finditer(..., re.I)` reasonably assumes case-insensitive matching, then has to trace all downstream calls to discover it's a no-op. ## Changes - `rag/nlp/__init__.py` — drop `re.I` from `get_delimiters` (line 1633). - `deepdoc/parser/txt_parser.py` — drop `re.I` from `parser_txt` (line 51). - `test/unit_test/rag/test_delimiter_case_sensitive.py` — new test file with: - 4 behavioral tests on `get_delimiters` (pattern output + `re.split` round-trip). - 3 end-to-end tests through `naive_merge` (bare-char + backtick-wrapped, both cases). - 2 parametrized static checks that `re.I` / `re.IGNORECASE` is not present at either of the two `re.finditer` sites. ## Testing ``` $ pytest test/unit_test/rag/test_delimiter_case_sensitive.py -v ============================= 9 passed in 0.19s ============================== ``` All tests pass on the patched code. Before the patch, the 2 static checks fail with a clear assertion message (the 7 behavioral tests pass either way, confirming `re.I` was dead code). ## Related - #17384 — the issue this PR closes. Note the issue's reproduction code (`re.split(..., flags=re.I)`) doesn't actually match what the production code does — the production `re.split` calls all omit `re.I`, which is why current behavior is already case-sensitive. The fix here is still valuable as a defensive cleanup + test coverage, but it's not a behavioral fix per se. - #17383 — broader parser consolidation (six implementations → one). The fix here is independent and small enough to land first. - #17385 — sibling UX PR (tooltip + live preview). Files are disjoint (`web/src/**` vs `rag/nlp/**` + `deepdoc/parser/**`), so no interaction. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
English | 简体中文
DeepDoc
1. Introduction
With a bunch of documents from various domains with various formats and along with diverse retrieval requirements, an accurate analysis becomes a very challenge task. DeepDoc is born for that purpose. There are 2 parts in DeepDoc so far: vision and parser. You can run the flowing test programs if you're interested in our results of OCR, layout recognition and TSR.
python deepdoc/vision/t_ocr.py -h
usage: t_ocr.py [-h] --inputs INPUTS [--output_dir OUTPUT_DIR]
options:
-h, --help show this help message and exit
--inputs INPUTS Directory where to store images or PDFs, or a file path to a single image or PDF
--output_dir OUTPUT_DIR
Directory where to store the output images. Default: './ocr_outputs'
python deepdoc/vision/t_recognizer.py -h
usage: t_recognizer.py [-h] --inputs INPUTS [--output_dir OUTPUT_DIR] [--threshold THRESHOLD] [--mode {layout,tsr}]
options:
-h, --help show this help message and exit
--inputs INPUTS Directory where to store images or PDFs, or a file path to a single image or PDF
--output_dir OUTPUT_DIR
Directory where to store the output images. Default: './layouts_outputs'
--threshold THRESHOLD
A threshold to filter out detections. Default: 0.5
--mode {layout,tsr} Task mode: layout recognition or table structure recognition
Our models are served on HuggingFace. If you have trouble downloading HuggingFace models, this might help!!
export HF_ENDPOINT=https://hf-mirror.com
2. Vision
We use vision information to resolve problems as human being.
-
OCR. Since a lot of documents presented as images or at least be able to transform to image, OCR is a very essential and fundamental or even universal solution for text extraction.
python deepdoc/vision/t_ocr.py --inputs=path_to_images_or_pdfs --output_dir=path_to_store_resultThe inputs could be directory to images or PDF, or an image or PDF. You can look into the folder 'path_to_store_result' where has images which demonstrate the positions of results, txt files which contain the OCR text.
-
Layout recognition. Documents from different domain may have various layouts, like, newspaper, magazine, book and résumé are distinct in terms of layout. Only when machine have an accurate layout analysis, it can decide if these text parts are successive or not, or this part needs Table Structure Recognition(TSR) to process, or this part is a figure and described with this caption. We have 10 basic layout components which covers most cases:
- Text
- Title
- Figure
- Figure caption
- Table
- Table caption
- Header
- Footer
- Reference
- Equation
Have a try on the following command to see the layout detection results.
python deepdoc/vision/t_recognizer.py --inputs=path_to_images_or_pdfs --threshold=0.2 --mode=layout --output_dir=path_to_store_resultThe inputs could be directory to images or PDF, or an image or PDF. You can look into the folder 'path_to_store_result' where has images which demonstrate the detection results as following:
-
Table Structure Recognition(TSR). Data table is a frequently used structure to present data including numbers or text. And the structure of a table might be very complex, like hierarchy headers, spanning cells and projected row headers. Along with TSR, we also reassemble the content into sentences which could be well comprehended by LLM. We have five labels for TSR task:
- Column
- Row
- Column header
- Projected row header
- Spanning cell
Have a try on the following command to see the layout detection results.
python deepdoc/vision/t_recognizer.py --inputs=path_to_images_or_pdfs --threshold=0.2 --mode=tsr --output_dir=path_to_store_resultThe inputs could be directory to images or PDF, or an image or PDF. You can look into the folder 'path_to_store_result' where has both images and html pages which demonstrate the detection results as following:
-
Table Auto-Rotation. For scanned PDFs where tables may be incorrectly oriented (rotated 90°, 180°, or 270°), the PDF parser automatically detects the best rotation angle using OCR confidence scores before performing table structure recognition. This significantly improves OCR accuracy and table structure detection for rotated tables.
The feature evaluates 4 rotation angles (0°, 90°, 180°, 270°) and selects the one with highest OCR confidence. After determining the best orientation, it re-performs OCR on the correctly rotated table image.
This feature is enabled by default. You can control it via environment variable:
# Disable table auto-rotation export TABLE_AUTO_ROTATE=false # Enable table auto-rotation (default) export TABLE_AUTO_ROTATE=trueOr via API parameter:
from deepdoc.parser import PdfParser parser = PdfParser() # Disable auto-rotation for this call boxes, tables = parser(pdf_path, auto_rotate_tables=False)
3. Parser
Four kinds of document formats as PDF, DOCX, EXCEL and PPT have their corresponding parser. The most complex one is PDF parser since PDF's flexibility. The output of PDF parser includes:
- Text chunks with their own positions in PDF(page number and rectangular positions).
- Tables with cropped image from the PDF, and contents which has already translated into natural language sentences.
- Figures with caption and text in the figures.
Résumé
The résumé is a very complicated kind of document. A résumé which is composed of unstructured text with various layouts could be resolved into structured data composed of nearly a hundred of fields. We haven't opened the parser yet, as we open the processing method after parsing procedure.