Spaces:
Configuration error
Configuration error
Michele Dolfi
commited on
feat: Add new docling-serve cli (#50)
Browse filesSigned-off-by: Michele Dolfi <[email protected]>
- .github/workflows/ci-images-dryrun.yml +2 -2
- .github/workflows/images.yml +2 -2
- .github/workflows/job-image.yml +0 -4
- Containerfile +5 -11
- Makefile +3 -3
- README.md +68 -10
- docling_serve/.env.example +1 -1
- docling_serve/__main__.py +278 -17
- docling_serve/app.py +141 -139
- docling_serve/settings.py +24 -2
- pyproject.toml +6 -0
- start_server.sh +0 -30
- uv.lock +3 -1
.github/workflows/ci-images-dryrun.yml
CHANGED
@@ -20,7 +20,7 @@ jobs:
|
|
20 |
with:
|
21 |
publish: false
|
22 |
build_args: |
|
23 |
-
|
24 |
ghcr_image_name: ds4sd/docling-serve-cpu
|
25 |
quay_image_name: ""
|
26 |
|
@@ -37,7 +37,7 @@ jobs:
|
|
37 |
with:
|
38 |
publish: false
|
39 |
build_args: |
|
40 |
-
|
41 |
platforms: linux/amd64
|
42 |
ghcr_image_name: ds4sd/docling-serve
|
43 |
quay_image_name: ""
|
|
|
20 |
with:
|
21 |
publish: false
|
22 |
build_args: |
|
23 |
+
UV_SYNC_EXTRA_ARGS=--no-extra cu124
|
24 |
ghcr_image_name: ds4sd/docling-serve-cpu
|
25 |
quay_image_name: ""
|
26 |
|
|
|
37 |
with:
|
38 |
publish: false
|
39 |
build_args: |
|
40 |
+
UV_SYNC_EXTRA_ARGS=--no-extra cpu
|
41 |
platforms: linux/amd64
|
42 |
ghcr_image_name: ds4sd/docling-serve
|
43 |
quay_image_name: ""
|
.github/workflows/images.yml
CHANGED
@@ -34,7 +34,7 @@ jobs:
|
|
34 |
publish: true
|
35 |
environment: registry-creds
|
36 |
build_args: |
|
37 |
-
|
38 |
ghcr_image_name: ds4sd/docling-serve-cpu
|
39 |
quay_image_name: ds4sd/docling-serve-cpu
|
40 |
|
@@ -53,7 +53,7 @@ jobs:
|
|
53 |
publish: true
|
54 |
environment: registry-creds
|
55 |
build_args: |
|
56 |
-
|
57 |
platforms: linux/amd64
|
58 |
ghcr_image_name: ds4sd/docling-serve
|
59 |
quay_image_name: ds4sd/docling-serve
|
|
|
34 |
publish: true
|
35 |
environment: registry-creds
|
36 |
build_args: |
|
37 |
+
UV_SYNC_EXTRA_ARGS=--no-extra cu124
|
38 |
ghcr_image_name: ds4sd/docling-serve-cpu
|
39 |
quay_image_name: ds4sd/docling-serve-cpu
|
40 |
|
|
|
53 |
publish: true
|
54 |
environment: registry-creds
|
55 |
build_args: |
|
56 |
+
UV_SYNC_EXTRA_ARGS=--no-extra cpu
|
57 |
platforms: linux/amd64
|
58 |
ghcr_image_name: ds4sd/docling-serve
|
59 |
quay_image_name: ds4sd/docling-serve
|
.github/workflows/job-image.yml
CHANGED
@@ -105,8 +105,6 @@ jobs:
|
|
105 |
cache-to: type=gha,mode=max
|
106 |
file: Containerfile
|
107 |
build-args: ${{ inputs.build_args }}
|
108 |
-
# |
|
109 |
-
# --build-arg CPU_ONLY=true
|
110 |
|
111 |
- name: Generate artifact attestation
|
112 |
if: ${{ inputs.publish }}
|
@@ -137,8 +135,6 @@ jobs:
|
|
137 |
cache-to: type=gha,mode=max
|
138 |
file: Containerfile
|
139 |
build-args: ${{ inputs.build_args }}
|
140 |
-
# |
|
141 |
-
# --build-arg CPU_ONLY=true
|
142 |
|
143 |
- name: Remove Local Docker Images
|
144 |
run: |
|
|
|
105 |
cache-to: type=gha,mode=max
|
106 |
file: Containerfile
|
107 |
build-args: ${{ inputs.build_args }}
|
|
|
|
|
108 |
|
109 |
- name: Generate artifact attestation
|
110 |
if: ${{ inputs.publish }}
|
|
|
135 |
cache-to: type=gha,mode=max
|
136 |
file: Containerfile
|
137 |
build-args: ${{ inputs.build_args }}
|
|
|
|
|
138 |
|
139 |
- name: Remove Local Docker Images
|
140 |
run: |
|
Containerfile
CHANGED
@@ -2,8 +2,8 @@ ARG BASE_IMAGE=quay.io/sclorg/python-312-c9s:c9s
|
|
2 |
|
3 |
FROM ${BASE_IMAGE}
|
4 |
|
5 |
-
ARG CPU_ONLY=false
|
6 |
ARG MODELS_LIST="layout tableformer picture_classifier easyocr"
|
|
|
7 |
|
8 |
USER 0
|
9 |
|
@@ -41,17 +41,10 @@ ENV PYTHONIOENCODING=utf-8
|
|
41 |
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
42 |
ENV UV_PROJECT_ENVIRONMENT=/opt/app-root
|
43 |
|
44 |
-
ENV WITH_UI=True
|
45 |
-
|
46 |
COPY --chown=1001:0 pyproject.toml uv.lock README.md ./
|
47 |
|
48 |
RUN --mount=type=cache,target=/opt/app-root/src/.cache/uv,uid=1001 \
|
49 |
-
|
50 |
-
NO_EXTRA=cu124; \
|
51 |
-
else \
|
52 |
-
NO_EXTRA=cpu; \
|
53 |
-
fi && \
|
54 |
-
uv sync --frozen --no-install-project --no-dev --all-extras --no-extra ${NO_EXTRA}
|
55 |
|
56 |
RUN echo "Downloading models..." && \
|
57 |
docling-tools models download ${MODELS_LIST} && \
|
@@ -59,8 +52,9 @@ RUN echo "Downloading models..." && \
|
|
59 |
chmod -R g=u /opt/app-root/src/.cache
|
60 |
|
61 |
COPY --chown=1001:0 --chmod=664 ./docling_serve ./docling_serve
|
62 |
-
|
|
|
63 |
|
64 |
EXPOSE 5001
|
65 |
|
66 |
-
CMD ["
|
|
|
2 |
|
3 |
FROM ${BASE_IMAGE}
|
4 |
|
|
|
5 |
ARG MODELS_LIST="layout tableformer picture_classifier easyocr"
|
6 |
+
ARG UV_SYNC_EXTRA_ARGS=""
|
7 |
|
8 |
USER 0
|
9 |
|
|
|
41 |
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
42 |
ENV UV_PROJECT_ENVIRONMENT=/opt/app-root
|
43 |
|
|
|
|
|
44 |
COPY --chown=1001:0 pyproject.toml uv.lock README.md ./
|
45 |
|
46 |
RUN --mount=type=cache,target=/opt/app-root/src/.cache/uv,uid=1001 \
|
47 |
+
uv sync --frozen --no-install-project --no-dev --all-extras ${UV_SYNC_EXTRA_ARGS} # --no-extra ${NO_EXTRA}
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
RUN echo "Downloading models..." && \
|
50 |
docling-tools models download ${MODELS_LIST} && \
|
|
|
52 |
chmod -R g=u /opt/app-root/src/.cache
|
53 |
|
54 |
COPY --chown=1001:0 --chmod=664 ./docling_serve ./docling_serve
|
55 |
+
RUN --mount=type=cache,target=/opt/app-root/src/.cache/uv,uid=1001 \
|
56 |
+
uv sync --frozen --no-dev --all-extras ${UV_SYNC_EXTRA_ARGS} # --no-extra ${NO_EXTRA}
|
57 |
|
58 |
EXPOSE 5001
|
59 |
|
60 |
+
CMD ["docling-serve", "run"]
|
Makefile
CHANGED
@@ -26,15 +26,15 @@ md-lint-file:
|
|
26 |
|
27 |
.PHONY: docling-serve-cpu-image
|
28 |
docling-serve-cpu-image: Containerfile ## Build docling-serve "cpu only" container image
|
29 |
-
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve CPU
|
30 |
-
$(CMD_PREFIX) docker build --build-arg
|
31 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) ghcr.io/ds4sd/docling-serve-cpu:main
|
32 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) quay.io/ds4sd/docling-serve-cpu:main
|
33 |
|
34 |
.PHONY: docling-serve-gpu-image
|
35 |
docling-serve-gpu-image: Containerfile ## Build docling-serve container image with GPU support
|
36 |
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve with GPU]"
|
37 |
-
$(CMD_PREFIX) docker build --build-arg
|
38 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) ghcr.io/ds4sd/docling-serve:main
|
39 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) quay.io/ds4sd/docling-serve:main
|
40 |
|
|
|
26 |
|
27 |
.PHONY: docling-serve-cpu-image
|
28 |
docling-serve-cpu-image: Containerfile ## Build docling-serve "cpu only" container image
|
29 |
+
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve CPU]"
|
30 |
+
$(CMD_PREFIX) docker build --load --build-arg "UV_SYNC_EXTRA_ARGS=--no-extra cu124" -f Containerfile -t ghcr.io/ds4sd/docling-serve-cpu:$(TAG) .
|
31 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) ghcr.io/ds4sd/docling-serve-cpu:main
|
32 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) quay.io/ds4sd/docling-serve-cpu:main
|
33 |
|
34 |
.PHONY: docling-serve-gpu-image
|
35 |
docling-serve-gpu-image: Containerfile ## Build docling-serve container image with GPU support
|
36 |
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve with GPU]"
|
37 |
+
$(CMD_PREFIX) docker build --load --build-arg "UV_SYNC_EXTRA_ARGS=--no-extra cpu" -f Containerfile --platform linux/amd64 -t ghcr.io/ds4sd/docling-serve:$(TAG) .
|
38 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) ghcr.io/ds4sd/docling-serve:main
|
39 |
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) quay.io/ds4sd/docling-serve:main
|
40 |
|
README.md
CHANGED
@@ -327,25 +327,83 @@ See `[project.optional-dependencies]` section in `pyproject.toml` for full list
|
|
327 |
|
328 |
### Run the server
|
329 |
|
330 |
-
The
|
|
|
331 |
|
332 |
```sh
|
333 |
-
# Run the server
|
334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
335 |
|
336 |
-
|
337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
```
|
339 |
|
340 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
341 |
|
342 |
-
The
|
|
|
|
|
|
|
|
|
|
|
343 |
|
344 |
- `DOCLING_ARTIFACTS_PATH`: if set Docling will use only the local weights of models, for example `/opt/app-root/.cache/docling/cache`.
|
345 |
- `TESSDATA_PREFIX`: Tesseract data location, example `/usr/share/tesseract/tessdata/`.
|
346 |
-
- `UVICORN_WORKERS`: Number of workers to use.
|
347 |
-
- `RELOAD`: If `True`, this will enable auto-reload when you modify files, useful for development.
|
348 |
-
- `WITH_UI`: If `True`, The Gradio UI will be available at `/ui`.
|
349 |
|
350 |
## Get help and support
|
351 |
|
|
|
327 |
|
328 |
### Run the server
|
329 |
|
330 |
+
The `docling-serve` executable is a convenient script for launching the webserver both in
|
331 |
+
development and production mode.
|
332 |
|
333 |
```sh
|
334 |
+
# Run the server in development mode
|
335 |
+
# - reload is enabled by default
|
336 |
+
# - listening on the 127.0.0.1 address
|
337 |
+
# - ui is enabled by default
|
338 |
+
docling-serve dev
|
339 |
+
|
340 |
+
# Run the server in production mode
|
341 |
+
# - reload is disabled by default
|
342 |
+
# - listening on the 0.0.0.0 address
|
343 |
+
# - ui is disabled by default
|
344 |
+
docling-serve run
|
345 |
+
```
|
346 |
+
|
347 |
+
### Options
|
348 |
|
349 |
+
The `docling-serve` executable allows is controlled with both command line
|
350 |
+
options and environment variables.
|
351 |
+
|
352 |
+
<details>
|
353 |
+
<summary>`docling-serve` help message</summary>
|
354 |
+
|
355 |
+
```sh
|
356 |
+
$ docling-serve dev --help
|
357 |
+
|
358 |
+
Usage: docling-serve dev [OPTIONS]
|
359 |
+
|
360 |
+
Run a Docling Serve app in development mode. 🧪
|
361 |
+
This is equivalent to docling-serve run but with reload
|
362 |
+
enabled and listening on the 127.0.0.1 address.
|
363 |
+
|
364 |
+
Options can be set also with the corresponding ENV variable, with the exception
|
365 |
+
of --enable-ui, --host and --reload.
|
366 |
+
|
367 |
+
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
368 |
+
│ --host TEXT The host to serve on. For local development in localhost │
|
369 |
+
│ use 127.0.0.1. To enable public access, e.g. in a │
|
370 |
+
│ container, use all the IP addresses available with │
|
371 |
+
│ 0.0.0.0. │
|
372 |
+
│ [default: 127.0.0.1] │
|
373 |
+
│ --port INTEGER The port to serve on. [default: 5001] │
|
374 |
+
│ --reload --no-reload Enable auto-reload of the server when (code) files │
|
375 |
+
│ change. This is resource intensive, use it only during │
|
376 |
+
│ development. │
|
377 |
+
│ [default: reload] │
|
378 |
+
│ --root-path TEXT The root path is used to tell your app that it is being │
|
379 |
+
│ served to the outside world with some path prefix set up │
|
380 |
+
│ in some termination proxy or similar. │
|
381 |
+
│ --proxy-headers --no-proxy-headers Enable/Disable X-Forwarded-Proto, X-Forwarded-For, │
|
382 |
+
│ X-Forwarded-Port to populate remote address info. │
|
383 |
+
│ [default: proxy-headers] │
|
384 |
+
│ --enable-ui --no-enable-ui Enable the development UI. [default: enable-ui] │
|
385 |
+
│ --help Show this message and exit. │
|
386 |
+
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
387 |
```
|
388 |
|
389 |
+
</details>
|
390 |
+
|
391 |
+
#### Environment variables
|
392 |
+
|
393 |
+
The environment variables controlling the `uvicorn` execution can be specified with the `UVICORN_` prefix:
|
394 |
+
|
395 |
+
- `UVICORN_WORKERS`: Number of workers to use.
|
396 |
+
- `UVICORN_RELOAD`: If `True`, this will enable auto-reload when you modify files, useful for development.
|
397 |
|
398 |
+
The environment variables controlling specifics of the Docling Serve app can be specified with the
|
399 |
+
`DOCLING_SERVE_` prefix:
|
400 |
+
|
401 |
+
- `DOCLING_SERVE_ENABLE_UI`: If `True`, The Gradio UI will be available at `/ui`.
|
402 |
+
|
403 |
+
Others:
|
404 |
|
405 |
- `DOCLING_ARTIFACTS_PATH`: if set Docling will use only the local weights of models, for example `/opt/app-root/.cache/docling/cache`.
|
406 |
- `TESSDATA_PREFIX`: Tesseract data location, example `/usr/share/tesseract/tessdata/`.
|
|
|
|
|
|
|
407 |
|
408 |
## Get help and support
|
409 |
|
docling_serve/.env.example
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
TESSDATA_PREFIX=/usr/share/tesseract/tessdata/
|
2 |
UVICORN_WORKERS=2
|
3 |
-
|
|
|
1 |
TESSDATA_PREFIX=/usr/share/tesseract/tessdata/
|
2 |
UVICORN_WORKERS=2
|
3 |
+
UVICORN_RELOAD=True
|
docling_serve/__main__.py
CHANGED
@@ -1,20 +1,281 @@
|
|
1 |
-
import
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
|
|
|
|
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import importlib
|
2 |
+
import logging
|
3 |
+
import platform
|
4 |
+
import sys
|
5 |
+
import warnings
|
6 |
+
from typing import Annotated, Any, Union
|
7 |
|
8 |
+
import typer
|
9 |
+
import uvicorn
|
10 |
+
from rich.console import Console
|
11 |
|
12 |
+
from docling_serve.settings import docling_serve_settings, uvicorn_settings
|
13 |
+
|
14 |
+
warnings.filterwarnings(action="ignore", category=UserWarning, module="pydantic|torch")
|
15 |
+
warnings.filterwarnings(action="ignore", category=FutureWarning, module="easyocr")
|
16 |
+
|
17 |
+
|
18 |
+
err_console = Console(stderr=True)
|
19 |
+
console = Console()
|
20 |
+
|
21 |
+
app = typer.Typer(
|
22 |
+
no_args_is_help=True,
|
23 |
+
rich_markup_mode="rich",
|
24 |
+
)
|
25 |
+
|
26 |
+
logger = logging.getLogger(__name__)
|
27 |
+
|
28 |
+
|
29 |
+
def version_callback(value: bool) -> None:
|
30 |
+
if value:
|
31 |
+
docling_serve_version = importlib.metadata.version("docling_serve")
|
32 |
+
docling_version = importlib.metadata.version("docling")
|
33 |
+
docling_core_version = importlib.metadata.version("docling-core")
|
34 |
+
docling_ibm_models_version = importlib.metadata.version("docling-ibm-models")
|
35 |
+
docling_parse_version = importlib.metadata.version("docling-parse")
|
36 |
+
platform_str = platform.platform()
|
37 |
+
py_impl_version = sys.implementation.cache_tag
|
38 |
+
py_lang_version = platform.python_version()
|
39 |
+
console.print(f"Docling Serve version: {docling_serve_version}")
|
40 |
+
console.print(f"Docling version: {docling_version}")
|
41 |
+
console.print(f"Docling Core version: {docling_core_version}")
|
42 |
+
console.print(f"Docling IBM Models version: {docling_ibm_models_version}")
|
43 |
+
console.print(f"Docling Parse version: {docling_parse_version}")
|
44 |
+
console.print(f"Python: {py_impl_version} ({py_lang_version})")
|
45 |
+
console.print(f"Platform: {platform_str}")
|
46 |
+
raise typer.Exit()
|
47 |
+
|
48 |
+
|
49 |
+
@app.callback()
|
50 |
+
def callback(
|
51 |
+
version: Annotated[
|
52 |
+
Union[bool, None],
|
53 |
+
typer.Option(
|
54 |
+
"--version", help="Show the version and exit.", callback=version_callback
|
55 |
+
),
|
56 |
+
] = None,
|
57 |
+
verbose: Annotated[
|
58 |
+
int,
|
59 |
+
typer.Option(
|
60 |
+
"--verbose",
|
61 |
+
"-v",
|
62 |
+
count=True,
|
63 |
+
help="Set the verbosity level. -v for info logging, -vv for debug logging.",
|
64 |
+
),
|
65 |
+
] = 0,
|
66 |
+
) -> None:
|
67 |
+
if verbose == 0:
|
68 |
+
logging.basicConfig(level=logging.WARNING)
|
69 |
+
elif verbose == 1:
|
70 |
+
logging.basicConfig(level=logging.INFO)
|
71 |
+
elif verbose == 2:
|
72 |
+
logging.basicConfig(level=logging.DEBUG)
|
73 |
+
|
74 |
+
|
75 |
+
def _run(
|
76 |
+
*,
|
77 |
+
command: str,
|
78 |
+
) -> None:
|
79 |
+
server_type = "development" if command == "dev" else "production"
|
80 |
+
|
81 |
+
console.print(f"Starting {server_type} server 🚀")
|
82 |
+
|
83 |
+
url = f"http://{uvicorn_settings.host}:{uvicorn_settings.port}"
|
84 |
+
url_docs = f"{url}/docs"
|
85 |
+
url_ui = f"{url}/ui"
|
86 |
+
|
87 |
+
console.print("")
|
88 |
+
console.print(f"Server started at [link={url}]{url}[/]")
|
89 |
+
console.print(f"Documentation at [link={url_docs}]{url_docs}[/]")
|
90 |
+
if docling_serve_settings.enable_ui:
|
91 |
+
console.print(f"UI at [link={url_ui}]{url_ui}[/]")
|
92 |
+
|
93 |
+
if command == "dev":
|
94 |
+
console.print("")
|
95 |
+
console.print(
|
96 |
+
"Running in development mode, for production use: "
|
97 |
+
"[bold]docling-serve run[/]",
|
98 |
+
)
|
99 |
+
|
100 |
+
console.print("")
|
101 |
+
console.print("Logs:")
|
102 |
+
|
103 |
+
uvicorn.run(
|
104 |
+
app="docling_serve.app:create_app",
|
105 |
+
factory=True,
|
106 |
+
host=uvicorn_settings.host,
|
107 |
+
port=uvicorn_settings.port,
|
108 |
+
reload=uvicorn_settings.reload,
|
109 |
+
workers=uvicorn_settings.workers,
|
110 |
+
root_path=uvicorn_settings.root_path,
|
111 |
+
proxy_headers=uvicorn_settings.proxy_headers,
|
112 |
+
)
|
113 |
+
|
114 |
+
|
115 |
+
@app.command()
|
116 |
+
def dev(
|
117 |
+
*,
|
118 |
+
# uvicorn options
|
119 |
+
host: Annotated[
|
120 |
+
str,
|
121 |
+
typer.Option(
|
122 |
+
help=(
|
123 |
+
"The host to serve on. For local development in localhost "
|
124 |
+
"use [blue]127.0.0.1[/blue]. To enable public access, "
|
125 |
+
"e.g. in a container, use all the IP addresses "
|
126 |
+
"available with [blue]0.0.0.0[/blue]."
|
127 |
+
)
|
128 |
+
),
|
129 |
+
] = "127.0.0.1",
|
130 |
+
port: Annotated[
|
131 |
+
int,
|
132 |
+
typer.Option(help="The port to serve on."),
|
133 |
+
] = uvicorn_settings.port,
|
134 |
+
reload: Annotated[
|
135 |
+
bool,
|
136 |
+
typer.Option(
|
137 |
+
help=(
|
138 |
+
"Enable auto-reload of the server when (code) files change. "
|
139 |
+
"This is [bold]resource intensive[/bold], "
|
140 |
+
"use it only during development."
|
141 |
+
)
|
142 |
+
),
|
143 |
+
] = True,
|
144 |
+
root_path: Annotated[
|
145 |
+
str,
|
146 |
+
typer.Option(
|
147 |
+
help=(
|
148 |
+
"The root path is used to tell your app that it is being served "
|
149 |
+
"to the outside world with some [bold]path prefix[/bold] "
|
150 |
+
"set up in some termination proxy or similar."
|
151 |
+
)
|
152 |
+
),
|
153 |
+
] = uvicorn_settings.root_path,
|
154 |
+
proxy_headers: Annotated[
|
155 |
+
bool,
|
156 |
+
typer.Option(
|
157 |
+
help=(
|
158 |
+
"Enable/Disable X-Forwarded-Proto, X-Forwarded-For, "
|
159 |
+
"X-Forwarded-Port to populate remote address info."
|
160 |
+
)
|
161 |
+
),
|
162 |
+
] = uvicorn_settings.proxy_headers,
|
163 |
+
# docling options
|
164 |
+
enable_ui: Annotated[bool, typer.Option(help="Enable the development UI.")] = True,
|
165 |
+
) -> Any:
|
166 |
+
"""
|
167 |
+
Run a [bold]Docling Serve[/bold] app in [yellow]development[/yellow] mode. 🧪
|
168 |
+
|
169 |
+
This is equivalent to [bold]docling-serve run[/bold] but with [bold]reload[/bold]
|
170 |
+
enabled and listening on the [blue]127.0.0.1[/blue] address.
|
171 |
+
|
172 |
+
Options can be set also with the corresponding ENV variable, with the exception
|
173 |
+
of --enable-ui, --host and --reload.
|
174 |
+
"""
|
175 |
+
|
176 |
+
uvicorn_settings.host = host
|
177 |
+
uvicorn_settings.port = port
|
178 |
+
uvicorn_settings.reload = reload
|
179 |
+
uvicorn_settings.root_path = root_path
|
180 |
+
uvicorn_settings.proxy_headers = proxy_headers
|
181 |
+
|
182 |
+
docling_serve_settings.enable_ui = enable_ui
|
183 |
+
|
184 |
+
_run(
|
185 |
+
command="dev",
|
186 |
+
)
|
187 |
+
|
188 |
+
|
189 |
+
@app.command()
|
190 |
+
def run(
|
191 |
+
*,
|
192 |
+
host: Annotated[
|
193 |
+
str,
|
194 |
+
typer.Option(
|
195 |
+
help=(
|
196 |
+
"The host to serve on. For local development in localhost "
|
197 |
+
"use [blue]127.0.0.1[/blue]. To enable public access, "
|
198 |
+
"e.g. in a container, use all the IP addresses "
|
199 |
+
"available with [blue]0.0.0.0[/blue]."
|
200 |
+
)
|
201 |
+
),
|
202 |
+
] = uvicorn_settings.host,
|
203 |
+
port: Annotated[
|
204 |
+
int,
|
205 |
+
typer.Option(help="The port to serve on."),
|
206 |
+
] = uvicorn_settings.port,
|
207 |
+
reload: Annotated[
|
208 |
+
bool,
|
209 |
+
typer.Option(
|
210 |
+
help=(
|
211 |
+
"Enable auto-reload of the server when (code) files change. "
|
212 |
+
"This is [bold]resource intensive[/bold], "
|
213 |
+
"use it only during development."
|
214 |
+
)
|
215 |
+
),
|
216 |
+
] = uvicorn_settings.reload,
|
217 |
+
workers: Annotated[
|
218 |
+
Union[int, None],
|
219 |
+
typer.Option(
|
220 |
+
help=(
|
221 |
+
"Use multiple worker processes. "
|
222 |
+
"Mutually exclusive with the --reload flag."
|
223 |
+
)
|
224 |
+
),
|
225 |
+
] = uvicorn_settings.workers,
|
226 |
+
root_path: Annotated[
|
227 |
+
str,
|
228 |
+
typer.Option(
|
229 |
+
help=(
|
230 |
+
"The root path is used to tell your app that it is being served "
|
231 |
+
"to the outside world with some [bold]path prefix[/bold] "
|
232 |
+
"set up in some termination proxy or similar."
|
233 |
+
)
|
234 |
+
),
|
235 |
+
] = uvicorn_settings.root_path,
|
236 |
+
proxy_headers: Annotated[
|
237 |
+
bool,
|
238 |
+
typer.Option(
|
239 |
+
help=(
|
240 |
+
"Enable/Disable X-Forwarded-Proto, X-Forwarded-For, "
|
241 |
+
"X-Forwarded-Port to populate remote address info."
|
242 |
+
)
|
243 |
+
),
|
244 |
+
] = uvicorn_settings.proxy_headers,
|
245 |
+
# docling options
|
246 |
+
enable_ui: Annotated[
|
247 |
+
bool, typer.Option(help="Enable the development UI.")
|
248 |
+
] = docling_serve_settings.enable_ui,
|
249 |
+
) -> Any:
|
250 |
+
"""
|
251 |
+
Run a [bold]Docling Serve[/bold] app in [green]production[/green] mode. 🚀
|
252 |
+
|
253 |
+
This is equivalent to [bold]docling-serve dev[/bold] but with [bold]reload[/bold]
|
254 |
+
disabled and listening on the [blue]0.0.0.0[/blue] address.
|
255 |
+
|
256 |
+
Options can be set also with the corresponding ENV variable, e.g. UVICORN_PORT
|
257 |
+
or DOCLING_SERVE_ENABLE_UI.
|
258 |
+
"""
|
259 |
+
|
260 |
+
uvicorn_settings.host = host
|
261 |
+
uvicorn_settings.port = port
|
262 |
+
uvicorn_settings.reload = reload
|
263 |
+
uvicorn_settings.workers = workers
|
264 |
+
uvicorn_settings.root_path = root_path
|
265 |
+
uvicorn_settings.proxy_headers = proxy_headers
|
266 |
+
|
267 |
+
docling_serve_settings.enable_ui = enable_ui
|
268 |
+
|
269 |
+
_run(
|
270 |
+
command="run",
|
271 |
)
|
272 |
+
|
273 |
+
|
274 |
+
def main() -> None:
|
275 |
+
app()
|
276 |
+
|
277 |
+
|
278 |
+
# Launch the CLI when calling python -m docling_serve
|
279 |
+
if __name__ == "__main__":
|
280 |
+
|
281 |
+
main()
|
docling_serve/app.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import logging
|
2 |
-
import os
|
3 |
import tempfile
|
4 |
from contextlib import asynccontextmanager
|
5 |
from io import BytesIO
|
@@ -8,7 +7,6 @@ from typing import Annotated, Any, Dict, List, Optional, Union
|
|
8 |
|
9 |
from docling.datamodel.base_models import DocumentStream, InputFormat
|
10 |
from docling.document_converter import DocumentConverter
|
11 |
-
from dotenv import load_dotenv
|
12 |
from fastapi import BackgroundTasks, FastAPI, UploadFile
|
13 |
from fastapi.middleware.cors import CORSMiddleware
|
14 |
from fastapi.responses import RedirectResponse
|
@@ -22,17 +20,9 @@ from docling_serve.docling_conversion import (
|
|
22 |
converters,
|
23 |
get_pdf_pipeline_opts,
|
24 |
)
|
25 |
-
from docling_serve.helper_functions import FormDepends
|
26 |
from docling_serve.response_preparation import ConvertDocumentResponse, process_results
|
27 |
-
|
28 |
-
# Load local env vars if present
|
29 |
-
load_dotenv()
|
30 |
-
|
31 |
-
WITH_UI = _str_to_bool(os.getenv("WITH_UI", "False"))
|
32 |
-
if WITH_UI:
|
33 |
-
import gradio as gr
|
34 |
-
|
35 |
-
from docling_serve.gradio_ui import ui as gradio_ui
|
36 |
|
37 |
|
38 |
# Set up custom logging as we'll be intermixes with FastAPI/Uvicorn's logging
|
@@ -70,7 +60,6 @@ _log = logging.getLogger(__name__)
|
|
70 |
# Context manager to initialize and clean up the lifespan of the FastAPI app
|
71 |
@asynccontextmanager
|
72 |
async def lifespan(app: FastAPI):
|
73 |
-
# settings = Settings()
|
74 |
|
75 |
# Converter with default options
|
76 |
pdf_format_option, options_hash = get_pdf_pipeline_opts(ConvertDocumentsOptions())
|
@@ -86,143 +75,156 @@ async def lifespan(app: FastAPI):
|
|
86 |
yield
|
87 |
|
88 |
converters.clear()
|
89 |
-
if WITH_UI:
|
90 |
-
|
91 |
|
92 |
|
93 |
##################################
|
94 |
# App creation and configuration #
|
95 |
##################################
|
96 |
|
97 |
-
app = FastAPI(
|
98 |
-
title="Docling Serve",
|
99 |
-
lifespan=lifespan,
|
100 |
-
)
|
101 |
-
|
102 |
-
origins = ["*"]
|
103 |
-
methods = ["*"]
|
104 |
-
headers = ["*"]
|
105 |
-
|
106 |
-
app.add_middleware(
|
107 |
-
CORSMiddleware,
|
108 |
-
allow_origins=origins,
|
109 |
-
allow_credentials=True,
|
110 |
-
allow_methods=methods,
|
111 |
-
allow_headers=headers,
|
112 |
-
)
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
app = gr.mount_gradio_app(
|
119 |
-
app,
|
120 |
-
gradio_ui,
|
121 |
-
path="/ui",
|
122 |
-
allowed_paths=["./logo.png", tmp_output_dir],
|
123 |
-
root_path="/ui",
|
124 |
)
|
125 |
|
|
|
|
|
|
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
@app.get("/favicon.ico", include_in_schema=False)
|
134 |
-
async def favicon():
|
135 |
-
response = RedirectResponse(url="https://ds4sd.github.io/docling/assets/logo.png")
|
136 |
-
return response
|
137 |
-
|
138 |
-
|
139 |
-
# Status
|
140 |
-
class HealthCheckResponse(BaseModel):
|
141 |
-
status: str = "ok"
|
142 |
-
|
143 |
-
|
144 |
-
@app.get("/health")
|
145 |
-
def health() -> HealthCheckResponse:
|
146 |
-
return HealthCheckResponse()
|
147 |
-
|
148 |
-
|
149 |
-
# API readiness compatibility for OpenShift AI Workbench
|
150 |
-
@app.get("/api", include_in_schema=False)
|
151 |
-
def api_check() -> HealthCheckResponse:
|
152 |
-
return HealthCheckResponse()
|
153 |
-
|
154 |
-
|
155 |
-
# Convert a document from URL(s)
|
156 |
-
@app.post(
|
157 |
-
"/v1alpha/convert/source",
|
158 |
-
response_model=ConvertDocumentResponse,
|
159 |
-
responses={
|
160 |
-
200: {
|
161 |
-
"content": {"application/zip": {}},
|
162 |
-
# "description": "Return the JSON item or an image.",
|
163 |
-
}
|
164 |
-
},
|
165 |
-
)
|
166 |
-
def process_url(
|
167 |
-
background_tasks: BackgroundTasks, conversion_request: ConvertDocumentsRequest
|
168 |
-
):
|
169 |
-
sources: List[Union[str, DocumentStream]] = []
|
170 |
-
headers: Optional[Dict[str, Any]] = None
|
171 |
-
if isinstance(conversion_request, ConvertDocumentFileSourcesRequest):
|
172 |
-
for file_source in conversion_request.file_sources:
|
173 |
-
sources.append(file_source.to_document_stream())
|
174 |
-
else:
|
175 |
-
for http_source in conversion_request.http_sources:
|
176 |
-
sources.append(http_source.url)
|
177 |
-
if headers is None and http_source.headers:
|
178 |
-
headers = http_source.headers
|
179 |
-
|
180 |
-
# Note: results are only an iterator->lazy evaluation
|
181 |
-
results = convert_documents(
|
182 |
-
sources=sources, options=conversion_request.options, headers=headers
|
183 |
)
|
184 |
|
185 |
-
#
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
)
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
|
|
|
|
226 |
)
|
227 |
-
|
228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import logging
|
|
|
2 |
import tempfile
|
3 |
from contextlib import asynccontextmanager
|
4 |
from io import BytesIO
|
|
|
7 |
|
8 |
from docling.datamodel.base_models import DocumentStream, InputFormat
|
9 |
from docling.document_converter import DocumentConverter
|
|
|
10 |
from fastapi import BackgroundTasks, FastAPI, UploadFile
|
11 |
from fastapi.middleware.cors import CORSMiddleware
|
12 |
from fastapi.responses import RedirectResponse
|
|
|
20 |
converters,
|
21 |
get_pdf_pipeline_opts,
|
22 |
)
|
23 |
+
from docling_serve.helper_functions import FormDepends
|
24 |
from docling_serve.response_preparation import ConvertDocumentResponse, process_results
|
25 |
+
from docling_serve.settings import docling_serve_settings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
|
28 |
# Set up custom logging as we'll be intermixes with FastAPI/Uvicorn's logging
|
|
|
60 |
# Context manager to initialize and clean up the lifespan of the FastAPI app
|
61 |
@asynccontextmanager
|
62 |
async def lifespan(app: FastAPI):
|
|
|
63 |
|
64 |
# Converter with default options
|
65 |
pdf_format_option, options_hash = get_pdf_pipeline_opts(ConvertDocumentsOptions())
|
|
|
75 |
yield
|
76 |
|
77 |
converters.clear()
|
78 |
+
# if WITH_UI:
|
79 |
+
# gradio_ui.close()
|
80 |
|
81 |
|
82 |
##################################
|
83 |
# App creation and configuration #
|
84 |
##################################
|
85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
+
def create_app():
|
88 |
+
app = FastAPI(
|
89 |
+
title="Docling Serve",
|
90 |
+
lifespan=lifespan,
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
)
|
92 |
|
93 |
+
origins = ["*"]
|
94 |
+
methods = ["*"]
|
95 |
+
headers = ["*"]
|
96 |
|
97 |
+
app.add_middleware(
|
98 |
+
CORSMiddleware,
|
99 |
+
allow_origins=origins,
|
100 |
+
allow_credentials=True,
|
101 |
+
allow_methods=methods,
|
102 |
+
allow_headers=headers,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
)
|
104 |
|
105 |
+
# Mount the Gradio app
|
106 |
+
if docling_serve_settings.enable_ui:
|
107 |
+
|
108 |
+
try:
|
109 |
+
import gradio as gr
|
110 |
+
|
111 |
+
from docling_serve.gradio_ui import ui as gradio_ui
|
112 |
+
|
113 |
+
tmp_output_dir = Path(tempfile.mkdtemp())
|
114 |
+
gradio_ui.gradio_output_dir = tmp_output_dir
|
115 |
+
app = gr.mount_gradio_app(
|
116 |
+
app,
|
117 |
+
gradio_ui,
|
118 |
+
path="/ui",
|
119 |
+
allowed_paths=["./logo.png", tmp_output_dir],
|
120 |
+
root_path="/ui",
|
121 |
+
)
|
122 |
+
except ImportError:
|
123 |
+
_log.warning(
|
124 |
+
"Docling Serve enable_ui is activated, but gradio is not installed. "
|
125 |
+
"Install it with `pip install docling-serve[ui]` "
|
126 |
+
"or `pip install gradio`"
|
127 |
+
)
|
128 |
+
|
129 |
+
#############################
|
130 |
+
# API Endpoints definitions #
|
131 |
+
#############################
|
132 |
+
|
133 |
+
# Favicon
|
134 |
+
@app.get("/favicon.ico", include_in_schema=False)
|
135 |
+
async def favicon():
|
136 |
+
response = RedirectResponse(
|
137 |
+
url="https://ds4sd.github.io/docling/assets/logo.png"
|
138 |
+
)
|
139 |
+
return response
|
140 |
+
|
141 |
+
# Status
|
142 |
+
class HealthCheckResponse(BaseModel):
|
143 |
+
status: str = "ok"
|
144 |
+
|
145 |
+
@app.get("/health")
|
146 |
+
def health() -> HealthCheckResponse:
|
147 |
+
return HealthCheckResponse()
|
148 |
+
|
149 |
+
# API readiness compatibility for OpenShift AI Workbench
|
150 |
+
@app.get("/api", include_in_schema=False)
|
151 |
+
def api_check() -> HealthCheckResponse:
|
152 |
+
return HealthCheckResponse()
|
153 |
+
|
154 |
+
# Convert a document from URL(s)
|
155 |
+
@app.post(
|
156 |
+
"/v1alpha/convert/source",
|
157 |
+
response_model=ConvertDocumentResponse,
|
158 |
+
responses={
|
159 |
+
200: {
|
160 |
+
"content": {"application/zip": {}},
|
161 |
+
# "description": "Return the JSON item or an image.",
|
162 |
+
}
|
163 |
+
},
|
164 |
)
|
165 |
+
def process_url(
|
166 |
+
background_tasks: BackgroundTasks, conversion_request: ConvertDocumentsRequest
|
167 |
+
):
|
168 |
+
sources: List[Union[str, DocumentStream]] = []
|
169 |
+
headers: Optional[Dict[str, Any]] = None
|
170 |
+
if isinstance(conversion_request, ConvertDocumentFileSourcesRequest):
|
171 |
+
for file_source in conversion_request.file_sources:
|
172 |
+
sources.append(file_source.to_document_stream())
|
173 |
+
else:
|
174 |
+
for http_source in conversion_request.http_sources:
|
175 |
+
sources.append(http_source.url)
|
176 |
+
if headers is None and http_source.headers:
|
177 |
+
headers = http_source.headers
|
178 |
+
|
179 |
+
# Note: results are only an iterator->lazy evaluation
|
180 |
+
results = convert_documents(
|
181 |
+
sources=sources, options=conversion_request.options, headers=headers
|
182 |
+
)
|
183 |
+
|
184 |
+
# The real processing will happen here
|
185 |
+
response = process_results(
|
186 |
+
background_tasks=background_tasks,
|
187 |
+
conversion_options=conversion_request.options,
|
188 |
+
conv_results=results,
|
189 |
+
)
|
190 |
+
|
191 |
+
return response
|
192 |
+
|
193 |
+
# Convert a document from file(s)
|
194 |
+
@app.post(
|
195 |
+
"/v1alpha/convert/file",
|
196 |
+
response_model=ConvertDocumentResponse,
|
197 |
+
responses={
|
198 |
+
200: {
|
199 |
+
"content": {"application/zip": {}},
|
200 |
+
}
|
201 |
+
},
|
202 |
)
|
203 |
+
async def process_file(
|
204 |
+
background_tasks: BackgroundTasks,
|
205 |
+
files: List[UploadFile],
|
206 |
+
options: Annotated[
|
207 |
+
ConvertDocumentsOptions, FormDepends(ConvertDocumentsOptions)
|
208 |
+
],
|
209 |
+
):
|
210 |
+
|
211 |
+
_log.info(f"Received {len(files)} files for processing.")
|
212 |
+
|
213 |
+
# Load the uploaded files to Docling DocumentStream
|
214 |
+
file_sources = []
|
215 |
+
for file in files:
|
216 |
+
buf = BytesIO(file.file.read())
|
217 |
+
name = file.filename if file.filename else "file.pdf"
|
218 |
+
file_sources.append(DocumentStream(name=name, stream=buf))
|
219 |
+
|
220 |
+
results = convert_documents(sources=file_sources, options=options)
|
221 |
+
|
222 |
+
response = process_results(
|
223 |
+
background_tasks=background_tasks,
|
224 |
+
conversion_options=options,
|
225 |
+
conv_results=results,
|
226 |
+
)
|
227 |
+
|
228 |
+
return response
|
229 |
+
|
230 |
+
return app
|
docling_serve/settings.py
CHANGED
@@ -1,6 +1,28 @@
|
|
|
|
|
|
1 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
2 |
|
3 |
|
4 |
-
class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
|
|
|
|
1 |
+
from typing import Union
|
2 |
+
|
3 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
4 |
|
5 |
|
6 |
+
class UvicornSettings(BaseSettings):
|
7 |
+
model_config = SettingsConfigDict(
|
8 |
+
env_prefix="UVICORN_", env_file=".env", extra="allow"
|
9 |
+
)
|
10 |
+
|
11 |
+
host: str = "0.0.0.0"
|
12 |
+
port: int = 5001
|
13 |
+
reload: bool = False
|
14 |
+
root_path: str = ""
|
15 |
+
proxy_headers: bool = True
|
16 |
+
workers: Union[int, None] = None
|
17 |
+
|
18 |
+
|
19 |
+
class DoclingServeSettings(BaseSettings):
|
20 |
+
model_config = SettingsConfigDict(
|
21 |
+
env_prefix="DOCLING_SERVE_", env_file=".env", extra="allow"
|
22 |
+
)
|
23 |
+
|
24 |
+
enable_ui: bool = False
|
25 |
+
|
26 |
|
27 |
+
uvicorn_settings = UvicornSettings()
|
28 |
+
docling_serve_settings = DoclingServeSettings()
|
pyproject.toml
CHANGED
@@ -36,6 +36,7 @@ dependencies = [
|
|
36 |
"pydantic~=2.10",
|
37 |
"pydantic-settings~=2.4",
|
38 |
"python-multipart>=0.0.14,<0.1.0",
|
|
|
39 |
"uvicorn[standard]>=0.29.0,<1.0.0",
|
40 |
]
|
41 |
|
@@ -74,6 +75,7 @@ dev = [
|
|
74 |
]
|
75 |
|
76 |
[tool.uv]
|
|
|
77 |
conflicts = [
|
78 |
[
|
79 |
{ extra = "cpu" },
|
@@ -104,6 +106,9 @@ explicit = true
|
|
104 |
[tool.setuptools.packages.find]
|
105 |
include = ["docling_serve"]
|
106 |
|
|
|
|
|
|
|
107 |
[project.urls]
|
108 |
Homepage = "https://github.com/DS4SD/docling-serve"
|
109 |
# Documentation = "https://ds4sd.github.io/docling"
|
@@ -118,6 +123,7 @@ include = '\.pyi?$'
|
|
118 |
|
119 |
[tool.isort]
|
120 |
profile = "black"
|
|
|
121 |
line_length = 88
|
122 |
py_version=311
|
123 |
|
|
|
36 |
"pydantic~=2.10",
|
37 |
"pydantic-settings~=2.4",
|
38 |
"python-multipart>=0.0.14,<0.1.0",
|
39 |
+
"typer~=0.12",
|
40 |
"uvicorn[standard]>=0.29.0,<1.0.0",
|
41 |
]
|
42 |
|
|
|
75 |
]
|
76 |
|
77 |
[tool.uv]
|
78 |
+
package = true
|
79 |
conflicts = [
|
80 |
[
|
81 |
{ extra = "cpu" },
|
|
|
106 |
[tool.setuptools.packages.find]
|
107 |
include = ["docling_serve"]
|
108 |
|
109 |
+
[project.scripts]
|
110 |
+
docling-serve = "docling_serve.__main__:main"
|
111 |
+
|
112 |
[project.urls]
|
113 |
Homepage = "https://github.com/DS4SD/docling-serve"
|
114 |
# Documentation = "https://ds4sd.github.io/docling"
|
|
|
123 |
|
124 |
[tool.isort]
|
125 |
profile = "black"
|
126 |
+
known_third_party = ["docling", "docling_core"]
|
127 |
line_length = 88
|
128 |
py_version=311
|
129 |
|
start_server.sh
DELETED
@@ -1,30 +0,0 @@
|
|
1 |
-
#!/bin/bash
|
2 |
-
set -Eeuo pipefail
|
3 |
-
|
4 |
-
# Network settings
|
5 |
-
export PORT="${PORT:-5001}"
|
6 |
-
export HOST="${HOST:-"0.0.0.0"}"
|
7 |
-
|
8 |
-
# Performance settings
|
9 |
-
UVICORN_WORKERS="${UVICORN_WORKERS:-1}"
|
10 |
-
|
11 |
-
# Development settings
|
12 |
-
export WITH_UI="${WITH_UI:-"true"}"
|
13 |
-
export RELOAD=${RELOAD:-"false"}
|
14 |
-
|
15 |
-
# --------------------------------------
|
16 |
-
# Process env settings
|
17 |
-
|
18 |
-
EXTRA_ARGS=""
|
19 |
-
if [ "$RELOAD" == "true" ]; then
|
20 |
-
EXTRA_ARGS="$EXTRA_ARGS --reload"
|
21 |
-
fi
|
22 |
-
|
23 |
-
# Launch
|
24 |
-
exec uv run uvicorn \
|
25 |
-
docling_serve.app:app \
|
26 |
-
--host=${HOST} \
|
27 |
-
--port=${PORT} \
|
28 |
-
--timeout-keep-alive=600 \
|
29 |
-
${EXTRA_ARGS} \
|
30 |
-
--workers=${UVICORN_WORKERS}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uv.lock
CHANGED
@@ -583,7 +583,7 @@ wheels = [
|
|
583 |
[[package]]
|
584 |
name = "docling-serve"
|
585 |
version = "0.2.0"
|
586 |
-
source = {
|
587 |
dependencies = [
|
588 |
{ name = "docling" },
|
589 |
{ name = "fastapi", extra = ["standard"] },
|
@@ -591,6 +591,7 @@ dependencies = [
|
|
591 |
{ name = "pydantic" },
|
592 |
{ name = "pydantic-settings" },
|
593 |
{ name = "python-multipart" },
|
|
|
594 |
{ name = "uvicorn", extra = ["standard"] },
|
595 |
]
|
596 |
|
@@ -646,6 +647,7 @@ requires-dist = [
|
|
646 |
{ name = "torch", marker = "extra == 'cu124'", specifier = ">=2.6.0", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "docling-serve", extra = "cu124" } },
|
647 |
{ name = "torchvision", marker = "extra == 'cpu'", specifier = ">=0.21.0", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "docling-serve", extra = "cpu" } },
|
648 |
{ name = "torchvision", marker = "extra == 'cu124'", specifier = ">=0.21.0", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "docling-serve", extra = "cu124" } },
|
|
|
649 |
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.29.0,<1.0.0" },
|
650 |
]
|
651 |
provides-extras = ["ui", "tesserocr", "rapidocr", "cpu", "cu124"]
|
|
|
583 |
[[package]]
|
584 |
name = "docling-serve"
|
585 |
version = "0.2.0"
|
586 |
+
source = { editable = "." }
|
587 |
dependencies = [
|
588 |
{ name = "docling" },
|
589 |
{ name = "fastapi", extra = ["standard"] },
|
|
|
591 |
{ name = "pydantic" },
|
592 |
{ name = "pydantic-settings" },
|
593 |
{ name = "python-multipart" },
|
594 |
+
{ name = "typer" },
|
595 |
{ name = "uvicorn", extra = ["standard"] },
|
596 |
]
|
597 |
|
|
|
647 |
{ name = "torch", marker = "extra == 'cu124'", specifier = ">=2.6.0", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "docling-serve", extra = "cu124" } },
|
648 |
{ name = "torchvision", marker = "extra == 'cpu'", specifier = ">=0.21.0", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "docling-serve", extra = "cpu" } },
|
649 |
{ name = "torchvision", marker = "extra == 'cu124'", specifier = ">=0.21.0", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "docling-serve", extra = "cu124" } },
|
650 |
+
{ name = "typer", specifier = "~=0.12" },
|
651 |
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.29.0,<1.0.0" },
|
652 |
]
|
653 |
provides-extras = ["ui", "tesserocr", "rapidocr", "cpu", "cu124"]
|