From 54bbf5166842932fa7abc34a14df850594daeb5e Mon Sep 17 00:00:00 2001 From: "achyuthan.s" <113010327+Achyuthan-S@users.noreply.github.com> Date: Sun, 14 Jun 2026 08:45:29 +0400 Subject: [PATCH] [Bugfix] nightly Docker images crash with ImportError: AnthropicOutputConfig since May 28 (#44795) Signed-off-by: achyuthan.s <113010327+Achyuthan-S@users.noreply.github.com> Signed-off-by: Achyuthan S Signed-off-by: Achyuthan Sivasankar Co-authored-by: Shengqi Chen --- docker/Dockerfile | 18 ++++++- .../anthropic/test_protocol_exports.py | 50 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/entrypoints/anthropic/test_protocol_exports.py diff --git a/docker/Dockerfile b/docker/Dockerfile index d03da7bcc37..7a3cc71d339 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -548,9 +548,17 @@ RUN --mount=type=cache,target=/opt/uv/cache \ fi && \ python3 setup.py bdist_wheel --dist-dir=dist --py-limited-api=cp38 +# Record the wheel checksum so downstream stages can bust their layer cache +# when the wheel changes, without copying the wheel itself into the image. +RUN sha256sum dist/*.whl > dist/wheel.sha256 + # Copy extension wheels from extensions-build stage for later use COPY --from=extensions-build /tmp/ep_kernels_workspace/dist /tmp/ep_kernels_workspace/dist +# Record the EP kernels wheel checksum for the same cache-busting purpose. +RUN sha256sum /tmp/ep_kernels_workspace/dist/*.whl \ + > /tmp/ep_kernels_workspace/dist/wheels.sha256 + # Check the size of the wheel if RUN_WHEEL_CHECK is true COPY .buildkite/check-wheel-size.py check-wheel-size.py # sync the default value with .buildkite/check-wheel-size.py @@ -838,6 +846,11 @@ ARG PYTORCH_NIGHTLY # Install vLLM wheel first, so that torch etc will be installed. # Check whether to install torch nightly instead of release for this build. COPY --from=base /workspace/torch_lib_versions.txt torch_lib_versions.txt +# Copy only the wheel checksum (a few bytes) so a wheel change invalidates this +# install layer. The wheel itself is bind-mounted below and never enters the +# image. Without this the bind mount is not part of the layer cache key, so a +# warm BuildKit agent can skip the install and ship a stale wheel. +COPY --from=build /workspace/dist/wheel.sha256 /tmp/vllm-wheel.sha256 RUN --mount=type=bind,from=build,src=/workspace/dist,target=/vllm-workspace/dist \ --mount=type=cache,target=/opt/uv/cache \ if [ "${PYTORCH_NIGHTLY}" = "1" ]; then \ @@ -860,7 +873,10 @@ uv pip list # Pytorch now installs NVSHMEM, setting LD_LIBRARY_PATH ENV LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH -# Install EP kernels wheels (DeepEP) that have been built in the `build` stage +# Install EP kernels wheels (DeepEP) that have been built in the `build` stage. +# As with the vLLM wheel above, copy only the checksum to bust the layer cache +# and bind-mount the wheel for the actual install to keep it out of the image. +COPY --from=build /tmp/ep_kernels_workspace/dist/wheels.sha256 /tmp/ep-kernels-wheels.sha256 RUN --mount=type=bind,from=build,src=/tmp/ep_kernels_workspace/dist,target=/vllm-workspace/ep_kernels/dist \ --mount=type=cache,target=/opt/uv/cache \ uv pip install --system ep_kernels/dist/*.whl --verbose \ diff --git a/tests/entrypoints/anthropic/test_protocol_exports.py b/tests/entrypoints/anthropic/test_protocol_exports.py new file mode 100644 index 00000000000..466f40e3ccf --- /dev/null +++ b/tests/entrypoints/anthropic/test_protocol_exports.py @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +"""Regression tests for Anthropic protocol exports used by serving. + +Guards against Docker/nightly images shipping a stale protocol module that is +missing symbols imported by ``vllm.entrypoints.anthropic.serving`` (issue #44759). +""" + +import pytest + +from vllm.entrypoints.anthropic.protocol import ( + AnthropicContentBlock, + AnthropicContextManagement, + AnthropicCountTokensRequest, + AnthropicCountTokensResponse, + AnthropicDelta, + AnthropicError, + AnthropicMessagesRequest, + AnthropicMessagesResponse, + AnthropicOutputConfig, + AnthropicStreamEvent, + AnthropicUsage, +) + +pytestmark = pytest.mark.skip_global_cleanup + +SERVING_PROTOCOL_EXPORTS = ( + AnthropicContentBlock, + AnthropicContextManagement, + AnthropicCountTokensRequest, + AnthropicCountTokensResponse, + AnthropicDelta, + AnthropicError, + AnthropicMessagesRequest, + AnthropicMessagesResponse, + AnthropicOutputConfig, + AnthropicStreamEvent, + AnthropicUsage, +) + + +def test_serving_protocol_exports_are_importable(): + for export in SERVING_PROTOCOL_EXPORTS: + assert export is not None + + +def test_anthropic_output_config_instantiation(): + config = AnthropicOutputConfig() + assert config.effort is None + assert config.format is None