docling-glm-ocr 0.1.0__py3-none-any.whl
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/__init__.py +8 -0
- docling_glm_ocr/model.py +159 -0
- docling_glm_ocr/options.py +46 -0
- docling_glm_ocr/plugin.py +8 -0
- docling_glm_ocr/py.typed +0 -0
- docling_glm_ocr-0.1.0.dist-info/METADATA +261 -0
- docling_glm_ocr-0.1.0.dist-info/RECORD +10 -0
- docling_glm_ocr-0.1.0.dist-info/WHEEL +4 -0
- docling_glm_ocr-0.1.0.dist-info/entry_points.txt +2 -0
- docling_glm_ocr-0.1.0.dist-info/licenses/LICENSE +21 -0
docling_glm_ocr/model.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""GLM-OCR remote OCR engine for the docling standard pipeline.
|
|
2
|
+
|
|
3
|
+
Sends each page crop to a vLLM-hosted GLM-OCR model and returns the
|
|
4
|
+
recognised text as ``TextCell`` objects that docling merges with its
|
|
5
|
+
standard-pipeline output.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import base64
|
|
11
|
+
import io
|
|
12
|
+
import logging
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
from docling.models.base_ocr_model import BaseOcrModel
|
|
17
|
+
from docling.utils.profiling import TimeRecorder
|
|
18
|
+
from docling_core.types.doc import BoundingBox, CoordOrigin
|
|
19
|
+
from docling_core.types.doc.page import BoundingRectangle, TextCell
|
|
20
|
+
|
|
21
|
+
from docling_glm_ocr.options import GlmOcrRemoteOptions
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Iterable
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from docling.datamodel.accelerator_options import AcceleratorOptions
|
|
28
|
+
from docling.datamodel.base_models import Page
|
|
29
|
+
from docling.datamodel.document import ConversionResult
|
|
30
|
+
from docling.datamodel.pipeline_options import OcrOptions
|
|
31
|
+
from PIL import Image
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _pil_to_base64_uri(image: Image.Image, fmt: str = "PNG") -> str:
|
|
37
|
+
buf = io.BytesIO()
|
|
38
|
+
image.save(buf, format=fmt)
|
|
39
|
+
b64 = base64.b64encode(buf.getvalue()).decode()
|
|
40
|
+
return f"data:image/{fmt.lower()};base64,{b64}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class GlmOcrRemoteModel(BaseOcrModel):
|
|
44
|
+
"""OCR engine that delegates recognition to a remote GLM-OCR vLLM server."""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
enabled: bool,
|
|
50
|
+
artifacts_path: Path | None,
|
|
51
|
+
options: GlmOcrRemoteOptions,
|
|
52
|
+
accelerator_options: AcceleratorOptions,
|
|
53
|
+
) -> None:
|
|
54
|
+
super().__init__(
|
|
55
|
+
enabled=enabled,
|
|
56
|
+
artifacts_path=artifacts_path,
|
|
57
|
+
options=options,
|
|
58
|
+
accelerator_options=accelerator_options,
|
|
59
|
+
)
|
|
60
|
+
self.options: GlmOcrRemoteOptions
|
|
61
|
+
self.scale = 3 # 72 dpi → 216 dpi
|
|
62
|
+
|
|
63
|
+
if self.enabled:
|
|
64
|
+
self._client = httpx.Client(timeout=self.options.timeout)
|
|
65
|
+
logger.info(
|
|
66
|
+
"GlmOcrRemoteModel initialised: api_url=%s model=%s",
|
|
67
|
+
self.options.api_url,
|
|
68
|
+
self.options.model_name,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def _recognise_crop(self, image: Image.Image) -> str:
|
|
72
|
+
"""Send a single cropped image to the remote GLM-OCR endpoint."""
|
|
73
|
+
data_uri = _pil_to_base64_uri(image)
|
|
74
|
+
payload = {
|
|
75
|
+
"model": self.options.model_name,
|
|
76
|
+
"max_tokens": self.options.max_tokens,
|
|
77
|
+
"messages": [
|
|
78
|
+
{
|
|
79
|
+
"role": "user",
|
|
80
|
+
"content": [
|
|
81
|
+
{"type": "text", "text": self.options.prompt},
|
|
82
|
+
{"type": "image_url", "image_url": {"url": data_uri}},
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
}
|
|
87
|
+
resp = self._client.post(self.options.api_url, json=payload)
|
|
88
|
+
resp.raise_for_status()
|
|
89
|
+
choices = resp.json().get("choices", [])
|
|
90
|
+
if not choices:
|
|
91
|
+
return ""
|
|
92
|
+
return choices[0].get("message", {}).get("content", "")
|
|
93
|
+
|
|
94
|
+
def __call__(
|
|
95
|
+
self,
|
|
96
|
+
conv_res: ConversionResult,
|
|
97
|
+
page_batch: Iterable[Page],
|
|
98
|
+
) -> Iterable[Page]:
|
|
99
|
+
"""Run OCR on each page crop and yield pages with recognised text cells."""
|
|
100
|
+
if not self.enabled:
|
|
101
|
+
yield from page_batch
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
for page in page_batch:
|
|
105
|
+
if page._backend is None or not page._backend.is_valid(): # noqa: SLF001
|
|
106
|
+
yield page
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
with TimeRecorder(conv_res, "ocr"):
|
|
110
|
+
ocr_rects = self.get_ocr_rects(page)
|
|
111
|
+
all_ocr_cells: list[TextCell] = []
|
|
112
|
+
|
|
113
|
+
for cell_idx, ocr_rect in enumerate(ocr_rects):
|
|
114
|
+
if ocr_rect.area() == 0:
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
high_res_image = page._backend.get_page_image( # noqa: SLF001
|
|
118
|
+
scale=self.scale,
|
|
119
|
+
cropbox=ocr_rect,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
text = self._recognise_crop(high_res_image)
|
|
124
|
+
except httpx.HTTPError:
|
|
125
|
+
logger.exception("GLM-OCR remote call failed for crop %d", cell_idx)
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
if not text.strip():
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
all_ocr_cells.append(
|
|
132
|
+
TextCell(
|
|
133
|
+
index=cell_idx,
|
|
134
|
+
text=text,
|
|
135
|
+
orig=text,
|
|
136
|
+
from_ocr=True,
|
|
137
|
+
confidence=1.0,
|
|
138
|
+
rect=BoundingRectangle.from_bounding_box(
|
|
139
|
+
BoundingBox.from_tuple(
|
|
140
|
+
coord=(
|
|
141
|
+
ocr_rect.l,
|
|
142
|
+
ocr_rect.t,
|
|
143
|
+
ocr_rect.r,
|
|
144
|
+
ocr_rect.b,
|
|
145
|
+
),
|
|
146
|
+
origin=CoordOrigin.TOPLEFT,
|
|
147
|
+
)
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self.post_process_cells(all_ocr_cells, page)
|
|
153
|
+
|
|
154
|
+
yield page
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def get_options_type(cls) -> type[OcrOptions]:
|
|
158
|
+
"""Return the options class for this OCR engine."""
|
|
159
|
+
return GlmOcrRemoteOptions
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Configuration model for the GLM-OCR remote OCR engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import ClassVar, Literal
|
|
7
|
+
|
|
8
|
+
from docling.datamodel.pipeline_options import OcrOptions
|
|
9
|
+
from pydantic import ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GlmOcrRemoteOptions(OcrOptions):
|
|
13
|
+
"""Options for the remote GLM-OCR OCR engine.
|
|
14
|
+
|
|
15
|
+
The engine sends each page crop to a vLLM-hosted GLM-OCR model via its
|
|
16
|
+
OpenAI-compatible chat completion endpoint and returns the recognised text.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
api_url: OpenAI-compatible chat completion URL of the vLLM server.
|
|
20
|
+
Falls back to the ``GLMOCR_REMOTE_OCR_API_URL`` env var, then to
|
|
21
|
+
a localhost default.
|
|
22
|
+
model_name: ``model`` parameter sent in the chat completion request.
|
|
23
|
+
prompt: Text prompt sent alongside each image crop.
|
|
24
|
+
Falls back to the ``GLMOCR_REMOTE_OCR_PROMPT`` env var.
|
|
25
|
+
timeout: HTTP request timeout in seconds per crop.
|
|
26
|
+
max_tokens: Maximum tokens for the chat completion response.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
kind: ClassVar[Literal["glm-ocr-remote"]] = "glm-ocr-remote"
|
|
30
|
+
|
|
31
|
+
lang: list[str] = Field(default_factory=lambda: ["en"])
|
|
32
|
+
api_url: str = os.environ.get(
|
|
33
|
+
"GLMOCR_REMOTE_OCR_API_URL",
|
|
34
|
+
"http://localhost:8001/v1/chat/completions",
|
|
35
|
+
)
|
|
36
|
+
model_name: str = "zai-org/GLM-OCR"
|
|
37
|
+
prompt: str = os.environ.get(
|
|
38
|
+
"GLMOCR_REMOTE_OCR_PROMPT",
|
|
39
|
+
"""Recognize the text in the image and output in Markdown format.
|
|
40
|
+
Preserve the original layout (headings/paragraphs/tables/formulas).
|
|
41
|
+
Do not fabricate content that does not exist in the image.""",
|
|
42
|
+
)
|
|
43
|
+
timeout: float = 120
|
|
44
|
+
max_tokens: int = 16384
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Docling plugin entry point registering the GLM-OCR remote OCR engine."""
|
|
2
|
+
|
|
3
|
+
from docling_glm_ocr.model import GlmOcrRemoteModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ocr_engines() -> dict[str, list[type]]:
|
|
7
|
+
"""Return the OCR engine classes provided by this plugin."""
|
|
8
|
+
return {"ocr_engines": [GlmOcrRemoteModel]}
|
docling_glm_ocr/py.typed
ADDED
|
File without changes
|
|
@@ -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
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
docling_glm_ocr/__init__.py,sha256=vu6BnFv3j7UPZX4sPxxNHyYZhh_eZggSzONFNvRAcLM,228
|
|
2
|
+
docling_glm_ocr/model.py,sha256=XlnqbPMOljLKxn9UR6qe5oaYqaQW-r1MUsALcbuyhqg,5470
|
|
3
|
+
docling_glm_ocr/options.py,sha256=wDIQWfUeck3n6t8uILp-e0UMh5IjzFYDTHhxjeHkG-g,1724
|
|
4
|
+
docling_glm_ocr/plugin.py,sha256=QsILIweZ1mr-n3SjdNbKi98sjJFcDbCSWfNAhhW77Ww,288
|
|
5
|
+
docling_glm_ocr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
docling_glm_ocr-0.1.0.dist-info/METADATA,sha256=4rdsiNTZMY8gq68Xi35GR-tann-nPN01b9-Wi7KqR9c,8025
|
|
7
|
+
docling_glm_ocr-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
docling_glm_ocr-0.1.0.dist-info/entry_points.txt,sha256=jLx9L5LwAeV8eolH8xufrZuKbnnnmUIWUxMMdxeiVFM,51
|
|
9
|
+
docling_glm_ocr-0.1.0.dist-info/licenses/LICENSE,sha256=APpdFXym3_5C78pFAfnlLvmP7umPBWYpQ2MKidnoRuw,1091
|
|
10
|
+
docling_glm_ocr-0.1.0.dist-info/RECORD,,
|
|
@@ -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.
|