Skip to content

feat: Grpc native converter#504

Open
krickert wants to merge 8 commits into
docling-project:mainfrom
ai-pipestream:grpc-native-converter
Open

feat: Grpc native converter#504
krickert wants to merge 8 commits into
docling-project:mainfrom
ai-pipestream:grpc-native-converter

Conversation

@krickert

@krickert krickert commented Feb 20, 2026

Copy link
Copy Markdown

This pull request introduces new support and documentation for running and testing the gRPC server in the docling-serve project. The main changes include adding full gRPC compatibility with the docling project.

gRPC-specific test jobs to CI, new Makefile commands for running gRPC tests, improved developer documentation for running tests, and the initial implementation of the gRPC server entrypoint and package structure.

gRPC Server Implementation

  • Introduced docling_serve/grpc/__main__.py as the Typer CLI entrypoint for running the gRPC server, with options for host, port, and artifact path, and a version command.
  • Created docling_serve/grpc/__init__.py to set up the Python import path for generated gRPC code, ensuring it is importable at runtime.

CI and Testing Improvements

  • Added two new GitHub Actions jobs to .github/workflows/job-checks.yml for running gRPC unit and integration tests separately, ensuring faster feedback and clearer test separation.
  • Added corresponding Makefile commands: test-grpc-unit, test-grpc-integration, and test-grpc-ocr to easily run gRPC unit, integration, and OCR tests locally.

Developer Documentation

  • Updated CONTRIBUTING.md with clear instructions and examples for running different types of tests using pytest markers, including gRPC-specific commands and explanations of what each test suite covers.

@github-actions

github-actions Bot commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

DCO Check Failed

Hi @krickert, your pull request has failed the Developer Certificate of Origin (DCO) check.

This repository supports remediation commits, so you can fix this without rewriting history — but you must follow the required message format.


🛠 Quick Fix: Add a remediation commit

Run this command:

git commit --allow-empty -s -m "DCO Remediation Commit for github-actions[bot] <github-actions[bot]@users.noreply.github.com>

I, github-actions[bot] <github-actions[bot]@users.noreply.github.com>, hereby add my Signed-off-by to this commit: ebdccd5acc56991b503b4f6ab65fc530aa707f28"
git push

🔧 Advanced: Sign off each commit directly

For the latest commit:

git commit --amend --signoff
git push --force-with-lease

For multiple commits:

git rebase --signoff origin/main
git push --force-with-lease

More info: DCO check report

@krickert
krickert marked this pull request as draft February 20, 2026 17:43
@mergify

mergify Bot commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@dosubot

dosubot Bot commented Feb 20, 2026

Copy link
Copy Markdown

Related Documentation

3 document(s) may need updating based on files changed in this PR:

Docling

How can I set up and run docling-serve on a MacBook Pro using Docker, and what performance and stability considerations should I be aware of?
View Suggested Changes
@@ -24,6 +24,26 @@
    - To use more CPU threads for a single document, add `-e DOCLING_NUM_THREADS=4` (or up to your CPU core count).
 
 5. **Access the UI**: Open [http://localhost:5001](http://localhost:5001) in your browser.
+
+**Choosing Between REST and gRPC:**
+
+Docling Serve supports both REST and gRPC protocols. Users can choose the protocol that best fits their application:
+
+- **REST API (default, port 5001)**: Uses JSON over HTTP/1.1. Supports multipart/form-data file uploads and provides a web UI for interactive use. This is the standard option and runs by default when starting the container.
+
+- **gRPC API (port 50051)**: Uses Protocol Buffers over HTTP/2. File uploads use base64 encoding in the `FileSource` message instead of multipart/form-data. gRPC provides server-streaming RPCs for status updates and is suitable for language-agnostic service-to-service communication.
+
+To run the gRPC server instead of REST using Docker:
+```sh
+docker run -p 50051:50051 \
+  -v $(pwd)/models:/opt/app-root/src/models \
+  -e DOCLING_SERVE_ARTIFACTS_PATH="/opt/app-root/src/models" \
+  quay.io/docling-project/docling-serve -- docling-serve-grpc run --host 0.0.0.0 --port 50051
+```
+
+The gRPC and REST servers run independently. If both protocols are needed, run them as separate containers on different ports.
+
+**Note:** gRPC support is experimental. File uploads in gRPC use base64-encoded content in the `FileSource` message rather than multipart/form-data. Refer to the gRPC documentation for details on protobuf schemas and client implementation.
 
 **Performance:**
 - On a modern MacBook Pro (M1/M2, 12 cores, 32GB RAM), expect to process about 5 PDFs (127 pages, 6.2MB total) in roughly 10 minutes (CPU-only). Increasing CPU cores, RAM, or worker count does not significantly improve throughput due to Python's concurrency limitations.

[Accept] [Decline]

How can you mount a PersistentVolumeClaim (PVC) in docling-serve on OpenShift to store EasyOCR models, and what steps are required to ensure docling-serve can access these models?
View Suggested Changes
@@ -1,4 +1,8 @@
-To mount a PVC in docling-serve on OpenShift for EasyOCR models, you need to edit the Deployment manifest directly (there is no Operator-specific CRD for this). Add the PVC as a volume and mount it in the container, then set the `DOCLING_SERVE_ARTIFACTS_PATH` environment variable to the mount path. Here is an example snippet:
+To mount a PVC in docling-serve on OpenShift for EasyOCR models, you need to edit the Deployment manifest directly (there is no Operator-specific CRD for this). Add the PVC as a volume and mount it in the container, then set the `DOCLING_SERVE_ARTIFACTS_PATH` environment variable to the mount path.
+
+**Note:** For the gRPC server, you can also configure the artifacts path using the `--artifacts-path` CLI argument instead of the environment variable. Both methods work with PVC mounts—the path must point to the mounted volume location. The environment variable approach works for both REST and gRPC servers.
+
+Here is an example snippet:
 
 ```yaml
 spec:
@@ -59,6 +63,16 @@
 
 Ensure your EasyOCR models are present in `/modelcache` (or your chosen path) and the directory structure matches what docling expects. If a required model is missing, docling-serve will raise a runtime error. It's recommended to preload models into the PVC using a Kubernetes Job before starting docling-serve. For more details, see the [official docling-serve documentation](https://github.com/docling-project/docling-serve/blob/a179338c785ef9b84696f41b7ab2f2cafe80973d/docs/models.md#L7-L173).
 
+**Example for gRPC server with custom artifacts path:**
+
+If running the gRPC server and using a custom mount path, you can specify it via the CLI:
+
+```sh
+docling-serve-grpc run --artifacts-path /modelcache --host 0.0.0.0 --port 50051
+```
+
+This is equivalent to setting `DOCLING_SERVE_ARTIFACTS_PATH=/modelcache` and works with the same PVC mount configuration shown above.
+
 ## Health probe configuration
 
 The deployment manifest includes health probes to ensure docling-serve is fully ready before accepting traffic:

[Accept] [Decline]

Models handling in Docling Serve
View Suggested Changes
@@ -4,7 +4,29 @@
 With Docling v2.56.x and later, EasyOCR models are now included in the default set of models downloaded by the auto-ocr feature. You no longer need to explicitly request EasyOCR models unless you are customizing the download set.
 
 ## Model Storage Location
-Docling Serve loads models from the directory specified by the `DOCLING_SERVE_ARTIFACTS_PATH` environment variable. This path must be consistent across model download and runtime. When running with multiple workers or reload enabled, you must use the environment variable rather than the CLI argument for configuration [[source]](https://github.com/docling-project/docling-serve/blob/fd1b987e8dc174f1a6013c003dde33e9acbae39a/docling_serve/settings.py).
+Docling Serve loads models from the directory specified by the `DOCLING_SERVE_ARTIFACTS_PATH` environment variable. This path must be consistent across model download and runtime.
+
+### Configuration Options
+
+**Environment Variable (REST and gRPC)**
+
+Set `DOCLING_SERVE_ARTIFACTS_PATH` to configure the artifacts path for both REST and gRPC servers:
+
+```sh
+export DOCLING_SERVE_ARTIFACTS_PATH=/path/to/models
+```
+
+This is the recommended approach for production deployments and is required when running the REST server with multiple workers or reload enabled [[source]](https://github.com/docling-project/docling-serve/blob/fd1b987e8dc174f1a6013c003dde33e9acbae39a/docling_serve/settings.py).
+
+**CLI Argument (gRPC Only)**
+
+When running the gRPC server via `docling-serve-grpc`, you can also configure the artifacts path using the `--artifacts-path` CLI argument:
+
+```sh
+docling-serve-grpc run --artifacts-path /path/to/models --host 0.0.0.0 --port 50051
+```
+
+The CLI argument is specific to the gRPC server entrypoint and provides a convenient way to set the path without environment variables. For REST server deployments with multiple workers or reload enabled, use the environment variable instead.
 
 ## Approaches for Making Extra Models Available
 There are several ways to ensure required models are present:
@@ -197,7 +219,7 @@
 ## Troubleshooting and Best Practices
 - If a required model is missing from the artifacts path, Docling Serve will raise a runtime error.
 - Always ensure the value of `DOCLING_SERVE_ARTIFACTS_PATH` matches the directory where models are stored and mounted.
-- For multi-worker or reload scenarios, use the environment variable, not the CLI argument, to set the artifacts path.
+- For REST server deployments with multiple workers or reload enabled, use the environment variable to set the artifacts path. For gRPC server deployments, you can use either the environment variable or the `--artifacts-path` CLI argument.
 - For production and cluster environments, prefer persistent storage and pre-loading models via a dedicated job.
 - EasyOCR models are now included by default in auto-ocr; explicit inclusion is only needed for custom workflows.
 - Use the `/ready` endpoint for startupProbe and readinessProbe to prevent traffic before models are loaded and dependencies are available.

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

@krickert krickert changed the title Grpc native converter (feat) Grpc native converter Feb 20, 2026
@krickert

Copy link
Copy Markdown
Author

@dolfim-ibm this is the first attempt at this. It's fully featured now, and this is what I'll run the 768/10K common crawl PDF test against.

I will look into how to move parts of this to the docling-core for the spec and grpc stubs.

@krickert krickert changed the title (feat) Grpc native converter feat: Grpc native converter Feb 23, 2026
@krickert
krickert marked this pull request as ready for review March 16, 2026 02:29
@krickert

Copy link
Copy Markdown
Author

This is tied with docling-project/docling-core#546 now - the protobuf definitions and mappers have been moved there while the server functionality is here.

@krickert

Copy link
Copy Markdown
Author

Just pushed the latest sync between docling-project/docling-core#546 since these two are tied together.

@krickert

krickert commented Apr 17, 2026

Copy link
Copy Markdown
Author

Quick funny thought: if anything can be critiqued, is that the checking for drift here is a bit overly-pedantic.. I don't think it is, but sorta like the irony if it were given that the underlying model is powered by... pydantic

(sorry, I know it's a bad joke... but imagine seeing that word 100x as you make changes..)

Update: sync with main and tighten the runtime schema validator

Pushed 98e5e53 on top of the existing branch. Branch is up to date with upstream/main, all gRPC tests pass, and the startup schema validator reports zero warnings against the latest Pydantic models.

This update is paired with docling-project/docling-core#546 (the proto changes live there). On this side, the work is catching up to upstream renames, regenerating stubs against the tightened IDL, and hardening the validator so the next round of Pydantic drift produces a loud, actionable signal instead of silent acceptance.

Mapping changes (upstream rename catch-up):

  • OutputFormat.MARKDOWN (renamed upstream from MD).
  • VlmModelType.GRANITEDOCLING (renamed from GRANITE_DOCLING).
  • TransformersModelType.AUTOMODEL_IMAGETEXTTOTEXT (renamed from AUTOMODEL_VISION2SEQ).

Legacy proto-only aliases were dropped rather than kept as compatibility shims. The PR is still pre-release and no clients are pinned, so there is no wire-stability budget to spend yet. The sync procedure documents when that policy flips.

Schema validator hardening:

  • Dropped CodeItem from the base-wrapper map. CodeItem no longer wraps TextItemBase in proto (see feat: add config options for presets and allow yaml config file #546), and keeping it in the wrapper map would mask real drift on the inlined fields.
  • Added script_raw to the recognized *_raw fallbacks so the new Formatting.script_raw round-trips cleanly without a warning.
  • Added a _PYDANTIC_ONLY_DISCRIMINATORS table and registered TrackSource.kind. Pydantic's Literal["track"] discriminator is absorbed by the proto oneof tag, and the validator now treats that as an intentional difference rather than a missing field.
  • Re-documented the rationale for each suppression so future contributors can tell "intentional" from "drift" at a glance.

Tests:

Keeping up with models can feel daunting. I'm trying to make the tests feel like this is a quick chore rather than "why support two models"? I thought a lot about how to accomplish this.

  • Extended drift-guard coverage for the new enum values and the inlined CodeItem fields.
  • Dropped the obsolete TrackSource.kind assertion in tests/test_docling_document_converter.py.
  • Updated converter tests for the tightened proto types pulled from docling-core (binary_hash as uint64, pages map keys as int32, PictureItem.label as DocItemLabel enum).

New docs:

I thought the earlier commits may have not been clear about the sync strategy I'm proposing. So I figured I'd include them here..

  • docs/grpc/upstream_sync_procedure.md describes the end-to-end sync workflow: fetch upstream, merge, regenerate stubs in both repos, run the schema validator, run the test suite, and act on warnings.
  • docs/grpc/architecture_discussion.md captures the design rationale (Pydantic as source of truth, proto as a standards-compliant interface, *_raw fallback policy, oneof-as-discriminator).

Verification:

  • python scripts/gen_grpc.py regenerates clean against docling-core's feat/add-protobuf.
  • uv run python -m pytest tests/test_grpc_mapping.py tests/test_schema_validator.py tests/test_docling_document_converter.py is green.
  • The startup schema validator (docling_serve.grpc.schema_validator.validate_docling_document_schema) reports zero warnings against the current Pydantic models.

@krickert

Copy link
Copy Markdown
Author

I've made a repository with examples demonstrating how to run docling via gRPC.

Here: ai-pipestream/docling-grpc-examples

Language / Environment Tooling & Stack Details
Python uv + grpcio
Go protoc-gen-go + grpc-go
Java (Vanilla) Gradle + protobuf-gradle-plugin + grpc-java
Node.js (TypeScript) @grpc/grpc-js + grpc-tools (TypeScript via tsx)
Rust tonic + prost (Compile-time stub gen via tonic-build)

@krickert

krickert commented May 5, 2026

Copy link
Copy Markdown
Author

Checked latest model changes - everything up-to-date with main. DCO wasn't signed off, fixed that.

@krickert

Copy link
Copy Markdown
Author

This is up-to-date with main and 100% of all fields in the model are covered. Compatibility has been maintained.

@mergify

mergify Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

2 similar comments
@mergify

mergify Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@mergify

mergify Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@krickert
krickert force-pushed the grpc-native-converter branch 2 times, most recently from 9f8d086 to 3a36d01 Compare June 10, 2026 18:50
@mergify

mergify Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@krickert
krickert force-pushed the grpc-native-converter branch from 31808c7 to 6077d32 Compare June 10, 2026 22:27
…ersion

Add a native gRPC converter alongside the REST API, with startup
pydantic-to-proto schema parity validation and 1:1 conversion mapping.

Signed-off-by: Kristian Rickert <krickert@gmail.com>
@krickert
krickert force-pushed the grpc-native-converter branch from ba859f8 to 288afc6 Compare June 22, 2026 12:25
@mergify

mergify Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

krickert added 2 commits July 3, 2026 15:12
Upstream removed ServicePolicy.s3_enabled in favor of allowed_target_types
and S3 source/target pairing rules. Update the gRPC policy bridge to match
REST policy.py, update the S3 fake-service tests accordingly, add an
OTHER_CHART picture classification proto test, and pin the integration
test OCR warmup to tesseract so the server can start where the RapidOCR
torch PP-OCRv6 preset is unavailable.

Signed-off-by: Kristian Rickert <krickert@gmail.com>
…docs

The serve protos import the DoclingDocument proto that lives in the
docling-core repository, so a bare buf lint here cannot resolve the
import. Add scripts/buf_check.py, which resolves the core proto the same
way gen_grpc.py does, assembles a combined tree, and runs buf lint plus
buf format checks. Update the gRPC README to state where each proto
lives and how to run the checks.

Signed-off-by: Kristian Rickert <krickert@gmail.com>
@mergify

mergify Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

Introduce DoclingStreamingService with a oneof StreamDocumentResponse
(status, source_result, final_document, error; part reserved) that
reuses core DoclingDocument types. Phase 1 emits honest progress around
the existing orchestrator and the final document: no fake page yields.
Keep REST-parity DoclingServeService intact so upstream can maintain
convert/poll while this fork injects real push streaming.

Signed-off-by: Kristian Rickert <krickert@gmail.com>
@mergify

mergify Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

1 similar comment
@mergify

mergify Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

…sers.noreply.github.com>

I, github-actions[bot] <github-actions[bot]@users.noreply.github.com>, hereby add my Signed-off-by to this commit: ebdccd5

Signed-off-by: Kristian Rickert <krickert@gmail.com>
@mergify

mergify Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@krickert krickert closed this Jul 14, 2026
@krickert
krickert deleted the grpc-native-converter branch July 14, 2026 10:14
@krickert krickert reopened this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants