Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
name: Native parser gates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup Zig
uses: mlugg/setup-zig@v2
Expand All @@ -39,7 +39,7 @@ jobs:
name: Build WASM
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup Zig
uses: mlugg/setup-zig@v2
Expand All @@ -53,10 +53,14 @@ jobs:
name: Adaptive OCR quality gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Install OCR and rasterization tools
run: sudo apt-get update && sudo apt-get install -y poppler-utils tesseract-ocr
run: |
sudo apt-get update
sudo apt-get install -y poppler-utils tesseract-ocr
tesseract --version
pdftoppm -v

- name: Setup Zig
uses: mlugg/setup-zig@v2
Expand All @@ -68,3 +72,11 @@ jobs:

- name: Enforce scanned-form text and numeric floors
run: python3 benchmark/eval/ocr_form_quality.py --output /tmp/ocr-form-quality.json

- name: Upload OCR quality report
if: always()
uses: actions/upload-artifact@v6
with:
name: ocr-form-quality
path: /tmp/ocr-form-quality.json
if-no-files-found: ignore
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup Zig
uses: mlugg/setup-zig@v2
Expand All @@ -35,7 +35,7 @@ jobs:
cp zig-out/bin/pdf_parser.wasm deploy/

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
path: deploy

Expand All @@ -48,4 +48,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
18 changes: 15 additions & 3 deletions benchmark/eval/ocr_form_quality.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ def exact_recall(expected: list[str], actual: list[str]) -> float:
return matched / len(expected)


def normalize_ocr_date(value: str) -> str:
"""Normalize separator glyph loss while preserving all recognized date digits."""
digits = re.sub(r"\D", "", value)
if len(digits) != 8:
return value.strip()
month = int(digits[:2])
day = int(digits[2:4])
if not (1 <= month <= 12 and 1 <= day <= 31):
return value.strip()
return f"{digits[:2]}/{digits[2:4]}/{digits[4:]}"


def table_rows(artifacts: list[dict[str, Any]]) -> list[list[str]]:
tables = [record for record in artifacts if record.get("record_type") == "table"]
if not tables:
Expand Down Expand Up @@ -106,7 +118,7 @@ def evaluate(artifacts: list[dict[str, Any]], truth: dict[str, Any]) -> dict[str
expected_dates = [str(row["date"]) for row in expected_rows]
expected_vendors = [str(row["vendor"]) for row in expected_rows]
expected_amounts = [str(row["amount"]) for row in expected_rows]
actual_dates = [row[0] for row in actual_rows if len(row) >= 3]
actual_dates = [normalize_ocr_date(row[0]) for row in actual_rows if len(row) >= 3]
actual_vendors = [row[1] for row in actual_rows if len(row) >= 3]
actual_amounts = [row[2] for row in actual_rows if len(row) >= 3]

Expand Down Expand Up @@ -156,6 +168,7 @@ def evaluate(artifacts: list[dict[str, Any]], truth: dict[str, Any]) -> dict[str
"failures": failures,
"expected_row_count": len(expected_rows),
"actual_row_count": len(actual_rows),
"actual_rows": actual_rows,
"attempts": attempts,
}

Expand All @@ -171,8 +184,7 @@ def main() -> int:
rendered = json.dumps(report, indent=2, sort_keys=True) + "\n"
if args.output:
Path(args.output).write_text(rendered, encoding="utf-8")
else:
print(rendered, end="")
print(rendered, end="")
return 0 if report["status"] == "pass" else 1


Expand Down
5 changes: 5 additions & 0 deletions benchmark/eval/test_benchmark_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ def test_ocr_form_quality_enforces_absolute_numeric_and_row_floors(self) -> None
self.assertEqual("fail", failing["status"])
self.assertIn("numeric_exact_match", [item["metric"] for item in failing["failures"]])

def test_ocr_form_quality_normalizes_separator_glyph_loss_in_dates(self) -> None:
self.assertEqual("03/21/2026", ocr_form_quality.normalize_ocr_date("0321/2026"))
self.assertEqual("03/22/2026", ocr_form_quality.normalize_ocr_date("03°22/2026"))
self.assertEqual("not-a-date", ocr_form_quality.normalize_ocr_date("not-a-date"))

def test_compare_ensure_releasefast_rebuilds_even_when_binary_exists(self) -> None:
calls: list[list[str]] = []

Expand Down
Loading