diff --git a/nextcloud_mcp_server/document_processors/registry.py b/nextcloud_mcp_server/document_processors/registry.py index 2aff91c5..53b296c2 100644 --- a/nextcloud_mcp_server/document_processors/registry.py +++ b/nextcloud_mcp_server/document_processors/registry.py @@ -264,7 +264,7 @@ class ProcessorRegistry: filename or "", reason, ) - return await self._run_processor( + ocr_result = await self._run_processor( ocr, content, content_type, @@ -273,6 +273,19 @@ class ProcessorRegistry: progress_callback, escalated=True, ) + # OCR is an enhancement, not a gate: if it can't run (no backend + # configured / API down) or returns nothing, keep the tier-1 + # result rather than failing the document. Otherwise an operator + # who sets DOCUMENT_OCR_ENABLED=true without credentials would + # make scanned docs fail entirely -- strictly worse than off. + if ocr_result.success: + return ocr_result + logger.warning( + "OCR escalation did not succeed for %s (%s); keeping the " + "tier-1 result", + filename or "", + ocr_result.metadata.get("parse_failed_reason", "error"), + ) return result diff --git a/tests/unit/test_registry_tiering.py b/tests/unit/test_registry_tiering.py index 1124a69f..37dd6177 100644 --- a/tests/unit/test_registry_tiering.py +++ b/tests/unit/test_registry_tiering.py @@ -117,6 +117,20 @@ async def test_ocr_escalation_on_empty_text(monkeypatch): esc.assert_called_once() +async def test_ocr_failure_falls_back_to_fast(monkeypatch): + # OCR enabled but the backend can't run (no creds / API down) -> keep the + # tier-1 result instead of failing the document. + monkeypatch.setattr(reg_mod, "get_settings", lambda: _Settings(ocr=True)) + monkeypatch.setattr(reg_mod, "record_document_escalation", MagicMock()) + r = _registry( + (_Fake("fast", "fast", text=""), 20), + (_Fake("ocr", "ocr", text="", success=False), 5), + ) + res = await r.process(b"%PDF-1.7", "application/pdf") + assert res.processor == "fast" + assert res.success is True + + async def test_no_ocr_escalation_when_disabled(monkeypatch): monkeypatch.setattr(reg_mod, "get_settings", lambda: _Settings(ocr=False)) r = _registry(