From 2747570b22ba10b0d384850ec3fc3defb12360da Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 13:15:55 +0100 Subject: [PATCH 01/18] Add docling-metric-hello-world as example Signed-off-by: Christoph Auer --- packages/docling-metric-hello-world/README.md | 47 ++++++++ .../docling_metric_hello_world/__init__.py | 21 ++++ .../hello_world_metric.py | 100 ++++++++++++++++++ .../docling-metric-hello-world/pyproject.toml | 69 ++++++++++++ .../tests/test_hello_world_metric.py | 44 ++++++++ .../docling-metrics-core/tests/__init__.py | 0 pyproject.toml | 6 ++ uv.lock | 32 ++++++ 8 files changed, 319 insertions(+) create mode 100644 packages/docling-metric-hello-world/README.md create mode 100644 packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py create mode 100644 packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py create mode 100644 packages/docling-metric-hello-world/pyproject.toml create mode 100644 packages/docling-metric-hello-world/tests/test_hello_world_metric.py delete mode 100644 packages/docling-metrics-core/tests/__init__.py diff --git a/packages/docling-metric-hello-world/README.md b/packages/docling-metric-hello-world/README.md new file mode 100644 index 0000000..9737401 --- /dev/null +++ b/packages/docling-metric-hello-world/README.md @@ -0,0 +1,47 @@ +# docling-metric-hello-world + +A minimal example metric implementation demonstrating how to build metrics on top of +`docling-metrics-core`. + +## Overview + +This package provides a simple "Hello World" metric that: +- Accepts string payloads as input samples +- Always returns a score of `1.0` for each sample evaluation +- Demonstrates the basic structure for implementing custom metrics + +## Installation + +```bash +pip install docling-metric-hello-world +``` + +## Usage + +```python +from docling_metric_hello_world import HelloWorldMetric, StringInputSample + +# Create metric instance +metric = HelloWorldMetric() + +# Create sample inputs +sample_a = StringInputSample(id="sample-1", payload="Hello") +sample_b = StringInputSample(id="sample-1", payload="World") + +# Evaluate a single sample pair +result = metric.evaluate_sample(sample_a, sample_b) +print(result.score) # Always 1.0 + +# Evaluate multiple sample pairs +pairs = [ + (StringInputSample(id="1", payload="a"), StringInputSample(id="1", payload="b")), + (StringInputSample(id="2", payload="c"), StringInputSample(id="2", payload="d")), +] +aggregate = metric.evaluate_dataset(pairs) +print(aggregate.mean_score) # 1.0 +print(aggregate.sample_count) # 2 +``` + +## License + +MIT diff --git a/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py b/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py new file mode 100644 index 0000000..3d57a90 --- /dev/null +++ b/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py @@ -0,0 +1,21 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""A hello-world example metric implementation for Docling metrics.""" + +from docling_metric_hello_world.hello_world_metric import ( + HelloWorldAggregateResult, + HelloWorldMetric, + HelloWorldSampleResult, + StringInputSample, +) + +__all__ = [ + "HelloWorldAggregateResult", + "HelloWorldMetric", + "HelloWorldSampleResult", + "StringInputSample", +] diff --git a/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py b/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py new file mode 100644 index 0000000..bfd00ef --- /dev/null +++ b/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py @@ -0,0 +1,100 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Hello World metric implementation demonstrating the docling-metrics-core interface.""" + +from typing import Annotated, Iterable, Tuple + +from docling_metrics_core.base_types import ( + BaseAggregateResult, + BaseInputSample, + BaseMetric, + BaseSampleResult, +) +from pydantic import Field + + +class StringInputSample(BaseInputSample): + """Input sample with a string payload.""" + + payload: Annotated[str, Field(description="The string payload for this sample")] + + +class HelloWorldSampleResult(BaseSampleResult): + """Result of evaluating a single sample pair.""" + + score: Annotated[ + float, Field(description="The evaluation score (always 1.0 for this metric)") + ] + + +class HelloWorldAggregateResult(BaseAggregateResult): + """Aggregated result from multiple sample evaluations.""" + + mean_score: Annotated[ + float, Field(description="Mean score across all evaluated samples") + ] + + +class HelloWorldMetric(BaseMetric): + """A minimal example metric that always returns 1.0. + + This metric demonstrates the basic structure for implementing custom metrics + using the docling-metrics-core base types. It accepts string payloads as input + and always produces a score of 1.0 for each sample evaluation. + """ + + def evaluate_sample( # type: ignore[override] + self, sample_a: StringInputSample, sample_b: StringInputSample + ) -> HelloWorldSampleResult: + """Evaluate a single sample pair. + + Args: + sample_a: First input sample with string payload. + sample_b: Second input sample with string payload. + + Returns: + HelloWorldSampleResult with score always equal to 1.0. + """ + return HelloWorldSampleResult(id=sample_a.id, score=1.0) + + def aggregate( # type: ignore[override] + self, results: Iterable[HelloWorldSampleResult] + ) -> HelloWorldAggregateResult: + """Aggregate multiple sample results. + + Args: + results: Iterable of sample evaluation results. + + Returns: + HelloWorldAggregateResult with mean score and sample count. + """ + results_list = list(results) + sample_count = len(results_list) + if sample_count == 0: + return HelloWorldAggregateResult(sample_count=0, mean_score=0.0) + + total_score = sum(r.score for r in results_list) + return HelloWorldAggregateResult( + sample_count=sample_count, mean_score=total_score / sample_count + ) + + def evaluate_dataset( # type: ignore[override] + self, sample_pairs: Iterable[Tuple[StringInputSample, StringInputSample]] + ) -> HelloWorldAggregateResult: + """Evaluate an entire dataset of sample pairs. + + Args: + sample_pairs: Iterable of (sample_a, sample_b) tuples. + + Returns: + HelloWorldAggregateResult with aggregated statistics. + """ + results = [ + self.evaluate_sample(sample_a, sample_b) + for sample_a, sample_b in sample_pairs + ] + return self.aggregate(results) diff --git a/packages/docling-metric-hello-world/pyproject.toml b/packages/docling-metric-hello-world/pyproject.toml new file mode 100644 index 0000000..2aa7722 --- /dev/null +++ b/packages/docling-metric-hello-world/pyproject.toml @@ -0,0 +1,69 @@ +[project] +name = "docling-metric-hello-world" +version = "0.0.1" # DO NOT EDIT, updated automatically +description = "A hello-world example metric implementation for Docling metrics" +license = "MIT" +keywords = ["docling", "metrics", "evaluation", "example", "hello-world"] +readme = "README.md" +authors = [ + { name = "Christoph Auer", email = "cau@zurich.ibm.com" }, + { name = "Panos Vagenas", email = "pva@zurich.ibm.com" }, + { name = "Ahmed Nassar", email = "ahn@zurich.ibm.com" }, + { name = "Nikos Livathinos", email = "nli@zurich.ibm.com" }, + { name = "Maxim Lysak", email = "mly@zurich.ibm.com" }, + { name = "Michele Dolfi", email = "dol@zurich.ibm.com" }, + { name = "Peter Staar", email = "taa@zurich.ibm.com" }, +] +classifiers = [ + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Programming Language :: Python :: 3", +] +requires-python = '>=3.10,<4.0' +dependencies = [ + "docling-metrics-core>=0.0.1", +] + +[project.urls] +homepage = "https://github.com/docling-project/docling-metrics" +repository = "https://github.com/docling-project/docling-metrics" +issues = "https://github.com/docling-project/docling-metrics/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["docling_metric_hello_world"] + +[tool.uv] +package = true + +[tool.uv.sources] +docling-metrics-core = { workspace = true } + +[dependency-groups] +test = [ + "coverage~=7.6", + "pytest~=8.3", + "pytest-cov>=6.1.1", + "pytest-dependency~=0.6", + "pytest-xdist~=3.3", +] + +[tool.semantic_release] +# for default values check: +# https://github.com/python-semantic-release/python-semantic-release/blob/v7.32.2/semantic_release/defaults.cfg + +version_source = "tag_only" +branch = "main" + +# configure types which should trigger minor and patch version bumps respectively +# (note that they must be a subset of the configured allowed types): +parser_angular_allowed_types = "build,chore,ci,docs,feat,fix,perf,style,refactor,test" +parser_angular_minor_types = "feat" +parser_angular_patch_types = "fix,perf" diff --git a/packages/docling-metric-hello-world/tests/test_hello_world_metric.py b/packages/docling-metric-hello-world/tests/test_hello_world_metric.py new file mode 100644 index 0000000..4505f5c --- /dev/null +++ b/packages/docling-metric-hello-world/tests/test_hello_world_metric.py @@ -0,0 +1,44 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Tests for the HelloWorld metric.""" + +from docling_metric_hello_world import ( + HelloWorldMetric, + StringInputSample, +) + + +def test_evaluate_sample_returns_one() -> None: + """Test that evaluate_sample always returns score of 1.0.""" + metric = HelloWorldMetric() + sample_a = StringInputSample(id="s1", payload="foo") + sample_b = StringInputSample(id="s1", payload="bar") + + result = metric.evaluate_sample(sample_a, sample_b) + + assert result.id == "s1" + assert result.score == 1.0 + + +def test_evaluate_dataset() -> None: + """Test evaluating a dataset of sample pairs.""" + metric = HelloWorldMetric() + pairs = [ + ( + StringInputSample(id="1", payload="a"), + StringInputSample(id="1", payload="b"), + ), + ( + StringInputSample(id="2", payload="c"), + StringInputSample(id="2", payload="d"), + ), + ] + + aggregate = metric.evaluate_dataset(pairs) + + assert aggregate.sample_count == 2 + assert aggregate.mean_score == 1.0 diff --git a/packages/docling-metrics-core/tests/__init__.py b/packages/docling-metrics-core/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pyproject.toml b/pyproject.toml index efb9563..873fd53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,10 +24,12 @@ dev = [ "pytest-xdist~=3.3", # Workspace members — ensures `uv sync` installs all packages "docling-metrics-core", + "docling-metric-hello-world", ] [tool.uv.sources] docling-metrics-core = { workspace = true } +docling-metric-hello-world = { workspace = true } [tool.mypy] pretty = true @@ -77,3 +79,7 @@ classmethod-decorators = [ [tool.ruff.lint.per-file-ignores] "__init__.py" = ["E402", "F401"] "**/tests/*.py" = ["ASYNC"] # Disable ASYNC check for tests + +[tool.pytest.ini_options] +# Use importlib import mode for monorepo compatibility +addopts = "--import-mode=importlib" diff --git a/uv.lock b/uv.lock index b33da5c..c3b6136 100644 --- a/uv.lock +++ b/uv.lock @@ -4,6 +4,7 @@ requires-python = ">=3.10, <4.0" [manifest] members = [ + "docling-metric-hello-world", "docling-metrics-core", "docling-metrics-workspace", ] @@ -381,6 +382,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "docling-metric-hello-world" +version = "0.0.1" +source = { editable = "packages/docling-metric-hello-world" } +dependencies = [ + { name = "docling-metrics-core" }, +] + +[package.dev-dependencies] +test = [ + { name = "coverage" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-dependency" }, + { name = "pytest-xdist" }, +] + +[package.metadata] +requires-dist = [{ name = "docling-metrics-core", editable = "packages/docling-metrics-core" }] + +[package.metadata.requires-dev] +test = [ + { name = "coverage", specifier = "~=7.6" }, + { name = "pytest", specifier = "~=8.3" }, + { name = "pytest-cov", specifier = ">=6.1.1" }, + { name = "pytest-dependency", specifier = "~=0.6" }, + { name = "pytest-xdist", specifier = "~=3.3" }, +] + [[package]] name = "docling-metrics-core" version = "0.0.1" @@ -418,6 +448,7 @@ source = { virtual = "." } [package.dev-dependencies] dev = [ { name = "coverage" }, + { name = "docling-metric-hello-world" }, { name = "docling-metrics-core" }, { name = "mypy" }, { name = "pre-commit" }, @@ -435,6 +466,7 @@ dev = [ [package.metadata.requires-dev] dev = [ { name = "coverage", specifier = "~=7.6" }, + { name = "docling-metric-hello-world", editable = "packages/docling-metric-hello-world" }, { name = "docling-metrics-core", editable = "packages/docling-metrics-core" }, { name = "mypy", specifier = "~=1.10" }, { name = "pre-commit", specifier = "~=3.7" }, From bf071678be2211297d4f37560fc1e2801ba83dd2 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 16:39:41 +0100 Subject: [PATCH 02/18] chore: ignore .worktrees directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 08888a6..3f2cbc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ scratch/ +.worktrees/ # Created by https://www.toptal.com/developers/gitignore/api/python,macos,virtualenv,pycharm,visualstudiocode,emacs,vim,jupyternotebooks # Edit at https://www.toptal.com/developers/gitignore?templates=python,macos,virtualenv,pycharm,visualstudiocode,emacs,vim,jupyternotebooks From 15be1cf629a57972fc16f2fc4b4f3c7607bb0920 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:10:49 +0100 Subject: [PATCH 03/18] feat: add hello world cpp extension scaffold --- .../CMakeLists.txt | 18 ++++++ .../docling-metric-hello-world-cpp/README.md | 3 + .../cpp/hello_world.cpp | 20 ++++++ .../__init__.py | 1 + .../_hello_world_cpp.pyi | 5 ++ .../docling_metric_hello_world_cpp/py.typed | 0 .../pyproject.toml | 64 +++++++++++++++++++ .../tests/test_cpp_binding.py | 13 ++++ 8 files changed, 124 insertions(+) create mode 100644 packages/docling-metric-hello-world-cpp/CMakeLists.txt create mode 100644 packages/docling-metric-hello-world-cpp/README.md create mode 100644 packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp create mode 100644 packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py create mode 100644 packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/_hello_world_cpp.pyi create mode 100644 packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/py.typed create mode 100644 packages/docling-metric-hello-world-cpp/pyproject.toml create mode 100644 packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py diff --git a/packages/docling-metric-hello-world-cpp/CMakeLists.txt b/packages/docling-metric-hello-world-cpp/CMakeLists.txt new file mode 100644 index 0000000..0f1fd8f --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.18) +project(docling_metric_hello_world_cpp LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(pybind11 CONFIG REQUIRED) + +pybind11_add_module(_hello_world_cpp cpp/hello_world.cpp) + +target_compile_features(_hello_world_cpp PRIVATE cxx_std_17) + +install( + TARGETS _hello_world_cpp + LIBRARY DESTINATION docling_metric_hello_world_cpp + RUNTIME DESTINATION docling_metric_hello_world_cpp + ARCHIVE DESTINATION docling_metric_hello_world_cpp +) diff --git a/packages/docling-metric-hello-world-cpp/README.md b/packages/docling-metric-hello-world-cpp/README.md new file mode 100644 index 0000000..0f76979 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/README.md @@ -0,0 +1,3 @@ +# docling-metric-hello-world-cpp + +A minimal C++ (pybind11) example metric for docling-metrics. diff --git a/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp b/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp new file mode 100644 index 0000000..1c61fb1 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace py = pybind11; + +static double evaluate_sample( + const std::string &id, + const std::string &payload_a, + const std::string &payload_b +) { + (void)id; + (void)payload_a; + (void)payload_b; + return 1.0; +} + +PYBIND11_MODULE(_hello_world_cpp, m) { + m.doc() = "Hello World C++ metric bindings"; + m.def("evaluate_sample", &evaluate_sample, "Return constant score"); +} diff --git a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py new file mode 100644 index 0000000..721e994 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py @@ -0,0 +1 @@ +"""Docling metric hello world C++ package.""" diff --git a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/_hello_world_cpp.pyi b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/_hello_world_cpp.pyi new file mode 100644 index 0000000..50e3d11 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/_hello_world_cpp.pyi @@ -0,0 +1,5 @@ +from typing import Final + +def evaluate_sample(id: str, payload_a: str, payload_b: str) -> float: ... + +__all__: Final = ["evaluate_sample"] diff --git a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/py.typed b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/packages/docling-metric-hello-world-cpp/pyproject.toml b/packages/docling-metric-hello-world-cpp/pyproject.toml new file mode 100644 index 0000000..4c5b49f --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/pyproject.toml @@ -0,0 +1,64 @@ +[project] +name = "docling-metric-hello-world-cpp" +version = "0.0.1" # DO NOT EDIT, updated automatically +description = "A hello-world example metric implementation for Docling metrics (C++/pybind11)" +license = "MIT" +keywords = ["docling", "metrics", "evaluation", "example", "hello-world", "cpp"] +readme = "README.md" +authors = [ + { name = "Christoph Auer", email = "cau@zurich.ibm.com" }, + { name = "Panos Vagenas", email = "pva@zurich.ibm.com" }, + { name = "Ahmed Nassar", email = "ahn@zurich.ibm.com" }, + { name = "Nikos Livathinos", email = "nli@zurich.ibm.com" }, + { name = "Maxim Lysak", email = "mly@zurich.ibm.com" }, + { name = "Michele Dolfi", email = "dol@zurich.ibm.com" }, + { name = "Peter Staar", email = "taa@zurich.ibm.com" }, +] +classifiers = [ + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Programming Language :: Python :: 3", +] +requires-python = ">=3.10,<4.0" +dependencies = [ + "docling-metrics-core>=0.0.1", +] + +[project.urls] +homepage = "https://github.com/docling-project/docling-metrics" +repository = "https://github.com/docling-project/docling-metrics" +issues = "https://github.com/docling-project/docling-metrics/issues" + +[build-system] +requires = ["scikit-build-core>=0.10", "pybind11>=2.12"] +build-backend = "scikit_build_core.build" + +[tool.scikit-build] +wheel.packages = ["docling_metric_hello_world_cpp"] + +[tool.uv] +package = true + +[tool.uv.sources] +docling-metrics-core = { workspace = true } + +[dependency-groups] +test = [ + "coverage~=7.6", + "pytest~=8.3", + "pytest-cov>=6.1.1", + "pytest-dependency~=0.6", + "pytest-xdist~=3.3", +] + +[tool.semantic_release] +version_source = "tag_only" +branch = "main" +parser_angular_allowed_types = "build,chore,ci,docs,feat,fix,perf,style,refactor,test" +parser_angular_minor_types = "feat" +parser_angular_patch_types = "fix,perf" diff --git a/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py b/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py new file mode 100644 index 0000000..4b9964e --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py @@ -0,0 +1,13 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Tests for the C++ bindings.""" + +from docling_metric_hello_world_cpp._hello_world_cpp import evaluate_sample + + +def test_cpp_evaluate_sample_returns_one() -> None: + assert evaluate_sample("s1", "foo", "bar") == 1.0 From e7aef78808e6479951c7c31962c326094b43492b Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:15:12 +0100 Subject: [PATCH 04/18] feat: add hello world cpp metric wrapper --- .../docling-metric-hello-world-cpp/README.md | 34 ++++++++- .../__init__.py | 4 ++ .../hello_world_metric.py | 69 +++++++++++++++++++ .../tests/test_hello_world_metric_cpp.py | 39 +++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/hello_world_metric.py create mode 100644 packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py diff --git a/packages/docling-metric-hello-world-cpp/README.md b/packages/docling-metric-hello-world-cpp/README.md index 0f76979..ddaa90c 100644 --- a/packages/docling-metric-hello-world-cpp/README.md +++ b/packages/docling-metric-hello-world-cpp/README.md @@ -1,3 +1,35 @@ # docling-metric-hello-world-cpp -A minimal C++ (pybind11) example metric for docling-metrics. +A minimal example metric implementation demonstrating a C++ (pybind11) backend +on top of `docling-metrics-core`. + +## Overview + +This package provides a simple "Hello World" metric that: +- Accepts string payloads as input samples +- Always returns a score of `1.0` for each sample evaluation +- Demonstrates a C++ extension using pybind11 and scikit-build-core + +## Installation + +```bash +pip install docling-metric-hello-world-cpp +``` + +## Usage + +```python +from docling_metric_hello_world_cpp import HelloWorldMetric, StringInputSample + +metric = HelloWorldMetric() + +sample_a = StringInputSample(id="sample-1", payload="Hello") +sample_b = StringInputSample(id="sample-1", payload="World") + +result = metric.evaluate_sample(sample_a, sample_b) +print(result.score) # Always 1.0 +``` + +## License + +MIT diff --git a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py index 721e994..fedd00f 100644 --- a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py +++ b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/__init__.py @@ -1 +1,5 @@ """Docling metric hello world C++ package.""" + +from .hello_world_metric import HelloWorldMetric, StringInputSample + +__all__ = ["HelloWorldMetric", "StringInputSample"] diff --git a/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/hello_world_metric.py b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/hello_world_metric.py new file mode 100644 index 0000000..ee98ba8 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/docling_metric_hello_world_cpp/hello_world_metric.py @@ -0,0 +1,69 @@ +"""Hello World metric implementation using a C++ backend.""" + +from typing import Annotated, Iterable, Tuple + +from docling_metrics_core.base_types import ( + BaseAggregateResult, + BaseInputSample, + BaseMetric, + BaseSampleResult, +) +from pydantic import Field + +from ._hello_world_cpp import evaluate_sample as cpp_evaluate_sample + + +class StringInputSample(BaseInputSample): + """Input sample with a string payload.""" + + payload: Annotated[str, Field(description="The string payload for this sample")] + + +class HelloWorldSampleResult(BaseSampleResult): + """Result of evaluating a single sample pair.""" + + score: Annotated[ + float, Field(description="The evaluation score (always 1.0 for this metric)") + ] + + +class HelloWorldAggregateResult(BaseAggregateResult): + """Aggregated result from multiple sample evaluations.""" + + mean_score: Annotated[ + float, Field(description="Mean score across all evaluated samples") + ] + + +class HelloWorldMetric(BaseMetric): + """A minimal example metric that always returns 1.0, backed by C++.""" + + def evaluate_sample( # type: ignore[override] + self, sample_a: StringInputSample, sample_b: StringInputSample + ) -> HelloWorldSampleResult: + score = float( + cpp_evaluate_sample(sample_a.id, sample_a.payload, sample_b.payload) + ) + return HelloWorldSampleResult(id=sample_a.id, score=score) + + def aggregate( # type: ignore[override] + self, results: Iterable[HelloWorldSampleResult] + ) -> HelloWorldAggregateResult: + results_list = list(results) + sample_count = len(results_list) + if sample_count == 0: + return HelloWorldAggregateResult(sample_count=0, mean_score=0.0) + + total_score = sum(r.score for r in results_list) + return HelloWorldAggregateResult( + sample_count=sample_count, mean_score=total_score / sample_count + ) + + def evaluate_dataset( # type: ignore[override] + self, sample_pairs: Iterable[Tuple[StringInputSample, StringInputSample]] + ) -> HelloWorldAggregateResult: + results = [ + self.evaluate_sample(sample_a, sample_b) + for sample_a, sample_b in sample_pairs + ] + return self.aggregate(results) diff --git a/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py b/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py new file mode 100644 index 0000000..e51f1f8 --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py @@ -0,0 +1,39 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Tests for the HelloWorld C++ metric.""" + +from docling_metric_hello_world_cpp import HelloWorldMetric, StringInputSample + + +def test_evaluate_sample_returns_one() -> None: + metric = HelloWorldMetric() + sample_a = StringInputSample(id="s1", payload="foo") + sample_b = StringInputSample(id="s1", payload="bar") + + result = metric.evaluate_sample(sample_a, sample_b) + + assert result.id == "s1" + assert result.score == 1.0 + + +def test_evaluate_dataset() -> None: + metric = HelloWorldMetric() + pairs = [ + ( + StringInputSample(id="1", payload="a"), + StringInputSample(id="1", payload="b"), + ), + ( + StringInputSample(id="2", payload="c"), + StringInputSample(id="2", payload="d"), + ), + ] + + aggregate = metric.evaluate_dataset(pairs) + + assert aggregate.sample_count == 2 + assert aggregate.mean_score == 1.0 From ff7239dc8f70a6f8a9d4c46066b88fc1cac14630 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:18:39 +0100 Subject: [PATCH 05/18] chore: register hello world cpp package --- .github/workflows/cd.yml | 3 +- .../tests/test_workspace_import.py | 13 ++++++++ pyproject.toml | 2 ++ uv.lock | 32 +++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/docling-metric-hello-world-cpp/tests/test_workspace_import.py diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 72de875..bff7937 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -9,7 +9,8 @@ on: type: choice options: - docling-metrics-core - # Add future packages here + - docling-metric-hello-world + - docling-metric-hello-world-cpp jobs: code-checks: diff --git a/packages/docling-metric-hello-world-cpp/tests/test_workspace_import.py b/packages/docling-metric-hello-world-cpp/tests/test_workspace_import.py new file mode 100644 index 0000000..c1d0f5a --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/tests/test_workspace_import.py @@ -0,0 +1,13 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Workspace import test for the C++ hello world package.""" + +from docling_metric_hello_world_cpp import HelloWorldMetric + + +def test_importable() -> None: + assert HelloWorldMetric is not None diff --git a/pyproject.toml b/pyproject.toml index 873fd53..11e220a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,11 +25,13 @@ dev = [ # Workspace members — ensures `uv sync` installs all packages "docling-metrics-core", "docling-metric-hello-world", + "docling-metric-hello-world-cpp", ] [tool.uv.sources] docling-metrics-core = { workspace = true } docling-metric-hello-world = { workspace = true } +docling-metric-hello-world-cpp = { workspace = true } [tool.mypy] pretty = true diff --git a/uv.lock b/uv.lock index c3b6136..7e389bb 100644 --- a/uv.lock +++ b/uv.lock @@ -5,6 +5,7 @@ requires-python = ">=3.10, <4.0" [manifest] members = [ "docling-metric-hello-world", + "docling-metric-hello-world-cpp", "docling-metrics-core", "docling-metrics-workspace", ] @@ -411,6 +412,35 @@ test = [ { name = "pytest-xdist", specifier = "~=3.3" }, ] +[[package]] +name = "docling-metric-hello-world-cpp" +version = "0.0.1" +source = { editable = "packages/docling-metric-hello-world-cpp" } +dependencies = [ + { name = "docling-metrics-core" }, +] + +[package.dev-dependencies] +test = [ + { name = "coverage" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-dependency" }, + { name = "pytest-xdist" }, +] + +[package.metadata] +requires-dist = [{ name = "docling-metrics-core", editable = "packages/docling-metrics-core" }] + +[package.metadata.requires-dev] +test = [ + { name = "coverage", specifier = "~=7.6" }, + { name = "pytest", specifier = "~=8.3" }, + { name = "pytest-cov", specifier = ">=6.1.1" }, + { name = "pytest-dependency", specifier = "~=0.6" }, + { name = "pytest-xdist", specifier = "~=3.3" }, +] + [[package]] name = "docling-metrics-core" version = "0.0.1" @@ -449,6 +479,7 @@ source = { virtual = "." } dev = [ { name = "coverage" }, { name = "docling-metric-hello-world" }, + { name = "docling-metric-hello-world-cpp" }, { name = "docling-metrics-core" }, { name = "mypy" }, { name = "pre-commit" }, @@ -467,6 +498,7 @@ dev = [ dev = [ { name = "coverage", specifier = "~=7.6" }, { name = "docling-metric-hello-world", editable = "packages/docling-metric-hello-world" }, + { name = "docling-metric-hello-world-cpp", editable = "packages/docling-metric-hello-world-cpp" }, { name = "docling-metrics-core", editable = "packages/docling-metrics-core" }, { name = "mypy", specifier = "~=1.10" }, { name = "pre-commit", specifier = "~=3.7" }, From 374545e7d5d67850fcbf57e6d2f2250095a508f0 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:22:11 +0100 Subject: [PATCH 06/18] ci: add cibuildwheel workflow for native package --- .github/workflows/ci.yml | 6 + .github/workflows/pypi.yml | 45 ++++- .github/workflows/wheels.yml | 83 ++++++++ .../tests/test_ci_config.py | 14 ++ pyproject.toml | 4 + uv.lock | 184 ++++++++++++++++++ 6 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/wheels.yml create mode 100644 packages/docling-metric-hello-world-cpp/tests/test_ci_config.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ffbb82..069e46e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,3 +14,9 @@ jobs: uses: ./.github/workflows/job-checks.yml secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + build-wheels: + if: ${{ github.event_name == 'push' || (github.event.pull_request.head.repo.full_name != 'docling-project/docling-metrics') }} + uses: ./.github/workflows/wheels.yml + with: + publish: false + package: docling-metric-hello-world-cpp diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index a315be2..703962e 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -8,27 +8,54 @@ permissions: contents: read jobs: - build-and-publish: + meta: + runs-on: ubuntu-latest + outputs: + package: ${{ steps.meta.outputs.package }} + is_native: ${{ steps.meta.outputs.is_native }} + steps: + - uses: actions/checkout@v5 + - name: Identify package from tag + id: meta + run: | + TAG="${{ github.event.release.tag_name }}" + PKG="${TAG%-v*}" + echo "package=${PKG}" >> "$GITHUB_OUTPUT" + if grep -q "scikit_build_core" "packages/${PKG}/pyproject.toml" 2>/dev/null; then + echo "is_native=true" >> "$GITHUB_OUTPUT" + else + echo "is_native=false" >> "$GITHUB_OUTPUT" + fi + + build-and-publish-pure: + if: needs.meta.outputs.is_native == 'false' + needs: [meta] runs-on: ubuntu-latest environment: name: pypi permissions: - id-token: write # IMPORTANT: mandatory for trusted publishing + id-token: write steps: - uses: actions/checkout@v5 - name: Install uv and set the python version uses: astral-sh/setup-uv@v6 with: enable-cache: true - - name: Identify package from tag - id: meta - run: | - TAG="${{ github.event.release.tag_name }}" - PKG="${TAG%-v*}" - echo "package=${PKG}" >> "$GITHUB_OUTPUT" - name: Install dependencies run: uv sync --all-extras --all-packages - name: Build - run: uv build --package ${{ steps.meta.outputs.package }} + run: uv build --package ${{ needs.meta.outputs.package }} - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + + build-and-publish-native: + if: needs.meta.outputs.is_native == 'true' + needs: [meta] + uses: ./.github/workflows/wheels.yml + with: + publish: true + package: ${{ needs.meta.outputs.package }} + secrets: inherit + permissions: + id-token: write + contents: write diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..8f8d40d --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,83 @@ +on: + workflow_call: + inputs: + publish: + type: boolean + description: "If true, the packages will be published." + default: false + package: + type: string + description: "Package to build" + required: true + +jobs: + build_sdist: + name: Build sdist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + - name: Install build tools + run: uv pip install --group build + - name: Build sdist + run: uv build --package ${{ inputs.package }} + - uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist/ + + build_wheels: + name: Build wheels for ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + os: ["ubuntu-latest", "macos-14", "windows-2025"] + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + with: + python-version: ${{ matrix.python-version }} + - name: Set CPython tag [posix] + if: runner.os != 'Windows' + run: | + version=${{ matrix.python-version }} + echo "CIBW_BUILD=cp${version/./}" >> "$GITHUB_ENV" + - name: Set CPython tag [windows] + if: runner.os == 'Windows' + shell: pwsh + run: | + $version = "${{ matrix.python-version }}" + $cp_version = "cp$($version -replace '\.', '')" + Add-Content -Path $env:GITHUB_ENV -Value "CIBW_BUILD=$cp_version" + - name: Install build tools + run: uv pip install --group build + - name: Build wheels + working-directory: packages/${{ inputs.package }} + env: + CIBW_BUILD_VERBOSITY: 2 + run: uv run python -m cibuildwheel --output-dir ../../dist + - uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.python-version }}-${{ matrix.os }} + path: dist/ + + publish: + name: Publish to PyPI + if: inputs.publish + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/${{ inputs.package }} + permissions: + id-token: write + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist/ + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/packages/docling-metric-hello-world-cpp/tests/test_ci_config.py b/packages/docling-metric-hello-world-cpp/tests/test_ci_config.py new file mode 100644 index 0000000..475eb2e --- /dev/null +++ b/packages/docling-metric-hello-world-cpp/tests/test_ci_config.py @@ -0,0 +1,14 @@ +# +# Copyright IBM Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +"""Tests for CI configuration.""" + +from pathlib import Path + + +def test_ci_references_wheels_workflow() -> None: + ci = Path(".github/workflows/ci.yml").read_text(encoding="utf-8") + assert "wheels.yml" in ci diff --git a/pyproject.toml b/pyproject.toml index 11e220a..f4918fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,10 @@ dev = [ "docling-metric-hello-world", "docling-metric-hello-world-cpp", ] +build = [ + "cibuildwheel>=2.19", + "delocate>=0.12", +] [tool.uv.sources] docling-metrics-core = { workspace = true } diff --git a/uv.lock b/uv.lock index 7e389bb..dafb759 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,10 @@ version = 1 revision = 2 requires-python = ">=3.10, <4.0" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version < '3.11'", +] [manifest] members = [ @@ -10,6 +14,15 @@ members = [ "docling-metrics-workspace", ] +[[package]] +name = "altgraph" +version = "0.17.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -28,6 +41,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, ] +[[package]] +name = "bashlex" +version = "0.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/60/aae0bb54f9af5e0128ba90eb83d8d0d506ee8f0475c4fdda3deeda20b1d2/bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee", size = 68742, upload-time = "2023-01-18T15:21:26.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/be/6985abb1011fda8a523cfe21ed9629e397d6e06fb5bae99750402b25c95b/bashlex-0.18-py2.py3-none-any.whl", hash = "sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa", size = 69539, upload-time = "2023-01-18T15:21:24.167Z" }, +] + +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + +[[package]] +name = "build" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and os_name == 'nt'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pyproject-hooks", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, +] + [[package]] name = "certifi" version = "2026.1.4" @@ -188,6 +233,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] +[[package]] +name = "cibuildwheel" +version = "2.23.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "bashlex", marker = "python_full_version < '3.11'" }, + { name = "bracex", marker = "python_full_version < '3.11'" }, + { name = "certifi", marker = "python_full_version < '3.11'" }, + { name = "dependency-groups", marker = "python_full_version < '3.11'" }, + { name = "filelock", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "platformdirs", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/f5/2c06c8229e291e121cb26ed2efa1ba5d89053a93631d8f1d795f2dacabb8/cibuildwheel-2.23.3.tar.gz", hash = "sha256:d85dd15b7eb81711900d8129e67efb32b12f99cc00fc271ab060fa6270c38397", size = 295383, upload-time = "2025-04-26T10:41:28.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/8e/127e75e087c0a55903deb447a938e97935c6a56bfd20e6070bcc26c06d1b/cibuildwheel-2.23.3-py3-none-any.whl", hash = "sha256:0fa40073ae23a56d5f995d8405e82c1206049999bb89b92aa0835ee62ab8a891", size = 91792, upload-time = "2025-04-26T10:41:26.148Z" }, +] + +[[package]] +name = "cibuildwheel" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "bashlex", marker = "python_full_version >= '3.11'" }, + { name = "bracex", marker = "python_full_version >= '3.11'" }, + { name = "build", marker = "python_full_version >= '3.11'" }, + { name = "certifi", marker = "python_full_version >= '3.11'" }, + { name = "dependency-groups", marker = "python_full_version >= '3.11'" }, + { name = "filelock", marker = "python_full_version >= '3.11'" }, + { name = "humanize", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "patchelf", marker = "(python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "platformdirs", marker = "python_full_version >= '3.11'" }, + { name = "pyelftools", marker = "python_full_version >= '3.11'" }, + { name = "wheel", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/8c/61b0cca8be7eef6af32b0c0e62f78dc8c91572ea8870bc6c4d061f259832/cibuildwheel-3.3.1.tar.gz", hash = "sha256:ae6eafe6f7ed3bab38919e08bf3eb92085f6387c5f7a746b40cc4775a8462f9a", size = 358374, upload-time = "2026-01-05T19:58:17.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/5c/3b6689ae5c8a720c275f9832c2d1611a2a1f445bfb16701a13f5f604af5c/cibuildwheel-3.3.1-py3-none-any.whl", hash = "sha256:6d3c387e77c5850819294863eeee4e57fb7e8ecdf87b7412e763222c16e26424", size = 127164, upload-time = "2026-01-05T19:58:16.338Z" }, +] + [[package]] name = "click" version = "8.3.1" @@ -374,6 +468,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" }, ] +[[package]] +name = "delocate" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "macholib" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/21/f1679803b2582632e7b9fdd83e675d5fc661513433c0f661938414978bc7/delocate-0.13.0.tar.gz", hash = "sha256:a93e67a9f56ee01a3f7096a042231d4ac37fecac873cd5ea34ea2b4f43a8fa13", size = 240774, upload-time = "2025-01-30T06:43:43.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/dd/437e601fa9d2b6bf8507256daf52196a226c3b340261f4cde8fac8e853ea/delocate-0.13.0-py3-none-any.whl", hash = "sha256:11f7596f88984c33f76b27fe2eea7637d1ce369a9e0b6737bbc706b6426e862c", size = 238040, upload-time = "2025-01-30T06:43:41.113Z" }, +] + +[[package]] +name = "dependency-groups" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093, upload-time = "2025-05-02T00:34:29.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -476,6 +597,11 @@ version = "0.0.0" source = { virtual = "." } [package.dev-dependencies] +build = [ + { name = "cibuildwheel", version = "2.23.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cibuildwheel", version = "3.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "delocate" }, +] dev = [ { name = "coverage" }, { name = "docling-metric-hello-world" }, @@ -495,6 +621,10 @@ dev = [ [package.metadata] [package.metadata.requires-dev] +build = [ + { name = "cibuildwheel", specifier = ">=2.19" }, + { name = "delocate", specifier = ">=0.12" }, +] dev = [ { name = "coverage", specifier = "~=7.6" }, { name = "docling-metric-hello-world", editable = "packages/docling-metric-hello-world" }, @@ -583,6 +713,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + [[package]] name = "identify" version = "2.6.16" @@ -767,6 +906,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, ] +[[package]] +name = "macholib" +version = "1.16.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" }, +] + [[package]] name = "more-itertools" version = "10.8.0" @@ -882,6 +1033,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] +[[package]] +name = "patchelf" +version = "0.17.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/a3/fdd3fa938c864aa2f11dd0b7f08befeda983d2dcdee44da493c6977a653f/patchelf-0.17.2.4.tar.gz", hash = "sha256:970ee5cd8af33e5ea2099510b2f9013fa1b8d5cd763bf3fd3961281c18101a09", size = 149629, upload-time = "2025-07-23T21:16:32.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/a7/8c4f86c78ec03db954d05fd9c57a114cc3a172a2d3e4a8b949cd5ff89471/patchelf-0.17.2.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:343bb1b94e959f9070ca9607453b04390e36bbaa33c88640b989cefad0aa049e", size = 184436, upload-time = "2025-07-23T21:16:20.578Z" }, + { url = "https://files.pythonhosted.org/packages/7e/19/f7821ef31aab01fa7dc8ebe697ece88ec4f7a0fdd3155dab2dfee4b00e5c/patchelf-0.17.2.4-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689", size = 482846, upload-time = "2025-07-23T21:16:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/d1/50/107fea848ecfd851d473b079cab79107487d72c4c3cdb25b9d2603a24ca2/patchelf-0.17.2.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:2931a1b5b85f3549661898af7bf746afbda7903c7c9a967cfc998a3563f84fad", size = 477811, upload-time = "2025-07-23T21:16:25.145Z" }, + { url = "https://files.pythonhosted.org/packages/89/a9/a9a2103e159fd65bffbc21ecc5c8c36e44eb34fe53b4ef85fb6d08c2a635/patchelf-0.17.2.4-py3-none-manylinux2014_armv7l.manylinux_2_17_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:ae44cb3c857d50f54b99e5697aa978726ada33a8a6129d4b8b7ffd28b996652d", size = 431226, upload-time = "2025-07-23T21:16:26.765Z" }, + { url = "https://files.pythonhosted.org/packages/87/93/897d612f6df7cfd987bdf668425127efeff8d8e4ad8bfbab1c69d2a0d861/patchelf-0.17.2.4-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:680a266a70f60a7a4f4c448482c5bdba80cc8e6bb155a49dcc24238ba49927b0", size = 540276, upload-time = "2025-07-23T21:16:27.983Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b8/2b92d11533482bac9ee989081d6880845287751b5f528adbd6bb27667fbd/patchelf-0.17.2.4-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.musllinux_1_1_s390x.whl", hash = "sha256:d842b51f0401460f3b1f3a3a67d2c266a8f515a5adfbfa6e7b656cb3ac2ed8bc", size = 596632, upload-time = "2025-07-23T21:16:29.253Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/975d4bdb418f942b53e6187b95bd9e0d5e0488b7bc214685a1e43e2c2751/patchelf-0.17.2.4-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:7076d9e127230982e20a81a6e2358d3343004667ba510d9f822d4fdee29b0d71", size = 508281, upload-time = "2025-07-23T21:16:30.865Z" }, +] + [[package]] name = "pathspec" version = "1.0.4" @@ -1076,6 +1242,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, ] +[[package]] +name = "pyelftools" +version = "0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/ab/33968940b2deb3d92f5b146bc6d4009a5f95d1d06c148ea2f9ee965071af/pyelftools-0.32.tar.gz", hash = "sha256:6de90ee7b8263e740c8715a925382d4099b354f29ac48ea40d840cf7aa14ace5", size = 15047199, upload-time = "2025-02-19T14:20:05.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/43/700932c4f0638c3421177144a2e86448c0d75dbaee2c7936bda3f9fd0878/pyelftools-0.32-py3-none-any.whl", hash = "sha256:013df952a006db5e138b1edf6d8a68ecc50630adbd0d83a2d41e7f846163d738", size = 188525, upload-time = "2025-02-19T14:19:59.919Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -1085,6 +1260,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + [[package]] name = "pytest" version = "8.4.2" From 7feb90848e55aac2344ce1269e7c088fef567a0c Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:52:35 +0100 Subject: [PATCH 07/18] chore: create empty dummy commit From 049823b57fee9fcfb5e8cbb5b4ef38bfd25d0c35 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Wed, 28 Jan 2026 17:53:20 +0100 Subject: [PATCH 08/18] DCO Remediation Commit for Christoph Auer I, Christoph Auer , hereby add my Signed-off-by to this commit: bf071678be2211297d4f37560fc1e2801ba83dd2 I, Christoph Auer , hereby add my Signed-off-by to this commit: 15be1cf629a57972fc16f2fc4b4f3c7607bb0920 I, Christoph Auer , hereby add my Signed-off-by to this commit: e7aef78808e6479951c7c31962c326094b43492b I, Christoph Auer , hereby add my Signed-off-by to this commit: ff7239dc8f70a6f8a9d4c46066b88fc1cac14630 I, Christoph Auer , hereby add my Signed-off-by to this commit: 374545e7d5d67850fcbf57e6d2f2250095a508f0 I, Christoph Auer , hereby add my Signed-off-by to this commit: 2604cf9477da56ae29fbf28b0c3e4857897dc143 I, Christoph Auer , hereby add my Signed-off-by to this commit: 7feb90848e55aac2344ce1269e7c088fef567a0c Signed-off-by: Christoph Auer From 6fb9f8e9c479880dedc265410787672072ed1f7a Mon Sep 17 00:00:00 2001 From: Nikos Livathinos Date: Wed, 28 Jan 2026 18:13:45 +0100 Subject: [PATCH 09/18] fix: Improve the C++ example Signed-off-by: Nikos Livathinos --- packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp b/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp index 1c61fb1..1f3d5fb 100644 --- a/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp +++ b/packages/docling-metric-hello-world-cpp/cpp/hello_world.cpp @@ -3,14 +3,11 @@ namespace py = pybind11; -static double evaluate_sample( +double evaluate_sample( const std::string &id, const std::string &payload_a, const std::string &payload_b ) { - (void)id; - (void)payload_a; - (void)payload_b; return 1.0; } From 19d5327f0bb6bb15e6590e63704300ca1de94d55 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 10:03:50 +0100 Subject: [PATCH 10/18] Separate build and publish workflows on CI Signed-off-by: Christoph Auer --- .github/workflows/ci.yml | 1 - .github/workflows/pypi.yml | 20 +++++++++++++++++--- .github/workflows/wheels.yml | 22 ---------------------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 069e46e..47ea547 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,5 +18,4 @@ jobs: if: ${{ github.event_name == 'push' || (github.event.pull_request.head.repo.full_name != 'docling-project/docling-metrics') }} uses: ./.github/workflows/wheels.yml with: - publish: false package: docling-metric-hello-world-cpp diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 703962e..d0eeb11 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -48,14 +48,28 @@ jobs: - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - build-and-publish-native: + build-native: if: needs.meta.outputs.is_native == 'true' needs: [meta] uses: ./.github/workflows/wheels.yml with: - publish: true package: ${{ needs.meta.outputs.package }} - secrets: inherit + + publish-native: + name: Publish native package to PyPI + if: needs.meta.outputs.is_native == 'true' + needs: [meta, build-native] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/${{ needs.meta.outputs.package }} permissions: id-token: write contents: write + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist/ + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 8f8d40d..a621213 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,10 +1,6 @@ on: workflow_call: inputs: - publish: - type: boolean - description: "If true, the packages will be published." - default: false package: type: string description: "Package to build" @@ -63,21 +59,3 @@ jobs: name: wheels-${{ matrix.python-version }}-${{ matrix.os }} path: dist/ - publish: - name: Publish to PyPI - if: inputs.publish - needs: [build_sdist, build_wheels] - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/${{ inputs.package }} - permissions: - id-token: write - contents: write - steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist/ - - name: Publish distribution to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 From 2e972b40b88af384aa0873f3a831cfd9cb65d374 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 10:09:03 +0100 Subject: [PATCH 11/18] Use uv sync --frozen in CI Signed-off-by: Christoph Auer --- .github/workflows/cd.yml | 4 ++-- .github/workflows/pypi.yml | 2 +- .github/workflows/wheels.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index bff7937..46c13ed 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -30,7 +30,7 @@ jobs: with: enable-cache: true - name: Install dependencies - run: uv sync --only-dev + run: uv sync --frozen --only-dev - name: Check version of potential release id: version_check run: | @@ -60,7 +60,7 @@ jobs: with: enable-cache: true - name: Install dependencies - run: uv sync --only-dev + run: uv sync --frozen --only-dev - name: Run release script env: GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index d0eeb11..74a0d38 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -42,7 +42,7 @@ jobs: with: enable-cache: true - name: Install dependencies - run: uv sync --all-extras --all-packages + run: uv sync --frozen --all-extras --all-packages - name: Build run: uv build --package ${{ needs.meta.outputs.package }} - name: Publish distribution 📦 to PyPI diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index a621213..005941b 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 - name: Install build tools - run: uv pip install --group build + run: uv sync --frozen --group build - name: Build sdist run: uv build --package ${{ inputs.package }} - uses: actions/upload-artifact@v4 @@ -48,7 +48,7 @@ jobs: $cp_version = "cp$($version -replace '\.', '')" Add-Content -Path $env:GITHUB_ENV -Value "CIBW_BUILD=$cp_version" - name: Install build tools - run: uv pip install --group build + run: uv sync --frozen --group build - name: Build wheels working-directory: packages/${{ inputs.package }} env: From ff936c307f43fb729ecedacc3a1a9a6b4611965c Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 10:12:12 +0100 Subject: [PATCH 12/18] Fix cibuildwheel errors Signed-off-by: Christoph Auer --- .github/workflows/wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 005941b..ae2e581 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -39,14 +39,14 @@ jobs: if: runner.os != 'Windows' run: | version=${{ matrix.python-version }} - echo "CIBW_BUILD=cp${version/./}" >> "$GITHUB_ENV" + echo "CIBW_BUILD=cp${version/./}-*" >> "$GITHUB_ENV" - name: Set CPython tag [windows] if: runner.os == 'Windows' shell: pwsh run: | $version = "${{ matrix.python-version }}" $cp_version = "cp$($version -replace '\.', '')" - Add-Content -Path $env:GITHUB_ENV -Value "CIBW_BUILD=$cp_version" + Add-Content -Path $env:GITHUB_ENV -Value "CIBW_BUILD=$cp_version-*" - name: Install build tools run: uv sync --frozen --group build - name: Build wheels From 03a6bddef5398b4f820c6a105d573347fc152b23 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 10:18:34 +0100 Subject: [PATCH 13/18] Skip unsupported platforms Signed-off-by: Christoph Auer --- .github/workflows/wheels.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index ae2e581..9fc7f12 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -53,6 +53,7 @@ jobs: working-directory: packages/${{ inputs.package }} env: CIBW_BUILD_VERBOSITY: 2 + CIBW_SKIP: "pp* *_i686* *_s390*" run: uv run python -m cibuildwheel --output-dir ../../dist - uses: actions/upload-artifact@v4 with: From 8421c9cb4dc69c1b6abbe6b777886966a078fba0 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 14:36:08 +0100 Subject: [PATCH 14/18] Align with cibuildwheel best practices, add sdist, use pypa/chbuildwheel action Signed-off-by: Christoph Auer --- .github/workflows/pypi.yml | 5 ++-- .github/workflows/wheels.yml | 56 ++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 74a0d38..e3bbbca 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -67,9 +67,10 @@ jobs: id-token: write contents: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: + pattern: cibw-* + path: dist merge-multiple: true - path: dist/ - name: Publish distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 9fc7f12..309abd2 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -8,7 +8,7 @@ on: jobs: build_sdist: - name: Build sdist + name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -16,47 +16,41 @@ jobs: - name: Install build tools run: uv sync --frozen --group build - name: Build sdist - run: uv build --package ${{ inputs.package }} + run: uv build --sdist --package ${{ inputs.package }} - uses: actions/upload-artifact@v4 with: - name: sdist - path: dist/ + name: cibw-sdist + path: dist/*.tar.gz build_wheels: - name: Build wheels for ${{ matrix.python-version }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] - os: ["ubuntu-latest", "macos-14", "windows-2025"] + include: + - os: linux-intel + runs-on: ubuntu-latest + - os: linux-arm + runs-on: ubuntu-24.04-arm + - os: windows-intel + runs-on: windows-latest + - os: windows-arm + runs-on: windows-11-arm + - os: macos-intel + runs-on: macos-15-intel + - os: macos-arm + runs-on: macos-14 steps: - uses: actions/checkout@v5 - - uses: astral-sh/setup-uv@v6 - with: - python-version: ${{ matrix.python-version }} - - name: Set CPython tag [posix] - if: runner.os != 'Windows' - run: | - version=${{ matrix.python-version }} - echo "CIBW_BUILD=cp${version/./}-*" >> "$GITHUB_ENV" - - name: Set CPython tag [windows] - if: runner.os == 'Windows' - shell: pwsh - run: | - $version = "${{ matrix.python-version }}" - $cp_version = "cp$($version -replace '\.', '')" - Add-Content -Path $env:GITHUB_ENV -Value "CIBW_BUILD=$cp_version-*" - - name: Install build tools - run: uv sync --frozen --group build - name: Build wheels - working-directory: packages/${{ inputs.package }} + uses: pypa/cibuildwheel@v3.3.1 + with: + package-dir: packages/${{ inputs.package }} env: CIBW_BUILD_VERBOSITY: 2 - CIBW_SKIP: "pp* *_i686* *_s390*" - run: uv run python -m cibuildwheel --output-dir ../../dist + CIBW_SKIP: "pp* *_i686 *_s390x" - uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.python-version }}-${{ matrix.os }} - path: dist/ - + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl From 0f539a7bda240b4d40934b2ad80a85aef9fcfc9d Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 14:43:56 +0100 Subject: [PATCH 15/18] chore: create empty dummy commit From 4214502b07f2d5399b9f2056c286f2fc3438bc1d Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 14:46:57 +0100 Subject: [PATCH 16/18] DCO Remediation Commit for Christoph Auer I, Christoph Auer , hereby add my Signed-off-by to this commit: bf071678be2211297d4f37560fc1e2801ba83dd2 I, Christoph Auer , hereby add my Signed-off-by to this commit: 15be1cf629a57972fc16f2fc4b4f3c7607bb0920 I, Christoph Auer , hereby add my Signed-off-by to this commit: e7aef78808e6479951c7c31962c326094b43492b I, Christoph Auer , hereby add my Signed-off-by to this commit: ff7239dc8f70a6f8a9d4c46066b88fc1cac14630 I, Christoph Auer , hereby add my Signed-off-by to this commit: 374545e7d5d67850fcbf57e6d2f2250095a508f0 I, Christoph Auer , hereby add my Signed-off-by to this commit: 2604cf9477da56ae29fbf28b0c3e4857897dc143 I, Christoph Auer , hereby add my Signed-off-by to this commit: 7feb90848e55aac2344ce1269e7c088fef567a0c I, Christoph Auer , hereby add my Signed-off-by to this commit: 0f539a7bda240b4d40934b2ad80a85aef9fcfc9d Signed-off-by: Christoph Auer From fac26985eadea5222fae5e3216bf2a7890ce420a Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 14:50:17 +0100 Subject: [PATCH 17/18] Remove copyright header Signed-off-by: Christoph Auer --- .../tests/test_cpp_binding.py | 6 ------ .../tests/test_hello_world_metric_cpp.py | 6 ------ .../docling_metric_hello_world/__init__.py | 6 ------ .../docling_metric_hello_world/hello_world_metric.py | 6 ------ .../tests/test_hello_world_metric.py | 6 ------ .../docling-metrics-core/docling_metrics_core/__init__.py | 6 ------ 6 files changed, 36 deletions(-) diff --git a/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py b/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py index 4b9964e..9c25dba 100644 --- a/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py +++ b/packages/docling-metric-hello-world-cpp/tests/test_cpp_binding.py @@ -1,9 +1,3 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """Tests for the C++ bindings.""" from docling_metric_hello_world_cpp._hello_world_cpp import evaluate_sample diff --git a/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py b/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py index e51f1f8..b720815 100644 --- a/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py +++ b/packages/docling-metric-hello-world-cpp/tests/test_hello_world_metric_cpp.py @@ -1,9 +1,3 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """Tests for the HelloWorld C++ metric.""" from docling_metric_hello_world_cpp import HelloWorldMetric, StringInputSample diff --git a/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py b/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py index 3d57a90..13511bf 100644 --- a/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py +++ b/packages/docling-metric-hello-world/docling_metric_hello_world/__init__.py @@ -1,9 +1,3 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """A hello-world example metric implementation for Docling metrics.""" from docling_metric_hello_world.hello_world_metric import ( diff --git a/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py b/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py index bfd00ef..24aad39 100644 --- a/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py +++ b/packages/docling-metric-hello-world/docling_metric_hello_world/hello_world_metric.py @@ -1,9 +1,3 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """Hello World metric implementation demonstrating the docling-metrics-core interface.""" from typing import Annotated, Iterable, Tuple diff --git a/packages/docling-metric-hello-world/tests/test_hello_world_metric.py b/packages/docling-metric-hello-world/tests/test_hello_world_metric.py index 4505f5c..c310135 100644 --- a/packages/docling-metric-hello-world/tests/test_hello_world_metric.py +++ b/packages/docling-metric-hello-world/tests/test_hello_world_metric.py @@ -1,9 +1,3 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """Tests for the HelloWorld metric.""" from docling_metric_hello_world import ( diff --git a/packages/docling-metrics-core/docling_metrics_core/__init__.py b/packages/docling-metrics-core/docling_metrics_core/__init__.py index 68f23c6..2217b54 100644 --- a/packages/docling-metrics-core/docling_metrics_core/__init__.py +++ b/packages/docling-metrics-core/docling_metrics_core/__init__.py @@ -1,7 +1 @@ -# -# Copyright IBM Inc. All rights reserved. -# -# SPDX-License-Identifier: MIT -# - """Metric types and evaluation utilities for Docling document processing.""" From a362fbf0cdf07e7f4cbd7d1e66e0bde6c7167961 Mon Sep 17 00:00:00 2001 From: Christoph Auer Date: Thu, 29 Jan 2026 15:30:50 +0100 Subject: [PATCH 18/18] Restrict which platforms and python versions to build wheels for Signed-off-by: Christoph Auer --- .github/workflows/wheels.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 309abd2..02d1405 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -48,8 +48,9 @@ jobs: with: package-dir: packages/${{ inputs.package }} env: + CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-*" + CIBW_SKIP: "cp*t-* *-win32 *_i686 *_s390x" CIBW_BUILD_VERBOSITY: 2 - CIBW_SKIP: "pp* *_i686 *_s390x" - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}