Metadata-Version: 2.4
Name: docling-surya-ocr
Version: 0.2.0
Summary: Docling OCR plugin backed by Surya with Surya 1 rollback and Surya 2 llama.cpp/vLLM runtime controls.
Author: docling-surya-ocr maintainers
License: MIT
Keywords: cuda,docling,ocr,pdf,surya
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Classifier: Topic :: Text Processing :: General
Requires-Python: <3.13,>=3.12
Requires-Dist: docling<3,>=2.115
Requires-Dist: surya-ocr
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Provides-Extra: gpu
Requires-Dist: torch>=2.13; extra == 'gpu'
Description-Content-Type: text/markdown

# docling-surya-ocr

Docling OCR plugin backed by Surya. It provides the Docling OCR engine key `suryaocr` and a stable Python import namespace `docling_surya_ocr`.

The repository currently contains three runtime paths:

- `surya2`: target production path, using Surya 2 full-page OCR with `llama.cpp` on CPU by default and `vLLM` as a supported GPU backend.
- `surya1`: validated v1-style Surya recognizer path, kept as an explicit rollback and benchmark reference.
- `unlimited`: experimental comparison backend for internal evaluation only; it is explicit-only and not part of the Surya production path.

For UVO-style document processing, the intended production shape is Docling + Surya 2 + `llama.cpp` on a large CPU host, with page-level parallelism tuned by benchmark. GPU/vLLM stays available behind the same backend contract so the converter does not need to be rewritten later.

## Install

From PyPI after release:

```bash
pip install docling-surya-ocr
```

From a built wheel:

```bash
pip install docling_surya_ocr-0.1.0-py3-none-any.whl
```

For local development:

```bash
pip install -e .[dev]
```

Runtime dependencies are declared for Python `>=3.12,<3.13`:

```bash
pip install "docling>=2.115,<3" surya-ocr
```

For GPU validation, install a CUDA-enabled Torch build that matches the target host/container.

## Docling Usage

Use Docling's external OCR plugin seam and enable external plugins:

```python
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling_surya_ocr import SuryaOcrOptions

pipeline_options = PdfPipelineOptions(
    do_ocr=True,
    ocr_model="suryaocr",
    allow_external_plugins=True,
    ocr_options=SuryaOcrOptions(
        lang=["sk", "en"],
        backend="surya2",
        surya2_inference_backend="llamacpp",
        surya2_inference_parallel=4,
    ),
)

converter = DocumentConverter(
    format_options={
        InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options),
    }
)

result = converter.convert("document.pdf")
text = result.document.export_to_text()
```

The package also exposes a compatibility import alias for deployments that already use the upstream plugin import path:

```python
from docling_surya import SuryaOcrOptions
```

The Docling OCR model key remains `suryaocr`.

## Backend Selection

Select the plugin backend in code or through `SURYA_OCR_BACKEND`:

```python
SuryaOcrOptions(backend="auto")    # prefers Surya 2 when available
SuryaOcrOptions(backend="surya2")  # target Surya 2 path; fails closed if unavailable
SuryaOcrOptions(backend="surya1")  # validated v1-style rollback path
```

```bash
export SURYA_OCR_BACKEND=surya2
```

`auto` prefers Surya 2 when the Surya 2 API is available. If Surya 2 is unavailable and no strong Surya 2 runtime setting is present, `auto` can fall back to `surya1`. Use explicit `backend="surya2"` for production validation when fallback must not hide failures.

The Surya 2 inference backend defaults to `llamacpp`:

```bash
export SURYA_OCR_BACKEND=surya2
export SURYA_INFERENCE_BACKEND=llamacpp
export SURYA_INFERENCE_PARALLEL=4
```

`vLLM` is supported as the GPU inference backend:

```bash
export SURYA_OCR_BACKEND=surya2
export SURYA_INFERENCE_BACKEND=vllm
export SURYA_INFERENCE_URL=http://localhost:8000/v1
export SURYA_INFERENCE_PARALLEL=8
```

## CPU Production Path

For Surya 2 CPU production, use `llama.cpp` and tune page-level parallelism:

```bash
export SURYA_OCR_BACKEND=surya2
export SURYA_INFERENCE_BACKEND=llamacpp
export SURYA_INFERENCE_PARALLEL=4
export SURYA_DEVICE=cpu
export SURYA_CPU_THREADS=8
```

Benchmark `SURYA_INFERENCE_PARALLEL=1/4/8/16/24/32` on the target EPYC host before selecting a default. The optimum may arrive before all CPU cores are saturated because Surya 2 OCR can become memory-bandwidth or decode limited.

## GPU Validation Path

For Surya 2 GPU attach mode, run or attach to an OpenAI-compatible vLLM Surya inference server and configure:

```bash
export SURYA_OCR_BACKEND=surya2
export SURYA_INFERENCE_BACKEND=vllm
export SURYA_INFERENCE_URL=http://localhost:8000/v1
export SURYA_INFERENCE_PARALLEL=8
export SURYA_DEVICE=cuda
export SURYA_PRECISION=fp16
```

Verify CUDA from inside the runtime container:

```bash
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
```

## Options

`SuryaOcrOptions` can be configured directly or via environment variables.

| Option | Environment variable | Example |
| --- | --- | --- |
| Languages | `SURYA_OCR_LANGUAGES` | `sk,en` |
| Backend | `SURYA_OCR_BACKEND` | `auto`, `surya1`, `surya2` |
| Device | `SURYA_DEVICE` | `cpu`, `cuda`, `auto` |
| GPU devices | `SURYA_GPU_DEVICES` | `0` |
| GPU memory fraction | `SURYA_GPU_MEMORY_FRACTION` | `0.8` |
| Precision | `SURYA_PRECISION` | `fp16`, `fp32`, `bf16`, `auto` |
| CPU threads | `SURYA_CPU_THREADS` | `8` |
| Recognition batch | `SURYA_RECOGNITION_BATCH` | `16` |
| Detection batch | `SURYA_DETECTION_BATCH` | `16` |
| Docling OCR batch | `DOCLING_OCR_BATCH` | `4` |
| Preserve provenance | `SURYA_OCR_PRESERVE_PROVENANCE` | `true` |
| Document analysis backend | `SURYA_DOCUMENT_ANALYSIS_BACKEND` | `surya2_ocr_only`, `surya2_full`, `docling_default` |
| Layout backend | `SURYA_LAYOUT_BACKEND` | `docling`, `surya2` |
| Table backend | `SURYA_TABLE_BACKEND` | `docling`, `surya2`, `disabled` |
| Surya 2 inference backend | `SURYA_INFERENCE_BACKEND` | `llamacpp`, `vllm`, `auto` |
| Surya 2 inference URL | `SURYA_INFERENCE_URL` | `http://localhost:8000/v1` |
| Surya 2 keep server | `SURYA_INFERENCE_KEEP_ALIVE` | `true`, `false` |
| Surya 2 parallelism | `SURYA_INFERENCE_PARALLEL` | `8` |

The codebase includes an explicit-only `unlimited` backend for internal experiments. It is not selected by `auto` and is intentionally omitted from the main Surya production configuration above.

## Document Analysis Modes

Full Surya 2 document analysis is optional and **off by default**. The default
document-analysis mode is `surya2_ocr_only`, which keeps the existing behavior:
Surya handles OCR only, while Docling continues to own layout and table
processing.

Select document-analysis behavior with `SuryaOcrOptions` or environment
variables:

```python
SuryaOcrOptions(
    backend="surya2",
    document_analysis_backend="surya2_ocr_only",  # default: full integration off
    layout_backend="docling",
    table_backend="docling",
)
```

```bash
export SURYA_DOCUMENT_ANALYSIS_BACKEND=surya2_ocr_only
export SURYA_LAYOUT_BACKEND=docling
export SURYA_TABLE_BACKEND=docling
```

Valid document-analysis values are:

| Mode | Valid layout/table settings | Behavior |
| --- | --- | --- |
| `surya2_ocr_only` | `SURYA_LAYOUT_BACKEND=docling`, `SURYA_TABLE_BACKEND=docling` by default | Default and recommended first-slice mode. Surya OCR is used through the OCR plugin seam; Docling layout/table remain in place. |
| `docling_default` | `docling` layout/table | Comparison/control mode. It must not be interpreted as Surya 2 full integration. |
| `surya2_full` | Requires `SURYA_OCR_BACKEND=surya2`, `SURYA_LAYOUT_BACKEND=surya2`, and `SURYA_TABLE_BACKEND=surya2` or `disabled` | Explicit, experimental, fail-closed mode. In this first slice it probes Surya 2 document-analysis capabilities and then fails with `surya2_full_not_implemented`; it does **not** yet replace Docling layout/table. |

Rollback/disable options:

- Leave `SURYA_DOCUMENT_ANALYSIS_BACKEND` unset, or set
  `SURYA_DOCUMENT_ANALYSIS_BACKEND=surya2_ocr_only`, to keep full integration
  disabled.
- Use `SURYA_OCR_BACKEND=surya1` to return to the validated v1-style OCR
  rollback path.
- Do not remove Docling layout/table components for this first slice; no
  Egret/tableformer removal is claimed here.

## Smoke Test

After installing the wheel, run a conversion without `PYTHONPATH=src`:

```bash
python scripts/smoke_convert.py sample.pdf --plugin suryaocr
```

Force the Surya 2 CPU path during smoke validation:

```bash
python scripts/smoke_convert.py sample.pdf \
  --plugin suryaocr \
  --backend surya2 \
  --inference-backend llamacpp \
  --inference-parallel 4
```

The smoke test fails closed if Docling external plugins are disabled, the plugin key is unavailable, Docling logs an OCR-stage failure, no text is produced, or Docling page/bbox provenance is missing.

## Benchmark

Corpus benchmark example for the Surya 2 CPU path:

```bash
python scripts/benchmark_docling_corpus.py benchmarks/pdf-corpus \
  --plugin suryaocr \
  --backend surya2 \
  --inference-backend llamacpp \
  --inference-parallel 16 \
  --jsonl benchmarks/results/surya2-llamacpp.jsonl
```

The historical benchmark report for this repository is in:

```text
benchmarks/results/docling-surya-ocr-benchmark-report.md
```

Those historical measurements are for the v1-style backend. They are retained as a rollback/reference baseline, not as Surya 2 `llama.cpp` production evidence.

## Surya 2 Model License

Surya code is Apache 2.0, but Surya 2 model weights are distributed under a modified OpenRAIL-M license. Review the weight license before commercial/production rollout; this package does not vendor or redistribute model weights.

## Compatibility Notes

The target verified runtime is Docling `2.115.0`. In that version:

- `BaseOcrModel.post_process_cells` uses the `docling_2115` style and may mutate the page in place.
- `BoundingRectangle` is available from `docling_core.types.doc.page`.
- Docling provenance uses `item.prov` entries with `page_no` and `bbox`.
- Surya v1 recognition is available through `surya.recognition.RecognitionPredictor`; Surya 2 additionally expects `surya.inference.SuryaInferenceManager`.

## Development Checks

```bash
python -m pytest tests/unit tests/contract
python -m ruff check src tests scripts
python -m mypy src
```
