2026-06-05 13:26:25 +02:00
#!/usr/bin/env python3
import os
import re
import time
import json
import requests
2026-06-08 12:29:43 +02:00
SCRIPT_DIR = os . path . dirname ( os . path . abspath ( __file__ ) )
API_URL = os . environ . get ( " API_URL " , " http://localhost:8001/v1/files/convert " )
2026-06-05 13:26:25 +02:00
TEST_CASES = [
{
" name " : " Text-focused Strategy Report " ,
2026-06-08 12:29:43 +02:00
" file " : os . path . join ( SCRIPT_DIR , " test_files " , " enisa " , " enisa-international-strategy-2026.pdf " ) ,
2026-06-05 13:26:25 +02:00
" pages " : " 1-2 " ,
" desc " : " Standard text-focused policy brief with small section headers. "
} ,
{
" name " : " Complex Policy Report with Tables " ,
2026-06-08 12:29:43 +02:00
" file " : os . path . join ( SCRIPT_DIR , " test_files " , " enisa " , " nis-investments-2025.pdf " ) ,
2026-06-05 13:26:25 +02:00
" pages " : " 5-6 " ,
" desc " : " Rich, multi-column cybersecurity report containing charts, lists, and formatted tables. "
} ,
{
" name " : " Dense Scientific Paper (arXiv) " ,
2026-06-08 12:29:43 +02:00
" file " : os . path . join ( SCRIPT_DIR , " test_files " , " downloaded " , " attention.pdf " ) ,
2026-06-05 13:26:25 +02:00
" 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 " \n Evaluating: { 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
2026-06-08 12:29:43 +02:00
report_path = os . path . join ( SCRIPT_DIR , " test_files " , " conversion_comparison_report.md " )
2026-06-05 13:26:25 +02:00
report = [ ]
report . append ( " # Marker API: AMD GPU vs. Deepseek-OCR (llama.cpp) Comparison " )
report . append ( " \n This 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 ( )