docling-glm-ocr 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ name: Main
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ # ---------------------------------------------------------------------------
13
+ # Quality checks via DCC-BS reusable workflow (ruff lint + format + ty)
14
+ # ---------------------------------------------------------------------------
15
+ quality:
16
+ uses: DCC-BS/ci-workflows/.github/workflows/python-backend-ci.yml@v8
17
+ with:
18
+ python_versions: '["3.13"]'
19
+ quality_python_version: "3.13"
20
+ check_command: "make check"
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Tests + coverage
24
+ # ---------------------------------------------------------------------------
25
+ tests:
26
+ name: Tests (Python ${{ matrix.python-version }})
27
+ runs-on: ubuntu-latest
28
+ needs: [quality]
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ python-version: ["3.13"]
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Install uv
38
+ uses: astral-sh/setup-uv@v5
39
+ with:
40
+ enable-cache: true
41
+ cache-dependency-glob: "uv.lock"
42
+
43
+ - name: Set up Python ${{ matrix.python-version }}
44
+ run: uv python install ${{ matrix.python-version }}
45
+
46
+ - name: Install dependencies
47
+ run: uv sync --all-extras --dev
48
+
49
+ - name: Run tests with coverage
50
+ run: uv run pytest --cov --cov-report=xml --junitxml=junit.xml
51
+
52
+ - name: Upload coverage to Codecov
53
+ uses: codecov/codecov-action@v5
54
+ with:
55
+ token: ${{ secrets.CODECOV_TOKEN }}
56
+ files: ./coverage.xml
57
+ - name: Upload test results to Codecov
58
+ if: ${{ !cancelled() }}
59
+ uses: codecov/test-results-action@v1
60
+ with:
61
+ token: ${{ secrets.CODECOV_TOKEN }}
62
+ files: ./junit.xml
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ id-token: write # required for PyPI trusted publishing
8
+ contents: write # required to push git tags
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v5
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python
25
+ run: uv python install 3.13
26
+
27
+ - name: Create and push git tag
28
+ run: |
29
+ VERSION=$(uv version --short)
30
+ TAG="v${VERSION}"
31
+ git config --global user.name "github-actions[bot]"
32
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
33
+ if git rev-parse "$TAG" >/dev/null 2>&1 || git ls-remote --tags origin | grep -q "refs/tags/$TAG"; then
34
+ echo "Tag $TAG already exists – skipping"
35
+ else
36
+ git tag "$TAG"
37
+ git push origin "$TAG"
38
+ echo "Created and pushed tag $TAG"
39
+ fi
40
+
41
+ - name: Build package
42
+ run: uv build
43
+
44
+ - name: Publish to PyPI
45
+ run: uv publish
@@ -0,0 +1,57 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .eggs/
11
+ .installed.cfg
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ share/python-wheels/
19
+ MANIFEST
20
+
21
+ # Virtual environments
22
+ .venv/
23
+ env/
24
+ venv/
25
+ ENV/
26
+
27
+
28
+ # Testing & coverage
29
+ .pytest_cache/
30
+ .coverage
31
+ coverage.xml
32
+ htmlcov/
33
+ .tox/
34
+
35
+ # Type checkers
36
+ .mypy_cache/
37
+ .ruff_cache/
38
+ .ty_cache/
39
+
40
+ # IDEs
41
+ .idea/
42
+ .vscode/
43
+ *.swp
44
+ *.swo
45
+ *~
46
+
47
+ # OS
48
+ .DS_Store
49
+ Thumbs.db
50
+
51
+ # Distribution
52
+ *.tar.gz
53
+ *.whl
54
+
55
+ # Environment variables
56
+ .env
57
+ .env.local
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: "v5.0.0"
4
+ hooks:
5
+ - id: check-case-conflict
6
+ - id: check-merge-conflict
7
+ - id: check-toml
8
+ - id: check-yaml
9
+ - id: check-json
10
+ - id: end-of-file-fixer
11
+ - id: trailing-whitespace
12
+ - id: detect-private-key
13
+
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ rev: "v0.9.9"
16
+ hooks:
17
+ - id: ruff
18
+ args: [--fix, --exit-non-zero-on-fix]
19
+ types_or: [python, pyi]
20
+ - id: ruff-format
21
+ types_or: [python, pyi]
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Data Competence Center Basel-Stadt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,56 @@
1
+ .DEFAULT_GOAL := help
2
+
3
+ .PHONY: help install check lint format type-check test test-xml build clean-build publish
4
+
5
+ help: ## Display available commands
6
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} \
7
+ /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
8
+
9
+ # ---------------------------------------------------------------------------
10
+ # Setup
11
+ # ---------------------------------------------------------------------------
12
+
13
+ install: ## Install dependencies and pre-commit hooks
14
+ uv sync --all-extras --dev
15
+ uv run pre-commit install
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Quality checks
19
+ # ---------------------------------------------------------------------------
20
+
21
+ lint: ## Run ruff linter (with auto-fix)
22
+ uv run ruff check . --fix
23
+
24
+ format: ## Run ruff formatter
25
+ uv run ruff format .
26
+
27
+ type-check: ## Run ty type checker
28
+ uv run ty check src/
29
+
30
+ check: ## Run all quality checks (lint, format check, type check)
31
+ uv run ruff check .
32
+ uv run ruff format --check .
33
+ uv run ty check src/
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Tests
37
+ # ---------------------------------------------------------------------------
38
+
39
+ test: ## Run tests with terminal coverage report
40
+ uv run pytest
41
+
42
+ test-xml: ## Run tests and produce XML coverage report only
43
+ uv run pytest --cov-report=xml --cov-report=
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Build & publish
47
+ # ---------------------------------------------------------------------------
48
+
49
+ build: clean-build ## Build distribution packages
50
+ uv build
51
+
52
+ clean-build: ## Remove build artefacts
53
+ rm -rf dist/ build/ *.egg-info
54
+
55
+ publish: build ## Publish package to PyPI
56
+ uv publish
@@ -0,0 +1,261 @@
1
+ Metadata-Version: 2.4
2
+ Name: docling-glm-ocr
3
+ Version: 0.1.0
4
+ Summary: A docling OCR plugin for GLM-OCR
5
+ Project-URL: Homepage, https://github.com/DCC-BS/docling-glm-ocr
6
+ Project-URL: Repository, https://github.com/DCC-BS/docling-glm-ocr
7
+ Project-URL: Issues, https://github.com/DCC-BS/docling-glm-ocr/issues
8
+ Project-URL: Changelog, https://github.com/DCC-BS/docling-glm-ocr/releases
9
+ Author-email: Yanick Schraner <yanick.schraner@bs.ch>, Tobias Bollinger <tobias.bollinger@bs.ch>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.13
19
+ Requires-Dist: docling>=2.73
20
+ Requires-Dist: httpx>=0.27
21
+ Requires-Dist: pillow>=10.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # docling-glm-ocr
25
+
26
+ A docling OCR plugin that delegates text recognition to a remote
27
+ [GLM-OCR](https://huggingface.co/zai-org/GLM-OCR) model served by vLLM.
28
+
29
+ ---
30
+
31
+ <p align="center">
32
+ <a href="https://github.com/DCC-BS/docling-glm-ocr">GitHub</a>
33
+ &nbsp;|&nbsp;
34
+ <a href="https://pypi.org/project/docling-glm-ocr/">PyPI</a>
35
+ </p>
36
+
37
+ ---
38
+
39
+ [![PyPI version](https://img.shields.io/pypi/v/docling-glm-ocr.svg)](https://pypi.org/project/docling-glm-ocr/)
40
+ [![Python versions](https://img.shields.io/pypi/pyversions/docling-glm-ocr.svg)](https://pypi.org/project/docling-glm-ocr/)
41
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/DCC-BS/docling-glm-ocr/blob/main/LICENSE)
42
+ [![CI](https://github.com/DCC-BS/docling-glm-ocr/actions/workflows/main.yml/badge.svg)](https://github.com/DCC-BS/docling-glm-ocr/actions/workflows/main.yml)
43
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
44
+ [![codecov](https://codecov.io/gh/DCC-BS/docling-glm-ocr/graph/badge.svg?token=LOPIP1UZOC)](https://codecov.io/gh/DCC-BS/docling-glm-ocr)
45
+
46
+ ## Overview
47
+
48
+ `docling-glm-ocr` is a [docling](https://github.com/DS4SD/docling) plugin that
49
+ replaces the built-in OCR stage with a call to a remote
50
+ [GLM-OCR](https://huggingface.co/zai-org/GLM-OCR) model hosted on a
51
+ [vLLM](https://github.com/vllm-project/vllm) server.
52
+
53
+ Each page crop is sent to the vLLM OpenAI-compatible chat completion endpoint
54
+ as a base64-encoded image. The model returns Markdown-formatted text which
55
+ docling merges back into the document structure.
56
+
57
+ The plugin registers itself under the `"glm-ocr-remote"` OCR engine key so it
58
+ can be selected per-request through docling or docling-serve without changing
59
+ application code.
60
+
61
+ ## Requirements
62
+
63
+ - Python 3.13+
64
+ - A running vLLM server hosting `zai-org/GLM-OCR` (or any compatible model)
65
+
66
+ ## Installation
67
+
68
+ ```bash
69
+ # with uv (recommended)
70
+ uv add docling-glm-ocr
71
+
72
+ # with pip
73
+ pip install docling-glm-ocr
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ ### Python SDK
79
+
80
+ ```python
81
+ from docling.datamodel.base_models import InputFormat
82
+ from docling.datamodel.pipeline_options import PdfPipelineOptions
83
+ from docling.document_converter import DocumentConverter, PdfFormatOption
84
+
85
+ from docling_glm_ocr import GlmOcrRemoteOptions
86
+
87
+ pipeline_options = PdfPipelineOptions(
88
+ allow_external_plugins=True,
89
+ ocr_options=GlmOcrRemoteOptions(
90
+ api_url="http://localhost:8001/v1/chat/completions",
91
+ model_name="zai-org/GLM-OCR",
92
+ ),
93
+ )
94
+
95
+ converter = DocumentConverter(
96
+ format_options={
97
+ InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)
98
+ }
99
+ )
100
+ result = converter.convert("document.pdf")
101
+ print(result.document.export_to_markdown())
102
+ ```
103
+
104
+ ### docling-serve
105
+
106
+ Select the engine per-request via the standard API:
107
+
108
+ ```bash
109
+ curl -X POST http://localhost:5001/v1/convert/source \
110
+ -H 'Content-Type: application/json' \
111
+ -d '{
112
+ "options": {
113
+ "ocr_engine": "glm-ocr-remote"
114
+ },
115
+ "sources": [{"kind": "http", "url": "https://arxiv.org/pdf/2501.17887"}]
116
+ }'
117
+ ```
118
+
119
+ The server must have `DOCLING_SERVE_ALLOW_EXTERNAL_PLUGINS=true` set so the
120
+ plugin is loaded automatically.
121
+
122
+ ## Configuration
123
+
124
+ ### Environment variables
125
+
126
+ | Variable | Description | Default |
127
+ |---|---|---|
128
+ | `GLMOCR_REMOTE_OCR_API_URL` | vLLM chat completion URL | `http://localhost:8001/v1/chat/completions` |
129
+ | `GLMOCR_REMOTE_OCR_PROMPT` | Text prompt sent with each image crop | see below |
130
+
131
+ ### `GlmOcrRemoteOptions`
132
+
133
+ All options can be set programmatically via `GlmOcrRemoteOptions`:
134
+
135
+ | Option | Type | Description | Default |
136
+ |---|---|---|---|
137
+ | `api_url` | `str` | OpenAI-compatible chat completion URL | `GLMOCR_REMOTE_OCR_API_URL` env or `http://localhost:8001/v1/chat/completions` |
138
+ | `model_name` | `str` | Model name sent to vLLM | `zai-org/GLM-OCR` |
139
+ | `prompt` | `str` | Text prompt for each image crop | `GLMOCR_REMOTE_OCR_PROMPT` env or default prompt |
140
+ | `timeout` | `float` | HTTP timeout per crop (seconds) | `120` |
141
+ | `max_tokens` | `int` | Max tokens per completion | `16384` |
142
+ | `lang` | `list[str]` | Language hint (passed to docling) | `["en"]` |
143
+
144
+ Default prompt:
145
+
146
+ ```
147
+ Recognize the text in the image and output in Markdown format.
148
+ Preserve the original layout (headings/paragraphs/tables/formulas).
149
+ Do not fabricate content that does not exist in the image.
150
+ ```
151
+
152
+ ## Architecture
153
+
154
+ ```mermaid
155
+ flowchart LR
156
+ subgraph docling
157
+ Pipeline --> GlmOcrRemoteModel
158
+ end
159
+
160
+ subgraph vLLM
161
+ GLMOCR["zai-org/GLM-OCR"]
162
+ end
163
+
164
+ GlmOcrRemoteModel -- "POST /v1/chat/completions\n(base64 image)" --> GLMOCR
165
+ GLMOCR -- "Markdown text" --> GlmOcrRemoteModel
166
+ ```
167
+
168
+ For each page the model:
169
+ 1. Collects OCR regions from the docling layout analysis
170
+ 2. Renders each region at 3× scale (216 dpi) using the page backend
171
+ 3. Encodes the crop as a base64 PNG data URI
172
+ 4. POSTs a chat completion request to the vLLM endpoint
173
+ 5. Returns the recognised text as `TextCell` objects for docling to merge
174
+
175
+ ## Starting a GLM-OCR vLLM server
176
+
177
+ ```bash
178
+ docker run -d \
179
+ --rm --name ocr-glm \
180
+ --gpus device=0 \
181
+ --ipc=host \
182
+ -p 8001:8000 \
183
+ -v "${HOME}/.cache/huggingface:/root/.cache/huggingface" \
184
+ -e "HF_TOKEN=${HF_TOKEN}" \
185
+ --entrypoint /bin/bash \
186
+ vllm/vllm-openai:latest \
187
+ -c "uv pip install --system --upgrade transformers && \
188
+ exec vllm serve zai-org/GLM-OCR \
189
+ --served-model-name zai-org/GLM-OCR \
190
+ --port 8000 \
191
+ --trust-remote-code"
192
+ ```
193
+
194
+ The plugin will connect to `http://localhost:8001/v1/chat/completions` by default.
195
+
196
+ ## Development
197
+
198
+ ### Setup
199
+
200
+ ```bash
201
+ git clone https://github.com/DCC-BS/docling-glm-ocr.git
202
+ cd docling-glm-ocr
203
+ make install
204
+ ```
205
+
206
+ ### Available commands
207
+
208
+ ```
209
+ make install Install dependencies and pre-commit hooks
210
+ make check Run all quality checks (ruff lint, format, ty type check)
211
+ make test Run tests with coverage report
212
+ make build Build distribution packages
213
+ make publish Publish to PyPI
214
+ ```
215
+
216
+ ### Running tests
217
+
218
+ ```bash
219
+ make test
220
+ ```
221
+
222
+ Tests are in `tests/` and use [pytest](https://pytest.org).
223
+ Coverage reports are generated at `coverage.xml` and printed to the terminal.
224
+
225
+ #### End-to-end tests
226
+
227
+ The e2e tests hit a real vLLM server and are **skipped by default**.
228
+ To run them, set the server URL and use the `e2e` marker:
229
+
230
+ ```bash
231
+ GLMOCR_REMOTE_OCR_API_URL=http://localhost:8001/v1/chat/completions pytest -m e2e
232
+ ```
233
+
234
+ ### Code quality
235
+
236
+ This project uses:
237
+
238
+ - **[ruff](https://github.com/astral-sh/ruff)** – linting and formatting
239
+ - **[ty](https://github.com/astral-sh/ty)** – type checking
240
+ - **[pre-commit](https://pre-commit.com/)** – pre-commit hooks
241
+
242
+ Run all checks:
243
+
244
+ ```bash
245
+ make check
246
+ ```
247
+
248
+ ### Releasing
249
+
250
+ Releases are published to PyPI automatically.
251
+ Update the version in `pyproject.toml`, then trigger the **Publish** workflow from GitHub Actions:
252
+
253
+ ```
254
+ GitHub → Actions → Publish to PyPI → Run workflow
255
+ ```
256
+
257
+ The workflow tags the commit, builds the package, and publishes to PyPI via trusted publishing.
258
+
259
+ ## License
260
+
261
+ [MIT](LICENSE) © Data Competence Center Basel-Stadt