parse-bench 1.0.6__tar.gz → 1.0.7__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.
Files changed (422) hide show
  1. {parse_bench-1.0.6 → parse_bench-1.0.7}/CHANGELOG.md +7 -0
  2. {parse_bench-1.0.6 → parse_bench-1.0.7}/PKG-INFO +1 -1
  3. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/__init__.py +1 -1
  4. parse_bench-1.0.7/src/parse_bench/evaluation/_registration.py +37 -0
  5. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_adapters/registry.py +11 -13
  6. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_label_mappers/registry.py +12 -13
  7. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_base.py +24 -8
  8. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_formatting.py +41 -11
  9. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_record_match_metric.py +48 -76
  10. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_splitting.py +65 -41
  11. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/utils.py +12 -12
  12. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/extensions.py +12 -4
  13. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/__init__.py +6 -7
  14. parse_bench-1.0.7/src/parse_bench/inference/providers/extract/__init__.py +29 -0
  15. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/extract/citations.py +3 -1
  16. parse_bench-1.0.7/src/parse_bench/inference/providers/layoutdet/__init__.py +33 -0
  17. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/__init__.py +12 -5
  18. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/registry.py +26 -2
  19. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/extract_output.py +2 -2
  20. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/parse_rule_schemas.py +10 -4
  21. parse_bench-1.0.7/tests/parse_bench/evaluation/metrics/parse/test_attributed_inline_tags.py +95 -0
  22. parse_bench-1.0.7/tests/parse_bench/evaluation/metrics/parse/test_mark_color_rule.py +87 -0
  23. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_splitting.py +158 -0
  24. parse_bench-1.0.7/tests/parse_bench/test_extensions.py +369 -0
  25. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_extract_integration.py +35 -0
  26. parse_bench-1.0.6/src/parse_bench/inference/providers/extract/__init__.py +0 -22
  27. parse_bench-1.0.6/src/parse_bench/inference/providers/layoutdet/__init__.py +0 -25
  28. parse_bench-1.0.6/tests/parse_bench/test_extensions.py +0 -149
  29. {parse_bench-1.0.6 → parse_bench-1.0.7}/.gitignore +0 -0
  30. {parse_bench-1.0.6 → parse_bench-1.0.7}/LICENSE +0 -0
  31. {parse_bench-1.0.6 → parse_bench-1.0.7}/README.md +0 -0
  32. {parse_bench-1.0.6 → parse_bench-1.0.7}/pyproject.toml +0 -0
  33. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/__init__.py +0 -0
  34. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/aggregation_report.py +0 -0
  35. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/cli.py +0 -0
  36. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/comparison.py +0 -0
  37. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/comparison_core.py +0 -0
  38. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/comparison_report.py +0 -0
  39. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/detailed_report.py +0 -0
  40. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/leaderboard_report.py +0 -0
  41. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/analysis/metric_definitions.py +0 -0
  42. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/cli.py +0 -0
  43. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/data/__init__.py +0 -0
  44. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/data/cli.py +0 -0
  45. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/data/download.py +0 -0
  46. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/data/repetition.py +0 -0
  47. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/__init__.py +0 -0
  48. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/cli.py +0 -0
  49. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/__init__.py +0 -0
  50. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/base.py +0 -0
  51. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/extract.py +0 -0
  52. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/layoutdet.py +0 -0
  53. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/parse.py +0 -0
  54. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/evaluators/qa.py +0 -0
  55. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_adapters/__init__.py +0 -0
  56. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_adapters/adapters.py +0 -0
  57. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_adapters/base.py +0 -0
  58. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_label_mappers/__init__.py +0 -0
  59. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_label_mappers/base.py +0 -0
  60. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_label_mappers/mappers.py +0 -0
  61. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/layout_label_mappers/projection.py +0 -0
  62. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metric_aggregation.py +0 -0
  63. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/__init__.py +0 -0
  64. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/__init__.py +0 -0
  65. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/constants.py +0 -0
  66. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/core.py +0 -0
  67. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/evaluate.py +0 -0
  68. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/geometry.py +0 -0
  69. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/attribution/text_utils.py +0 -0
  70. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/base.py +0 -0
  71. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/downstream/__init__.py +0 -0
  72. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/__init__.py +0 -0
  73. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/json_subset_match.py +0 -0
  74. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/json_subset_match_metric.py +0 -0
  75. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/list_unwrap.py +0 -0
  76. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/rule_based_metric.py +0 -0
  77. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/test_rules.py +0 -0
  78. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/extract/test_types.py +0 -0
  79. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/__init__.py +0 -0
  80. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/core.py +0 -0
  81. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/extract_adapter.py +0 -0
  82. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/parse_adapter.py +0 -0
  83. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/rule_filters.py +0 -0
  84. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/field_grounding/value_compare.py +0 -0
  85. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/layoutdet/__init__.py +0 -0
  86. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/layoutdet/classification_utils.py +0 -0
  87. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/layoutdet/furniture.py +0 -0
  88. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/layoutdet/iou.py +0 -0
  89. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/layoutdet/overlap.py +0 -0
  90. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/__init__.py +0 -0
  91. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/_vendor_grits_reference.py +0 -0
  92. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/chart_json_to_html.py +0 -0
  93. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/cross_page_table_consistency.py +0 -0
  94. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/emphasis_spans.py +0 -0
  95. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/fast_tree_edit.py +0 -0
  96. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/grits_metric.py +0 -0
  97. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/grits_reference_metric.py +0 -0
  98. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/header_accuracy_metric.py +0 -0
  99. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/llm_normalization/__init__.py +0 -0
  100. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/llm_normalization/base.py +0 -0
  101. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/llm_normalization/config.py +0 -0
  102. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/llm_normalization/postprocess.py +0 -0
  103. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/llm_normalization/strategy_judge.py +0 -0
  104. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/mermaid_graph.py +0 -0
  105. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rule_based_judge_metric.py +0 -0
  106. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rule_based_metric.py +0 -0
  107. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_bag.py +0 -0
  108. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_chart.py +0 -0
  109. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_diagram.py +0 -0
  110. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_form.py +0 -0
  111. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_heading.py +0 -0
  112. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_list.py +0 -0
  113. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_page_decoration.py +0 -0
  114. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_repetition.py +0 -0
  115. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_table.py +0 -0
  116. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_text.py +0 -0
  117. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/rules_watermark.py +0 -0
  118. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/structural_consistency_metric.py +0 -0
  119. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_extraction.py +0 -0
  120. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_merging.py +0 -0
  121. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_pairing.py +0 -0
  122. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_parsing.py +0 -0
  123. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/table_title_stripping.py +0 -0
  124. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/teds_metric.py +0 -0
  125. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/test_rules.py +0 -0
  126. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/test_types.py +0 -0
  127. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/text_content_projection.py +0 -0
  128. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/text_similarity_metric.py +0 -0
  129. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/parse/text_v21.py +0 -0
  130. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/qa/__init__.py +0 -0
  131. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/metrics/qa/answer_comparison.py +0 -0
  132. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/qa/__init__.py +0 -0
  133. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/qa/llm_service.py +0 -0
  134. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/reports/__init__.py +0 -0
  135. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/reports/csv.py +0 -0
  136. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/reports/html.py +0 -0
  137. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/reports/markdown.py +0 -0
  138. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/reports/rule_csv.py +0 -0
  139. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/runner.py +0 -0
  140. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/evaluation/stats.py +0 -0
  141. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/geometry/__init__.py +0 -0
  142. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/geometry/rotated_bbox.py +0 -0
  143. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/__init__.py +0 -0
  144. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/chunkr_layout_extraction.py +0 -0
  145. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/cli.py +0 -0
  146. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/layout_extraction.py +0 -0
  147. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/pipelines/__init__.py +0 -0
  148. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/pipelines/extract.py +0 -0
  149. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/pipelines/layout.py +0 -0
  150. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/pipelines/parse.py +0 -0
  151. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/pipelines.py +0 -0
  152. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/base.py +0 -0
  153. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/cancellation.py +0 -0
  154. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/extract/extend.py +0 -0
  155. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/extract/llamaextract_v2_api.py +0 -0
  156. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/adapters.py +0 -0
  157. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/base.py +0 -0
  158. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/chandra.py +0 -0
  159. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/docling.py +0 -0
  160. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/dots_ocr.py +0 -0
  161. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/layout_v3.py +0 -0
  162. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/layout_v3_byoc.py +0 -0
  163. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/paddle.py +0 -0
  164. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/qwen3vl.py +0 -0
  165. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/surya.py +0 -0
  166. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/layoutdet/yolo.py +0 -0
  167. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/_docling_common.py +0 -0
  168. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/_layout_utils.py +0 -0
  169. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/_unlimitedocr2_convert.py +0 -0
  170. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/aeddix_alpine_ocr_kdl.py +0 -0
  171. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/amazon_nova.py +0 -0
  172. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/anthropic.py +0 -0
  173. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/anyformat.py +0 -0
  174. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/azure_document_intelligence.py +0 -0
  175. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/chandra2.py +0 -0
  176. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/chunkr.py +0 -0
  177. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/cohere_parse.py +0 -0
  178. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/databricks_ai_parse.py +0 -0
  179. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/datalab.py +0 -0
  180. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/deepseek.py +0 -0
  181. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/deepseekocr2.py +0 -0
  182. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/docai.py +0 -0
  183. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/docling.py +0 -0
  184. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/docling_serve.py +0 -0
  185. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/dots_ocr.py +0 -0
  186. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/extend_parse.py +0 -0
  187. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/falconocr.py +0 -0
  188. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/florin_parser_nano.py +0 -0
  189. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/gemma4.py +0 -0
  190. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/glm_zai.py +0 -0
  191. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/google.py +0 -0
  192. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/google_agentic_vision.py +0 -0
  193. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/google_docai.py +0 -0
  194. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/google_docai_layout_normalization.py +0 -0
  195. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/granite_vision.py +0 -0
  196. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/hpd_parsing.py +0 -0
  197. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/hunyuanocr.py +0 -0
  198. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/infinity_parser2.py +0 -0
  199. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/jinaocr.py +0 -0
  200. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/kdl_frontier_nano.py +0 -0
  201. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/landingai.py +0 -0
  202. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/lightonocr-3.py +0 -0
  203. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/liteparse.py +0 -0
  204. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/llamaparse.py +0 -0
  205. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/llamaparse_v2_normalization.py +0 -0
  206. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/markitdown.py +0 -0
  207. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/mineru25.py +0 -0
  208. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/mineru2605pro.py +0 -0
  209. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/mineru_diffusion.py +0 -0
  210. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/minicpm_ocr.py +0 -0
  211. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/mistral_ocr.py +0 -0
  212. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/nemotron_omni.py +0 -0
  213. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/nemotron_parse_3_5.py +0 -0
  214. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/nutrient_dws.py +0 -0
  215. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/oi_parser.py +0 -0
  216. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/openai.py +0 -0
  217. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/opendataloader.py +0 -0
  218. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/ovisocr2.py +0 -0
  219. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/ovisocr3.py +0 -0
  220. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/paddleocr.py +0 -0
  221. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/pdf_inspector.py +0 -0
  222. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/pulse.py +0 -0
  223. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/pymupdf.py +0 -0
  224. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/pymupdf4llm.py +0 -0
  225. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/pypdf.py +0 -0
  226. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/qwen.py +0 -0
  227. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/rakedoc_nano.py +0 -0
  228. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/reducto.py +0 -0
  229. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/surya2.py +0 -0
  230. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/teleocr.py +0 -0
  231. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/tesseract.py +0 -0
  232. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/textract.py +0 -0
  233. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/unlimitedocr.py +0 -0
  234. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/unlimitedocr2.py +0 -0
  235. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/unstructured.py +0 -0
  236. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/upstage.py +0 -0
  237. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/upstage_normalization.py +0 -0
  238. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/warp_ingest.py +0 -0
  239. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/providers/parse/wevisdoc.py +0 -0
  240. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/renormalize.py +0 -0
  241. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/inference/runner.py +0 -0
  242. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/layout_label_mapping.py +0 -0
  243. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/layout_projection.py +0 -0
  244. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/pipeline/__init__.py +0 -0
  245. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/pipeline/cli.py +0 -0
  246. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/py.typed +0 -0
  247. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/__init__.py +0 -0
  248. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/evaluation.py +0 -0
  249. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/layout_detection_output.py +0 -0
  250. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/layout_ontology.py +0 -0
  251. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/metrics.py +0 -0
  252. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/parse_output.py +0 -0
  253. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/pipeline.py +0 -0
  254. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/pipeline_io.py +0 -0
  255. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/schemas/product.py +0 -0
  256. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/__init__.py +0 -0
  257. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/bbox_value_strict_comparator.py +0 -0
  258. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/extract_field_paths.py +0 -0
  259. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/layout_attribution_generation.py +0 -0
  260. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/loader.py +0 -0
  261. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/rule_filters.py +0 -0
  262. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/rule_ids.py +0 -0
  263. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/test_cases/schema.py +0 -0
  264. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/utils/__init__.py +0 -0
  265. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/utils/gemini_layout_utils.py +0 -0
  266. {parse_bench-1.0.6 → parse_bench-1.0.7}/src/parse_bench/utils/text_aggregation.py +0 -0
  267. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/analysis/test_comparison_consistency.py +0 -0
  268. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/analysis/test_comparison_core.py +0 -0
  269. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/analysis/test_comparison_directory_suffix.py +0 -0
  270. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/analysis/test_detailed_report.py +0 -0
  271. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/evaluators/test_extract_empty_expected_output.py +0 -0
  272. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/evaluators/test_layoutdet_missing_ro_index.py +0 -0
  273. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/evaluators/test_parse_evaluator_layout_rule_filter.py +0 -0
  274. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/evaluators/test_parse_evaluator_styling_rollup.py +0 -0
  275. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/evaluators/test_parse_evaluator_table_pages.py +0 -0
  276. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_anyformat_prediction_granularity.py +0 -0
  277. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_docling_parse_layout_adapter.py +0 -0
  278. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_llamaparse_attribution_scope.py +0 -0
  279. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_llamaparse_canonical_layout_pages.py +0 -0
  280. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_registry.py +0 -0
  281. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_retained_typed_writers.py +0 -0
  282. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_adapters/test_rotated_attribution_adapters.py +0 -0
  283. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_label_mappers/test_llamaparse_label_mapper.py +0 -0
  284. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_label_mappers/test_projection_attributes.py +0 -0
  285. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_label_mappers/test_projection_dual_read.py +0 -0
  286. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/layout_label_mappers/test_pymupdf4llm_label_mapper.py +0 -0
  287. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/attribution/test_attribution_overclaim.py +0 -0
  288. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/attribution/test_rotated_attribution.py +0 -0
  289. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/extract/test_json_subset_match.py +0 -0
  290. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/field_grounding/test_extract_adapter.py +0 -0
  291. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/layoutdet/test_average_precision.py +0 -0
  292. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/layoutdet/test_classification_utils_paging.py +0 -0
  293. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/layoutdet/test_rotated_iou.py +0 -0
  294. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_chart_data_point_rule.py +0 -0
  295. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_chart_json_to_html.py +0 -0
  296. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_chart_label_punctuation.py +0 -0
  297. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_cross_page_table_consistency_metric.py +0 -0
  298. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_degenerate_marker_rules.py +0 -0
  299. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_diagram_rules.py +0 -0
  300. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_fast_table_parity.py +0 -0
  301. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_form_field_rule.py +0 -0
  302. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_formatting_rule_adjacent_spans.py +0 -0
  303. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_grits_metric.py +0 -0
  304. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_grits_perf_equivalence.py +0 -0
  305. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_grits_trm_composite.py +0 -0
  306. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_header_accuracy_metric.py +0 -0
  307. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_heading_structure_rule.py +0 -0
  308. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_latex_arrow_length.py +0 -0
  309. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_latex_script_order.py +0 -0
  310. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_merge_preceding_titles.py +0 -0
  311. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_normalize_cell_text.py +0 -0
  312. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_normalize_text_inline_tag_separator.py +0 -0
  313. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_page_decoration_rule.py +0 -0
  314. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_combining_mark_tokenization.py +0 -0
  315. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_formatting_rule_paragraph_spans.py +0 -0
  316. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_formatting_rule_quotes.py +0 -0
  317. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_formatting_rule_span_scope.py +0 -0
  318. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_list_level_rule.py +0 -0
  319. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_table_cell_augmentation.py +0 -0
  320. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_text_order_rule_html.py +0 -0
  321. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_parse_text_presence_rule.py +0 -0
  322. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_relaxed_normalize_cell_text.py +0 -0
  323. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_repetition_overshoot_rule.py +0 -0
  324. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_rule_alarm_thread_context.py +0 -0
  325. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_rule_metric_budget_and_bag_images.py +0 -0
  326. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_rule_metric_side_inputs.py +0 -0
  327. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_caption_title_band.py +0 -0
  328. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_cell_inline_marker_separator.py +0 -0
  329. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_dot_leaders.py +0 -0
  330. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_extraction.py +0 -0
  331. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_marker_cells_rule.py +0 -0
  332. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_merging_metric.py +0 -0
  333. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_pairing.py +0 -0
  334. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_record_match_metric.py +0 -0
  335. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_row_header_th.py +0 -0
  336. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_tbody_provenance.py +0 -0
  337. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_table_title_stripping.py +0 -0
  338. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_teds_metric.py +0 -0
  339. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_text_color_rule.py +0 -0
  340. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_text_content_projection.py +0 -0
  341. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_text_similarity_metric.py +0 -0
  342. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_title_hierarchy_inapplicable.py +0 -0
  343. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/parse/test_watermark_removal_rule.py +0 -0
  344. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/metrics/qa/test_answer_comparison_exact_choice.py +0 -0
  345. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/test_cli_detailed_report_nonfatal.py +0 -0
  346. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/test_runner_aggregation.py +0 -0
  347. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/test_runner_result_files.py +0 -0
  348. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/test_runner_worker_timeout.py +0 -0
  349. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/evaluation/test_stats.py +0 -0
  350. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/geometry/test_rotated_bbox.py +0 -0
  351. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/extract/test_extend_cost_stats.py +0 -0
  352. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/extract/test_extend_schema_adapter.py +0 -0
  353. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_aeddix_alpine_ocr_kdl.py +0 -0
  354. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_amazon_nova.py +0 -0
  355. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_anthropic_pricing.py +0 -0
  356. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_anyformat.py +0 -0
  357. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_azure_document_intelligence_layout.py +0 -0
  358. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_chandra2_multipage.py +0 -0
  359. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_databricks_chart_descriptions.py +0 -0
  360. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_databricks_rest.py +0 -0
  361. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_datalab.py +0 -0
  362. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_deepseekocr2_multipage.py +0 -0
  363. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_docai.py +0 -0
  364. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_extend_parse_pages.py +0 -0
  365. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_falconocr_multipage.py +0 -0
  366. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_gemma4_multipage.py +0 -0
  367. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_glm_zai.py +0 -0
  368. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_google_parse_pricing.py +0 -0
  369. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_granite_vision_multipage.py +0 -0
  370. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_hpd_parsing.py +0 -0
  371. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_hunyuanocr.py +0 -0
  372. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_infinity_parser2.py +0 -0
  373. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_kdl_frontier_nano.py +0 -0
  374. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_layout_coordinate_frames.py +0 -0
  375. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_layout_utils.py +0 -0
  376. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_lightonocr_3.py +0 -0
  377. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_liteparse_layout.py +0 -0
  378. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_llamaparse_picture_type.py +0 -0
  379. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_llamaparse_polling.py +0 -0
  380. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_llamaparse_usage_metadata.py +0 -0
  381. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_llamaparse_v2_normalization.py +0 -0
  382. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_llamaparse_v2_null_page_fields.py +0 -0
  383. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_mineru25_multipage.py +0 -0
  384. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_mineru2605pro_multipage.py +0 -0
  385. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_mineru_diffusion_multipage.py +0 -0
  386. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_minicpm_ocr.py +0 -0
  387. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_nemotron_omni_multipage.py +0 -0
  388. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_nemotron_parse_3_5.py +0 -0
  389. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_nutrient_dws_errors.py +0 -0
  390. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_nutrient_dws_markdown.py +0 -0
  391. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_openai_parse_errors.py +0 -0
  392. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_ovisocr2.py +0 -0
  393. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_ovisocr3.py +0 -0
  394. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_paddleocr_multipage.py +0 -0
  395. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_pymupdf4llm.py +0 -0
  396. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_qwen.py +0 -0
  397. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_reducto_normalize.py +0 -0
  398. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_surya2_multipage.py +0 -0
  399. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_teleocr.py +0 -0
  400. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_textract_cost.py +0 -0
  401. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_unlimitedocr2.py +0 -0
  402. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_unlimitedocr_multipage.py +0 -0
  403. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_upstage.py +0 -0
  404. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_warp_ingest.py +0 -0
  405. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/providers/parse/test_wevisdoc.py +0 -0
  406. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/test_cli.py +0 -0
  407. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/test_layout_extraction.py +0 -0
  408. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/test_parse_pipelines.py +0 -0
  409. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/test_renormalize.py +0 -0
  410. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/inference/test_runner.py +0 -0
  411. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_cases/test_loader.py +0 -0
  412. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_cases/test_loader_stem_twins.py +0 -0
  413. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_cases/test_parse_rule_schemas.py +0 -0
  414. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_cases/test_rule_ids.py +0 -0
  415. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_evaluation_schema_extra_fields.py +0 -0
  416. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/parse_bench/test_layout_label_mapping_code_alias.py +0 -0
  417. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_data_dir_routing.py +0 -0
  418. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_llamaparse_base_url.py +0 -0
  419. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_pulse_public_api.py +0 -0
  420. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_repetition_cli.py +0 -0
  421. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_text_v21.py +0 -0
  422. {parse_bench-1.0.6 → parse_bench-1.0.7}/tests/test_text_v21_isolation.py +0 -0
@@ -66,6 +66,13 @@ All notable changes to `parse-bench` are recorded here. The format follows
66
66
  now computed with a small numpy implementation that matches
67
67
  `sklearn.metrics.average_precision_score` (including tied-score handling),
68
68
  so evaluation numbers are unchanged. `scipy` remains a core dependency.
69
+ - Built-in providers now register on the first `create_provider` call instead
70
+ of when `parse_bench.inference.providers` (or any module under it) is
71
+ imported. Importing `parse_bench.extensions` or the layout adapter and label
72
+ mapper registries no longer loads every provider and its SDK: about 0.4 s
73
+ instead of about 4.8 s in a full install. The provider subpackages expose
74
+ `load_providers()` and the registry `load_builtin_providers()`; code that
75
+ read `_PROVIDER_REGISTRY` directly should call the latter first.
69
76
 
70
77
  ## [1.0.2] - 2026-09-04
71
78
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: parse-bench
3
- Version: 1.0.6
3
+ Version: 1.0.7
4
4
  Summary: ParseBench: a benchmark and evaluation harness for document parsing systems on real-world enterprise documents
5
5
  Project-URL: Homepage, https://parsebench.ai
6
6
  Project-URL: Repository, https://github.com/run-llama/ParseBench
@@ -1,3 +1,3 @@
1
1
  """Document parsing evaluation system for competitive benchmarking."""
2
2
 
3
- __version__ = "1.0.6"
3
+ __version__ = "1.0.7"
@@ -0,0 +1,37 @@
1
+ """Shared replacement semantics for keyed layout registrations."""
2
+
3
+ from collections.abc import Sequence
4
+ from typing import Protocol
5
+
6
+
7
+ class _KeyedRegistration(Protocol):
8
+ @property
9
+ def keys(self) -> tuple[str, ...]: ...
10
+
11
+
12
+ def updated_registrations[RegistrationT: _KeyedRegistration](
13
+ registrations: Sequence[RegistrationT],
14
+ replacement: RegistrationT,
15
+ *,
16
+ dangerous_override: bool,
17
+ ) -> Sequence[RegistrationT]:
18
+ """Replace at the first matched position, retaining unrelated tie order."""
19
+ requested = set(replacement.keys)
20
+ updated: list[RegistrationT] = []
21
+ inserted = False
22
+ for entry in registrations:
23
+ overlap = requested.intersection(entry.keys)
24
+ if len(overlap) == 0:
25
+ updated.append(entry)
26
+ continue
27
+ if not dangerous_override:
28
+ raise ValueError(f"Layout implementation already registered for keys {sorted(overlap)}")
29
+ omitted = set(entry.keys) - requested
30
+ if len(omitted) > 0:
31
+ raise ValueError(f"Override must include all aliases of the replaced registration: {sorted(omitted)}")
32
+ if not inserted:
33
+ updated.append(replacement)
34
+ inserted = True
35
+ if not inserted:
36
+ updated.append(replacement)
37
+ return updated
@@ -4,7 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass
7
+ from typing import TypeVar
7
8
 
9
+ from parse_bench.evaluation._registration import updated_registrations
8
10
  from parse_bench.evaluation.layout_adapters.base import LayoutAdapter
9
11
  from parse_bench.inference.pipelines import get_pipeline
10
12
  from parse_bench.schemas.pipeline import PipelineSpec
@@ -22,27 +24,23 @@ class _LayoutAdapterRegistration:
22
24
 
23
25
  _LAYOUT_ADAPTER_REGISTRY: list[_LayoutAdapterRegistration] = []
24
26
 
27
+ # Bound to the concrete class so the decorator hands back that class, not ``type[LayoutAdapter]``.
28
+ LayoutAdapterT = TypeVar("LayoutAdapterT", bound=type[LayoutAdapter])
29
+
25
30
 
26
31
  def register_layout_adapter(
27
32
  *provider_keys: str,
28
33
  priority: int = 0,
29
- ) -> Callable[[type[LayoutAdapter]], type[LayoutAdapter]]:
34
+ _DANGEROUSLY_OVERRIDE: bool = False,
35
+ ) -> Callable[[LayoutAdapterT], LayoutAdapterT]:
30
36
  """Register a layout adapter class for one or more provider keys."""
31
37
  if not provider_keys:
32
38
  raise ValueError("register_layout_adapter requires at least one provider key")
33
39
 
34
- def decorator(cls: type[LayoutAdapter]) -> type[LayoutAdapter]:
35
- existing_keys = {key for entry in _LAYOUT_ADAPTER_REGISTRY for key in entry.keys}
36
- for key in provider_keys:
37
- if key in existing_keys:
38
- raise ValueError(f"Layout adapter already registered for provider key '{key}'")
39
-
40
- _LAYOUT_ADAPTER_REGISTRY.append(
41
- _LayoutAdapterRegistration(
42
- keys=tuple(provider_keys),
43
- priority=priority,
44
- adapter_cls=cls,
45
- )
40
+ def decorator(cls: LayoutAdapterT) -> LayoutAdapterT:
41
+ replacement = _LayoutAdapterRegistration(keys=tuple(provider_keys), priority=priority, adapter_cls=cls)
42
+ _LAYOUT_ADAPTER_REGISTRY[:] = updated_registrations(
43
+ _LAYOUT_ADAPTER_REGISTRY, replacement, dangerous_override=_DANGEROUSLY_OVERRIDE
46
44
  )
47
45
  return cls
48
46
 
@@ -4,7 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass
7
+ from typing import TypeVar
7
8
 
9
+ from parse_bench.evaluation._registration import updated_registrations
8
10
  from parse_bench.evaluation.layout_adapters.registry import resolve_layout_provider_name
9
11
  from parse_bench.evaluation.layout_label_mappers.base import (
10
12
  LayoutLabelMapper,
@@ -26,26 +28,23 @@ _LAYOUT_LABEL_MAPPER_REGISTRY: list[_LayoutLabelMapperRegistration] = []
26
28
  _DEFAULT_LAYOUT_MAPPER_KEY = "__default__"
27
29
 
28
30
 
31
+ # Bound to the concrete class so the decorator hands back that class, not ``type[LayoutLabelMapper]``.
32
+ LayoutLabelMapperT = TypeVar("LayoutLabelMapperT", bound=type[LayoutLabelMapper])
33
+
34
+
29
35
  def register_layout_label_mapper(
30
36
  *mapper_keys: str,
31
37
  priority: int = 0,
32
- ) -> Callable[[type[LayoutLabelMapper]], type[LayoutLabelMapper]]:
38
+ _DANGEROUSLY_OVERRIDE: bool = False,
39
+ ) -> Callable[[LayoutLabelMapperT], LayoutLabelMapperT]:
33
40
  """Register a layout label mapper for one or more keys."""
34
41
  if not mapper_keys:
35
42
  raise ValueError("register_layout_label_mapper requires at least one key")
36
43
 
37
- def decorator(cls: type[LayoutLabelMapper]) -> type[LayoutLabelMapper]:
38
- existing_keys = {key for entry in _LAYOUT_LABEL_MAPPER_REGISTRY for key in entry.keys}
39
- for key in mapper_keys:
40
- if key in existing_keys:
41
- raise ValueError(f"Layout label mapper already registered for key '{key}'")
42
-
43
- _LAYOUT_LABEL_MAPPER_REGISTRY.append(
44
- _LayoutLabelMapperRegistration(
45
- keys=tuple(mapper_keys),
46
- priority=priority,
47
- mapper_cls=cls,
48
- )
44
+ def decorator(cls: LayoutLabelMapperT) -> LayoutLabelMapperT:
45
+ replacement = _LayoutLabelMapperRegistration(keys=tuple(mapper_keys), priority=priority, mapper_cls=cls)
46
+ _LAYOUT_LABEL_MAPPER_REGISTRY[:] = updated_registrations(
47
+ _LAYOUT_LABEL_MAPPER_REGISTRY, replacement, dangerous_override=_DANGEROUSLY_OVERRIDE
49
48
  )
50
49
  return cls
51
50
 
@@ -3,6 +3,7 @@
3
3
  import logging
4
4
  import re
5
5
  from collections.abc import Callable
6
+ from dataclasses import dataclass
6
7
  from html import unescape
7
8
  from pathlib import Path
8
9
  from typing import Any
@@ -494,26 +495,38 @@ class ParseTestRule:
494
495
  }
495
496
 
496
497
 
497
- _RULE_CLASS_REGISTRY: dict[str, type["ParseTestRule"]] = {}
498
+ @dataclass(frozen=True)
499
+ class _RuleClassRegistration:
500
+ rule_class: type[ParseTestRule]
501
+ overrides_normalization: bool
498
502
 
499
503
 
500
- def register_rule_class(rule_type: str, rule_class: type["ParseTestRule"]) -> None:
504
+ _RULE_CLASS_REGISTRY: dict[str, _RuleClassRegistration] = {}
505
+
506
+
507
+ def register_rule_class(
508
+ rule_type: str, rule_class: type["ParseTestRule"], *, _DANGEROUSLY_OVERRIDE: bool = False
509
+ ) -> None:
501
510
  """Register the scoring class for an additional rule ``type``.
502
511
 
503
512
  Consulted by :func:`create_test_rule` before the built-in dispatch, so an
504
513
  extension can add rule types without editing this module. Pair with
505
514
  ``parse_rule_schemas.register_parse_rule_model`` so the payload validates.
506
515
  """
507
- if not isinstance(rule_type, str) or not rule_type:
516
+ _validate_rule_class(rule_type, rule_class)
517
+ _RULE_CLASS_REGISTRY[rule_type] = _RuleClassRegistration(rule_class, _DANGEROUSLY_OVERRIDE)
518
+
519
+
520
+ def _validate_rule_class(rule_type: str, rule_class: type["ParseTestRule"]) -> None:
521
+ if not isinstance(rule_type, str) or len(rule_type) == 0:
508
522
  raise ValueError("rule_type must be a non-empty string")
509
- if not (isinstance(rule_type, str) and isinstance(rule_class, type) and issubclass(rule_class, ParseTestRule)):
523
+ if not (isinstance(rule_class, type) and issubclass(rule_class, ParseTestRule)):
510
524
  raise TypeError("rule_class must subclass ParseTestRule")
511
- _RULE_CLASS_REGISTRY[rule_type] = rule_class
512
525
 
513
526
 
514
527
  def registered_rule_classes() -> dict[str, type["ParseTestRule"]]:
515
528
  """Snapshot of extension rule classes keyed by rule ``type``."""
516
- return dict(_RULE_CLASS_REGISTRY)
529
+ return {rule_type: entry.rule_class for rule_type, entry in _RULE_CLASS_REGISTRY.items()}
517
530
 
518
531
 
519
532
  _REFERENCE_THUMBNAILER: Callable[[Path], str | None] | None = None
@@ -643,6 +656,10 @@ def create_test_rule(rule_data: ParseRuleInput) -> "ParseTestRule":
643
656
 
644
657
  typed_rule: Any = coerce_parse_rule(rule_data)
645
658
  rule_type = get_rule_type(typed_rule)
659
+ registered = _RULE_CLASS_REGISTRY.get(rule_type)
660
+ if registered is not None and registered.overrides_normalization:
661
+ return registered.rule_class(typed_rule)
662
+
646
663
  if typed_rule.get("text_normalization") == "text-v2.1":
647
664
  from parse_bench.evaluation.metrics.parse.text_v21 import RULE_TYPES, VisibleTextBagRule
648
665
 
@@ -652,9 +669,8 @@ def create_test_rule(rule_data: ParseRuleInput) -> "ParseTestRule":
652
669
  if not rule_type:
653
670
  raise ValueError("Rule must have a 'type' field")
654
671
 
655
- registered = _RULE_CLASS_REGISTRY.get(rule_type)
656
672
  if registered is not None:
657
- return registered(typed_rule)
673
+ return registered.rule_class(typed_rule)
658
674
 
659
675
  if rule_type in {TestType.PRESENT.value, TestType.ABSENT.value}:
660
676
  return TextPresenceRule(typed_rule)
@@ -12,7 +12,7 @@ from parse_bench.evaluation.metrics.parse.rules_base import (
12
12
  is_degenerate_marker_text,
13
13
  )
14
14
  from parse_bench.evaluation.metrics.parse.test_types import TestType
15
- from parse_bench.evaluation.metrics.parse.utils import _normalize_quotes, normalize_text
15
+ from parse_bench.evaluation.metrics.parse.utils import _TAG_REST, _normalize_quotes, normalize_text
16
16
  from parse_bench.test_cases.parse_rule_schemas import (
17
17
  ParseAbsentUnlessStrikeoutRule,
18
18
  ParseCodeBlockRule,
@@ -121,28 +121,28 @@ _STRIP_PATTERNS: dict[str, list[tuple[re.Pattern, str]]] = {
121
121
  "bold": [
122
122
  (re.compile(r"\*\*\*(.+?)\*\*\*", re.DOTALL), r"\1"),
123
123
  (re.compile(r"\*\*(.+?)\*\*", re.DOTALL), r"\1"),
124
- (re.compile(r"</?(?:b|strong)>", re.IGNORECASE), ""),
124
+ (re.compile(rf"</?(?:b|strong){_TAG_REST}", re.IGNORECASE), ""),
125
125
  ],
126
126
  "italic": [
127
127
  (re.compile(r"(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)"), r"\1"),
128
128
  (re.compile(r"(?<!_)_(?!_)(.+?)(?<!_)_(?!_)"), r"\1"),
129
- (re.compile(r"</?(?:i|em)>", re.IGNORECASE), ""),
129
+ (re.compile(rf"</?(?:i|em){_TAG_REST}", re.IGNORECASE), ""),
130
130
  ],
131
131
  "underline": [
132
- (re.compile(r"</?(?:u|ins)>", re.IGNORECASE), ""),
132
+ (re.compile(rf"</?(?:u|ins){_TAG_REST}", re.IGNORECASE), ""),
133
133
  ],
134
134
  "strikeout": [
135
135
  (re.compile(r"~~"), ""),
136
- (re.compile(r"</?(?:s|del|strike)>", re.IGNORECASE), ""),
136
+ (re.compile(rf"</?(?:s|del|strike){_TAG_REST}", re.IGNORECASE), ""),
137
137
  ],
138
138
  "mark": [
139
- (re.compile(r"</?mark\b[^>]*>", re.IGNORECASE), ""),
139
+ (re.compile(rf"</?mark{_TAG_REST}", re.IGNORECASE), ""),
140
140
  ],
141
141
  "sup": [
142
- (re.compile(r"</?sup>", re.IGNORECASE), ""),
142
+ (re.compile(rf"</?sup{_TAG_REST}", re.IGNORECASE), ""),
143
143
  ],
144
144
  "sub": [
145
- (re.compile(r"</?sub>", re.IGNORECASE), ""),
145
+ (re.compile(rf"</?sub{_TAG_REST}", re.IGNORECASE), ""),
146
146
  ],
147
147
  }
148
148
 
@@ -514,6 +514,9 @@ class MarkColorRule(ParseTestRule):
514
514
  2. The ``<mark>`` tag contains the expected color string in any of its attributes
515
515
  (e.g. ``style="background-color: yellow"``, ``background="yellow"``,
516
516
  ``backgroundColor="yellow"``).
517
+ When the expected color is a hue family name (``yellow``, ``green``, ...),
518
+ a hex, ``rgb()`` or named value in that family also passes
519
+ (``background-color:#ffff00``).
517
520
  """
518
521
 
519
522
  def __init__(self, rule_data: ParseMarkColorRule | dict):
@@ -533,6 +536,14 @@ class MarkColorRule(ParseTestRule):
533
536
  raise ValueError("Color field cannot be empty")
534
537
  self.color = raw_color.strip().lower()
535
538
 
539
+ def _attrs_carry_color(self, attrs_str: str) -> bool:
540
+ """True when the attributes name the expected color, or a highlight
541
+ (background) value is a hex, ``rgb()`` or named color in its hue family
542
+ (adjacent families accepted, as in ``TextColorRule``)."""
543
+ if self.color in attrs_str.lower():
544
+ return True
545
+ return any(_families_match(self.color, f) for f in _highlight_color_families(attrs_str))
546
+
536
547
  def run(self, md_content: str, normalized_content: str | None = None) -> tuple[bool, str]:
537
548
  """Check if text is inside a <mark> tag that contains the expected color."""
538
549
  escaped_query = re.escape(self.text)
@@ -549,8 +560,7 @@ class MarkColorRule(ParseTestRule):
549
560
  if not text_pattern.search(inner_text):
550
561
  continue
551
562
 
552
- # Check if the color string appears in the tag attributes
553
- if self.color in attrs_str.lower():
563
+ if self._attrs_carry_color(attrs_str):
554
564
  return True, ""
555
565
 
556
566
  # Fallback: strip other formatting and retry
@@ -562,7 +572,7 @@ class MarkColorRule(ParseTestRule):
562
572
  if not text_pattern.search(inner_text):
563
573
  continue
564
574
 
565
- if self.color in attrs_str.lower():
575
+ if self._attrs_carry_color(attrs_str):
566
576
  return True, ""
567
577
 
568
578
  return (
@@ -787,6 +797,26 @@ def _extract_color_families(attrs_str: str) -> set[str]:
787
797
  return families
788
798
 
789
799
 
800
+ # Highlight color declarations or attributes: CSS background / background-color,
801
+ # HTML background=, React-style backgroundColor=. Foreground color is excluded.
802
+ _HIGHLIGHT_VALUE_RE = re.compile(r"(?<![\w-])background(?:-?color)?\s*[:=]\s*[\"']?([^;\"'>]+)", re.IGNORECASE)
803
+
804
+
805
+ def _highlight_color_families(attrs_str: str) -> set[str]:
806
+ """Hue families of a tag's highlight (background) colors only: hex, ``rgb()``
807
+ or a named color such as ``gold``. A foreground ``color`` never counts."""
808
+ families: set[str] = set()
809
+ for m in _HIGHLIGHT_VALUE_RE.finditer(attrs_str):
810
+ value = m.group(1)
811
+ families |= _extract_color_families(value)
812
+ for name in re.findall(r"[a-zA-Z]+", value):
813
+ rgb = _NAMED_COLOR_RGB.get(name.lower())
814
+ fam = _rgb_to_hue_family(*rgb) if rgb else None
815
+ if fam:
816
+ families.add(fam)
817
+ return families
818
+
819
+
790
820
  class TextColorRule(ParseTestRule):
791
821
  """Test rule verifying that text carries a specific text color.
792
822
 
@@ -10,6 +10,8 @@ from __future__ import annotations
10
10
 
11
11
  import logging
12
12
  import re
13
+ from collections.abc import Sequence
14
+ from dataclasses import replace
13
15
  from typing import Any
14
16
 
15
17
  import numpy as np
@@ -132,87 +134,57 @@ def _normalize_trm_table_text(text: str) -> str:
132
134
  return _normalize_trm_cell_text(text)
133
135
 
134
136
 
135
- def normalize_table(table: TableData) -> TableData:
136
- """Normalize all cell text in a table for comparison.
137
-
138
- Applies normalize_text() + _normalize_trm_cell_text() to every cell in
139
- table.data and to col_headers text, producing a new TableData with
140
- normalized values throughout. Columns whose data cells AND header
141
- entries are all empty strings after normalization are dropped, and
142
- remaining column indices are renumbered to stay contiguous.
143
- """
144
- if table.data.size == 0:
145
- return table
146
- n_rows, n_cols = table.data.shape
137
+ def _normalize_table_text(table: TableData) -> TableData:
147
138
  normalized = np.empty_like(table.data)
148
- for r in range(n_rows):
149
- for c in range(n_cols):
150
- normalized[r, c] = _normalize_trm_table_text(str(table.data[r, c]))
151
- # Normalize col_headers and row_headers metadata to match data cell normalization
152
- normalized_col_headers: dict[int, list[tuple[int, str]]] = {}
153
- for col_idx, entries in table.col_headers.items():
154
- normalized_col_headers[col_idx] = [(row_idx, _normalize_trm_table_text(text)) for row_idx, text in entries]
155
- normalized_row_headers: dict[int, list[tuple[int, str]]] = {}
156
- for row_idx, entries in table.row_headers.items():
157
- normalized_row_headers[row_idx] = [(col_idx, _normalize_trm_table_text(text)) for col_idx, text in entries]
158
-
159
- # Drop columns whose data cells AND header entries are all literally
160
- # empty strings after normalization. Such columns add noise to header
161
- # alignment and inflate the union denominator used by _record_similarity
162
- # without contributing any signal. Sentinel values like "-" or "n/a"
163
- # count as real content and are NOT dropped.
164
- #
165
- # This runs independently on GT and pred, so the two normalized tables
166
- # may end up with *different* column counts when (say) GT has an empty
167
- # column 2 but pred has real content there. That's intentional: record
168
- # matching is content-driven — ``_record_similarity`` compares columns
169
- # via the ``column_mapping`` keyed by header text, not by positional
170
- # index, so differing shapes are fine and the unmapped column on the
171
- # longer side penalizes the score via ``max(n_gt_cols, n_pred_cols)``.
172
- def _col_is_empty(c: int) -> bool:
173
- for r in range(n_rows):
174
- if str(normalized[r, c]) != "":
175
- return False
176
- for _row_idx, text in normalized_col_headers.get(c, []):
177
- if text != "":
178
- return False
179
- return True
180
-
181
- keep_cols = [c for c in range(n_cols) if not _col_is_empty(c)]
182
- if len(keep_cols) != n_cols:
183
- old_to_new = {old: new for new, old in enumerate(keep_cols)}
184
- normalized = normalized[:, keep_cols]
185
- normalized_col_headers = {
186
- old_to_new[c]: normalized_col_headers[c] for c in keep_cols if c in normalized_col_headers
187
- }
188
- header_cols = {old_to_new[c] for c in table.header_cols if c in old_to_new}
189
- header_cells = {(r, old_to_new[c]) for (r, c) in table.header_cells if c in old_to_new}
190
- normalized_row_headers = {
191
- row_idx: [(old_to_new[c], text) for c, text in entries if c in old_to_new]
192
- for row_idx, entries in normalized_row_headers.items()
193
- }
194
- else:
195
- header_cols = table.header_cols
196
- header_cells = table.header_cells
197
-
198
- return TableData(
139
+ for row in range(table.data.shape[0]):
140
+ for col in range(table.data.shape[1]):
141
+ normalized[row, col] = _normalize_trm_table_text(str(table.data[row, col]))
142
+ return replace(
143
+ table,
199
144
  data=normalized,
200
- header_rows=table.header_rows,
201
- header_cols=header_cols,
202
- col_headers=normalized_col_headers,
203
- row_headers=normalized_row_headers,
204
- header_cells=header_cells,
205
- thead_rows=table.thead_rows,
206
- tbody_rows=table.tbody_rows,
207
- tfoot_rows=table.tfoot_rows,
208
- column_scope_rows=table.column_scope_rows,
209
- row_scope_rows=table.row_scope_rows,
210
- caption=table.caption,
211
- context_before=table.context_before,
212
- context_after=table.context_after,
145
+ col_headers={
146
+ col: [(row, _normalize_trm_table_text(text)) for row, text in entries]
147
+ for col, entries in table.col_headers.items()
148
+ },
149
+ row_headers={
150
+ row: [(col, _normalize_trm_table_text(text)) for col, text in entries]
151
+ for row, entries in table.row_headers.items()
152
+ },
213
153
  )
214
154
 
215
155
 
156
+ def _select_table_columns(table: TableData, columns: Sequence[int]) -> TableData:
157
+ old_to_new = {old: new for new, old in enumerate(columns)}
158
+ return replace(
159
+ table,
160
+ data=table.data[:, list(columns)],
161
+ header_cols={old_to_new[col] for col in table.header_cols if col in old_to_new},
162
+ col_headers={old_to_new[col]: entries for col, entries in table.col_headers.items() if col in old_to_new},
163
+ row_headers={
164
+ row: [(old_to_new[col], text) for col, text in entries if col in old_to_new]
165
+ for row, entries in table.row_headers.items()
166
+ },
167
+ header_cells={(row, old_to_new[col]) for row, col in table.header_cells if col in old_to_new},
168
+ spanned_cells={(row, old_to_new[col]) for row, col in table.spanned_cells if col in old_to_new},
169
+ )
170
+
171
+
172
+ def normalize_table(table: TableData, *, preserve_text: bool = False) -> TableData:
173
+ """Drop normalized-empty columns, returning normalized or original text."""
174
+ if table.data.size == 0:
175
+ return table
176
+ normalized = _normalize_table_text(table)
177
+ # Both representations use normalized emptiness, including header text.
178
+ # Sentinel values such as "-" and "n/a" remain real content.
179
+ columns = [
180
+ col
181
+ for col in range(normalized.data.shape[1])
182
+ if any(value != "" for value in normalized.data[:, col])
183
+ or any(text != "" for _, text in normalized.col_headers.get(col, []))
184
+ ]
185
+ return _select_table_columns(table if preserve_text else normalized, columns)
186
+
187
+
216
188
  def cell_score(expected: str, actual: str) -> float:
217
189
  """Score similarity between two cell values: 1.0 for match, 0.0 otherwise.
218
190
 
@@ -22,7 +22,7 @@ circular import with ``table_record_match_metric``.
22
22
  from __future__ import annotations
23
23
 
24
24
  import itertools
25
- from dataclasses import dataclass
25
+ from dataclasses import dataclass, replace
26
26
  from typing import TYPE_CHECKING
27
27
 
28
28
  from rapidfuzz import fuzz
@@ -99,50 +99,56 @@ def _detect_period_candidates(
99
99
  return candidates
100
100
 
101
101
 
102
+ def _trim_trailing_padding(table: TableData) -> TableData:
103
+ """Remove only empty tail rows, preserving metadata for retained rows."""
104
+ end = table.data.shape[0]
105
+ # Metadata can carry header text even when the grid cell is empty.
106
+ header_text_rows = {
107
+ r for entries in table.col_headers.values() for r, text in entries if len(str(text).strip()) > 0
108
+ }
109
+ header_text_rows.update(
110
+ r for r, entries in table.row_headers.items() if any(len(str(text).strip()) > 0 for _, text in entries)
111
+ )
112
+ while (
113
+ end > 0
114
+ and end - 1 not in header_text_rows
115
+ and not any(len(str(value).strip()) > 0 for value in table.data[end - 1])
116
+ ):
117
+ end -= 1
118
+ if end == table.data.shape[0]:
119
+ return table
120
+ row_sets = ("header_rows", "thead_rows", "tbody_rows", "tfoot_rows", "column_scope_rows", "row_scope_rows")
121
+ return replace(
122
+ table,
123
+ data=table.data[:end],
124
+ **{name: {r for r in getattr(table, name) if r < end} for name in row_sets},
125
+ col_headers={c: [(r, text) for r, text in entries if r < end] for c, entries in table.col_headers.items()},
126
+ row_headers={r: entries for r, entries in table.row_headers.items() if r < end},
127
+ header_cells={(r, c) for r, c in table.header_cells if r < end},
128
+ spanned_cells={(r, c) for r, c in table.spanned_cells if r < end},
129
+ )
130
+
131
+
102
132
  def build_sub_table(
103
133
  pred_table: TableData,
104
134
  start: int,
105
135
  end: int,
106
136
  ) -> TableData:
107
- """Build a sub-table from a column range of the pred table."""
108
- sub_data = pred_table.data[:, start:end]
109
-
110
- n_rows = sub_data.shape[0]
111
- last_nonempty = n_rows
112
- for r in range(n_rows - 1, -1, -1):
113
- if any(str(sub_data[r, c]).strip() for c in range(sub_data.shape[1])):
114
- last_nonempty = r + 1
115
- break
116
- else:
117
- last_nonempty = 0
118
- if last_nonempty < n_rows:
119
- sub_data = sub_data[:last_nonempty, :]
120
-
121
- sub_col_headers: dict[int, list[tuple[int, str]]] = {}
122
- sub_header_cols: set[int] = set()
123
- for new_c, old_c in enumerate(range(start, end)):
124
- if old_c in pred_table.col_headers:
125
- sub_col_headers[new_c] = pred_table.col_headers[old_c]
126
- if old_c in pred_table.header_cols:
127
- sub_header_cols.add(new_c)
128
-
129
- sub_header_cells: set[tuple[int, int]] = set()
130
- for r, c in pred_table.header_cells:
131
- if start <= c < end and r < last_nonempty:
132
- sub_header_cells.add((r, c - start))
133
-
134
- return TableData(
135
- data=sub_data,
136
- header_rows=pred_table.header_rows.copy(),
137
- header_cols=sub_header_cols,
138
- col_headers=sub_col_headers,
139
- row_headers={},
140
- header_cells=sub_header_cells,
141
- thead_rows=set(pred_table.thead_rows),
142
- tbody_rows={r for r in pred_table.tbody_rows if r < last_nonempty},
143
- tfoot_rows={r for r in pred_table.tfoot_rows if r < last_nonempty},
144
- column_scope_rows={r for r in pred_table.column_scope_rows if r < last_nonempty},
145
- row_scope_rows={r for r in pred_table.row_scope_rows if r < last_nonempty},
137
+ """Slice columns and trim blank tail rows without losing header relationships."""
138
+ return _trim_trailing_padding(
139
+ replace(
140
+ pred_table,
141
+ data=pred_table.data[:, start:end],
142
+ header_cols={c - start for c in pred_table.header_cols if start <= c < end},
143
+ col_headers={c - start: entries for c, entries in pred_table.col_headers.items() if start <= c < end},
144
+ row_headers={
145
+ r: [(c - start, text) for c, text in entries if start <= c < end]
146
+ for r, entries in pred_table.row_headers.items()
147
+ if any(start <= c < end for c, _ in entries)
148
+ },
149
+ header_cells={(r, c - start) for r, c in pred_table.header_cells if start <= c < end},
150
+ spanned_cells={(r, c - start) for r, c in pred_table.spanned_cells if start <= c < end},
151
+ )
146
152
  )
147
153
 
148
154
 
@@ -283,8 +289,15 @@ def split_ambiguous_merged_pred(
283
289
  sub-tables emitted from a split have ``raw_html=""`` since they have no
284
290
  meaningful HTML to attribute back to the source.
285
291
 
292
+ When a split is applied, normalize trailing blank padding on both sides.
293
+ The peer ``expected`` list is updated in place with replacement tables;
294
+ original TableData objects and source HTML remain unchanged. No-split
295
+ calls leave both inputs untouched.
296
+
286
297
  Returns ``(possibly_rewritten_actual, did_split)``.
287
298
  """
299
+ from parse_bench.evaluation.metrics.parse.table_record_match_metric import normalize_table
300
+
288
301
  if len(expected) <= len(actual):
289
302
  return actual, False
290
303
 
@@ -297,5 +310,16 @@ def split_ambiguous_merged_pred(
297
310
  if opt.sub_tables is None:
298
311
  new_actual.append(original)
299
312
  else:
300
- new_actual.extend(ExtractedTable(raw_html="", table_data=sub) for sub in opt.sub_tables)
313
+ # Keep the candidate geometry: normalization may remove spacer
314
+ # columns, so its offsets cannot slice the original grid directly.
315
+ source = normalize_table(original.table_data, preserve_text=True)
316
+ new_actual.extend(
317
+ ExtractedTable(
318
+ raw_html="",
319
+ table_data=build_sub_table(source, i * opt.period, (i + 1) * opt.period),
320
+ )
321
+ for i in range(opt.n_segments)
322
+ )
323
+ expected[:] = [replace(t, table_data=_trim_trailing_padding(t.table_data)) for t in expected]
324
+ new_actual = [replace(t, table_data=_trim_trailing_padding(t.table_data)) for t in new_actual]
301
325
  return new_actual, True