visual-parser 2.2.1__tar.gz → 2.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {visual_parser-2.2.1/visual_parser.egg-info → visual_parser-2.2.3}/PKG-INFO +15 -9
- {visual_parser-2.2.1 → visual_parser-2.2.3}/README.md +12 -6
- {visual_parser-2.2.1 → visual_parser-2.2.3}/pyproject.toml +3 -3
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_config.py +14 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_image_describer.py +5 -5
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_prompts.py +1 -1
- visual_parser-2.2.3/visual_parser/__init__.py +22 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/cli.py +15 -5
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/cli_main.py +16 -6
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/config.py +9 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/image_describer.py +5 -5
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/kb_redo.py +8 -3
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/pipeline.py +1 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/text_extractor.py +5 -1
- {visual_parser-2.2.1 → visual_parser-2.2.3/visual_parser.egg-info}/PKG-INFO +15 -9
- visual_parser-2.2.1/visual_parser/__init__.py +0 -20
- {visual_parser-2.2.1 → visual_parser-2.2.3}/LICENSE +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/setup.cfg +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_cli_parity.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_figure_describer.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_image_tracker.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_integration.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_jsonl_writer.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_kb_redo.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_metadata_extractor.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_model_catalog.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_ollama_local.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_pdf_tracker.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_pipeline.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/tests/test_vision_llm.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/__main__.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/figure_describer.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/image_tracker.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/jsonl_writer.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/metadata_extractor.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/model_catalog.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/nougat_engine.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/ollama_local.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/openai_gateway.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/pdf_tracker.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/prompts.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser/vision_llm.py +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser.egg-info/SOURCES.txt +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser.egg-info/dependency_links.txt +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser.egg-info/entry_points.txt +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser.egg-info/requires.txt +0 -0
- {visual_parser-2.2.1 → visual_parser-2.2.3}/visual_parser.egg-info/top_level.txt +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visual-parser
|
|
3
|
-
Version: 2.2.
|
|
4
|
-
Summary: Standalone Visual-RAG PDF Parser - text extraction and Vision-LLM figure descriptions to JSONL
|
|
3
|
+
Version: 2.2.3
|
|
4
|
+
Summary: Standalone Visual-RAG PDF & Image Parser - text extraction and Vision-LLM figure/image descriptions to JSONL
|
|
5
5
|
Author: Zavier N. Ndum
|
|
6
6
|
Author-email: zavier.ndum@tamu.edu
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
Project-URL: Homepage, https://github.com/SmartLabNuclear/RADIANT_LLM
|
|
9
9
|
Project-URL: Repository, https://github.com/SmartLabNuclear/RADIANT_LLM
|
|
10
10
|
Project-URL: Docker Hub, https://hub.docker.com/r/zev94/radiant-llm
|
|
11
|
-
Keywords: pdf,rag,nougat,vision-llm,ocr,document-parsing,jsonl,knowledge-base
|
|
11
|
+
Keywords: pdf,image,rag,nougat,vision-llm,ocr,document-parsing,jsonl,knowledge-base
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -43,16 +43,16 @@ Requires-Dist: ruff; extra == "dev"
|
|
|
43
43
|
Requires-Dist: mypy; extra == "dev"
|
|
44
44
|
Dynamic: license-file
|
|
45
45
|
|
|
46
|
-
# visual-parser (Standalone Visual-RAG PDF Ingestion)
|
|
46
|
+
# visual-parser (Standalone Visual-RAG PDF & Image Ingestion)
|
|
47
47
|
|
|
48
48
|

|
|
49
49
|

|
|
50
50
|

|
|
51
51
|

|
|
52
52
|
|
|
53
|
-
`visual-parser` converts PDFs into a multi-modal JSONL knowledge base (text chunks, figure descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-ingestion tool, independent of any chatbot.
|
|
53
|
+
`visual-parser` converts PDFs (and standalone images alongside them) into a multi-modal JSONL knowledge base (text chunks, figure descriptions, standalone image descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-and-image ingestion tool, independent of any chatbot.
|
|
54
54
|
|
|
55
|
-
1) Run `visual-parser` on curated PDFs to generate JSONL KB files.
|
|
55
|
+
1) Run `visual-parser` on a directory of curated PDFs and/or standalone images to generate JSONL KB files.
|
|
56
56
|
2) Point any downstream RAG system at the generated KB — RADIANT-LLM, AutoSAM, and AutoFLUKA all consume the identical JSONL/registry format, so the same KB works with any of them without re-parsing.
|
|
57
57
|
|
|
58
58
|
## Table of Contents
|
|
@@ -72,10 +72,13 @@ Dynamic: license-file
|
|
|
72
72
|
|
|
73
73
|
By default, the pipeline writes:
|
|
74
74
|
- `01_chunks_kb.jsonl`: chunked text extracted from PDFs (Nougat by default).
|
|
75
|
-
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM).
|
|
75
|
+
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM), one row per figure found in a PDF.
|
|
76
76
|
- `03_metadata_kb.jsonl`: document metadata rows (title/author/etc.).
|
|
77
|
+
- `image_descriptions_kb.jsonl`: one holistic description per **standalone image** file found alongside your PDFs in `--input-dir` (not a PDF's own figures) — auto-detected by extension, no separate flag needed; disable with `--skip-images`.
|
|
77
78
|
|
|
78
|
-
Alongside the KB, the pipeline also writes
|
|
79
|
+
Alongside the KB, the pipeline also writes bookkeeping files: `04_processed_pdfs.txt` / `parsed_images.txt` (track which PDFs/images have already been processed, so re-runs skip them unless `--rebuild` or `--redo NAME`) and `05_pipeline.log` (run log at the verbosity set by `--log-level`, default `ERROR`).
|
|
80
|
+
|
|
81
|
+
> **Breaking change in 2.2.3:** the standalone-image KB file was renamed from `image_descriptions.jsonl` to `image_descriptions_kb.jsonl` (matching the `NN_name_kb.jsonl` convention used by the other three KB files). Upgrading from an older version does **not** migrate existing data automatically — the resume check only looks at the new filename, so already-described images in an old `image_descriptions.jsonl` would otherwise appear "new" again and get re-described. If you have existing data, rename the file yourself before re-running: `image_descriptions.jsonl` → `image_descriptions_kb.jsonl`.
|
|
79
82
|
|
|
80
83
|
## API Keys (.env)
|
|
81
84
|
|
|
@@ -262,6 +265,7 @@ Text extraction:
|
|
|
262
265
|
- `--nougat-model facebook/nougat-small`
|
|
263
266
|
- `--chunk-size 500`
|
|
264
267
|
- `--chunk-overlap 100`
|
|
268
|
+
- `--nougat-crop-margin` (off by default — lets Nougat autocrop whitespace margins instead of this package's long-standing uncropped behavior; autocropping can silently crop into real content on unusual page layouts)
|
|
265
269
|
|
|
266
270
|
Vision LLM:
|
|
267
271
|
- `--vision-provider gpt|gemini|ollama` (default: `gpt`) — see [Local Ollama vision provider](#local-ollama-vision-provider) for the `ollama` option
|
|
@@ -273,8 +277,10 @@ Vision LLM:
|
|
|
273
277
|
|
|
274
278
|
Performance / misc:
|
|
275
279
|
- `--max-workers 4`
|
|
276
|
-
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`)
|
|
280
|
+
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`/`parsed_images.txt`)
|
|
281
|
+
- `--redo NAME` (surgically clear one PDF/image's rows from every KB file + both registries, so it's reprocessed fresh on the next run — a targeted alternative to `--rebuild`)
|
|
277
282
|
- `--skip-text` (skip text extraction and resume only the vision steps; use after an interrupted run)
|
|
283
|
+
- `--skip-images` (don't process standalone images in `--input-dir`, PDFs only)
|
|
278
284
|
- `--list-models` — print the live, currently-available vision models for whichever provider(s) you have a key configured for, then exit (no PDF processing). Reflects Portkey's catalog too when that's active.
|
|
279
285
|
- `--version` / `-V`
|
|
280
286
|
- `--log-level DEBUG|INFO|WARNING|ERROR` (default: `ERROR`)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# visual-parser (Standalone Visual-RAG PDF Ingestion)
|
|
1
|
+
# visual-parser (Standalone Visual-RAG PDF & Image Ingestion)
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|

|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
`visual-parser` converts PDFs into a multi-modal JSONL knowledge base (text chunks, figure descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-ingestion tool, independent of any chatbot.
|
|
8
|
+
`visual-parser` converts PDFs (and standalone images alongside them) into a multi-modal JSONL knowledge base (text chunks, figure descriptions, standalone image descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-and-image ingestion tool, independent of any chatbot.
|
|
9
9
|
|
|
10
|
-
1) Run `visual-parser` on curated PDFs to generate JSONL KB files.
|
|
10
|
+
1) Run `visual-parser` on a directory of curated PDFs and/or standalone images to generate JSONL KB files.
|
|
11
11
|
2) Point any downstream RAG system at the generated KB — RADIANT-LLM, AutoSAM, and AutoFLUKA all consume the identical JSONL/registry format, so the same KB works with any of them without re-parsing.
|
|
12
12
|
|
|
13
13
|
## Table of Contents
|
|
@@ -27,10 +27,13 @@
|
|
|
27
27
|
|
|
28
28
|
By default, the pipeline writes:
|
|
29
29
|
- `01_chunks_kb.jsonl`: chunked text extracted from PDFs (Nougat by default).
|
|
30
|
-
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM).
|
|
30
|
+
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM), one row per figure found in a PDF.
|
|
31
31
|
- `03_metadata_kb.jsonl`: document metadata rows (title/author/etc.).
|
|
32
|
+
- `image_descriptions_kb.jsonl`: one holistic description per **standalone image** file found alongside your PDFs in `--input-dir` (not a PDF's own figures) — auto-detected by extension, no separate flag needed; disable with `--skip-images`.
|
|
32
33
|
|
|
33
|
-
Alongside the KB, the pipeline also writes
|
|
34
|
+
Alongside the KB, the pipeline also writes bookkeeping files: `04_processed_pdfs.txt` / `parsed_images.txt` (track which PDFs/images have already been processed, so re-runs skip them unless `--rebuild` or `--redo NAME`) and `05_pipeline.log` (run log at the verbosity set by `--log-level`, default `ERROR`).
|
|
35
|
+
|
|
36
|
+
> **Breaking change in 2.2.3:** the standalone-image KB file was renamed from `image_descriptions.jsonl` to `image_descriptions_kb.jsonl` (matching the `NN_name_kb.jsonl` convention used by the other three KB files). Upgrading from an older version does **not** migrate existing data automatically — the resume check only looks at the new filename, so already-described images in an old `image_descriptions.jsonl` would otherwise appear "new" again and get re-described. If you have existing data, rename the file yourself before re-running: `image_descriptions.jsonl` → `image_descriptions_kb.jsonl`.
|
|
34
37
|
|
|
35
38
|
## API Keys (.env)
|
|
36
39
|
|
|
@@ -217,6 +220,7 @@ Text extraction:
|
|
|
217
220
|
- `--nougat-model facebook/nougat-small`
|
|
218
221
|
- `--chunk-size 500`
|
|
219
222
|
- `--chunk-overlap 100`
|
|
223
|
+
- `--nougat-crop-margin` (off by default — lets Nougat autocrop whitespace margins instead of this package's long-standing uncropped behavior; autocropping can silently crop into real content on unusual page layouts)
|
|
220
224
|
|
|
221
225
|
Vision LLM:
|
|
222
226
|
- `--vision-provider gpt|gemini|ollama` (default: `gpt`) — see [Local Ollama vision provider](#local-ollama-vision-provider) for the `ollama` option
|
|
@@ -228,8 +232,10 @@ Vision LLM:
|
|
|
228
232
|
|
|
229
233
|
Performance / misc:
|
|
230
234
|
- `--max-workers 4`
|
|
231
|
-
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`)
|
|
235
|
+
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`/`parsed_images.txt`)
|
|
236
|
+
- `--redo NAME` (surgically clear one PDF/image's rows from every KB file + both registries, so it's reprocessed fresh on the next run — a targeted alternative to `--rebuild`)
|
|
232
237
|
- `--skip-text` (skip text extraction and resume only the vision steps; use after an interrupted run)
|
|
238
|
+
- `--skip-images` (don't process standalone images in `--input-dir`, PDFs only)
|
|
233
239
|
- `--list-models` — print the live, currently-available vision models for whichever provider(s) you have a key configured for, then exit (no PDF processing). Reflects Portkey's catalog too when that's active.
|
|
234
240
|
- `--version` / `-V`
|
|
235
241
|
- `--log-level DEBUG|INFO|WARNING|ERROR` (default: `ERROR`)
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "visual-parser"
|
|
7
|
-
version = "2.2.
|
|
8
|
-
description = "Standalone Visual-RAG PDF Parser - text extraction and Vision-LLM figure descriptions to JSONL"
|
|
7
|
+
version = "2.2.3"
|
|
8
|
+
description = "Standalone Visual-RAG PDF & Image Parser - text extraction and Vision-LLM figure/image descriptions to JSONL"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "Apache-2.0"
|
|
@@ -17,7 +17,7 @@ authors = [
|
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
keywords = [
|
|
20
|
-
"pdf", "rag", "nougat", "vision-llm", "ocr",
|
|
20
|
+
"pdf", "image", "rag", "nougat", "vision-llm", "ocr",
|
|
21
21
|
"document-parsing", "jsonl", "knowledge-base",
|
|
22
22
|
]
|
|
23
23
|
|
|
@@ -59,6 +59,20 @@ def test_from_env_reads_redo_names_comma_separated(monkeypatch):
|
|
|
59
59
|
assert ParserConfig.from_env().redo_names == ["doc1.pdf", "diagram2.png", "doc3.pdf"]
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def test_nougat_crop_margin_defaults_to_false():
|
|
63
|
+
assert ParserConfig(input_dir=".").nougat_crop_margin is False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_from_env_nougat_crop_margin_defaults_to_false(monkeypatch):
|
|
67
|
+
monkeypatch.delenv("VISUAL_PARSER_NOUGAT_CROP_MARGIN", raising=False)
|
|
68
|
+
assert ParserConfig.from_env().nougat_crop_margin is False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_from_env_reads_nougat_crop_margin(monkeypatch):
|
|
72
|
+
monkeypatch.setenv("VISUAL_PARSER_NOUGAT_CROP_MARGIN", "true")
|
|
73
|
+
assert ParserConfig.from_env().nougat_crop_margin is True
|
|
74
|
+
|
|
75
|
+
|
|
62
76
|
def test_vision_context_pages_defaults_to_zero(monkeypatch):
|
|
63
77
|
monkeypatch.delenv("VISUAL_PARSER_VISION_CONTEXT_PAGES", raising=False)
|
|
64
78
|
assert ParserConfig.from_env().vision_context_pages == 0
|
|
@@ -145,7 +145,7 @@ def test_describe_images_skips_non_string_description(tmp_path, monkeypatch):
|
|
|
145
145
|
)
|
|
146
146
|
|
|
147
147
|
assert written == []
|
|
148
|
-
assert _read_jsonl(tmp_path /
|
|
148
|
+
assert _read_jsonl(tmp_path / image_describer.IMAGE_DESCRIPTIONS_FILE) == []
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
def test_describe_images_writes_rows_for_each_image(tmp_path, monkeypatch):
|
|
@@ -166,7 +166,7 @@ def test_describe_images_writes_rows_for_each_image(tmp_path, monkeypatch):
|
|
|
166
166
|
)
|
|
167
167
|
|
|
168
168
|
assert set(written) == {"a.png", "b.png"}
|
|
169
|
-
rows = _read_jsonl(tmp_path /
|
|
169
|
+
rows = _read_jsonl(tmp_path / image_describer.IMAGE_DESCRIPTIONS_FILE)
|
|
170
170
|
assert {r["source"] for r in rows} == {"a.png", "b.png"}
|
|
171
171
|
for row in rows:
|
|
172
172
|
assert row["title"] == "Something"
|
|
@@ -195,7 +195,7 @@ def test_failed_image_is_not_marked_written(tmp_path, monkeypatch):
|
|
|
195
195
|
)
|
|
196
196
|
|
|
197
197
|
assert written == ["good.png"]
|
|
198
|
-
rows = _read_jsonl(tmp_path /
|
|
198
|
+
rows = _read_jsonl(tmp_path / image_describer.IMAGE_DESCRIPTIONS_FILE)
|
|
199
199
|
assert {r["source"] for r in rows} == {"good.png"}
|
|
200
200
|
|
|
201
201
|
|
|
@@ -223,7 +223,7 @@ def test_vision_call_exception_is_not_marked_written(tmp_path, monkeypatch):
|
|
|
223
223
|
)
|
|
224
224
|
|
|
225
225
|
assert len(written) == 1
|
|
226
|
-
rows = _read_jsonl(tmp_path /
|
|
226
|
+
rows = _read_jsonl(tmp_path / image_describer.IMAGE_DESCRIPTIONS_FILE)
|
|
227
227
|
assert len(rows) == 1
|
|
228
228
|
|
|
229
229
|
|
|
@@ -231,7 +231,7 @@ def test_resume_skips_already_done_images(tmp_path, monkeypatch):
|
|
|
231
231
|
img1 = _make_png(tmp_path / "a.png")
|
|
232
232
|
img2 = _make_png(tmp_path / "b.png")
|
|
233
233
|
|
|
234
|
-
descriptions_path = tmp_path /
|
|
234
|
+
descriptions_path = tmp_path / image_describer.IMAGE_DESCRIPTIONS_FILE
|
|
235
235
|
descriptions_path.write_text(
|
|
236
236
|
json.dumps({"source": "a.png", "image_id": "x", "title": "Old", "description": "already done"}) + "\n",
|
|
237
237
|
encoding="utf-8",
|
|
@@ -14,7 +14,7 @@ def test_figure_prompt_instructs_verbatim_table_transcription():
|
|
|
14
14
|
def test_figure_prompt_and_image_prompt_use_matching_discrepancy_terminology():
|
|
15
15
|
"""Regression test: the two prompts' final heading must use the same
|
|
16
16
|
label and boilerplate 'nothing found' phrase, so the two JSONL outputs
|
|
17
|
-
(02_visuals_kb.jsonl /
|
|
17
|
+
(02_visuals_kb.jsonl / image_descriptions_kb.jsonl) don't drift into
|
|
18
18
|
inconsistent wording for the same underlying concept."""
|
|
19
19
|
assert "**Discrepancy Check**" in FIGURE_PROMPT
|
|
20
20
|
assert "No discrepancies detected" in FIGURE_PROMPT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
visual_parser — Standalone Visual-RAG PDF & Image Parser
|
|
3
|
+
=========================================================
|
|
4
|
+
Detects new PDFs (and standalone images alongside them) in a user-supplied
|
|
5
|
+
directory, extracts text (via Nougat or lightweight PyMuPDF/PyPDFLoader),
|
|
6
|
+
describes every figure/chart/schematic and every standalone image using a
|
|
7
|
+
Vision LLM (OpenAI GPT, Google Gemini, or a local model via Ollama), and
|
|
8
|
+
writes JSONL knowledge bases ready for any downstream RAG system:
|
|
9
|
+
|
|
10
|
+
01_chunks_kb.jsonl – text chunks with stable IDs
|
|
11
|
+
02_visuals_kb.jsonl – per-figure visual descriptions
|
|
12
|
+
03_metadata_kb.jsonl – document-level metadata (title, authors, DOI …)
|
|
13
|
+
image_descriptions_kb.jsonl – one holistic description per standalone image
|
|
14
|
+
|
|
15
|
+
No chatbot, no vector store, no retrieval – just a robust parser.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from visual_parser.config import ParserConfig
|
|
19
|
+
from visual_parser.pipeline import run_pipeline
|
|
20
|
+
|
|
21
|
+
__all__ = ["ParserConfig", "run_pipeline"]
|
|
22
|
+
__version__ = "2.2.3"
|
|
@@ -44,13 +44,13 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
44
44
|
p = argparse.ArgumentParser(
|
|
45
45
|
prog="visual-parser",
|
|
46
46
|
description=(
|
|
47
|
-
"Visual-RAG PDF Parser — detects new PDFs and standalone images, "
|
|
47
|
+
"Visual-RAG PDF & Image Parser — detects new PDFs and standalone images, "
|
|
48
48
|
"extracts text and figure/image descriptions, and writes JSONL "
|
|
49
49
|
"knowledge bases:\n"
|
|
50
|
-
" 01_chunks_kb.jsonl
|
|
51
|
-
" 02_visuals_kb.jsonl
|
|
52
|
-
" 03_metadata_kb.jsonl
|
|
53
|
-
"
|
|
50
|
+
" 01_chunks_kb.jsonl text chunks\n"
|
|
51
|
+
" 02_visuals_kb.jsonl PDF figure descriptions\n"
|
|
52
|
+
" 03_metadata_kb.jsonl document metadata\n"
|
|
53
|
+
" image_descriptions_kb.jsonl standalone image descriptions"
|
|
54
54
|
),
|
|
55
55
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
56
56
|
epilog=USAGE_EXAMPLES,
|
|
@@ -111,6 +111,15 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
111
111
|
metavar="N",
|
|
112
112
|
help="Overlap characters between adjacent chunks (default: 100).",
|
|
113
113
|
)
|
|
114
|
+
text_group.add_argument(
|
|
115
|
+
"--nougat-crop-margin",
|
|
116
|
+
action="store_true",
|
|
117
|
+
help=(
|
|
118
|
+
"Let Nougat autocrop whitespace margins (its own default). "
|
|
119
|
+
"Off by default -- autocropping can silently crop into real "
|
|
120
|
+
"content on unusual page layouts."
|
|
121
|
+
),
|
|
122
|
+
)
|
|
114
123
|
|
|
115
124
|
# ---- Vision LLM ---------------------------------------------------------
|
|
116
125
|
vision_group = p.add_argument_group("Vision LLM (figure descriptions & metadata)")
|
|
@@ -366,6 +375,7 @@ def main(argv=None) -> int:
|
|
|
366
375
|
nougat_model = args.nougat_model,
|
|
367
376
|
chunk_size = args.chunk_size,
|
|
368
377
|
chunk_overlap = args.chunk_overlap,
|
|
378
|
+
nougat_crop_margin = args.nougat_crop_margin,
|
|
369
379
|
vision_provider = args.vision_provider,
|
|
370
380
|
gpt_vision_model = args.vision_model if args.vision_provider == "gpt" else "gpt-5.4",
|
|
371
381
|
gemini_vision_model = args.vision_model if args.vision_provider == "gemini" else "gemini-3.8-flash",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
cli_main.py - ASCII-safe CLI entry point for the Visual-RAG PDF Parser.
|
|
2
|
+
cli_main.py - ASCII-safe CLI entry point for the Visual-RAG PDF & Image Parser.
|
|
3
3
|
|
|
4
4
|
This module exists to keep Windows console help output stable for the
|
|
5
5
|
installed ``visual-parser`` command and ``python -m visual_parser``.
|
|
@@ -42,13 +42,13 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
42
42
|
parser = argparse.ArgumentParser(
|
|
43
43
|
prog="visual-parser",
|
|
44
44
|
description=(
|
|
45
|
-
"Visual-RAG PDF Parser - detects new PDFs and standalone images, "
|
|
45
|
+
"Visual-RAG PDF & Image Parser - detects new PDFs and standalone images, "
|
|
46
46
|
"extracts text and figure/image descriptions, and writes JSONL "
|
|
47
47
|
"knowledge bases:\n"
|
|
48
|
-
" 01_chunks_kb.jsonl
|
|
49
|
-
" 02_visuals_kb.jsonl
|
|
50
|
-
" 03_metadata_kb.jsonl
|
|
51
|
-
"
|
|
48
|
+
" 01_chunks_kb.jsonl text chunks\n"
|
|
49
|
+
" 02_visuals_kb.jsonl PDF figure descriptions\n"
|
|
50
|
+
" 03_metadata_kb.jsonl document metadata\n"
|
|
51
|
+
" image_descriptions_kb.jsonl standalone image descriptions"
|
|
52
52
|
),
|
|
53
53
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
54
54
|
epilog=USAGE_EXAMPLES,
|
|
@@ -109,6 +109,15 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
109
109
|
metavar="N",
|
|
110
110
|
help="Overlap characters between adjacent chunks (default: 100).",
|
|
111
111
|
)
|
|
112
|
+
text_group.add_argument(
|
|
113
|
+
"--nougat-crop-margin",
|
|
114
|
+
action="store_true",
|
|
115
|
+
help=(
|
|
116
|
+
"Let Nougat autocrop whitespace margins (its own default). "
|
|
117
|
+
"Off by default -- autocropping can silently crop into real "
|
|
118
|
+
"content on unusual page layouts."
|
|
119
|
+
),
|
|
120
|
+
)
|
|
112
121
|
|
|
113
122
|
vision_group = parser.add_argument_group("Vision LLM (figure descriptions & metadata)")
|
|
114
123
|
vision_group.add_argument(
|
|
@@ -282,6 +291,7 @@ def main(argv=None) -> int:
|
|
|
282
291
|
nougat_model=args.nougat_model,
|
|
283
292
|
chunk_size=args.chunk_size,
|
|
284
293
|
chunk_overlap=args.chunk_overlap,
|
|
294
|
+
nougat_crop_margin=args.nougat_crop_margin,
|
|
285
295
|
vision_provider=args.vision_provider,
|
|
286
296
|
gpt_vision_model=args.vision_model if args.vision_provider == "gpt" else "gpt-5.4",
|
|
287
297
|
gemini_vision_model=(
|
|
@@ -79,6 +79,14 @@ class ParserConfig:
|
|
|
79
79
|
nougat_model: str = "facebook/nougat-small"
|
|
80
80
|
"""HuggingFace model identifier for Nougat."""
|
|
81
81
|
|
|
82
|
+
nougat_crop_margin: bool = False
|
|
83
|
+
"""
|
|
84
|
+
Passed through to Nougat's image processor as do_crop_margin. False
|
|
85
|
+
(default) matches this package's long-standing behavior -- autocropping
|
|
86
|
+
whitespace margins can silently crop into real content on unusual page
|
|
87
|
+
layouts. Set True to opt into Nougat's own autocrop behavior instead.
|
|
88
|
+
"""
|
|
89
|
+
|
|
82
90
|
chunk_size: int = 500
|
|
83
91
|
"""Target character count per text chunk."""
|
|
84
92
|
|
|
@@ -186,6 +194,7 @@ class ParserConfig:
|
|
|
186
194
|
output_dir = os.getenv("VISUAL_PARSER_OUTPUT_DIR", ""),
|
|
187
195
|
text_mode = os.getenv("VISUAL_PARSER_TEXT_MODE", "nougat"), # type: ignore[arg-type]
|
|
188
196
|
nougat_model = os.getenv("VISUAL_PARSER_NOUGAT_MODEL", "facebook/nougat-small"),
|
|
197
|
+
nougat_crop_margin = os.getenv("VISUAL_PARSER_NOUGAT_CROP_MARGIN", "false").lower() == "true",
|
|
189
198
|
chunk_size = int(os.getenv("VISUAL_PARSER_CHUNK_SIZE", "500")),
|
|
190
199
|
chunk_overlap = int(os.getenv("VISUAL_PARSER_CHUNK_OVERLAP", "100")),
|
|
191
200
|
vision_provider = os.getenv("VISUAL_PARSER_VISION_PROVIDER", "gpt"), # type: ignore[arg-type]
|
|
@@ -10,7 +10,7 @@ supplied image files instead of PyMuPDF-rasterized PDF pages.
|
|
|
10
10
|
|
|
11
11
|
Output
|
|
12
12
|
------
|
|
13
|
-
One record per image is appended to ``
|
|
13
|
+
One record per image is appended to ``image_descriptions_kb.jsonl`` in
|
|
14
14
|
*output_dir*:
|
|
15
15
|
|
|
16
16
|
{
|
|
@@ -40,7 +40,7 @@ from visual_parser.vision_llm import call_vision_llm
|
|
|
40
40
|
|
|
41
41
|
logger = logging.getLogger(__name__)
|
|
42
42
|
|
|
43
|
-
IMAGE_DESCRIPTIONS_FILE = "
|
|
43
|
+
IMAGE_DESCRIPTIONS_FILE = "image_descriptions_kb.jsonl"
|
|
44
44
|
|
|
45
45
|
# Decoupled from figure_describer.py's context-window-scaled formula (10 min
|
|
46
46
|
# for 1 image there): IMAGE_DESCRIPTION_PROMPT's "Verbatim Text Content"
|
|
@@ -171,7 +171,7 @@ def describe_images(
|
|
|
171
171
|
"""
|
|
172
172
|
For each image in *new_image_paths*, call the Vision LLM once (no
|
|
173
173
|
context window -- a single standalone image per call), parse the
|
|
174
|
-
description, and append the result to ``
|
|
174
|
+
description, and append the result to ``image_descriptions_kb.jsonl`` in
|
|
175
175
|
*output_dir*.
|
|
176
176
|
|
|
177
177
|
Returns the basenames actually written a row for -- NOT the full input
|
|
@@ -183,7 +183,7 @@ def describe_images(
|
|
|
183
183
|
|
|
184
184
|
Args:
|
|
185
185
|
new_image_paths: Full paths of images to describe.
|
|
186
|
-
output_dir: Directory where ``
|
|
186
|
+
output_dir: Directory where ``image_descriptions_kb.jsonl`` is written.
|
|
187
187
|
vision_provider: ``'gpt'``, ``'gemini'``, or ``'ollama'``.
|
|
188
188
|
vision_api_key: API key for the chosen provider (unused for ollama).
|
|
189
189
|
vision_model: Vision model name string.
|
|
@@ -191,7 +191,7 @@ def describe_images(
|
|
|
191
191
|
image_prompt: The instruction prompt sent with each image.
|
|
192
192
|
max_workers: Thread-pool size for concurrent vision-LLM calls.
|
|
193
193
|
|
|
194
|
-
Always resumes from
|
|
194
|
+
Always resumes from image_descriptions_kb.jsonl's existing content (via the
|
|
195
195
|
done_images set below) -- callers that want an image redescribed must
|
|
196
196
|
first remove its existing row and its basename from the processed-
|
|
197
197
|
images registry (see kb_redo.redo_entries()), then pass it in here as if
|
|
@@ -14,7 +14,7 @@ dependency-free package and cannot depend on the sibling RADIANT-LLM repo)
|
|
|
14
14
|
the proven basename-match + backup-once + atomic-rewrite pattern from
|
|
15
15
|
RADIANT-LLM's knowledge_base_sanitizer() (radiant_lmm/RADIANT_LLM_ChatBot_
|
|
16
16
|
Local/utils/pdf_helpers.py:1464-1607), extended to the 4th JSONL file
|
|
17
|
-
(
|
|
17
|
+
(image_descriptions_kb.jsonl) and both registries (04_processed_pdfs.txt,
|
|
18
18
|
parsed_images.txt), which that function never touches at all.
|
|
19
19
|
"""
|
|
20
20
|
|
|
@@ -25,6 +25,7 @@ import os
|
|
|
25
25
|
import shutil
|
|
26
26
|
from typing import Any, Dict, Iterable, List
|
|
27
27
|
|
|
28
|
+
from visual_parser.image_describer import IMAGE_DESCRIPTIONS_FILE
|
|
28
29
|
from visual_parser.image_tracker import PROCESSED_IMAGES_REGISTRY
|
|
29
30
|
from visual_parser.jsonl_writer import atomic_rewrite_jsonl
|
|
30
31
|
from visual_parser.pdf_tracker import PROCESSED_REGISTRY, load_processed_pdfs, unmark_as_processed
|
|
@@ -33,12 +34,16 @@ logger = logging.getLogger(__name__)
|
|
|
33
34
|
|
|
34
35
|
# Hardcoded, not glob("*.jsonl") like the RADIANT precedent -- output_dir
|
|
35
36
|
# often equals input_dir (the documented default), so globbing risks
|
|
36
|
-
# touching unrelated JSONL files a user happens to keep there.
|
|
37
|
+
# touching unrelated JSONL files a user happens to keep there. The image
|
|
38
|
+
# filename is imported (not hardcoded like the other three) because it was
|
|
39
|
+
# renamed once already (image_descriptions.jsonl -> image_descriptions_kb.jsonl,
|
|
40
|
+
# 2.2.3) with this exact file as a hardcoded duplicate that needed a manual
|
|
41
|
+
# fix -- importing the constant means it can't drift out of sync again.
|
|
37
42
|
KB_JSONL_FILENAMES = (
|
|
38
43
|
"01_chunks_kb.jsonl",
|
|
39
44
|
"02_visuals_kb.jsonl",
|
|
40
45
|
"03_metadata_kb.jsonl",
|
|
41
|
-
|
|
46
|
+
IMAGE_DESCRIPTIONS_FILE,
|
|
42
47
|
)
|
|
43
48
|
REGISTRY_FILENAMES = (PROCESSED_REGISTRY, PROCESSED_IMAGES_REGISTRY)
|
|
44
49
|
BACKUP_SUFFIX = "_backup_before_redo"
|
|
@@ -501,6 +501,7 @@ def run_pipeline(config: Optional[ParserConfig] = None) -> Dict:
|
|
|
501
501
|
chunk_size = config.chunk_size,
|
|
502
502
|
chunk_overlap = config.chunk_overlap,
|
|
503
503
|
max_workers = config.max_workers,
|
|
504
|
+
nougat_crop_margin = config.nougat_crop_margin,
|
|
504
505
|
)
|
|
505
506
|
print(nougat_summary)
|
|
506
507
|
except Exception as exc:
|
|
@@ -69,6 +69,7 @@ def nougat_extract_pdfs(
|
|
|
69
69
|
chunk_size: int = 500,
|
|
70
70
|
chunk_overlap: int = 100,
|
|
71
71
|
max_workers: int = 4,
|
|
72
|
+
nougat_crop_margin: bool = False,
|
|
72
73
|
) -> Tuple[str, List[str], List[str], int]:
|
|
73
74
|
"""
|
|
74
75
|
Extract text from each PDF in *only_process_these* using the Nougat model,
|
|
@@ -83,6 +84,9 @@ def nougat_extract_pdfs(
|
|
|
83
84
|
chunk_size: Characters per chunk.
|
|
84
85
|
chunk_overlap: Overlap between adjacent chunks.
|
|
85
86
|
max_workers: Thread-pool size for parallel PDF processing.
|
|
87
|
+
nougat_crop_margin: Passed through to Nougat's image processor as
|
|
88
|
+
do_crop_margin (default False -- see
|
|
89
|
+
ParserConfig.nougat_crop_margin's docstring).
|
|
86
90
|
|
|
87
91
|
Always resumes against the 04_processed_pdfs.txt registry -- callers
|
|
88
92
|
that want a PDF reprocessed must first clear its existing chunks and
|
|
@@ -124,7 +128,7 @@ def nougat_extract_pdfs(
|
|
|
124
128
|
pixel_values = processor(
|
|
125
129
|
images=image,
|
|
126
130
|
return_tensors="pt",
|
|
127
|
-
do_crop_margin=
|
|
131
|
+
do_crop_margin=nougat_crop_margin,
|
|
128
132
|
).pixel_values.to(device)
|
|
129
133
|
except TypeError:
|
|
130
134
|
pixel_values = processor(
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visual-parser
|
|
3
|
-
Version: 2.2.
|
|
4
|
-
Summary: Standalone Visual-RAG PDF Parser - text extraction and Vision-LLM figure descriptions to JSONL
|
|
3
|
+
Version: 2.2.3
|
|
4
|
+
Summary: Standalone Visual-RAG PDF & Image Parser - text extraction and Vision-LLM figure/image descriptions to JSONL
|
|
5
5
|
Author: Zavier N. Ndum
|
|
6
6
|
Author-email: zavier.ndum@tamu.edu
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
Project-URL: Homepage, https://github.com/SmartLabNuclear/RADIANT_LLM
|
|
9
9
|
Project-URL: Repository, https://github.com/SmartLabNuclear/RADIANT_LLM
|
|
10
10
|
Project-URL: Docker Hub, https://hub.docker.com/r/zev94/radiant-llm
|
|
11
|
-
Keywords: pdf,rag,nougat,vision-llm,ocr,document-parsing,jsonl,knowledge-base
|
|
11
|
+
Keywords: pdf,image,rag,nougat,vision-llm,ocr,document-parsing,jsonl,knowledge-base
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -43,16 +43,16 @@ Requires-Dist: ruff; extra == "dev"
|
|
|
43
43
|
Requires-Dist: mypy; extra == "dev"
|
|
44
44
|
Dynamic: license-file
|
|
45
45
|
|
|
46
|
-
# visual-parser (Standalone Visual-RAG PDF Ingestion)
|
|
46
|
+
# visual-parser (Standalone Visual-RAG PDF & Image Ingestion)
|
|
47
47
|
|
|
48
48
|

|
|
49
49
|

|
|
50
50
|

|
|
51
51
|

|
|
52
52
|
|
|
53
|
-
`visual-parser` converts PDFs into a multi-modal JSONL knowledge base (text chunks, figure descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-ingestion tool, independent of any chatbot.
|
|
53
|
+
`visual-parser` converts PDFs (and standalone images alongside them) into a multi-modal JSONL knowledge base (text chunks, figure descriptions, standalone image descriptions, metadata). It was extracted from [RADIANT-LLM](https://github.com/SmartLabNuclear/RADIANT_LLM) as a standalone PDF-and-image ingestion tool, independent of any chatbot.
|
|
54
54
|
|
|
55
|
-
1) Run `visual-parser` on curated PDFs to generate JSONL KB files.
|
|
55
|
+
1) Run `visual-parser` on a directory of curated PDFs and/or standalone images to generate JSONL KB files.
|
|
56
56
|
2) Point any downstream RAG system at the generated KB — RADIANT-LLM, AutoSAM, and AutoFLUKA all consume the identical JSONL/registry format, so the same KB works with any of them without re-parsing.
|
|
57
57
|
|
|
58
58
|
## Table of Contents
|
|
@@ -72,10 +72,13 @@ Dynamic: license-file
|
|
|
72
72
|
|
|
73
73
|
By default, the pipeline writes:
|
|
74
74
|
- `01_chunks_kb.jsonl`: chunked text extracted from PDFs (Nougat by default).
|
|
75
|
-
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM).
|
|
75
|
+
- `02_visuals_kb.jsonl`: figure/page visual descriptions (Vision LLM), one row per figure found in a PDF.
|
|
76
76
|
- `03_metadata_kb.jsonl`: document metadata rows (title/author/etc.).
|
|
77
|
+
- `image_descriptions_kb.jsonl`: one holistic description per **standalone image** file found alongside your PDFs in `--input-dir` (not a PDF's own figures) — auto-detected by extension, no separate flag needed; disable with `--skip-images`.
|
|
77
78
|
|
|
78
|
-
Alongside the KB, the pipeline also writes
|
|
79
|
+
Alongside the KB, the pipeline also writes bookkeeping files: `04_processed_pdfs.txt` / `parsed_images.txt` (track which PDFs/images have already been processed, so re-runs skip them unless `--rebuild` or `--redo NAME`) and `05_pipeline.log` (run log at the verbosity set by `--log-level`, default `ERROR`).
|
|
80
|
+
|
|
81
|
+
> **Breaking change in 2.2.3:** the standalone-image KB file was renamed from `image_descriptions.jsonl` to `image_descriptions_kb.jsonl` (matching the `NN_name_kb.jsonl` convention used by the other three KB files). Upgrading from an older version does **not** migrate existing data automatically — the resume check only looks at the new filename, so already-described images in an old `image_descriptions.jsonl` would otherwise appear "new" again and get re-described. If you have existing data, rename the file yourself before re-running: `image_descriptions.jsonl` → `image_descriptions_kb.jsonl`.
|
|
79
82
|
|
|
80
83
|
## API Keys (.env)
|
|
81
84
|
|
|
@@ -262,6 +265,7 @@ Text extraction:
|
|
|
262
265
|
- `--nougat-model facebook/nougat-small`
|
|
263
266
|
- `--chunk-size 500`
|
|
264
267
|
- `--chunk-overlap 100`
|
|
268
|
+
- `--nougat-crop-margin` (off by default — lets Nougat autocrop whitespace margins instead of this package's long-standing uncropped behavior; autocropping can silently crop into real content on unusual page layouts)
|
|
265
269
|
|
|
266
270
|
Vision LLM:
|
|
267
271
|
- `--vision-provider gpt|gemini|ollama` (default: `gpt`) — see [Local Ollama vision provider](#local-ollama-vision-provider) for the `ollama` option
|
|
@@ -273,8 +277,10 @@ Vision LLM:
|
|
|
273
277
|
|
|
274
278
|
Performance / misc:
|
|
275
279
|
- `--max-workers 4`
|
|
276
|
-
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`)
|
|
280
|
+
- `--rebuild` (reprocess everything; ignore `04_processed_pdfs.txt`/`parsed_images.txt`)
|
|
281
|
+
- `--redo NAME` (surgically clear one PDF/image's rows from every KB file + both registries, so it's reprocessed fresh on the next run — a targeted alternative to `--rebuild`)
|
|
277
282
|
- `--skip-text` (skip text extraction and resume only the vision steps; use after an interrupted run)
|
|
283
|
+
- `--skip-images` (don't process standalone images in `--input-dir`, PDFs only)
|
|
278
284
|
- `--list-models` — print the live, currently-available vision models for whichever provider(s) you have a key configured for, then exit (no PDF processing). Reflects Portkey's catalog too when that's active.
|
|
279
285
|
- `--version` / `-V`
|
|
280
286
|
- `--log-level DEBUG|INFO|WARNING|ERROR` (default: `ERROR`)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
visual_parser — Standalone Visual-RAG PDF Parser
|
|
3
|
-
=================================================
|
|
4
|
-
Detects new PDFs in a user-supplied directory, extracts text (via Nougat or
|
|
5
|
-
lightweight PyMuPDF/PyPDFLoader), describes every figure/chart/schematic using
|
|
6
|
-
a Vision LLM (OpenAI GPT, Google Gemini, or a local model via Ollama), and
|
|
7
|
-
writes three JSONL knowledge bases ready for any downstream RAG system:
|
|
8
|
-
|
|
9
|
-
01_chunks_kb.jsonl – text chunks with stable IDs
|
|
10
|
-
02_visuals_kb.jsonl – per-figure visual descriptions
|
|
11
|
-
03_metadata_kb.jsonl – document-level metadata (title, authors, DOI …)
|
|
12
|
-
|
|
13
|
-
No chatbot, no vector store, no retrieval – just a robust parser.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
from visual_parser.config import ParserConfig
|
|
17
|
-
from visual_parser.pipeline import run_pipeline
|
|
18
|
-
|
|
19
|
-
__all__ = ["ParserConfig", "run_pipeline"]
|
|
20
|
-
__version__ = "2.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|