diff --git a/libs/kotaemon/kotaemon/loaders/paddleocr_loader/paddleocr_vl_loader.py b/libs/kotaemon/kotaemon/loaders/paddleocr_loader/paddleocr_vl_loader.py index 2fc646ee..d7da3b7a 100644 --- a/libs/kotaemon/kotaemon/loaders/paddleocr_loader/paddleocr_vl_loader.py +++ b/libs/kotaemon/kotaemon/loaders/paddleocr_loader/paddleocr_vl_loader.py @@ -101,6 +101,13 @@ class PaddleOCRVLReader(BaseReader): "markdown_ignore_labels": self.markdown_ignore_labels, "use_queues": self.use_queues, } + + # PaddleX defaults CPU inference to MKL-DNN, which hits a PIR/oneDNN + # crash in PaddlePaddle 3.3+; disable until upstream is fixed. + # https://github.com/PaddlePaddle/Paddle/issues/77340 + if str(self.device).startswith("cpu"): + kwargs["enable_mkldnn"] = False + kwargs = {k: v for k, v in kwargs.items() if v is not None} return PaddleOCRVL(**kwargs) diff --git a/libs/kotaemon/kotaemon/loaders/paddleocr_loader/ppstructure_v3_loader.py b/libs/kotaemon/kotaemon/loaders/paddleocr_loader/ppstructure_v3_loader.py index f96a8415..a45a941a 100644 --- a/libs/kotaemon/kotaemon/loaders/paddleocr_loader/ppstructure_v3_loader.py +++ b/libs/kotaemon/kotaemon/loaders/paddleocr_loader/ppstructure_v3_loader.py @@ -197,6 +197,13 @@ class PPStructureV3Reader(BaseReader): "lang": self.lang, "ocr_version": self.ocr_version, } + + # PaddleX defaults CPU inference to MKL-DNN, which hits a PIR/oneDNN + # crash in PaddlePaddle 3.3+; disable until upstream is fixed. + # https://github.com/PaddlePaddle/Paddle/issues/77340 + if str(self.device).startswith("cpu"): + kwargs["enable_mkldnn"] = False + kwargs = {k: v for k, v in kwargs.items() if v is not None} return PPStructureV3(**kwargs) diff --git a/libs/kotaemon/tests/test_paddleocr_loader.py b/libs/kotaemon/tests/test_paddleocr_loader.py index ca1a4c89..a7081d51 100644 --- a/libs/kotaemon/tests/test_paddleocr_loader.py +++ b/libs/kotaemon/tests/test_paddleocr_loader.py @@ -167,6 +167,51 @@ def test_ppstructure_v3_load_data_with_mocked_pipeline( mock_pipeline.predict.assert_called_once_with("/tmp/doc.pdf") +@skip_when_paddleocr_not_installed +@pytest.mark.parametrize( + "device", + ("cpu", "cpu:0"), +) +def test_ppstructure_v3_cpu_disables_mkldnn( + mocker: MockerFixture, + device: str, +) -> None: + """CPU init passes enable_mkldnn=False (Paddle#77340 workaround).""" + from kotaemon.loaders.paddleocr_loader import ppstructure_v3_loader + + mock_pipeline = MagicMock() + mock_ctor = mocker.patch( + "paddleocr.PPStructureV3", + return_value=mock_pipeline, + ) + + reader = ppstructure_v3_loader.PPStructureV3Reader(device=device) + _ = reader.pipeline_ + + mock_ctor.assert_called_once() + assert mock_ctor.call_args.kwargs["enable_mkldnn"] is False + + +@skip_when_paddleocr_not_installed +def test_ppstructure_v3_gpu_omits_mkldnn_override( + mocker: MockerFixture, +) -> None: + """GPU init leaves enable_mkldnn to paddleocr defaults.""" + from kotaemon.loaders.paddleocr_loader import ppstructure_v3_loader + + mock_pipeline = MagicMock() + mock_ctor = mocker.patch( + "paddleocr.PPStructureV3", + return_value=mock_pipeline, + ) + + reader = ppstructure_v3_loader.PPStructureV3Reader(device="gpu:0") + _ = reader.pipeline_ + + mock_ctor.assert_called_once() + assert "enable_mkldnn" not in mock_ctor.call_args.kwargs + + # --------------------------------------------------------------------------- # PaddleOCRVLReader tests # --------------------------------------------------------------------------- @@ -225,3 +270,48 @@ def test_paddleocr_vl_load_data_with_mocked_pipeline( assert len(docs) >= 1 assert all(isinstance(d, Document) for d in docs) mock_pipeline.predict.assert_called_once_with("/tmp/img.png") + + +@skip_when_paddleocr_not_installed +@pytest.mark.parametrize( + "device", + ("cpu", "cpu:0"), +) +def test_paddleocr_vl_cpu_disables_mkldnn( + mocker: MockerFixture, + device: str, +) -> None: + """CPU init passes enable_mkldnn=False (Paddle#77340 workaround).""" + from kotaemon.loaders.paddleocr_loader import paddleocr_vl_loader + + mock_pipeline = MagicMock() + mock_ctor = mocker.patch( + "paddleocr.PaddleOCRVL", + return_value=mock_pipeline, + ) + + reader = paddleocr_vl_loader.PaddleOCRVLReader(device=device) + _ = reader.pipeline_ + + mock_ctor.assert_called_once() + assert mock_ctor.call_args.kwargs["enable_mkldnn"] is False + + +@skip_when_paddleocr_not_installed +def test_paddleocr_vl_gpu_omits_mkldnn_override( + mocker: MockerFixture, +) -> None: + """GPU init leaves enable_mkldnn to paddleocr defaults.""" + from kotaemon.loaders.paddleocr_loader import paddleocr_vl_loader + + mock_pipeline = MagicMock() + mock_ctor = mocker.patch( + "paddleocr.PaddleOCRVL", + return_value=mock_pipeline, + ) + + reader = paddleocr_vl_loader.PaddleOCRVLReader(device="gpu:0") + _ = reader.pipeline_ + + mock_ctor.assert_called_once() + assert "enable_mkldnn" not in mock_ctor.call_args.kwargs