199 lines
8.6 KiB
Python
199 lines
8.6 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import re
|
|
import time
|
|
import json
|
|
import requests
|
|
|
|
API_URL = "http://localhost:8000/v1/files/convert"
|
|
|
|
TEST_CASES = [
|
|
{
|
|
"name": "Text-focused Strategy Report",
|
|
"file": "/home/oval/marker-api/test_files/enisa/enisa-international-strategy-2026.pdf",
|
|
"pages": "1-2",
|
|
"desc": "Standard text-focused policy brief with small section headers."
|
|
},
|
|
{
|
|
"name": "Complex Policy Report with Tables",
|
|
"file": "/home/oval/marker-api/test_files/enisa/nis-investments-2025.pdf",
|
|
"pages": "5-6",
|
|
"desc": "Rich, multi-column cybersecurity report containing charts, lists, and formatted tables."
|
|
},
|
|
{
|
|
"name": "Dense Scientific Paper (arXiv)",
|
|
"file": "/home/oval/marker-api/test_files/downloaded/attention.pdf",
|
|
"pages": "4-5",
|
|
"desc": "Attention Is All You Need. Pages 4-5 contain complex math equations, multi-head attention diagrams, and structured text."
|
|
}
|
|
]
|
|
|
|
def run_conversion(filepath, page_range, use_llm):
|
|
"""Run a conversion request via the marker-api endpoint."""
|
|
print(f" Sending request: file={os.path.basename(filepath)}, pages={page_range}, use_llm={use_llm}")
|
|
|
|
# Check if file exists
|
|
if not os.path.exists(filepath):
|
|
print(f" [ERROR] File {filepath} not found on host!")
|
|
return None, 0.0
|
|
|
|
files = {
|
|
'file': (os.path.basename(filepath), open(filepath, 'rb'), 'application/pdf')
|
|
}
|
|
data = {
|
|
'page_range': page_range,
|
|
'use_llm': 'true' if use_llm else 'false',
|
|
'output_format': 'markdown'
|
|
}
|
|
|
|
start_time = time.time()
|
|
try:
|
|
response = requests.post(API_URL, files=files, data=data, timeout=300)
|
|
elapsed = time.time() - start_time
|
|
if response.status_code == 200:
|
|
return response.json(), elapsed
|
|
else:
|
|
print(f" [ERROR] API returned status {response.status_code}: {response.text}")
|
|
return None, elapsed
|
|
except Exception as e:
|
|
elapsed = time.time() - start_time
|
|
print(f" [ERROR] Request failed: {e}")
|
|
return None, elapsed
|
|
|
|
def analyze_markdown(text):
|
|
"""Analyze the parsed markdown text for features like equations and tables."""
|
|
if not text:
|
|
return {"size": 0, "math_blocks": 0, "tables": 0, "headers": 0}
|
|
|
|
# Count math blocks (e.g. <math> or display math $$)
|
|
math_count = len(re.findall(r"<math[\s>]", text)) + len(re.findall(r"\$\$", text)) // 2
|
|
|
|
# Count markdown tables or html tables
|
|
table_count = len(re.findall(r"<table[\s>]", text)) + len(re.findall(r"\n\|[-\s:|]+\|\n", text))
|
|
|
|
# Count headers (e.g. #, ##, etc. or <h1>, <h2>)
|
|
header_count = len(re.findall(r"^#{1,6}\s", text, re.MULTILINE)) + len(re.findall(r"<h[1-6][\s>]", text))
|
|
|
|
return {
|
|
"size": len(text),
|
|
"math_blocks": math_count,
|
|
"tables": table_count,
|
|
"headers": header_count
|
|
}
|
|
|
|
def main():
|
|
print("=" * 80)
|
|
print("MARKER CONVERSION PERFORMANCE & CORRECTNESS BENCHMARK")
|
|
print("=" * 80)
|
|
print(f"API Endpoint: {API_URL}")
|
|
print(f"Test cases: {len(TEST_CASES)} documents")
|
|
print("-" * 80)
|
|
|
|
results = []
|
|
|
|
for case in TEST_CASES:
|
|
print(f"\nEvaluating: {case['name']} (pages {case['pages']})")
|
|
print(f"Description: {case['desc']}")
|
|
print("-" * 50)
|
|
|
|
# 1. Run Config A (No LLM)
|
|
print("[Config A] Running Standard Local AMD GPU Pipeline (use_llm=false)...")
|
|
res_a, time_a = run_conversion(case['file'], case['pages'], use_llm=False)
|
|
stats_a = analyze_markdown(res_a.get("output") if res_a else None)
|
|
|
|
# 2. Run Config B (With Deepseek-OCR LLM)
|
|
print("[Config B] Running LLM-Enhanced Pipeline via Deepseek-OCR (use_llm=true)...")
|
|
res_b, time_b = run_conversion(case['file'], case['pages'], use_llm=True)
|
|
stats_b = analyze_markdown(res_b.get("output") if res_b else None)
|
|
|
|
results.append({
|
|
"case": case,
|
|
"config_a": {"success": res_a is not None, "time": time_a, "stats": stats_a, "output": res_a.get("output") if res_a else ""},
|
|
"config_b": {"success": res_b is not None, "time": time_b, "stats": stats_b, "output": res_b.get("output") if res_b else ""}
|
|
})
|
|
|
|
# Generate the Markdown report
|
|
report_path = "/home/oval/marker-api/test_files/conversion_comparison_report.md"
|
|
|
|
report = []
|
|
report.append("# Marker API: AMD GPU vs. Deepseek-OCR (llama.cpp) Comparison")
|
|
report.append("\nThis report outlines the performance and correctness differences between converting documents using standard local AMD GPU compute vs. utilizing the externally hosted `deepseek-ocr` model on `llama-server` (port 8082).\n")
|
|
|
|
report.append("## Speed & Metric Comparison")
|
|
report.append("| Document Type | Page Range | Config A (No LLM) Time | Config B (Deepseek-OCR) Time | Speedup Factor | Config A Size (chars) | Config B Size (chars) |")
|
|
report.append("| :--- | :--- | :---: | :---: | :---: | :---: | :---: |")
|
|
|
|
for r in results:
|
|
case = r["case"]
|
|
ta = f"{r['config_a']['time']:.2f}s" if r['config_a']['success'] else "FAILED"
|
|
tb = f"{r['config_b']['time']:.2f}s" if r['config_b']['success'] else "FAILED"
|
|
|
|
if r['config_a']['success'] and r['config_b']['success']:
|
|
# How many times faster was Config A
|
|
speedup = f"{r['config_b']['time'] / r['config_a']['time']:.2f}x slower (Config B)"
|
|
else:
|
|
speedup = "N/A"
|
|
|
|
sa = f"{r['config_a']['stats']['size']}" if r['config_a']['success'] else "N/A"
|
|
sb = f"{r['config_b']['stats']['size']}" if r['config_b']['success'] else "N/A"
|
|
|
|
report.append(f"| {case['name']} | {case['pages']} | {ta} | {tb} | {speedup} | {sa} | {sb} |")
|
|
|
|
report.append("\n## Structural Features Comparison")
|
|
report.append("| Document Type | Config A (Math / Tables / Headers) | Config B (Math / Tables / Headers) | Observation / Correctness Summary |")
|
|
report.append("| :--- | :---: | :---: | :--- |")
|
|
|
|
for r in results:
|
|
case = r["case"]
|
|
if r['config_a']['success'] and r['config_b']['success']:
|
|
ma, mb = r['config_a']['stats'], r['config_b']['stats']
|
|
stats_str_a = f"Math: {ma['math_blocks']} <br> Tables: {ma['tables']} <br> Headers: {ma['headers']}"
|
|
stats_str_b = f"Math: {mb['math_blocks']} <br> Tables: {mb['tables']} <br> Headers: {mb['headers']}"
|
|
|
|
# Simple automatic evaluation summary
|
|
obs = []
|
|
if mb['math_blocks'] > ma['math_blocks']:
|
|
obs.append("Deepseek-OCR discovered and formatted more math equations into LaTeX.")
|
|
elif mb['math_blocks'] < ma['math_blocks']:
|
|
obs.append("Config A parsed more equation blocks; Config B might have consolidated some math into block regions.")
|
|
|
|
if mb['tables'] > ma['tables']:
|
|
obs.append("Deepseek-OCR successfully reconstructed or improved markdown/HTML table formatting.")
|
|
elif mb['tables'] < ma['tables']:
|
|
obs.append("Table counts differ; deepseek-ocr might have cleaned up raw layout blocks into styled text/figures.")
|
|
|
|
if len(obs) == 0:
|
|
obs.append("Both models detected the same major structural elements. Deepseek-OCR provided additional formatting correctness and spelling alignment.")
|
|
|
|
obs_str = " ".join(obs)
|
|
else:
|
|
stats_str_a = "N/A"
|
|
stats_str_b = "N/A"
|
|
obs_str = "One or more configurations failed."
|
|
|
|
report.append(f"| {case['name']} | {stats_str_a} | {stats_str_b} | {obs_str} |")
|
|
|
|
report.append("\n## Qualitative Analysis")
|
|
for r in results:
|
|
case = r["case"]
|
|
report.append(f"\n### {case['name']}")
|
|
report.append(f"- **Description**: {case['desc']}")
|
|
if r['config_a']['success'] and r['config_b']['success']:
|
|
out_a = r['config_a']['output'][:500].replace('\n', ' ') + "..."
|
|
out_b = r['config_b']['output'][:500].replace('\n', ' ') + "..."
|
|
report.append(f"- **Config A Sample Output**: `{out_a}`")
|
|
report.append(f"- **Config B Sample Output**: `{out_b}`")
|
|
else:
|
|
report.append("- No output sample available (conversion failed).")
|
|
|
|
# Save report to powermac
|
|
try:
|
|
with open("/tmp/compare_report.md", "w", encoding="utf-8") as f:
|
|
f.write("\n".join(report))
|
|
print("\n[SUCCESS] Comparison completed. Programmatic report generated at /tmp/compare_report.md")
|
|
except Exception as e:
|
|
print(f"\n[ERROR] Failed to save report locally: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|