extract-core 0.5.4__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.
- extract_core-0.5.4/.gitignore +19 -0
- extract_core-0.5.4/PKG-INFO +15 -0
- extract_core-0.5.4/README.md +0 -0
- extract_core-0.5.4/extract_core/__init__.py +63 -0
- extract_core-0.5.4/extract_core/configs.py +27 -0
- extract_core-0.5.4/extract_core/docling_.py +268 -0
- extract_core-0.5.4/extract_core/marker_.py +40 -0
- extract_core-0.5.4/extract_core/miner_u.py +76 -0
- extract_core-0.5.4/extract_core/objects.py +295 -0
- extract_core-0.5.4/extract_core/pipeline.py +14 -0
- extract_core-0.5.4/extract_core/utils.py +9 -0
- extract_core-0.5.4/pyproject.toml +121 -0
- extract_core-0.5.4/uv.lock +3017 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: extract-core
|
|
3
|
+
Version: 0.5.4
|
|
4
|
+
Summary: Core of extract-python
|
|
5
|
+
Project-URL: Homepage, https://github.com/ICIJ/extract-python
|
|
6
|
+
Project-URL: Repository, https://github.com/ICIJ/extract-python
|
|
7
|
+
Project-URL: Issues, https://github.com/ICIJ/extract-python/issues
|
|
8
|
+
Author-email: Clément Doumouro <cdoumouro@icij.org>
|
|
9
|
+
Requires-Python: <3.14,>=3.11
|
|
10
|
+
Requires-Dist: docling-slim~=2.96
|
|
11
|
+
Requires-Dist: icij-common~=0.8.2
|
|
12
|
+
Requires-Dist: marker-pdf~=1.10
|
|
13
|
+
Requires-Dist: mineru~=3.2
|
|
14
|
+
Requires-Dist: pydantic-extra-types[pycountry]>=2.11.1
|
|
15
|
+
Requires-Dist: pydantic~=2.13
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from icij_common.pydantic_utils import make_enum_discriminator, tagged_union
|
|
4
|
+
from pydantic import Discriminator
|
|
5
|
+
|
|
6
|
+
from .configs import BasePipelineConfig, PipelineType
|
|
7
|
+
from .objects import (
|
|
8
|
+
BaseModel,
|
|
9
|
+
ConversionOutput,
|
|
10
|
+
Error,
|
|
11
|
+
InputDoc,
|
|
12
|
+
MarkdownDoc,
|
|
13
|
+
OutputFormat,
|
|
14
|
+
PageIndexes,
|
|
15
|
+
Result,
|
|
16
|
+
Status,
|
|
17
|
+
)
|
|
18
|
+
from .pipeline import Pipeline
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from .docling_ import DoclingFormatOption, DoclingPipelineConfig
|
|
22
|
+
except ImportError:
|
|
23
|
+
DoclingPipelineConfig, DoclingFormatOption = None, None
|
|
24
|
+
try:
|
|
25
|
+
from .marker_ import MarkerPipelineConfig
|
|
26
|
+
except ImportError:
|
|
27
|
+
MarkerPipelineConfig = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from .miner_u import MinerUBackend, MinerUConfig, MinerUPipelineConfig
|
|
32
|
+
except ImportError:
|
|
33
|
+
MinerUBackend, MinerUPipelineConfig, MinerUConfig = None, None, None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
pipeline_type_discriminator = make_enum_discriminator("pipeline", PipelineType)
|
|
37
|
+
PipelineConfig = Annotated[
|
|
38
|
+
tagged_union(
|
|
39
|
+
BasePipelineConfig.__subclasses__(), lambda t: t.pipeline.default.value
|
|
40
|
+
),
|
|
41
|
+
Discriminator(pipeline_type_discriminator),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"BaseModel",
|
|
47
|
+
"BasePipelineConfig",
|
|
48
|
+
"ConversionOutput",
|
|
49
|
+
"DoclingPipelineConfig",
|
|
50
|
+
"Error",
|
|
51
|
+
"InputDoc",
|
|
52
|
+
"MarkdownDoc",
|
|
53
|
+
"MarkerPipelineConfig",
|
|
54
|
+
"MinerUBackend",
|
|
55
|
+
"MinerUConfig",
|
|
56
|
+
"MinerUPipelineConfig",
|
|
57
|
+
"OutputFormat",
|
|
58
|
+
"PageIndexes",
|
|
59
|
+
"Pipeline",
|
|
60
|
+
"PipelineType",
|
|
61
|
+
"Result",
|
|
62
|
+
"Status",
|
|
63
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from icij_common.pydantic_utils import icij_config, merge_configs, no_enum_values_config
|
|
6
|
+
from icij_common.registrable import RegistrableConfig
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from .objects import SupportedExt
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PipelineType(StrEnum):
|
|
13
|
+
DOCLING = "docling"
|
|
14
|
+
MARKER = "marker"
|
|
15
|
+
MINER_U = "miner_u"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BasePipelineConfig(RegistrableConfig, ABC):
|
|
19
|
+
# TODO: move this icij_config() to RegistrableConfig
|
|
20
|
+
model_config = merge_configs(icij_config(), no_enum_values_config())
|
|
21
|
+
|
|
22
|
+
registry_key: ClassVar[str] = Field(frozen=True, default="pipeline")
|
|
23
|
+
pipeline: ClassVar[PipelineType]
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def supported_exts(cls) -> set[SupportedExt]: ...
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from functools import cache
|
|
3
|
+
from typing import Annotated, Any, ClassVar, TypeVar, get_type_hints
|
|
4
|
+
|
|
5
|
+
from docling.datamodel.backend_options import BackendOptions, BaseBackendOptions
|
|
6
|
+
from docling.datamodel.base_models import (
|
|
7
|
+
BaseFormatOption,
|
|
8
|
+
FormatToExtensions,
|
|
9
|
+
InputFormat,
|
|
10
|
+
)
|
|
11
|
+
from docling.datamodel.pipeline_options import (
|
|
12
|
+
BaseLayoutOptions,
|
|
13
|
+
BaseTableStructureOptions,
|
|
14
|
+
EasyOcrOptions,
|
|
15
|
+
LayoutOptions,
|
|
16
|
+
OcrOptions,
|
|
17
|
+
PictureDescriptionBaseOptions,
|
|
18
|
+
PictureDescriptionVlmEngineOptions,
|
|
19
|
+
PipelineOptions,
|
|
20
|
+
TableStructureOptions,
|
|
21
|
+
ThreadedPdfPipelineOptions,
|
|
22
|
+
)
|
|
23
|
+
from icij_common.pydantic_utils import (
|
|
24
|
+
merge_configs,
|
|
25
|
+
tagged_union,
|
|
26
|
+
to_lower_snake_case,
|
|
27
|
+
)
|
|
28
|
+
from pydantic import (
|
|
29
|
+
ConfigDict,
|
|
30
|
+
Discriminator,
|
|
31
|
+
Field,
|
|
32
|
+
TypeAdapter,
|
|
33
|
+
WrapSerializer,
|
|
34
|
+
)
|
|
35
|
+
from pydantic_core.core_schema import SerializerFunctionWrapHandler
|
|
36
|
+
|
|
37
|
+
from .configs import BasePipelineConfig, PipelineType
|
|
38
|
+
from .objects import BaseModel, SupportedExt
|
|
39
|
+
from .utils import all_subclasses
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@cache
|
|
43
|
+
def _ext_to_docling_input_format() -> dict:
|
|
44
|
+
|
|
45
|
+
mapping = dict()
|
|
46
|
+
supported = DoclingPipelineConfig.supported_exts()
|
|
47
|
+
for input_f, exts in FormatToExtensions.items():
|
|
48
|
+
for ext in exts:
|
|
49
|
+
try:
|
|
50
|
+
ext = SupportedExt(f".{ext.lower()}") # noqa: PLW2901
|
|
51
|
+
except ValueError:
|
|
52
|
+
continue
|
|
53
|
+
if ext in supported:
|
|
54
|
+
mapping[ext] = input_f
|
|
55
|
+
return mapping
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _validate_pipeline_opts(v: PipelineOptions) -> PipelineOptions:
|
|
59
|
+
generate_picture_images = getattr(v, "generate_picture_images", None)
|
|
60
|
+
if generate_picture_images is False:
|
|
61
|
+
msg = "generate_picture_images should be set to True"
|
|
62
|
+
raise ValueError(msg)
|
|
63
|
+
return v
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
T = TypeVar("T")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _find_subcls(cls: type[T], name: str) -> type[T]:
|
|
70
|
+
# Check if the class available
|
|
71
|
+
for c in all_subclasses(cls):
|
|
72
|
+
if c.__name__ == name:
|
|
73
|
+
return c
|
|
74
|
+
# Then apply ad-hoc search
|
|
75
|
+
if "pipeline" in cls.__name__.lower():
|
|
76
|
+
module_name = f"docling.pipeline.{to_lower_snake_case(name)}"
|
|
77
|
+
try:
|
|
78
|
+
module = importlib.import_module(module_name)
|
|
79
|
+
return getattr(module, name)
|
|
80
|
+
except (ModuleNotFoundError, AttributeError):
|
|
81
|
+
pass
|
|
82
|
+
raise ValueError(f"unknown {cls.__name__} subclass {name}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _find_init_arg_type(cls: type[Any], arg: str) -> type[BaseModel]:
|
|
86
|
+
hints = get_type_hints(cls.__init__)
|
|
87
|
+
return hints[arg]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _resolve_pipeline_cls(v: str) -> Any:
|
|
91
|
+
if isinstance(v, str):
|
|
92
|
+
from docling.pipeline.base_pipeline import BasePipeline # noqa: PLC0415
|
|
93
|
+
|
|
94
|
+
return _find_subcls(BasePipeline, v)
|
|
95
|
+
return v
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _ser_as_str(v: type) -> str:
|
|
99
|
+
return v.__name__
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _ser_with_backend_option_kind(
|
|
103
|
+
v: Any, handler: SerializerFunctionWrapHandler
|
|
104
|
+
) -> Any:
|
|
105
|
+
serialized = handler(v)
|
|
106
|
+
if isinstance(v, BaseBackendOptions):
|
|
107
|
+
kind = getattr(v, "kind", None)
|
|
108
|
+
if kind is not None:
|
|
109
|
+
serialized["kind"] = kind
|
|
110
|
+
return serialized
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _resolve_backend(v: Any) -> Any:
|
|
114
|
+
from docling.backend.abstract_backend import ( # noqa: PLC0415
|
|
115
|
+
AbstractDocumentBackend,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if isinstance(v, str):
|
|
119
|
+
return _find_subcls(AbstractDocumentBackend, v)
|
|
120
|
+
return v
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@cache
|
|
124
|
+
def _picture_descr_opts_type_adapter() -> TypeAdapter:
|
|
125
|
+
_PictureDescriptionModel = Annotated[ # noqa: N806
|
|
126
|
+
tagged_union(
|
|
127
|
+
PictureDescriptionBaseOptions.__subclasses__(), tag_getter=lambda x: x.kind
|
|
128
|
+
),
|
|
129
|
+
Discriminator(lambda x: x["kind"]),
|
|
130
|
+
]
|
|
131
|
+
return TypeAdapter(_PictureDescriptionModel)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@cache
|
|
135
|
+
def _ocr_opts_type_adapter() -> TypeAdapter:
|
|
136
|
+
_OcrOptions = Annotated[ # noqa: N806
|
|
137
|
+
tagged_union(OcrOptions.__subclasses__(), tag_getter=lambda x: x.kind),
|
|
138
|
+
Discriminator(lambda x: x.pop("kind")),
|
|
139
|
+
]
|
|
140
|
+
return TypeAdapter(_OcrOptions)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@cache
|
|
144
|
+
def _layout_opts_type_adapter() -> TypeAdapter:
|
|
145
|
+
_LayoutOptions = Annotated[ # noqa: N806
|
|
146
|
+
tagged_union(BaseLayoutOptions.__subclasses__(), tag_getter=lambda x: x.kind),
|
|
147
|
+
Discriminator(lambda x: x["kind"]),
|
|
148
|
+
]
|
|
149
|
+
return TypeAdapter(_LayoutOptions)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@cache
|
|
153
|
+
def _table_structure_opts_type_adapter() -> TypeAdapter:
|
|
154
|
+
_TableStructureOptions = Annotated[ # noqa: N806
|
|
155
|
+
tagged_union(
|
|
156
|
+
BaseTableStructureOptions.__subclasses__(), tag_getter=lambda x: x.kind
|
|
157
|
+
),
|
|
158
|
+
Discriminator(lambda x: x["kind"]),
|
|
159
|
+
]
|
|
160
|
+
return TypeAdapter(_TableStructureOptions)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _resolve_pipeline_options(
|
|
164
|
+
pipeline_options: dict[str, Any] | None | PipelineOptions, pipeline_cls: type
|
|
165
|
+
) -> PipelineOptions:
|
|
166
|
+
option_cls = _find_init_arg_type(pipeline_cls, "pipeline_options")
|
|
167
|
+
picture_descr_opts = pipeline_options.get("picture_description_options")
|
|
168
|
+
if picture_descr_opts is not None:
|
|
169
|
+
if "kind" not in picture_descr_opts:
|
|
170
|
+
msg = f"missing picture description options kind: {picture_descr_opts}"
|
|
171
|
+
raise ValueError(msg)
|
|
172
|
+
|
|
173
|
+
picture_descr_opts = _picture_descr_opts_type_adapter().validate_python(
|
|
174
|
+
picture_descr_opts
|
|
175
|
+
)
|
|
176
|
+
pipeline_options["picture_description_options"] = picture_descr_opts
|
|
177
|
+
ocr_opts = pipeline_options.get("ocr_options")
|
|
178
|
+
if ocr_opts is not None:
|
|
179
|
+
if "kind" not in ocr_opts:
|
|
180
|
+
msg = f"missing ocr options kind: {ocr_opts}"
|
|
181
|
+
raise ValueError(msg)
|
|
182
|
+
ocr_opts = _ocr_opts_type_adapter().validate_python(ocr_opts)
|
|
183
|
+
pipeline_options["ocr_options"] = ocr_opts
|
|
184
|
+
layout_opts = pipeline_options.get("layout_options")
|
|
185
|
+
if layout_opts is not None:
|
|
186
|
+
if "kind" not in layout_opts:
|
|
187
|
+
msg = f"missing layout options kind: {layout_opts}"
|
|
188
|
+
raise ValueError(msg)
|
|
189
|
+
layout_opts = _layout_opts_type_adapter().validate_python(layout_opts)
|
|
190
|
+
pipeline_options["layout_options"] = layout_opts
|
|
191
|
+
table_structure_opts = pipeline_options.get("table_structure_options")
|
|
192
|
+
if table_structure_opts is not None:
|
|
193
|
+
if "kind" not in table_structure_opts:
|
|
194
|
+
msg = f"missing table structure options kind: {table_structure_opts}"
|
|
195
|
+
raise ValueError(msg)
|
|
196
|
+
table_structure_opts = _table_structure_opts_type_adapter().validate_python(
|
|
197
|
+
table_structure_opts
|
|
198
|
+
)
|
|
199
|
+
pipeline_options["table_structure_options"] = table_structure_opts
|
|
200
|
+
pipeline_options = option_cls.model_validate(pipeline_options)
|
|
201
|
+
return pipeline_options
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# Mimics the docling FormatOption but only with lightweight types,
|
|
205
|
+
# the heavy convertion is done at runtime
|
|
206
|
+
class DoclingFormatOption(BaseFormatOption):
|
|
207
|
+
model_config = merge_configs(
|
|
208
|
+
BaseModel.model_config, ConfigDict(polymorphic_serialization=True)
|
|
209
|
+
)
|
|
210
|
+
backend: str
|
|
211
|
+
backend_options: Annotated[
|
|
212
|
+
BackendOptions | None, WrapSerializer(_ser_with_backend_option_kind)
|
|
213
|
+
] = None
|
|
214
|
+
pipeline_cls: str
|
|
215
|
+
pipeline_options: dict[str, Any] | None = None
|
|
216
|
+
|
|
217
|
+
def to_docling(self) -> BaseFormatOption: # noqa: ANN201
|
|
218
|
+
from docling.document_converter import FormatOption # noqa: PLC0415
|
|
219
|
+
|
|
220
|
+
pipeline_cls = _resolve_pipeline_cls(self.pipeline_cls)
|
|
221
|
+
pipeline_opts = _resolve_pipeline_options(self.pipeline_options, pipeline_cls)
|
|
222
|
+
pipeline_opts = _validate_pipeline_opts(pipeline_opts)
|
|
223
|
+
return FormatOption(
|
|
224
|
+
pipeline_cls=pipeline_cls,
|
|
225
|
+
pipeline_options=pipeline_opts,
|
|
226
|
+
backend=_resolve_backend(self.backend),
|
|
227
|
+
backend_options=self.backend_options,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@cache
|
|
232
|
+
def _default_format_opts() -> dict[InputFormat, DoclingFormatOption]:
|
|
233
|
+
pipeline_opts = ThreadedPdfPipelineOptions(
|
|
234
|
+
ocr_options=EasyOcrOptions(), generate_picture_images=True
|
|
235
|
+
).model_dump(polymorphic_serialization=True)
|
|
236
|
+
pipeline_opts["picture_description_options"]["kind"] = (
|
|
237
|
+
PictureDescriptionVlmEngineOptions.kind
|
|
238
|
+
)
|
|
239
|
+
pipeline_opts["ocr_options"]["kind"] = EasyOcrOptions.kind
|
|
240
|
+
pipeline_opts["layout_options"]["kind"] = LayoutOptions.kind
|
|
241
|
+
pipeline_opts["table_structure_options"]["kind"] = TableStructureOptions.kind
|
|
242
|
+
return {
|
|
243
|
+
InputFormat.PDF: DoclingFormatOption(
|
|
244
|
+
pipeline_cls="StandardPdfPipeline",
|
|
245
|
+
backend="DoclingParseDocumentBackend",
|
|
246
|
+
pipeline_options=pipeline_opts,
|
|
247
|
+
),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class DoclingPipelineConfig(BasePipelineConfig):
|
|
252
|
+
pipeline: ClassVar[PipelineType] = Field(frozen=True, default=PipelineType.DOCLING)
|
|
253
|
+
|
|
254
|
+
format_options: dict[InputFormat, DoclingFormatOption] = Field(
|
|
255
|
+
default_factory=_default_format_opts
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
@cache
|
|
260
|
+
def supported_exts(cls) -> set[SupportedExt]:
|
|
261
|
+
unsupported = {InputFormat.AUDIO, InputFormat.METS_GBS, InputFormat.VTT}
|
|
262
|
+
supported = set()
|
|
263
|
+
for f in InputFormat:
|
|
264
|
+
if f in unsupported:
|
|
265
|
+
continue
|
|
266
|
+
for ext in FormatToExtensions[f]:
|
|
267
|
+
supported.add(SupportedExt(f".{ext.lower()}"))
|
|
268
|
+
return supported
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
from typing import Any, ClassVar
|
|
3
|
+
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from .configs import BasePipelineConfig, PipelineType
|
|
7
|
+
from .objects import SupportedExt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MarkerPipelineConfig(BasePipelineConfig):
|
|
11
|
+
pipeline: ClassVar[PipelineType] = Field(frozen=True, default=PipelineType.MARKER)
|
|
12
|
+
|
|
13
|
+
config: dict[str, Any] = dict()
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
@cache
|
|
17
|
+
def supported_exts(cls) -> set[SupportedExt]:
|
|
18
|
+
# Subset of https://documentation.datalab.to/docs/common/supportedfiletypes
|
|
19
|
+
return {
|
|
20
|
+
SupportedExt.PDF,
|
|
21
|
+
SupportedExt.XLS,
|
|
22
|
+
SupportedExt.XLSX,
|
|
23
|
+
SupportedExt.XLSM,
|
|
24
|
+
SupportedExt.CSV,
|
|
25
|
+
SupportedExt.ODS,
|
|
26
|
+
SupportedExt.DOC,
|
|
27
|
+
SupportedExt.DOCX,
|
|
28
|
+
SupportedExt.ODT,
|
|
29
|
+
SupportedExt.PPT,
|
|
30
|
+
SupportedExt.PPTX,
|
|
31
|
+
SupportedExt.ODP,
|
|
32
|
+
SupportedExt.HTLM,
|
|
33
|
+
SupportedExt.EPUB,
|
|
34
|
+
SupportedExt.PNG,
|
|
35
|
+
SupportedExt.JPG,
|
|
36
|
+
SupportedExt.JPEG,
|
|
37
|
+
SupportedExt.WEBP,
|
|
38
|
+
SupportedExt.GIF,
|
|
39
|
+
SupportedExt.TIFF,
|
|
40
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from copy import copy
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from functools import cache
|
|
5
|
+
from typing import Any, ClassVar
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
from pydantic_extra_types.language_code import LanguageAlpha2
|
|
9
|
+
|
|
10
|
+
from .configs import BasePipelineConfig, PipelineType
|
|
11
|
+
from .objects import BaseModel, SupportedExt
|
|
12
|
+
|
|
13
|
+
_MINER_U_CONVERSION_ERRORS = tuple()
|
|
14
|
+
MDMakeFunction = Callable[[list, str, str], str | None]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MinerUBackend(StrEnum):
|
|
18
|
+
PIPELINE = "pipeline"
|
|
19
|
+
VLM = "vlm"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MinerUConfig(BaseModel):
|
|
23
|
+
backend: MinerUBackend = MinerUBackend.PIPELINE
|
|
24
|
+
enable_formula_extraction: bool = True
|
|
25
|
+
enable_table_extraction: bool = True
|
|
26
|
+
# TODO: use enum or literal here
|
|
27
|
+
parse_method: str = "auto"
|
|
28
|
+
|
|
29
|
+
def as_parse_kwargs(self) -> dict[str, Any]:
|
|
30
|
+
kwargs = copy(self._get_default_kwargs())
|
|
31
|
+
kwargs["backend"] = self.backend
|
|
32
|
+
kwargs["parse_method"] = self.parse_method
|
|
33
|
+
kwargs["formula_enable"] = self.enable_formula_extraction
|
|
34
|
+
kwargs["table_enable"] = self.enable_table_extraction
|
|
35
|
+
return kwargs
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
@cache
|
|
39
|
+
def _get_default_kwargs(cls) -> dict[str, Any]:
|
|
40
|
+
from mineru.utils.enum_class import MakeMode # noqa: PLC0415
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
"server_url": None,
|
|
44
|
+
# We don't dump md directly we process, we dump the middle json in order
|
|
45
|
+
# to be able to get page indexes
|
|
46
|
+
"parse_method": "auto",
|
|
47
|
+
"dump_md": False,
|
|
48
|
+
"dump_middle_json": True,
|
|
49
|
+
"f_draw_layout_bbox": False,
|
|
50
|
+
"f_draw_span_bbox": False,
|
|
51
|
+
"f_dump_model_output": False, # might be useful for debug though
|
|
52
|
+
"f_dump_orig_pdf": False,
|
|
53
|
+
"f_dump_content_list": False, # might be useful for debug though
|
|
54
|
+
"start_page_id": 0,
|
|
55
|
+
"f_make_md_mode": MakeMode.MM_MD,
|
|
56
|
+
"image_analysis": True,
|
|
57
|
+
"end_page_id": None,
|
|
58
|
+
"client_side_output_generation": False,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MinerUPipelineConfig(BasePipelineConfig): # noqa: F821
|
|
63
|
+
pipeline: ClassVar[PipelineType] = Field(frozen=True, default=PipelineType.MINER_U)
|
|
64
|
+
|
|
65
|
+
config: MinerUConfig = Field(frozen=True, default=MinerUConfig())
|
|
66
|
+
language: LanguageAlpha2 = Field(frozen=True, default="en")
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
@cache
|
|
70
|
+
def supported_exts(cls) -> set[SupportedExt]:
|
|
71
|
+
return {
|
|
72
|
+
SupportedExt.PDF,
|
|
73
|
+
SupportedExt.DOCX,
|
|
74
|
+
SupportedExt.PPTX,
|
|
75
|
+
SupportedExt.XLSX,
|
|
76
|
+
}
|