HowdenParser 6.0.0__tar.gz → 7.0.0__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-6.0.0 → howdenparser-7.0.0}/HowdenParser/parser.py +61 -41
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parsers/llama_parser.py +36 -15
- {howdenparser-6.0.0 → howdenparser-7.0.0}/PKG-INFO +3 -2
- {howdenparser-6.0.0 → howdenparser-7.0.0}/pyproject.toml +4 -2
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/__init__.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parameter/__init__.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parameter/llamaparser.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parameter/mistralocr.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parsers/__init__.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parsers/langchain_parser.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/HowdenParser/parsers/mistral_parser.py +0 -0
- {howdenparser-6.0.0 → howdenparser-7.0.0}/README.md +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
import logging
|
|
3
3
|
import dotenv
|
|
4
|
-
from typing import overload, Literal
|
|
4
|
+
from typing import overload, Literal, Any, cast
|
|
5
5
|
from inspect import signature, Signature
|
|
6
|
-
from typing import Any
|
|
7
6
|
import json
|
|
8
7
|
import hashlib
|
|
9
8
|
from pathlib import Path
|
|
9
|
+
import re
|
|
10
|
+
|
|
10
11
|
dotenv.load_dotenv()
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -26,7 +27,7 @@ class BaseParser(ABC):
|
|
|
26
27
|
BaseParser._registry.pop("", None)
|
|
27
28
|
|
|
28
29
|
@abstractmethod
|
|
29
|
-
def parse(self, text:
|
|
30
|
+
def parse(self, text: Path, include_pagenumbers: bool = False):
|
|
30
31
|
pass
|
|
31
32
|
|
|
32
33
|
def make_serializable(self, obj: Any) -> Any:
|
|
@@ -70,12 +71,52 @@ class BaseParser(ABC):
|
|
|
70
71
|
class Parser(BaseParser):
|
|
71
72
|
"""Factory + registry interface for all parsers."""
|
|
72
73
|
|
|
73
|
-
def __new__(cls,
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
def __new__(cls, *args, **kwargs) -> "Parser":
|
|
75
|
+
"""Factory entrypoint — returns the correct concrete parser subclass."""
|
|
76
|
+
return cls._create(*args, **kwargs)
|
|
77
|
+
|
|
78
|
+
# ---- Overloads so IDE validates kwargs per provider ----
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
*,
|
|
84
|
+
provider_and_model: Literal["llamaparser:"],
|
|
85
|
+
tier: str,
|
|
86
|
+
version: str,
|
|
87
|
+
result_type: str,
|
|
88
|
+
preserve_layout_alignment_across_pages: bool,
|
|
89
|
+
merge_continued_tables: bool,
|
|
90
|
+
) -> None: ...
|
|
91
|
+
|
|
92
|
+
@overload
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
config_or_dict: Literal["mistralocr:ocr-large", "mistralocr:ocr-small"],
|
|
96
|
+
) -> None: ...
|
|
97
|
+
|
|
98
|
+
@overload
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
*,
|
|
102
|
+
provider_and_model: Literal["mistralocr:ocr-large", "mistralocr:ocr-small"],
|
|
103
|
+
) -> None: ...
|
|
104
|
+
|
|
105
|
+
@overload
|
|
106
|
+
def __init__(
|
|
107
|
+
self,
|
|
108
|
+
config_or_dict: Literal["langchain:gpt-3.5-turbo", "langchain:gpt-4"],
|
|
109
|
+
) -> None: ...
|
|
110
|
+
|
|
111
|
+
@overload
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
provider_and_model: Literal["langchain:gpt-3.5-turbo", "langchain:gpt-4"],
|
|
116
|
+
) -> None: ...
|
|
117
|
+
|
|
118
|
+
def __init__(self, *_args, **_kwargs) -> None:
|
|
119
|
+
super().__init__() # Never called — __new__ always returns a subclass instance
|
|
79
120
|
|
|
80
121
|
@classmethod
|
|
81
122
|
def available_parsers(cls) -> None:
|
|
@@ -89,39 +130,17 @@ class Parser(BaseParser):
|
|
|
89
130
|
for key, values in result.items():
|
|
90
131
|
print(f"{key} with parameters: {values}")
|
|
91
132
|
|
|
92
|
-
# ---- Overloads for IDE autocomplete ----
|
|
93
|
-
@overload
|
|
94
|
-
@classmethod
|
|
95
|
-
def _create(
|
|
96
|
-
cls,
|
|
97
|
-
*,
|
|
98
|
-
provider_and_model: Literal["llamaparser:"],
|
|
99
|
-
tier: str,
|
|
100
|
-
version: str,
|
|
101
|
-
result_type: str,
|
|
102
|
-
preserve_layout_alignment_across_pages: bool,
|
|
103
|
-
merge_continued_tables: bool,
|
|
104
|
-
) -> "LlamaParser": ...
|
|
105
|
-
|
|
106
|
-
@overload
|
|
107
133
|
@classmethod
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
def _validate_provider_and_model(cls,provider_and_model: str) -> None:
|
|
135
|
+
if not re.match(r"^.+:", provider_and_model):
|
|
136
|
+
raise ValueError(
|
|
137
|
+
"provider_and_model must include a provider before the colon, e.g. 'llamaparser:'"
|
|
138
|
+
)
|
|
113
139
|
|
|
114
|
-
@overload
|
|
115
|
-
@classmethod
|
|
116
|
-
def _create(
|
|
117
|
-
cls,
|
|
118
|
-
*,
|
|
119
|
-
provider_and_model: Literal["langchain:gpt-3.5-turbo", "langchain:gpt-4"],
|
|
120
|
-
) -> "LangChainParser": ...
|
|
121
140
|
|
|
122
141
|
# ---- Implementation ----
|
|
123
142
|
@classmethod
|
|
124
|
-
def _create(cls, config_or_dict:
|
|
143
|
+
def _create(cls, config_or_dict: dict | str = None, **kwargs) -> "Parser":
|
|
125
144
|
"""
|
|
126
145
|
Dynamically create parser instances.
|
|
127
146
|
Supports: Parameter, dict, str (provider_and_model), or kwargs.
|
|
@@ -145,6 +164,8 @@ class Parser(BaseParser):
|
|
|
145
164
|
|
|
146
165
|
provider_and_model = str(merged_args.get("provider_and_model")).strip()
|
|
147
166
|
|
|
167
|
+
Parser._validate_provider_and_model(provider_and_model)
|
|
168
|
+
|
|
148
169
|
if ":" not in provider_and_model:
|
|
149
170
|
raise ValueError("provider_and_model must include a colon, e.g. 'llamaparser:'")
|
|
150
171
|
|
|
@@ -197,8 +218,7 @@ class Parser(BaseParser):
|
|
|
197
218
|
f"{parser_cls.__name__} cannot be created because it is missing required argument(s): {', '.join(missing)}. {example}"
|
|
198
219
|
)
|
|
199
220
|
|
|
200
|
-
return parser_cls(**valid_args)
|
|
221
|
+
return cast("Parser", cast(Any, parser_cls)(**valid_args))
|
|
201
222
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
pass
|
|
223
|
+
def parse(self, text: Path, include_pagenumbers: bool = False):
|
|
224
|
+
raise NotImplementedError
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import logging
|
|
3
|
+
from HowdenCommonObjects.howden_result import HowdenResult
|
|
4
|
+
from HowdenCommonObjects.usage import Usage
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
from ..parser import BaseParser
|
|
5
7
|
from typing import Any
|
|
@@ -7,8 +9,8 @@ from typing import Any
|
|
|
7
9
|
class LlamaParser(BaseParser, name="llamaparser"):
|
|
8
10
|
def __init__(self,
|
|
9
11
|
result_type: str,
|
|
10
|
-
tier: str,
|
|
11
|
-
version: str,
|
|
12
|
+
tier: str, # One of: ["fast", "cost_effective", "agentic", "agentic_plus"]
|
|
13
|
+
version: str, # one of: ["latest","2026-03-12","2026-03-11"...] (many not listed)
|
|
12
14
|
provider_and_model: str,
|
|
13
15
|
merge_continued_tables: bool,
|
|
14
16
|
preserve_layout_alignment_across_pages: bool,
|
|
@@ -48,12 +50,15 @@ class LlamaParser(BaseParser, name="llamaparser"):
|
|
|
48
50
|
if not api_key:
|
|
49
51
|
raise EnvironmentError("Missing LLAMA-PARSER-API-TOKEN in .env file.")
|
|
50
52
|
|
|
51
|
-
rt_lower = str(
|
|
52
|
-
|
|
53
|
+
rt_lower = str(
|
|
54
|
+
self.result_type).lower() # Possible values for LlamaCloud expand: ["markdown", "markdown_full", "text", "metadata", "items"]
|
|
55
|
+
if rt_lower in ("md", "markdown"):
|
|
53
56
|
self.rt = 'markdown'
|
|
54
|
-
elif rt_lower in ("
|
|
57
|
+
elif rt_lower in ("md_full", "markdown_full"):
|
|
58
|
+
self.rt = 'markdown_full'
|
|
59
|
+
elif rt_lower in ("txt", "text"):
|
|
55
60
|
self.rt = 'text'
|
|
56
|
-
elif rt_lower
|
|
61
|
+
elif rt_lower == "json":
|
|
57
62
|
self.rt = 'items'
|
|
58
63
|
else:
|
|
59
64
|
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
@@ -62,9 +67,10 @@ class LlamaParser(BaseParser, name="llamaparser"):
|
|
|
62
67
|
|
|
63
68
|
logging.info("LlamaParser initialized inside worker thread")
|
|
64
69
|
|
|
65
|
-
def parse(self, file_path: Path, include_pagenumbers: bool = False) ->
|
|
70
|
+
def parse(self, file_path: Path, include_pagenumbers: bool = False) -> HowdenResult:
|
|
66
71
|
# Ensure parser is created inside the thread event loop
|
|
67
72
|
self._lazy_init()
|
|
73
|
+
|
|
68
74
|
def page_break(text: str, idx: int) -> str:
|
|
69
75
|
return f"<PAGE_NUMBER {idx}>{text}</PAGE_NUMBER {idx}>"
|
|
70
76
|
|
|
@@ -83,22 +89,37 @@ class LlamaParser(BaseParser, name="llamaparser"):
|
|
|
83
89
|
"preserve_layout_alignment_across_pages": self.preserve_layout_alignment_across_pages,
|
|
84
90
|
}
|
|
85
91
|
},
|
|
86
|
-
expand=[self.rt],
|
|
92
|
+
expand=[self.rt,"job_metadata","usage"],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
usage = Usage(
|
|
96
|
+
type="parser",
|
|
97
|
+
provider=self.provider_and_model,
|
|
98
|
+
model=self.version + " " + self.tier,
|
|
99
|
+
operation=self.name,
|
|
100
|
+
pages = documents.job_metadata["pdf-pages"]
|
|
87
101
|
)
|
|
88
102
|
|
|
89
103
|
if self.rt == 'markdown':
|
|
90
|
-
if include_pagenumbers:
|
|
91
|
-
|
|
104
|
+
if include_pagenumbers:
|
|
105
|
+
result = "\n".join(page_break(page.markdown, idx) for idx, page in enumerate(documents.markdown.pages, start=1))
|
|
106
|
+
else:
|
|
107
|
+
result = "\n".join(page.markdown for page in documents.markdown.pages)
|
|
108
|
+
elif self.rt == 'markdown_full':
|
|
109
|
+
result = documents.markdown_full
|
|
92
110
|
elif self.rt == 'text':
|
|
93
|
-
if include_pagenumbers:
|
|
94
|
-
|
|
111
|
+
if include_pagenumbers:
|
|
112
|
+
result = "\n".join(page_break(page.text, idx) for idx, page in enumerate(documents.text.pages, start=1))
|
|
113
|
+
else:
|
|
114
|
+
result = "\n".join(page.text for page in documents.text.pages)
|
|
95
115
|
elif self.rt == 'items':
|
|
96
|
-
|
|
116
|
+
result = documents.model_dump()
|
|
97
117
|
else:
|
|
98
118
|
raise NotImplementedError(f"Result type {self.result_type} is not supported.")
|
|
119
|
+
return HowdenResult(result,usage)
|
|
99
120
|
|
|
100
|
-
def __call__(self, file_path: Path) ->
|
|
121
|
+
def __call__(self, file_path: Path) -> HowdenResult:
|
|
101
122
|
return self.parse(file_path)
|
|
102
123
|
|
|
103
124
|
def write_json_hyperparameter(self, folder_file_path: Path) -> None:
|
|
104
|
-
self.write_parameters(self._input_params, folder_file_path)
|
|
125
|
+
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: 7.0.0
|
|
4
4
|
Summary: A simple configuration manager with Pydantic and JSON export.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: config,configuration,pydantic,json
|
|
@@ -12,8 +12,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
14
|
Requires-Dist: dotenv (>=0.9.9,<0.10.0)
|
|
15
|
+
Requires-Dist: howdencommonobjects (>=1.1.0)
|
|
15
16
|
Requires-Dist: langchain (>=1.1.3,<2.0.0)
|
|
16
|
-
Requires-Dist: llama-cloud (
|
|
17
|
+
Requires-Dist: llama-cloud (==2.16.0)
|
|
17
18
|
Requires-Dist: mistralai (>=1.9.3,<2.0.0)
|
|
18
19
|
Requires-Dist: pdf2image (>=1.17.0,<2.0.0)
|
|
19
20
|
Requires-Dist: pypdf2 (>=3.0.1,<4.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "HowdenParser"
|
|
3
|
-
version = "
|
|
3
|
+
version = "7.0.0"
|
|
4
4
|
description = "A simple configuration manager with Pydantic and JSON export."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12,<3.14"
|
|
@@ -18,13 +18,14 @@ repository = "https://github.com/yourusername/config"
|
|
|
18
18
|
documentation = "https://github.com/yourusername/config"
|
|
19
19
|
dependencies = [
|
|
20
20
|
"mistralai (>=1.9.3,<2.0.0)",
|
|
21
|
-
"llama-cloud
|
|
21
|
+
"llama-cloud==2.16.0",
|
|
22
22
|
"pypdf2 (>=3.0.1,<4.0.0)",
|
|
23
23
|
"pdf2image (>=1.17.0,<2.0.0)",
|
|
24
24
|
"langchain (>=1.1.3,<2.0.0)",
|
|
25
25
|
"pytest (>=9.0.2,<10.0.0)",
|
|
26
26
|
"tomli-w (>=1.2.0,<2.0.0)",
|
|
27
27
|
"dotenv (>=0.9.9,<0.10.0)",
|
|
28
|
+
"howdencommonobjects>=1.1.0",
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.license]
|
|
@@ -41,4 +42,5 @@ dev = [
|
|
|
41
42
|
"toml (>=0.10.2,<0.11.0)",
|
|
42
43
|
"tomli-w (>=1.2.0,<2.0.0)",
|
|
43
44
|
"howdenconfig (>=1.0.6,<2.0.0)",
|
|
45
|
+
"howdencommonobjects",
|
|
44
46
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|