"""OCRClient Protocol (spec ยง6.2). Structural typing: any object with an async ``ocr(pages) -> OCRResult`` method satisfies the Protocol. :class:`~ix.pipeline.ocr_step.OCRStep` depends on the Protocol, not a concrete class, so swapping engines (``FakeOCRClient`` in tests, ``SuryaOCRClient`` in prod) stays a wiring change at the app factory. """ from __future__ import annotations from typing import Protocol, runtime_checkable from ix.contracts import OCRResult, Page @runtime_checkable class OCRClient(Protocol): """Async OCR backend. Implementations receive the flat page list the pipeline built in :class:`~ix.pipeline.setup_step.SetupStep` and return an :class:`~ix.contracts.OCRResult` with one :class:`~ix.contracts.Page` per input page (in the same order). """ async def ocr(self, pages: list[Page]) -> OCRResult: """Run OCR over the input pages; return the structured result.""" ... __all__ = ["OCRClient"]