HowdenParser 5.2.6__tar.gz → 6.0.1__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.
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parser.py +3 -5
- howdenparser-6.0.1/HowdenParser/parsers/llama_parser.py +116 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/PKG-INFO +3 -2
- {howdenparser-5.2.6 → howdenparser-6.0.1}/pyproject.toml +44 -43
- howdenparser-5.2.6/HowdenParser/parsers/llama_parser.py +0 -106
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/__init__.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parameter/__init__.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parameter/llamaparser.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parameter/mistralocr.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parsers/__init__.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parsers/langchain_parser.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/HowdenParser/parsers/mistral_parser.py +0 -0
- {howdenparser-5.2.6 → howdenparser-6.0.1}/README.md +0 -0
|
@@ -96,13 +96,11 @@ class Parser(BaseParser):
|
|
|
96
96
|
cls,
|
|
97
97
|
*,
|
|
98
98
|
provider_and_model: Literal["llamaparser:"],
|
|
99
|
+
tier: str,
|
|
100
|
+
version: str,
|
|
99
101
|
result_type: str,
|
|
100
|
-
model: str,
|
|
101
|
-
parse_mode: str,
|
|
102
102
|
preserve_layout_alignment_across_pages: bool,
|
|
103
|
-
|
|
104
|
-
hide_footers: bool,
|
|
105
|
-
hide_headers: bool,
|
|
103
|
+
merge_continued_tables: bool,
|
|
106
104
|
) -> "LlamaParser": ...
|
|
107
105
|
|
|
108
106
|
@overload
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from ..parser import BaseParser
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LlamaParser(BaseParser, name="llamaparser"):
|
|
9
|
+
def __init__(self,
|
|
10
|
+
result_type: str,
|
|
11
|
+
tier: str, # One of: ["fast", "cost_effective", "agentic", "agentic_plus"]
|
|
12
|
+
version: str, # one of: ["latest","2026-03-12","2026-03-11"...] (many not listed)
|
|
13
|
+
provider_and_model: str,
|
|
14
|
+
merge_continued_tables: bool,
|
|
15
|
+
preserve_layout_alignment_across_pages: bool,
|
|
16
|
+
) -> None:
|
|
17
|
+
|
|
18
|
+
super().__init__()
|
|
19
|
+
# Capture only input parameters
|
|
20
|
+
self._input_params = {
|
|
21
|
+
k: v
|
|
22
|
+
for k, v in locals().items()
|
|
23
|
+
if k not in ("self", "__class__", "__len__")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
logging.info("Configuring LlamaParser (lazy init enabled)...")
|
|
27
|
+
|
|
28
|
+
# Store configuration only
|
|
29
|
+
self.result_type = result_type
|
|
30
|
+
self.tier = tier
|
|
31
|
+
self.version = version
|
|
32
|
+
self.provider_and_model = provider_and_model
|
|
33
|
+
self.merge_continued_tables = merge_continued_tables
|
|
34
|
+
self.preserve_layout_alignment_across_pages = preserve_layout_alignment_across_pages
|
|
35
|
+
self.hashed = self.compute_hash(self._input_params)
|
|
36
|
+
# DO NOT CREATE LlamaParse HERE
|
|
37
|
+
# It creates asyncio objects bound to the wrong loop
|
|
38
|
+
self._parser = None
|
|
39
|
+
self.rt = None
|
|
40
|
+
|
|
41
|
+
def _lazy_init(self):
|
|
42
|
+
"""Initialize LlamaParse inside the worker thread event loop."""
|
|
43
|
+
if self._parser is not None:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
from llama_cloud import LlamaCloud
|
|
47
|
+
|
|
48
|
+
api_key = os.getenv("LLAMA-PARSER-API-TOKEN")
|
|
49
|
+
if not api_key:
|
|
50
|
+
raise EnvironmentError("Missing LLAMA-PARSER-API-TOKEN in .env file.")
|
|
51
|
+
|
|
52
|
+
rt_lower = str(
|
|
53
|
+
self.result_type).lower() # Possible values for LlamaCloud expand: ["markdown", "markdown_full", "text", "metadata", "items"]
|
|
54
|
+
if rt_lower in ("md", "markdown"):
|
|
55
|
+
self.rt = 'markdown'
|
|
56
|
+
elif rt_lower in ("md_full", "markdown_full"):
|
|
57
|
+
self.rt = 'markdown_full'
|
|
58
|
+
elif rt_lower in ("txt", "text"):
|
|
59
|
+
self.rt = 'text'
|
|
60
|
+
elif rt_lower in ("json"):
|
|
61
|
+
self.rt = 'items'
|
|
62
|
+
else:
|
|
63
|
+
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
64
|
+
|
|
65
|
+
self._parser = LlamaCloud(api_key=os.getenv("LLAMA-PARSER-API-TOKEN"))
|
|
66
|
+
|
|
67
|
+
logging.info("LlamaParser initialized inside worker thread")
|
|
68
|
+
|
|
69
|
+
def parse(self, file_path: Path, include_pagenumbers: bool = False) -> Any:
|
|
70
|
+
# Ensure parser is created inside the thread event loop
|
|
71
|
+
self._lazy_init()
|
|
72
|
+
|
|
73
|
+
def page_break(text: str, idx: int) -> str:
|
|
74
|
+
return f"<PAGE_NUMBER {idx}>{text}</PAGE_NUMBER {idx}>"
|
|
75
|
+
|
|
76
|
+
documents = self._parser.parsing.parse(
|
|
77
|
+
upload_file=file_path,
|
|
78
|
+
tier=self.tier,
|
|
79
|
+
version=self.version,
|
|
80
|
+
output_options={
|
|
81
|
+
"markdown": {
|
|
82
|
+
"tables": {
|
|
83
|
+
"output_tables_as_markdown": True,
|
|
84
|
+
"merge_continued_tables": self.merge_continued_tables,
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"spatial_text": {
|
|
88
|
+
"preserve_layout_alignment_across_pages": self.preserve_layout_alignment_across_pages,
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
expand=[self.rt],
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if self.rt == 'markdown':
|
|
95
|
+
if include_pagenumbers:
|
|
96
|
+
return "\n".join(
|
|
97
|
+
page_break(page.markdown, idx) for idx, page in enumerate(documents.markdown.pages, start=1))
|
|
98
|
+
else:
|
|
99
|
+
return "\n".join(page.markdown for page in documents.markdown.pages)
|
|
100
|
+
elif self.rt == 'markdown_full':
|
|
101
|
+
return documents.markdown_full
|
|
102
|
+
elif self.rt == 'text':
|
|
103
|
+
if include_pagenumbers:
|
|
104
|
+
return "\n".join(page_break(page.text, idx) for idx, page in enumerate(documents.text.pages, start=1))
|
|
105
|
+
else:
|
|
106
|
+
return "\n".join(page.text for page in documents.text.pages)
|
|
107
|
+
elif self.rt == 'items':
|
|
108
|
+
return documents.model_dump()
|
|
109
|
+
else:
|
|
110
|
+
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
111
|
+
|
|
112
|
+
def __call__(self, file_path: Path) -> str:
|
|
113
|
+
return self.parse(file_path)
|
|
114
|
+
|
|
115
|
+
def write_json_hyperparameter(self, folder_file_path: Path) -> None:
|
|
116
|
+
self.write_parameters(self._input_params, folder_file_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: HowdenParser
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.1
|
|
4
4
|
Summary: A simple configuration manager with Pydantic and JSON export.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: config,configuration,pydantic,json
|
|
@@ -11,8 +11,9 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Requires-Dist: dotenv (>=0.9.9,<0.10.0)
|
|
14
15
|
Requires-Dist: langchain (>=1.1.3,<2.0.0)
|
|
15
|
-
Requires-Dist: llama-
|
|
16
|
+
Requires-Dist: llama-cloud (>=1.0)
|
|
16
17
|
Requires-Dist: mistralai (>=1.9.3,<2.0.0)
|
|
17
18
|
Requires-Dist: pdf2image (>=1.17.0,<2.0.0)
|
|
18
19
|
Requires-Dist: pypdf2 (>=3.0.1,<4.0.0)
|
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "HowdenParser"
|
|
3
|
-
version = "
|
|
4
|
-
description = "A simple configuration manager with Pydantic and JSON export."
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.12,<3.14"
|
|
7
|
-
authors = [
|
|
8
|
-
{ name = "JesperThoftIllemannJ", email = "jesper.jaeger@howdendanmark.dk" },
|
|
9
|
-
]
|
|
10
|
-
keywords = [
|
|
11
|
-
"config",
|
|
12
|
-
"configuration",
|
|
13
|
-
"pydantic",
|
|
14
|
-
"json",
|
|
15
|
-
]
|
|
16
|
-
homepage = "https://github.com/yourusername/config"
|
|
17
|
-
repository = "https://github.com/yourusername/config"
|
|
18
|
-
documentation = "https://github.com/yourusername/config"
|
|
19
|
-
dependencies = [
|
|
20
|
-
"mistralai (>=1.9.3,<2.0.0)",
|
|
21
|
-
"llama-
|
|
22
|
-
"pypdf2 (>=3.0.1,<4.0.0)",
|
|
23
|
-
"pdf2image (>=1.17.0,<2.0.0)",
|
|
24
|
-
"langchain (>=1.1.3,<2.0.0)",
|
|
25
|
-
"pytest (>=9.0.2,<10.0.0)",
|
|
26
|
-
"tomli-w (>=1.2.0,<2.0.0)",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
1
|
+
[project]
|
|
2
|
+
name = "HowdenParser"
|
|
3
|
+
version = "6.0.1"
|
|
4
|
+
description = "A simple configuration manager with Pydantic and JSON export."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12,<3.14"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "JesperThoftIllemannJ", email = "jesper.jaeger@howdendanmark.dk" },
|
|
9
|
+
]
|
|
10
|
+
keywords = [
|
|
11
|
+
"config",
|
|
12
|
+
"configuration",
|
|
13
|
+
"pydantic",
|
|
14
|
+
"json",
|
|
15
|
+
]
|
|
16
|
+
homepage = "https://github.com/yourusername/config"
|
|
17
|
+
repository = "https://github.com/yourusername/config"
|
|
18
|
+
documentation = "https://github.com/yourusername/config"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"mistralai (>=1.9.3,<2.0.0)",
|
|
21
|
+
"llama-cloud (>=1.0)",
|
|
22
|
+
"pypdf2 (>=3.0.1,<4.0.0)",
|
|
23
|
+
"pdf2image (>=1.17.0,<2.0.0)",
|
|
24
|
+
"langchain (>=1.1.3,<2.0.0)",
|
|
25
|
+
"pytest (>=9.0.2,<10.0.0)",
|
|
26
|
+
"tomli-w (>=1.2.0,<2.0.0)",
|
|
27
|
+
"dotenv (>=0.9.9,<0.10.0)",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.license]
|
|
31
|
+
text = "MIT"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = [
|
|
35
|
+
"poetry-core>=2.0.0,<3.0.0",
|
|
36
|
+
]
|
|
37
|
+
build-backend = "poetry.core.masonry.api"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"toml (>=0.10.2,<0.11.0)",
|
|
42
|
+
"tomli-w (>=1.2.0,<2.0.0)",
|
|
43
|
+
"howdenconfig (>=1.0.6,<2.0.0)",
|
|
44
|
+
]
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from ..parser import BaseParser
|
|
6
|
-
from typing import Any, Optional
|
|
7
|
-
from llama_parse import ResultType
|
|
8
|
-
|
|
9
|
-
class LlamaParser(BaseParser, name="llamaparser"):
|
|
10
|
-
def __init__(self, result_type: str,
|
|
11
|
-
model: str, parse_mode: str,
|
|
12
|
-
provider_and_model: str,
|
|
13
|
-
merge_tables_across_pages_in_markdown: bool,
|
|
14
|
-
preserve_layout_alignment_across_pages: bool,
|
|
15
|
-
hide_footers: bool,
|
|
16
|
-
hide_headers: bool,
|
|
17
|
-
precise_bounding_box: Optional[bool] = None,
|
|
18
|
-
line_level_bounding_box: Optional[bool] = None,
|
|
19
|
-
) -> None:
|
|
20
|
-
|
|
21
|
-
super().__init__()
|
|
22
|
-
# Capture only input parameters
|
|
23
|
-
self._input_params = {
|
|
24
|
-
k: v
|
|
25
|
-
for k, v in locals().items()
|
|
26
|
-
if k not in ("self", "__class__", "__len__")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
logging.info("Configuring LlamaParser (lazy init enabled)...")
|
|
30
|
-
|
|
31
|
-
# Store configuration only
|
|
32
|
-
self.result_type = result_type
|
|
33
|
-
self.model = model
|
|
34
|
-
self.parse_mode = parse_mode
|
|
35
|
-
self.provider_and_model = provider_and_model
|
|
36
|
-
self.merge_tables_across_pages_in_markdown = merge_tables_across_pages_in_markdown
|
|
37
|
-
self.preserve_layout_alignment_across_pages = preserve_layout_alignment_across_pages
|
|
38
|
-
self.hide_footers = hide_footers
|
|
39
|
-
self.hide_headers = hide_headers
|
|
40
|
-
self.precise_bounding_box = precise_bounding_box
|
|
41
|
-
self.line_level_bounding_box = line_level_bounding_box
|
|
42
|
-
self.hashed = self.compute_hash(self._input_params)
|
|
43
|
-
# DO NOT CREATE LlamaParse HERE
|
|
44
|
-
# It creates asyncio objects bound to the wrong loop
|
|
45
|
-
self._parser = None
|
|
46
|
-
|
|
47
|
-
def _lazy_init(self):
|
|
48
|
-
"""Initialize LlamaParse inside the worker thread event loop."""
|
|
49
|
-
if self._parser is not None:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
from llama_parse import LlamaParse, ResultType
|
|
53
|
-
|
|
54
|
-
api_key = os.getenv("LLAMA-PARSER-API-TOKEN")
|
|
55
|
-
if not api_key:
|
|
56
|
-
raise EnvironmentError("Missing LLAMA-PARSER-API-TOKEN in .env file.")
|
|
57
|
-
|
|
58
|
-
rt_lower = str(self.result_type).lower()
|
|
59
|
-
if rt_lower in ("md","markdown"):
|
|
60
|
-
rt = ResultType.MD
|
|
61
|
-
elif rt_lower in ("txt","text"):
|
|
62
|
-
rt = ResultType.TXT
|
|
63
|
-
elif rt_lower in ("json"):
|
|
64
|
-
rt = ResultType.JSON
|
|
65
|
-
else:
|
|
66
|
-
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
67
|
-
|
|
68
|
-
# Construct the parser INSIDE the worker thread
|
|
69
|
-
self._parser = LlamaParse(
|
|
70
|
-
api_key=api_key,
|
|
71
|
-
result_type=rt,
|
|
72
|
-
model=self.model,
|
|
73
|
-
parse_mode=self.parse_mode,
|
|
74
|
-
merge_tables_across_pages_in_markdown=self.merge_tables_across_pages_in_markdown,
|
|
75
|
-
preserve_layout_alignment_across_pages=self.preserve_layout_alignment_across_pages,
|
|
76
|
-
hide_footers=self.hide_footers,
|
|
77
|
-
hide_headers=self.hide_headers,
|
|
78
|
-
precise_bounding_box=self.precise_bounding_box,
|
|
79
|
-
line_level_bounding_box=self.line_level_bounding_box,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
logging.info("LlamaParser initialized inside worker thread")
|
|
83
|
-
|
|
84
|
-
def parse(self, file_path: Path, include_pagenumbers: bool = False) -> Any:
|
|
85
|
-
# Ensure parser is created inside the thread event loop
|
|
86
|
-
self._lazy_init()
|
|
87
|
-
def page_break(text: str, idx: int) -> str:
|
|
88
|
-
return f"<PAGE_NUMBER {idx}>{text}</PAGE_NUMBER {idx}>"
|
|
89
|
-
|
|
90
|
-
documents = self._parser.parse(file_path)
|
|
91
|
-
if self.result_type in ['md','markdown']:
|
|
92
|
-
if include_pagenumbers: return "\n".join(page_break(page.md, idx) for idx, page in enumerate(documents.pages, start=1))
|
|
93
|
-
else: return "\n".join(page.md for page in documents.pages)
|
|
94
|
-
elif self.result_type in ['txt','text']:
|
|
95
|
-
if include_pagenumbers: return "\n".join(page_break(page.text, idx) for idx, page in enumerate(documents.pages, start=1))
|
|
96
|
-
else: return "\n".join(page.text for page in documents.pages)
|
|
97
|
-
elif self.result_type.lower() == 'json':
|
|
98
|
-
return documents.model_dump()
|
|
99
|
-
else:
|
|
100
|
-
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
101
|
-
|
|
102
|
-
def __call__(self, file_path: Path) -> str:
|
|
103
|
-
return self.parse(file_path)
|
|
104
|
-
|
|
105
|
-
def write_json_hyperparameter(self, folder_file_path: Path) -> None:
|
|
106
|
-
self.write_parameters(self._input_params, folder_file_path)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|