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.
- docling_glm_ocr-0.1.0/.github/workflows/main.yml +62 -0
- docling_glm_ocr-0.1.0/.github/workflows/publish.yml +45 -0
- docling_glm_ocr-0.1.0/.gitignore +57 -0
- docling_glm_ocr-0.1.0/.pre-commit-config.yaml +21 -0
- docling_glm_ocr-0.1.0/.python-version +1 -0
- docling_glm_ocr-0.1.0/LICENSE +21 -0
- docling_glm_ocr-0.1.0/Makefile +56 -0
- docling_glm_ocr-0.1.0/PKG-INFO +261 -0
- docling_glm_ocr-0.1.0/README.md +238 -0
- docling_glm_ocr-0.1.0/codecov.yml +15 -0
- docling_glm_ocr-0.1.0/pyproject.toml +152 -0
- docling_glm_ocr-0.1.0/renovate.json +105 -0
- docling_glm_ocr-0.1.0/src/docling_glm_ocr/__init__.py +8 -0
- docling_glm_ocr-0.1.0/src/docling_glm_ocr/model.py +159 -0
- docling_glm_ocr-0.1.0/src/docling_glm_ocr/options.py +46 -0
- docling_glm_ocr-0.1.0/src/docling_glm_ocr/plugin.py +8 -0
- docling_glm_ocr-0.1.0/src/docling_glm_ocr/py.typed +0 -0
- docling_glm_ocr-0.1.0/tests/__init__.py +0 -0
- docling_glm_ocr-0.1.0/tests/conftest.py +47 -0
- docling_glm_ocr-0.1.0/tests/test_e2e.py +96 -0
- docling_glm_ocr-0.1.0/tests/test_model.py +391 -0
- docling_glm_ocr-0.1.0/tests/test_options.py +89 -0
- docling_glm_ocr-0.1.0/tests/test_plugin.py +25 -0
- docling_glm_ocr-0.1.0/uv.lock +2434 -0
|
@@ -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
|
+
|
|
|
34
|
+
<a href="https://pypi.org/project/docling-glm-ocr/">PyPI</a>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/docling-glm-ocr/)
|
|
40
|
+
[](https://pypi.org/project/docling-glm-ocr/)
|
|
41
|
+
[](https://github.com/DCC-BS/docling-glm-ocr/blob/main/LICENSE)
|
|
42
|
+
[](https://github.com/DCC-BS/docling-glm-ocr/actions/workflows/main.yml)
|
|
43
|
+
[](https://github.com/astral-sh/ruff)
|
|
44
|
+
[](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
|