feat(compat): DoclingDocument downgrade projector system - #703
Open
ceberam wants to merge 2 commits into
Open
Conversation
Adds docling_core/compat.py with a register_projector decorator, project_to() API, and five retroactive projectors (1.10→1.9→1.8→1.7→ 1.6→1.5). Adds scripts/check_compat_projectors.py as a pre-commit hook that detects unannounced structural breaking changes and enforces that every schema minor bump ships a projector, a JSON Schema snapshot, and a test class. 36 new tests in test/test_compat.py; CONTRIBUTING.md documents the change-classification table and 7-step checklist. Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Contributor
|
✅ DCO Check Passed Thanks @ceberam, all your commits are properly signed off. 🎉 |
Contributor
Merge Protections🟢 All 2 merge protections satisfied — ready to merge. Show 2 satisfied protections🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 Require two reviewer for test updatesWhen test data is updated, we require two reviewers
|
PeterStaar-IBM
self-requested a review
August 6, 2026 10:45
dolfim-ibm
approved these changes
Aug 6, 2026
dolfim-ibm
left a comment
Member
There was a problem hiding this comment.
very nice and needed!
I just wonder if docling_core/compat.py will become a huge file, at some point we could split it apart, and maybe declare all of it as "internal".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a server-side downgrade projector system so that a
docling-serveinstance running a newerdocling-corecan serve aDoclingDocumentJSON that is still parseable by a client running an older version.Closes #702
What changed
docling_core/compat.py(new)register_projector(*, from_minor, to_minor)— decorator that registers a single-step downgrade function in a module-level registry.project_to(doc, target_version)— walks the registry chain from the document's current schema version down totarget_version, applying each projector in sequence, then returns a validatedDoclingDocumentwith the version field restored to the target.list_projectors()— returns sorted(from_minor, to_minor)pairs; useful for introspection and tests.Five retroactive projectors covering every schema step from the current version back to the oldest v2.x schema:
field_regions,field_items, 8 newDocItemLabelvalues, newCodeLanguageLabelvalues, newPictureClassificationLabelvalues,TableData.orientation, newBaseMetasub-fieldsDocItem.comments,DocItem.sourceNodeItem.metaTableCell.fillableRichTableCell.refdocling_core/types/doc/common/constants.py(modified)SCHEMA_VERSION_HISTORY— ordered list of every schema version in the v2.x era with the first library release that introduced it._CURRENT_MINORandFIRST_SUPPORTED_MINOR— derived constants consumed by the check script and tests to stay in sync withCURRENT_VERSIONwithout duplication.scripts/check_compat_projectors.py(new)A standalone enforcement script run as a pre-commit hook and in CI.
Nine checks in a single pass:
_CURRENT_MINOR - FIRST_SUPPORTED_MINOR.N → N-1has a registered function.SCHEMA_VERSION_HISTORYhas the right number of entries.CURRENT_VERSION.6–7. JSON Schema snapshots exist for every target minor version and for the current version; missing snapshots are auto-generated by copying
docs/DoclingDocument.json(guaranteed to run before thedocshook regenerates it).model_validate()to raise on old clients: new enum values, new fields on strict models (additionalProperties: false), new required fields, newanyOfunion subtypes. If any are found without a matchingCURRENT_VERSIONbump, the hook fails with a precise diagnostic.docs/schemas/(new)JSON Schema snapshots for every supported minor version (
DoclingDocument_1_5.jsonthroughDoclingDocument_1_10.json). Versions 1.5–1.9 are bootstrap copies of the current schema (permissive superset); going forward each snapshot is captured automatically at the moment of the version bump, beforegenerate_docsoverwrites the live schema.test/test_compat.py(new)36 tests across 8 classes:
TestProjectorCoverage— parametrised count, step-coverage, history-length, history-sort,list_projectorssort, single-step-key, output-shape (every projector), and test-class-name presence checks.TestProjector_1_{N}_to_1_{N-1}— one class per projector with at least one non-trivial field-level assertion.TestChainProjection,TestNoOpProjection,TestErrorPaths.CONTRIBUTING.md(modified)New section "Breaking changes and schema bumps" covering:
schema diff.
project_to()..pre-commit-config.yaml(modified)Added
check-compat-projectorshook (runs beforemypy,pytest, anddocs) so the schema snapshot is captured beforegenerate_docsoverwritesdocs/DoclingDocument.json.How to use on the server side
What happens the next time a breaking change lands
A contributor adds a new field or enum value, then:
$defsentry.CURRENT_VERSION, appends aSCHEMA_VERSION_HISTORYentry, adds a@register_projectorfunction, and adds aTestProjector_1_{N+1}_to_1_{N}test class.CI (pytest) enforces that the new test class exists and the projector output validates against the target-version snapshot.