parse-bench 1.0.0__py3-none-any.whl
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.
- parse_bench/__init__.py +3 -0
- parse_bench/analysis/__init__.py +6 -0
- parse_bench/analysis/aggregation_report.py +582 -0
- parse_bench/analysis/cli.py +472 -0
- parse_bench/analysis/comparison.py +382 -0
- parse_bench/analysis/comparison_core.py +357 -0
- parse_bench/analysis/comparison_report.py +2066 -0
- parse_bench/analysis/detailed_report.py +2254 -0
- parse_bench/analysis/leaderboard_report.py +852 -0
- parse_bench/analysis/metric_definitions.py +771 -0
- parse_bench/cli.py +267 -0
- parse_bench/data/__init__.py +1 -0
- parse_bench/data/cli.py +118 -0
- parse_bench/data/download.py +127 -0
- parse_bench/evaluation/__init__.py +11 -0
- parse_bench/evaluation/cli.py +435 -0
- parse_bench/evaluation/evaluators/__init__.py +17 -0
- parse_bench/evaluation/evaluators/base.py +34 -0
- parse_bench/evaluation/evaluators/extract.py +429 -0
- parse_bench/evaluation/evaluators/layoutdet.py +1682 -0
- parse_bench/evaluation/evaluators/parse.py +1353 -0
- parse_bench/evaluation/evaluators/qa.py +199 -0
- parse_bench/evaluation/layout_adapters/__init__.py +21 -0
- parse_bench/evaluation/layout_adapters/adapters.py +3180 -0
- parse_bench/evaluation/layout_adapters/base.py +105 -0
- parse_bench/evaluation/layout_adapters/registry.py +109 -0
- parse_bench/evaluation/layout_label_mappers/__init__.py +22 -0
- parse_bench/evaluation/layout_label_mappers/base.py +66 -0
- parse_bench/evaluation/layout_label_mappers/mappers.py +332 -0
- parse_bench/evaluation/layout_label_mappers/projection.py +74 -0
- parse_bench/evaluation/layout_label_mappers/registry.py +119 -0
- parse_bench/evaluation/metric_aggregation.py +56 -0
- parse_bench/evaluation/metrics/__init__.py +5 -0
- parse_bench/evaluation/metrics/attribution/__init__.py +35 -0
- parse_bench/evaluation/metrics/attribution/constants.py +12 -0
- parse_bench/evaluation/metrics/attribution/core.py +1108 -0
- parse_bench/evaluation/metrics/attribution/evaluate.py +446 -0
- parse_bench/evaluation/metrics/attribution/geometry.py +161 -0
- parse_bench/evaluation/metrics/attribution/text_utils.py +233 -0
- parse_bench/evaluation/metrics/base.py +33 -0
- parse_bench/evaluation/metrics/downstream/__init__.py +0 -0
- parse_bench/evaluation/metrics/extract/__init__.py +29 -0
- parse_bench/evaluation/metrics/extract/json_subset_match.py +473 -0
- parse_bench/evaluation/metrics/extract/json_subset_match_metric.py +81 -0
- parse_bench/evaluation/metrics/extract/list_unwrap.py +340 -0
- parse_bench/evaluation/metrics/extract/rule_based_metric.py +90 -0
- parse_bench/evaluation/metrics/extract/test_rules.py +409 -0
- parse_bench/evaluation/metrics/extract/test_types.py +11 -0
- parse_bench/evaluation/metrics/field_grounding/__init__.py +21 -0
- parse_bench/evaluation/metrics/field_grounding/core.py +437 -0
- parse_bench/evaluation/metrics/field_grounding/extract_adapter.py +1224 -0
- parse_bench/evaluation/metrics/field_grounding/parse_adapter.py +697 -0
- parse_bench/evaluation/metrics/field_grounding/rule_filters.py +19 -0
- parse_bench/evaluation/metrics/field_grounding/value_compare.py +190 -0
- parse_bench/evaluation/metrics/layoutdet/__init__.py +17 -0
- parse_bench/evaluation/metrics/layoutdet/classification_utils.py +300 -0
- parse_bench/evaluation/metrics/layoutdet/iou.py +76 -0
- parse_bench/evaluation/metrics/parse/__init__.py +5 -0
- parse_bench/evaluation/metrics/parse/_vendor_grits_reference.py +531 -0
- parse_bench/evaluation/metrics/parse/cross_page_table_consistency.py +165 -0
- parse_bench/evaluation/metrics/parse/emphasis_spans.py +242 -0
- parse_bench/evaluation/metrics/parse/fast_tree_edit.py +282 -0
- parse_bench/evaluation/metrics/parse/grits_metric.py +1125 -0
- parse_bench/evaluation/metrics/parse/grits_reference_metric.py +142 -0
- parse_bench/evaluation/metrics/parse/header_accuracy_metric.py +1662 -0
- parse_bench/evaluation/metrics/parse/llm_normalization/__init__.py +51 -0
- parse_bench/evaluation/metrics/parse/llm_normalization/base.py +125 -0
- parse_bench/evaluation/metrics/parse/llm_normalization/config.py +44 -0
- parse_bench/evaluation/metrics/parse/llm_normalization/postprocess.py +322 -0
- parse_bench/evaluation/metrics/parse/llm_normalization/strategy_judge.py +541 -0
- parse_bench/evaluation/metrics/parse/mermaid_graph.py +682 -0
- parse_bench/evaluation/metrics/parse/rule_based_judge_metric.py +56 -0
- parse_bench/evaluation/metrics/parse/rule_based_metric.py +434 -0
- parse_bench/evaluation/metrics/parse/rules_bag.py +1161 -0
- parse_bench/evaluation/metrics/parse/rules_base.py +751 -0
- parse_bench/evaluation/metrics/parse/rules_chart.py +1556 -0
- parse_bench/evaluation/metrics/parse/rules_diagram.py +591 -0
- parse_bench/evaluation/metrics/parse/rules_form.py +2274 -0
- parse_bench/evaluation/metrics/parse/rules_formatting.py +1500 -0
- parse_bench/evaluation/metrics/parse/rules_heading.py +228 -0
- parse_bench/evaluation/metrics/parse/rules_list.py +226 -0
- parse_bench/evaluation/metrics/parse/rules_page_decoration.py +276 -0
- parse_bench/evaluation/metrics/parse/rules_table.py +1666 -0
- parse_bench/evaluation/metrics/parse/rules_text.py +340 -0
- parse_bench/evaluation/metrics/parse/rules_watermark.py +105 -0
- parse_bench/evaluation/metrics/parse/structural_consistency_metric.py +251 -0
- parse_bench/evaluation/metrics/parse/table_extraction.py +152 -0
- parse_bench/evaluation/metrics/parse/table_merging.py +195 -0
- parse_bench/evaluation/metrics/parse/table_pairing.py +87 -0
- parse_bench/evaluation/metrics/parse/table_parsing.py +955 -0
- parse_bench/evaluation/metrics/parse/table_record_match_metric.py +1453 -0
- parse_bench/evaluation/metrics/parse/table_splitting.py +301 -0
- parse_bench/evaluation/metrics/parse/table_title_stripping.py +530 -0
- parse_bench/evaluation/metrics/parse/teds_metric.py +600 -0
- parse_bench/evaluation/metrics/parse/test_rules.py +120 -0
- parse_bench/evaluation/metrics/parse/test_types.py +103 -0
- parse_bench/evaluation/metrics/parse/text_content_projection.py +175 -0
- parse_bench/evaluation/metrics/parse/text_similarity_metric.py +61 -0
- parse_bench/evaluation/metrics/parse/utils.py +885 -0
- parse_bench/evaluation/metrics/qa/__init__.py +5 -0
- parse_bench/evaluation/metrics/qa/answer_comparison.py +380 -0
- parse_bench/evaluation/qa/__init__.py +5 -0
- parse_bench/evaluation/qa/llm_service.py +335 -0
- parse_bench/evaluation/reports/__init__.py +8 -0
- parse_bench/evaluation/reports/csv.py +64 -0
- parse_bench/evaluation/reports/html.py +338 -0
- parse_bench/evaluation/reports/markdown.py +98 -0
- parse_bench/evaluation/reports/rule_csv.py +22 -0
- parse_bench/evaluation/runner.py +1864 -0
- parse_bench/evaluation/stats.py +104 -0
- parse_bench/extensions.py +72 -0
- parse_bench/inference/__init__.py +33 -0
- parse_bench/inference/chunkr_layout_extraction.py +160 -0
- parse_bench/inference/cli.py +484 -0
- parse_bench/inference/layout_extraction.py +422 -0
- parse_bench/inference/pipelines/__init__.py +59 -0
- parse_bench/inference/pipelines/extract.py +39 -0
- parse_bench/inference/pipelines/layout.py +142 -0
- parse_bench/inference/pipelines/parse.py +2603 -0
- parse_bench/inference/pipelines.py +0 -0
- parse_bench/inference/providers/__init__.py +28 -0
- parse_bench/inference/providers/base.py +196 -0
- parse_bench/inference/providers/cancellation.py +137 -0
- parse_bench/inference/providers/extract/__init__.py +22 -0
- parse_bench/inference/providers/extract/citations.py +549 -0
- parse_bench/inference/providers/extract/extend.py +851 -0
- parse_bench/inference/providers/extract/llamaextract_v2_api.py +583 -0
- parse_bench/inference/providers/layoutdet/__init__.py +25 -0
- parse_bench/inference/providers/layoutdet/adapters.py +946 -0
- parse_bench/inference/providers/layoutdet/base.py +203 -0
- parse_bench/inference/providers/layoutdet/chandra.py +449 -0
- parse_bench/inference/providers/layoutdet/docling.py +125 -0
- parse_bench/inference/providers/layoutdet/dots_ocr.py +606 -0
- parse_bench/inference/providers/layoutdet/layout_v3.py +137 -0
- parse_bench/inference/providers/layoutdet/layout_v3_byoc.py +204 -0
- parse_bench/inference/providers/layoutdet/paddle.py +117 -0
- parse_bench/inference/providers/layoutdet/qwen3vl.py +360 -0
- parse_bench/inference/providers/layoutdet/surya.py +250 -0
- parse_bench/inference/providers/layoutdet/yolo.py +109 -0
- parse_bench/inference/providers/parse/__init__.py +64 -0
- parse_bench/inference/providers/parse/_docling_common.py +233 -0
- parse_bench/inference/providers/parse/_layout_utils.py +611 -0
- parse_bench/inference/providers/parse/amazon_nova.py +515 -0
- parse_bench/inference/providers/parse/anthropic.py +882 -0
- parse_bench/inference/providers/parse/azure_document_intelligence.py +700 -0
- parse_bench/inference/providers/parse/chandra2.py +633 -0
- parse_bench/inference/providers/parse/chunkr.py +268 -0
- parse_bench/inference/providers/parse/databricks_ai_parse.py +724 -0
- parse_bench/inference/providers/parse/datalab.py +370 -0
- parse_bench/inference/providers/parse/deepseekocr2.py +382 -0
- parse_bench/inference/providers/parse/docling.py +281 -0
- parse_bench/inference/providers/parse/docling_serve.py +289 -0
- parse_bench/inference/providers/parse/dots_ocr.py +574 -0
- parse_bench/inference/providers/parse/extend_parse.py +710 -0
- parse_bench/inference/providers/parse/falconocr.py +436 -0
- parse_bench/inference/providers/parse/florin_parser_nano.py +559 -0
- parse_bench/inference/providers/parse/gemma4.py +472 -0
- parse_bench/inference/providers/parse/glm_zai.py +229 -0
- parse_bench/inference/providers/parse/google.py +1125 -0
- parse_bench/inference/providers/parse/google_agentic_vision.py +819 -0
- parse_bench/inference/providers/parse/google_docai.py +776 -0
- parse_bench/inference/providers/parse/google_docai_layout_normalization.py +573 -0
- parse_bench/inference/providers/parse/granite_vision.py +515 -0
- parse_bench/inference/providers/parse/infinity_parser2.py +704 -0
- parse_bench/inference/providers/parse/kdl_frontier_nano.py +3327 -0
- parse_bench/inference/providers/parse/landingai.py +452 -0
- parse_bench/inference/providers/parse/liteparse.py +350 -0
- parse_bench/inference/providers/parse/llamaparse.py +677 -0
- parse_bench/inference/providers/parse/llamaparse_v2_normalization.py +1013 -0
- parse_bench/inference/providers/parse/markitdown.py +138 -0
- parse_bench/inference/providers/parse/mineru25.py +405 -0
- parse_bench/inference/providers/parse/mineru2605pro.py +432 -0
- parse_bench/inference/providers/parse/mineru_diffusion.py +371 -0
- parse_bench/inference/providers/parse/mistral_ocr.py +546 -0
- parse_bench/inference/providers/parse/nemotron_omni.py +473 -0
- parse_bench/inference/providers/parse/oi_parser.py +222 -0
- parse_bench/inference/providers/parse/openai.py +740 -0
- parse_bench/inference/providers/parse/opendataloader.py +152 -0
- parse_bench/inference/providers/parse/paddleocr.py +624 -0
- parse_bench/inference/providers/parse/pdf_inspector.py +142 -0
- parse_bench/inference/providers/parse/pulse.py +785 -0
- parse_bench/inference/providers/parse/pymupdf.py +207 -0
- parse_bench/inference/providers/parse/pymupdf4llm.py +356 -0
- parse_bench/inference/providers/parse/pypdf.py +179 -0
- parse_bench/inference/providers/parse/qwen.py +678 -0
- parse_bench/inference/providers/parse/rakedoc_nano.py +70 -0
- parse_bench/inference/providers/parse/reducto.py +546 -0
- parse_bench/inference/providers/parse/surya2.py +372 -0
- parse_bench/inference/providers/parse/tesseract.py +301 -0
- parse_bench/inference/providers/parse/textract.py +694 -0
- parse_bench/inference/providers/parse/unlimitedocr.py +346 -0
- parse_bench/inference/providers/parse/unstructured.py +485 -0
- parse_bench/inference/providers/parse/warp_ingest.py +199 -0
- parse_bench/inference/providers/registry.py +49 -0
- parse_bench/inference/renormalize.py +170 -0
- parse_bench/inference/runner.py +2023 -0
- parse_bench/layout_label_mapping.py +424 -0
- parse_bench/layout_projection.py +179 -0
- parse_bench/pipeline/__init__.py +1 -0
- parse_bench/pipeline/cli.py +549 -0
- parse_bench/schemas/__init__.py +33 -0
- parse_bench/schemas/evaluation.py +93 -0
- parse_bench/schemas/extract_output.py +36 -0
- parse_bench/schemas/layout_detection_output.py +545 -0
- parse_bench/schemas/layout_ontology.py +315 -0
- parse_bench/schemas/metrics.py +69 -0
- parse_bench/schemas/parse_output.py +152 -0
- parse_bench/schemas/pipeline.py +22 -0
- parse_bench/schemas/pipeline_io.py +106 -0
- parse_bench/schemas/product.py +97 -0
- parse_bench/test_cases/__init__.py +25 -0
- parse_bench/test_cases/bbox_value_strict_comparator.py +880 -0
- parse_bench/test_cases/extract_field_paths.py +164 -0
- parse_bench/test_cases/layout_attribution_generation.py +287 -0
- parse_bench/test_cases/loader.py +652 -0
- parse_bench/test_cases/parse_rule_schemas.py +1071 -0
- parse_bench/test_cases/rule_filters.py +32 -0
- parse_bench/test_cases/rule_ids.py +107 -0
- parse_bench/test_cases/schema.py +427 -0
- parse_bench/utils/__init__.py +15 -0
- parse_bench/utils/gemini_layout_utils.py +670 -0
- parse_bench/utils/text_aggregation.py +100 -0
- parse_bench-1.0.0.dist-info/METADATA +476 -0
- parse_bench-1.0.0.dist-info/RECORD +227 -0
- parse_bench-1.0.0.dist-info/WHEEL +4 -0
- parse_bench-1.0.0.dist-info/entry_points.txt +2 -0
- parse_bench-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Provider for Chunkr PARSE."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from parse_bench.inference.providers.base import (
|
|
9
|
+
Provider,
|
|
10
|
+
ProviderConfigError,
|
|
11
|
+
ProviderPermanentError,
|
|
12
|
+
ProviderTransientError,
|
|
13
|
+
)
|
|
14
|
+
from parse_bench.inference.providers.registry import register_provider
|
|
15
|
+
from parse_bench.schemas.parse_output import ParseOutput
|
|
16
|
+
from parse_bench.schemas.pipeline import PipelineSpec
|
|
17
|
+
from parse_bench.schemas.pipeline_io import (
|
|
18
|
+
InferenceRequest,
|
|
19
|
+
InferenceResult,
|
|
20
|
+
RawInferenceResult,
|
|
21
|
+
)
|
|
22
|
+
from parse_bench.schemas.product import ProductType
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@register_provider("chunkr")
|
|
26
|
+
class ChunkrProvider(Provider):
|
|
27
|
+
"""
|
|
28
|
+
Provider for Chunkr PARSE.
|
|
29
|
+
|
|
30
|
+
Uses the Chunkr API for parsing documents with HTML table output.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, provider_name: str, base_config: dict[str, Any] | None = None):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the provider.
|
|
36
|
+
|
|
37
|
+
:param provider_name: Name of the provider
|
|
38
|
+
:param base_config: Optional configuration with:
|
|
39
|
+
- `api_key`: Chunkr API key (defaults to CHUNKR_API_KEY env var)
|
|
40
|
+
- `segmentation_strategy`: "LayoutAnalysis" or "Page" (default: "LayoutAnalysis")
|
|
41
|
+
- `ocr_strategy`: "Auto" or "All" (default: "Auto")
|
|
42
|
+
- `high_resolution`: Whether to use high resolution processing (default: False)
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(provider_name, base_config)
|
|
45
|
+
|
|
46
|
+
# Get API key
|
|
47
|
+
self._api_key = self.base_config.get("api_key") or os.getenv("CHUNKR_API_KEY")
|
|
48
|
+
if not self._api_key:
|
|
49
|
+
raise ProviderConfigError(
|
|
50
|
+
"Chunkr API key is required. Set CHUNKR_API_KEY environment variable or pass api_key in base_config."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Configuration options
|
|
54
|
+
self._segmentation_strategy = self.base_config.get("segmentation_strategy", "LayoutAnalysis")
|
|
55
|
+
self._ocr_strategy = self.base_config.get("ocr_strategy", "Auto")
|
|
56
|
+
self._high_resolution = self.base_config.get("high_resolution", False)
|
|
57
|
+
|
|
58
|
+
async def _parse_document_async(self, file_path: str) -> dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Parse a document using Chunkr API (async).
|
|
61
|
+
|
|
62
|
+
:param file_path: Path to the document file
|
|
63
|
+
:return: Raw API response as dictionary
|
|
64
|
+
:raises ProviderError: For any API errors
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
from chunkr_ai import Chunkr # type: ignore[import-untyped]
|
|
68
|
+
from chunkr_ai.models import ( # type: ignore[import-untyped]
|
|
69
|
+
Configuration,
|
|
70
|
+
OcrStrategy,
|
|
71
|
+
SegmentationStrategy,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Map string config values to enum values
|
|
75
|
+
segmentation_map = {
|
|
76
|
+
"layoutanalysis": SegmentationStrategy.LAYOUT_ANALYSIS,
|
|
77
|
+
"layout_analysis": SegmentationStrategy.LAYOUT_ANALYSIS,
|
|
78
|
+
"page": SegmentationStrategy.PAGE,
|
|
79
|
+
}
|
|
80
|
+
ocr_map = {
|
|
81
|
+
"auto": OcrStrategy.AUTO,
|
|
82
|
+
"all": OcrStrategy.ALL,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
seg_strategy = segmentation_map.get(
|
|
86
|
+
self._segmentation_strategy.lower(),
|
|
87
|
+
SegmentationStrategy.LAYOUT_ANALYSIS,
|
|
88
|
+
)
|
|
89
|
+
ocr_strategy = ocr_map.get(
|
|
90
|
+
self._ocr_strategy.lower(),
|
|
91
|
+
OcrStrategy.AUTO,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Initialize client
|
|
95
|
+
client = Chunkr(api_key=self._api_key)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# Configure for HTML output (tables are HTML by default in Chunkr)
|
|
99
|
+
config = Configuration(
|
|
100
|
+
segmentation_strategy=seg_strategy,
|
|
101
|
+
ocr_strategy=ocr_strategy,
|
|
102
|
+
high_resolution=self._high_resolution,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Upload and process
|
|
106
|
+
# Note: The Chunkr SDK is async-native with an @anywhere() decorator.
|
|
107
|
+
# We must call it directly as async (not via asyncio.to_thread) to avoid
|
|
108
|
+
# race conditions with the SDK's global _sync_loop singleton.
|
|
109
|
+
task = await client.upload(file_path, config)
|
|
110
|
+
|
|
111
|
+
# poll() ensures task is complete (no-op if already complete)
|
|
112
|
+
if hasattr(task, "poll") and callable(task.poll):
|
|
113
|
+
task = await task.poll()
|
|
114
|
+
|
|
115
|
+
# Extract raw response
|
|
116
|
+
if hasattr(task, "model_dump"):
|
|
117
|
+
raw_response = task.model_dump()
|
|
118
|
+
elif hasattr(task, "dict"):
|
|
119
|
+
raw_response = task.dict()
|
|
120
|
+
else:
|
|
121
|
+
# Manual extraction as fallback
|
|
122
|
+
raw_response = {
|
|
123
|
+
"task_id": getattr(task, "task_id", None),
|
|
124
|
+
"status": getattr(task, "status", None),
|
|
125
|
+
"output": getattr(task, "output", None),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Get HTML content (includes tables as HTML)
|
|
129
|
+
try:
|
|
130
|
+
html_content = await task.html() if hasattr(task, "html") else ""
|
|
131
|
+
except Exception:
|
|
132
|
+
html_content = ""
|
|
133
|
+
raw_response["_html_content"] = html_content
|
|
134
|
+
|
|
135
|
+
# Get markdown content as alternative
|
|
136
|
+
try:
|
|
137
|
+
markdown_content = await task.markdown() if hasattr(task, "markdown") else ""
|
|
138
|
+
except Exception:
|
|
139
|
+
markdown_content = ""
|
|
140
|
+
raw_response["_markdown_content"] = markdown_content
|
|
141
|
+
|
|
142
|
+
# Store configuration for reference
|
|
143
|
+
raw_response["_config"] = {
|
|
144
|
+
"segmentation_strategy": self._segmentation_strategy,
|
|
145
|
+
"ocr_strategy": self._ocr_strategy,
|
|
146
|
+
"high_resolution": self._high_resolution,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
result: dict[str, Any] = raw_response
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
finally:
|
|
153
|
+
# Close the client (async method with @anywhere decorator)
|
|
154
|
+
await client.close()
|
|
155
|
+
|
|
156
|
+
except ImportError as e:
|
|
157
|
+
raise ProviderConfigError("chunkr-ai package not installed. Run: pip install chunkr-ai") from e
|
|
158
|
+
except Exception as e:
|
|
159
|
+
error_str = str(e).lower()
|
|
160
|
+
transient_keywords = ["timeout", "network", "connection", "503", "502", "504"]
|
|
161
|
+
if any(keyword in error_str for keyword in transient_keywords):
|
|
162
|
+
raise ProviderTransientError(f"Transient error during parsing: {e}") from e
|
|
163
|
+
raise ProviderPermanentError(f"Error during parsing: {e}") from e
|
|
164
|
+
|
|
165
|
+
def run_inference(self, pipeline: PipelineSpec, request: InferenceRequest) -> RawInferenceResult:
|
|
166
|
+
"""
|
|
167
|
+
Run inference and return raw results.
|
|
168
|
+
|
|
169
|
+
:param pipeline: Pipeline specification
|
|
170
|
+
:param request: Inference request
|
|
171
|
+
:return: Raw inference result
|
|
172
|
+
:raises ProviderError: For any provider-related failures
|
|
173
|
+
"""
|
|
174
|
+
if request.product_type != ProductType.PARSE:
|
|
175
|
+
raise ProviderPermanentError(f"ChunkrProvider only supports PARSE product type, got {request.product_type}")
|
|
176
|
+
|
|
177
|
+
started_at = datetime.now()
|
|
178
|
+
|
|
179
|
+
file_path = Path(request.source_file_path)
|
|
180
|
+
if not file_path.exists():
|
|
181
|
+
raise ProviderPermanentError(f"File not found: {file_path}")
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
raw_output = self.run_async_from_sync(self._parse_document_async(str(file_path)))
|
|
185
|
+
|
|
186
|
+
completed_at = datetime.now()
|
|
187
|
+
latency_ms = int((completed_at - started_at).total_seconds() * 1000)
|
|
188
|
+
|
|
189
|
+
return RawInferenceResult(
|
|
190
|
+
request=request,
|
|
191
|
+
pipeline=pipeline,
|
|
192
|
+
pipeline_name=pipeline.pipeline_name,
|
|
193
|
+
product_type=request.product_type,
|
|
194
|
+
raw_output=raw_output,
|
|
195
|
+
started_at=started_at,
|
|
196
|
+
completed_at=completed_at,
|
|
197
|
+
latency_in_ms=latency_ms,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
except (ProviderPermanentError, ProviderTransientError):
|
|
201
|
+
raise
|
|
202
|
+
except Exception as e:
|
|
203
|
+
raise ProviderPermanentError(f"Unexpected error during inference: {e}") from e
|
|
204
|
+
|
|
205
|
+
def normalize(self, raw_result: RawInferenceResult) -> InferenceResult:
|
|
206
|
+
"""
|
|
207
|
+
Normalize raw inference result to produce ParseOutput.
|
|
208
|
+
|
|
209
|
+
:param raw_result: Raw inference result from run_inference()
|
|
210
|
+
:return: Inference result with both raw and normalized outputs
|
|
211
|
+
:raises ProviderError: For any normalization failures
|
|
212
|
+
"""
|
|
213
|
+
if raw_result.product_type != ProductType.PARSE:
|
|
214
|
+
raise ProviderPermanentError(
|
|
215
|
+
f"ChunkrProvider only supports PARSE product type, got {raw_result.product_type}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Extract HTML content (preferred for table tests)
|
|
219
|
+
html_content = raw_result.raw_output.get("_html_content", "")
|
|
220
|
+
|
|
221
|
+
# Fallback: extract from output.chunks if _html_content not available
|
|
222
|
+
if not html_content:
|
|
223
|
+
output = raw_result.raw_output.get("output", {})
|
|
224
|
+
chunks = output.get("chunks", [])
|
|
225
|
+
# Concatenate HTML from all chunks/segments
|
|
226
|
+
html_parts = []
|
|
227
|
+
for chunk in chunks:
|
|
228
|
+
segments = chunk.get("segments", [])
|
|
229
|
+
for segment in segments:
|
|
230
|
+
html = segment.get("html", "")
|
|
231
|
+
if html:
|
|
232
|
+
html_parts.append(html)
|
|
233
|
+
html_content = "\n".join(html_parts)
|
|
234
|
+
|
|
235
|
+
# If still no HTML content, try markdown
|
|
236
|
+
if not html_content:
|
|
237
|
+
html_content = raw_result.raw_output.get("_markdown_content", "")
|
|
238
|
+
|
|
239
|
+
# Final fallback: concatenate content from chunks
|
|
240
|
+
if not html_content:
|
|
241
|
+
output = raw_result.raw_output.get("output", {})
|
|
242
|
+
chunks = output.get("chunks", [])
|
|
243
|
+
content_parts = []
|
|
244
|
+
for chunk in chunks:
|
|
245
|
+
content = chunk.get("content", "")
|
|
246
|
+
if content:
|
|
247
|
+
content_parts.append(content)
|
|
248
|
+
html_content = "\n".join(content_parts)
|
|
249
|
+
|
|
250
|
+
output = ParseOutput(
|
|
251
|
+
task_type="parse",
|
|
252
|
+
example_id=raw_result.request.example_id,
|
|
253
|
+
pipeline_name=raw_result.pipeline_name,
|
|
254
|
+
pages=[], # Chunkr doesn't provide per-page split by default
|
|
255
|
+
markdown=html_content, # HTML content goes here for table tests
|
|
256
|
+
job_id=raw_result.raw_output.get("task_id"),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return InferenceResult(
|
|
260
|
+
request=raw_result.request,
|
|
261
|
+
pipeline_name=raw_result.pipeline_name,
|
|
262
|
+
product_type=raw_result.product_type,
|
|
263
|
+
raw_output=raw_result.raw_output,
|
|
264
|
+
output=output,
|
|
265
|
+
started_at=raw_result.started_at,
|
|
266
|
+
completed_at=raw_result.completed_at,
|
|
267
|
+
latency_in_ms=raw_result.latency_in_ms,
|
|
268
|
+
)
|