Skip to content

Commit 762ccb5

Browse files
committed
fix(ocr): prevent native binary crash and handle AVX2 CPUs (RR-1325)
1 parent dc9bffa commit 762ccb5

3 files changed

Lines changed: 48 additions & 6 deletions

File tree

nodes/src/nodes/ocr/requirements.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,5 @@
44
# by ai.common.models which uses ai.common.torch for torch installation.
55
# This node only needs img2table for table structure detection.
66
#
7-
# numpy and pillow are base engine dependencies - don't reinstall them here
7+
# numpy, pillow, and img2table are base engine dependencies - don't reinstall them here
88
# as that can corrupt already-loaded modules in the server process.
9-
#
10-
img2table
11-
pillow
12-
numpy

nodes/src/nodes/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ safetensors
2323
# Needed for dependencies compilations
2424
Cython
2525
versioneer
26+
27+
# OCR base dependencies
28+
img2table

packages/server/engine-lib/rocketlib-python/lib/depends.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,14 +654,52 @@ def _save_hash(hash_file: str, hash_value: str):
654654
f.write(hash_value)
655655

656656

657+
def _is_x86_64_missing_avx2() -> bool:
658+
"""Check if CPU is x86_64 but lacks AVX2 (e.g. Rosetta 2 or old Intel)."""
659+
machine = platform.machine().lower()
660+
if machine not in ('x86_64', 'amd64'):
661+
return False
662+
663+
system = platform.system()
664+
if system == 'Darwin':
665+
# Check Rosetta or old Mac without AVX2
666+
try:
667+
output = subprocess.check_output(['sysctl', '-n', 'hw.optional.avx2_0'], text=True)
668+
return output.strip() != '1'
669+
except Exception:
670+
return True
671+
elif system == 'Linux':
672+
try:
673+
with open('/proc/cpuinfo', 'r', encoding='utf-8') as f:
674+
return 'avx2' not in f.read()
675+
except Exception:
676+
return True
677+
elif system == 'Windows':
678+
try:
679+
import ctypes
680+
# PF_AVX2_INSTRUCTIONS_AVAILABLE is 40
681+
return not ctypes.windll.kernel32.IsProcessorFeaturePresent(40)
682+
except Exception:
683+
return False
684+
685+
return False
686+
687+
657688
def _combine_requirements(file_paths: list[str], output_path: str):
658689
"""Concatenate all requirement files into one."""
690+
needs_polars_override = False
659691
with open(output_path, 'w', encoding='utf-8') as out:
660692
for path in file_paths:
661693
out.write(f'# Source: {path}\n')
662694
with open(path, 'r', encoding='utf-8') as inp:
663-
out.write(inp.read())
695+
content = inp.read()
696+
if 'img2table' in content or 'polars' in content:
697+
needs_polars_override = _is_x86_64_missing_avx2()
698+
out.write(content)
664699
out.write('\n')
700+
701+
if needs_polars_override:
702+
out.write('\n# Injected to support older/emulated CPUs without AVX2\npolars-lts-cpu\n')
665703

666704

667705
def _compile_constraints(constraints_path: str):
@@ -764,6 +802,11 @@ def _write_excludes_file() -> str:
764802
excludes = 'uv\n'
765803
if platform.system() != 'Darwin':
766804
excludes += 'onnxruntime\n'
805+
806+
if _is_x86_64_missing_avx2():
807+
# Exclude standard polars so uv doesn't install it alongside polars-lts-cpu
808+
excludes += 'polars\n'
809+
767810
with open(excludes_path, 'w', encoding='utf-8') as f:
768811
f.write(excludes)
769812
return excludes_path

0 commit comments

Comments
 (0)