chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:24:33 +08:00
commit f213ec8976
2101 changed files with 494002 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# Since dependabot cannot update workflows using docker,
# we use this indirection since dependabot can update this file.
FROM rhysd/actionlint:1.7.7@sha256:887a259a5a534f3c4f36cb02dca341673c6089431057242cdc931e9f133147e9
+52
View File
@@ -0,0 +1,52 @@
name: Lint GitHub Actions workflows
on:
push:
branches:
- "dev"
- "release-**"
paths:
- '.github/actions/*.ya?ml'
- '.github/workflows/*.ya?ml'
- '.github/workflows/actionlint.*' # This workflow
pull_request:
branches:
- "dev"
- "release-**"
paths:
- '.github/actions/*.ya?ml'
- '.github/workflows/*.ya?ml'
- '.github/workflows/actionlint.*' # This workflow
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: "Checkout"
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: "Download actionlint"
run: |
docker build --tag actionlint - < .github/workflows/actionlint.dockerfile
- name: "Check workflow files"
run: |
echo "::add-matcher::.github/workflows/matchers/actionlint.json"
docker run --volume="${PWD}:/repo" --workdir=/repo actionlint -color
+142
View File
@@ -0,0 +1,142 @@
# Optional Aerospike native L2 integration (BUILD_AEROSPIKE=1 + CE Docker).
name: Aerospike integration
on:
push:
branches:
- "dev"
- "release-**"
paths:
- "csrc/storage_backends/aerospike/**"
- "lmcache/v1/distributed/l2_adapters/aerospike_l2_adapter.py"
- "lmcache/v1/storage_backend/native_clients/aerospike_client.py"
- "setup.py"
- "tests/aerospike_ce.conf.template"
- "tests/v1/distributed/test_aerospike_*"
- ".github/workflows/aerospike_integration.yml"
pull_request:
branches:
- "dev"
- "release-**"
paths:
- "csrc/storage_backends/aerospike/**"
- "lmcache/v1/distributed/l2_adapters/aerospike_l2_adapter.py"
- "lmcache/v1/storage_backend/native_clients/aerospike_client.py"
- "setup.py"
- "tests/aerospike_ce.conf.template"
- "tests/v1/distributed/test_aerospike_*"
- ".github/workflows/aerospike_integration.yml"
workflow_dispatch:
permissions:
contents: read
env:
AEROSPIKE_CLIENT_C_VERSION: "7.3.0"
AEROSPIKE_CLIENT_C_UBUNTU: "ubuntu22.04"
AEROSPIKE_CONTAINER_NAME: lmcache-aerospike-ci
jobs:
aerospike-native-l2:
name: Aerospike native L2
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
cache: pip
- name: Install Aerospike C client (prebuilt)
run: |
set -euo pipefail
DEPS="${GITHUB_WORKSPACE}/.deps"
INSTALL="${DEPS}/aerospike-install"
VERSION="${AEROSPIKE_CLIENT_C_VERSION}"
UBUNTU="${AEROSPIKE_CLIENT_C_UBUNTU}"
BASE="aerospike-client-c-libuv_${VERSION}_${UBUNTU}_x86_64"
URL="https://download.aerospike.com/artifacts/aerospike-client-c/${VERSION}/${BASE}.tgz"
mkdir -p "${DEPS}"
curl -fsSL -o "${DEPS}/${BASE}.tgz" "${URL}"
tar xzf "${DEPS}/${BASE}.tgz" -C "${DEPS}"
rm -rf "${INSTALL}"
mkdir -p "${INSTALL}"
dpkg-deb -x "${DEPS}/${BASE}/aerospike-client-c-libuv-devel_${VERSION}-${UBUNTU}_amd64.deb" "${INSTALL}"
dpkg-deb -x "${DEPS}/${BASE}/aerospike-client-c-libuv_${VERSION}-${UBUNTU}_amd64.deb" "${INSTALL}"
sudo apt-get update -qq
sudo apt-get install -y -qq libyaml-0-2 libuv1-dev
(cd "${DEPS}" && apt download libyaml-dev && \
rm -rf libyaml-install && mkdir -p libyaml-install && \
dpkg-deb -x libyaml-dev_*.deb libyaml-install)
{
echo "AEROSPIKE_INCLUDE_DIR=${INSTALL}/usr/include"
echo "AEROSPIKE_LIBRARY_DIR=${INSTALL}/usr/lib"
echo "LD_LIBRARY_PATH=${INSTALL}/usr/lib:${DEPS}/libyaml-install/usr/lib/x86_64-linux-gnu"
} >> "${GITHUB_ENV}"
- name: Build LMCache with Aerospike extension
run: |
set -euo pipefail
python -m pip install --upgrade pip wheel
python -m pip install 'setuptools>=77.0.3,<81.0.0' 'pybind11>=2.12'
python -m pip install numpy
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
export BUILD_AEROSPIKE=1
export NO_GPU_EXT=1
python -m pip install -e . --no-build-isolation
python -m pip install 'aerospike>=14.0.0,<19.0.0' pytest
python -c "from lmcache.lmcache_aerospike import LMCacheAerospikeClient; print('OK:', LMCacheAerospikeClient)"
- name: Start Aerospike CE
id: aerospike
run: |
set -euo pipefail
HOST_PORT=$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()')
CONF_DIR=$(mktemp -d)
sed "s/__ACCESS_PORT__/${HOST_PORT}/g" tests/aerospike_ce.conf.template > "${CONF_DIR}/aerospike.conf"
docker rm -f "${AEROSPIKE_CONTAINER_NAME}" 2>/dev/null || true
docker run -d \
--name "${AEROSPIKE_CONTAINER_NAME}" \
-p "${HOST_PORT}:3000" \
-v "${CONF_DIR}/aerospike.conf:/etc/aerospike/aerospike.template.conf:ro" \
aerospike/aerospike-server:latest
deadline=$((SECONDS + 90))
until docker exec "${AEROSPIKE_CONTAINER_NAME}" asinfo -v status 2>/dev/null | grep -qE 'ok|normal'; do
if (( SECONDS >= deadline )); then
docker logs "${AEROSPIKE_CONTAINER_NAME}" 2>&1 | tail -80
exit 1
fi
sleep 1
done
host_deadline=$((SECONDS + 90))
until python -c "
import aerospike
c = aerospike.client({'hosts': [('127.0.0.1', ${HOST_PORT})]})
c.connect()
info = c.info_random_node('namespace/lmcache')
c.close()
assert 'nsup-period=120' in info
"; do
if (( SECONDS >= host_deadline )); then
docker logs "${AEROSPIKE_CONTAINER_NAME}" 2>&1 | tail -80
exit 1
fi
sleep 1
done
{
echo "AEROSPIKE_TEST_HOST=127.0.0.1"
echo "AEROSPIKE_TEST_PORT=${HOST_PORT}"
echo "AEROSPIKE_TEST_NAMESPACE=lmcache"
echo "RUN_AEROSPIKE_INTEGRATION=1"
} >> "${GITHUB_ENV}"
- name: Integration tests
run: |
pytest tests/v1/distributed/test_aerospike_l2_integration.py -v --tb=short
- name: Stop Aerospike CE
if: always()
run: docker rm -f "${AEROSPIKE_CONTAINER_NAME}" 2>/dev/null || true
+17
View File
@@ -0,0 +1,17 @@
name: Label auto-merge PRs
on:
pull_request_target:
types: [ auto_merge_enabled, auto_merge_disabled ]
permissions:
pull-requests: write
jobs:
add_remove_labels:
runs-on: ubuntu-latest
steps:
- uses: ubuntudroid/automerge-labeler@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
label: 'full'
+106
View File
@@ -0,0 +1,106 @@
name: Build CLI Artifacts
# Reusable workflow that builds the pure-Python lmcache-cli wheel (no CUDA).
# Uploaded as the `release-cli-artifacts` GHA artifact.
on:
workflow_call:
pull_request:
branches:
- dev
- "release-**"
paths:
- 'lmcache/cli/**.py'
- 'pyproject_cli.toml'
- 'requirements/cli.txt'
- '.github/workflows/build_cli_artifacts.yml'
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
# Cancel older in-flight runs on the same PR / branch.
concurrency:
group: build-cli-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build-cli-artifacts:
name: Build CLI artifacts
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
# Keep stable vX.Y.Z and rc/a/b pre-release tags; strip variant tags
# (v*-cu129, v*-alpha) that yield invalid PEP 440 versions (see #3123).
- name: Remove non-release tags (keep stable + rc/a/b pre-releases)
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+((rc|a|b)[0-9]+)?$' | xargs -r git tag -d
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install build tooling
run: |
python -m pip install --upgrade pip
pip install build
- name: Clean up release artifacts
run: |
rm -rf dist/
- name: Build lmcache-cli wheel (pure Python, no native extensions)
run: |
cp pyproject_cli.toml pyproject.toml
NO_NATIVE_EXT=1 python -m build --wheel
- name: Smoke-test CLI wheel (no torch)
run: |
pip install dist/lmcache_cli-*.whl
# CLI wheel must be pure-Python: no compiled extension bundled.
if unzip -l dist/lmcache_cli-*.whl | grep -E '\.(so|pyd)$'; then
echo "::error::CLI wheel contains compiled extensions; should be pure-Python"
exit 1
fi
# Pure-Python surface must import without torch / native ops.
python -c "import lmcache"
# Native CUDA ops must be unreachable from the slim wheel.
if python -c "import lmcache.c_ops" 2>/dev/null; then
echo "::error::CLI wheel can import lmcache.c_ops; native ops must be absent from the pure-Python wheel"
exit 1
fi
echo "OK: lmcache.c_ops is not importable from the CLI wheel"
lmcache --help
# Slim-supported subcommands only (server is full-only).
for cmd in mock kvcache describe ping query bench tool trace; do
echo "::group::lmcache $cmd --help"
lmcache "$cmd" --help
echo "::endgroup::"
done
- name: Upload CLI release artifacts to GHA
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: release-cli-artifacts
path: dist/
+122
View File
@@ -0,0 +1,122 @@
name: Build CPU Artifacts
# Build LMCache with NO_GPU_EXT=1 (common C++ extensions only).
# Gates the CPU-only build path against regression.
on:
workflow_call:
pull_request:
branches:
- dev
- "release-**"
paths:
- 'setup.py'
- 'pyproject.toml'
- 'csrc/**'
- 'requirements/common.txt'
- 'requirements/build.txt'
- 'lmcache/__init__.py'
- '.github/workflows/build_cpu_artifacts.yml'
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
# Cancel stale runs per ref.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build-cpu-artifacts:
name: Build CPU artifacts
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Remove non-release tags
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+$' | xargs -r git tag -d
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install build tooling and CPU torch
run: |
python -m pip install --upgrade pip
pip install --index-url https://download.pytorch.org/whl/cpu torch
# Pre-install build deps for --no-isolation below.
pip install -r requirements/build.txt
pip install build
- name: Clean up release artifacts
run: |
rm -rf dist/
- name: Build CPU-only wheel (common C++ extensions, no GPU backend)
# --skip-dependency-check: the pre-installed CPU torch may not
# match pyproject.toml's exact build-system pin, but is ABI-
# compatible for compiling CppExtensions.
run: |
NO_GPU_EXT=1 python -m build --wheel --no-isolation \
--skip-dependency-check
- name: Verify CPU wheel contents
run: |
set -e
# Required: common C++ extensions.
for mod in native_storage_ops lmcache_redis lmcache_fs; do
if ! unzip -l dist/lmcache-*.whl | grep -qE "lmcache/${mod}\..*\.so"; then
echo "::error::missing common C++ extension: ${mod}"
exit 1
fi
done
# Forbidden: GPU extensions.
for mod in c_ops xpu_ops; do
if unzip -l dist/lmcache-*.whl | grep -qE "lmcache/${mod}\..*\.so"; then
echo "::error::unexpected GPU extension in CPU wheel: ${mod}"
exit 1
fi
done
- name: Smoke-test CPU wheel
# Run from $RUNNER_TEMP so Python resolves `lmcache` from the
# installed wheel, not the checkout on CWD.
working-directory: ${{ runner.temp }}
run: |
set -e
pip install ${{ github.workspace }}/dist/lmcache-*.whl
python -c "
import lmcache
assert lmcache.torch_device_type == 'cpu', lmcache.torch_device_type
"
python -c "import lmcache.native_storage_ops"
python -c "import lmcache.lmcache_redis"
python -c "import lmcache.lmcache_fs"
# c_ops resolves to the python_ops_fallback shim on CPU.
python -c "
import sys, lmcache, lmcache.c_ops
assert sys.modules['lmcache.c_ops'].__name__ == 'lmcache.python_ops_fallback'
"
- name: Upload CPU release artifacts to GHA
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: release-cpu-artifacts
path: dist/
+105
View File
@@ -0,0 +1,105 @@
name: Build cu129 Artifacts
# Reusable workflow that builds the cu129 CUDA 12.9 wheel via cibuildwheel
# against the pytorch manylinux cu12.9 base image.
# Uploaded as the `release-cu129-artifacts` GHA artifact.
# The cu12.9 wheel carries no local version suffix and is published to a
# dedicated v{tag}-cu12 GitHub Release (not PyPI).
on:
workflow_call:
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
build-cu129-artifacts:
name: Build cu129 artifacts
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
# Keep stable vX.Y.Z and rc/a/b pre-release tags; strip variant tags
# (v*-cu129, v*-alpha) that yield invalid PEP 440 versions (see #3123).
- name: Remove non-release tags (keep stable + rc/a/b pre-releases)
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+((rc|a|b)[0-9]+)?$' | xargs -r git tag -d
- name: Login to DockerHub
# Skip when either the username var or the token secret is
# absent. Secrets are not inherited into reusable workflows
# without `secrets: inherit`, and fork PRs never get secrets,
# so the token can legitimately be empty on some runs.
if: vars.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != ''
env:
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install build tooling
run: |
python -m pip install --upgrade pip
pip install build cibuildwheel
- name: Clean up release artifacts
run: |
rm -rf dist/
- name: Get base version for cu12.9 wheel
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
BASE="${GITHUB_REF_NAME#v}"
else
BASE=$(git tag --list 'v[0-9]*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1 | sed 's/^v//' || echo "0.0.0.dev")
fi
echo "CU129_VERSION=${BASE}" >> "$GITHUB_ENV"
- name: Build cu12.9 CUDA wheels with cibuildwheel
env:
CIBW_MANYLINUX_X86_64_IMAGE: docker.io/pytorch/manylinux2_28-builder:cuda12.9
CIBW_ENVIRONMENT: 'TORCH_CUDA_ARCH_LIST="7.0;7.5;8.0;8.6;8.9;9.0;10.0;12.0" ENABLE_CXX11_ABI=1 LMCACHE_CUDA_MAJOR=12 SETUPTOOLS_SCM_PRETEND_VERSION=${{ env.CU129_VERSION }} PIP_EXTRA_INDEX_URL=https://download.pytorch.org/whl/cu129'
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >-
auditwheel repair
--plat manylinux_2_28_x86_64
--exclude libtorch.so
--exclude libtorch_cuda.so
--exclude libtorch_python.so
--exclude libtorch_cpu.so
--exclude libc10.so
--exclude libc10_cuda.so
--exclude libcudart.so.12
-w {dest_dir} {wheel}
CIBW_TEST_COMMAND: "python -c 'import lmcache.c_ops'"
run: |
python -m cibuildwheel --output-dir dist
- name: Upload cu12.9 release artifacts to GHA
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: release-cu129-artifacts
path: dist/
+106
View File
@@ -0,0 +1,106 @@
name: Build and Deploy Online Documentation
on:
# Trigger the workflow on push request,
# for the dev branch
push:
branches:
- 'dev'
paths:
- 'docs/**'
- 'lmcache/**'
- 'examples/**'
- '.github/workflows/build_doc.yml'
jobs:
build-docs:
name: Build docs
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Full git history needed for sphinx-multiversion to detect all tags/branches
fetch-depth: 0
# Fetch all branches and tags
fetch-tags: true
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/docs.txt
- name: Build Versioned Sphinx Documentation
run: |
sphinx-multiversion docs/source output
SPHINX_LANGUAGE=zh_CN sphinx-multiversion docs/source output-zh
continue-on-error: false
- name: Setup root docs, .nojekyll, and Chinese docs
run: |
touch output/.nojekyll
# Copy dev docs to root (latest docs at root URL)
cp -r output/dev/* output/
# Remove dev directory to avoid duplication
rm -rf output/dev
# Copy Chinese dev docs to /zh_CN/ and versioned docs under /zh_CN/<version>/
mkdir -p output/zh_CN
cp -r output-zh/dev/* output/zh_CN/
rm -rf output-zh/dev
cp -r output-zh/. output/zh_CN/
rm -rf output-zh
- name: Upload doc artifacts to GHA
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: doc-artifacts
path: output/
deploy-docs:
name: Deploy docs online
runs-on: ubuntu-latest
needs: build-docs
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Fetch doc artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: doc-artifacts
path: output
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: LM-Cache-Website/lm-cache-website.github.io
token: ${{ secrets.LMCACHE_DOC }}
path: target-repo
- name: Copy Files to Target Repository
run: |
cp -r output/* target-repo/
- name: Commit and Push to Target Repository
run: |
cd target-repo
git config user.name "github-actions[bot]"
git config user.email "github-actions@github.com"
git add .
if ! git diff --cached --quiet; then
git commit -m "Deploy updated online docs"
git push https://x-access-token:${{ secrets.LMCACHE_DOC }}@github.com/LM-Cache-Website/lm-cache-website.github.io.git main --force
else
echo "No changes to commit."
fi
@@ -0,0 +1,82 @@
name: Build Main Artifacts
# Reusable workflow that builds the main release artifacts:
# - source distribution (tar ball)
# - CUDA wheel (built via cibuildwheel, latest torch)
# Uploaded as the `release-artifacts` GHA artifact.
on:
workflow_call:
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
build-artifacts:
name: Build artifacts
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
# Keep stable vX.Y.Z and rc/a/b pre-release tags; strip variant tags
# (v*-cu129, v*-alpha) that yield invalid PEP 440 versions (see #3123).
- name: Remove non-release tags (keep stable + rc/a/b pre-releases)
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+((rc|a|b)[0-9]+)?$' | xargs -r git tag -d
- name: Pin release version from tag (setuptools-scm)
if: startsWith(github.ref, 'refs/tags/v')
run: echo "SETUPTOOLS_SCM_PRETEND_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install build tooling
run: |
python -m pip install --upgrade pip
pip install build cibuildwheel
- name: Clean up release artifacts
run: |
rm -rf dist/
- name: Build source distribution (no native extensions)
run: |
NO_NATIVE_EXT=1 python -m build --sdist
- name: Build CUDA wheels with cibuildwheel
# Configuration is set in pyproject.toml
# this will build lmcache with the latest version of torch
# building from source will be required if a serving engine uses
# an earlier non-ABI compatible version of torch
env:
CIBW_TEST_COMMAND: "python -c 'import lmcache.c_ops'"
run: |
python -m cibuildwheel --output-dir dist
- name: Upload release artifacts to GHA
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: release-artifacts
path: dist/
+68
View File
@@ -0,0 +1,68 @@
name: Code Quality
on:
workflow_call:
pull_request:
# Cancel older in-flight runs on the same PR / branch.
concurrency:
group: code-quality-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
lmcache: ${{ github.event_name == 'release' || steps.filter.outputs.lmcache == 'true' }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push'
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: github.event_name == 'push' || github.event_name == 'pull_request'
id: filter
with:
filters: |
lmcache:
- '**'
- '!operator/**'
- '!.github/workflows/operator_*.yml'
pre-commit:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Check code quality
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: true
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- run: echo "::add-matcher::.github/workflows/matchers/mypy.json"
- name: Run code quality checks - lint, format, spell, & static checks
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
with:
extra_args: --all-files
+112
View File
@@ -0,0 +1,112 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: [ "dev" ]
pull_request:
branches: [ "dev" ]
schedule:
- cron: '29 4 * * 0'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
# Disable C/CPP scan for now until GPU runner available for the repo
# - language: c-cpp
# build-mode: autobuild
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: true
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
with:
category: "/language:${{matrix.language}}"
+174
View File
@@ -0,0 +1,174 @@
name: CPU Device Tests
# CPU device tests: server bench + vLLM e2e. Both job types share a
# single environment (built once per OS) to avoid re-installing vLLM.
on:
workflow_call:
workflow_dispatch:
pull_request:
branches:
- "dev"
- "release-**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
lmcache: >-
${{
github.event_name == 'workflow_dispatch' ||
github.event_name == 'workflow_call' ||
steps.filter.outputs.lmcache == 'true'
}}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push'
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: >-
github.event_name == 'push' ||
github.event_name == 'pull_request'
id: filter
with:
filters: |
lmcache:
- 'lmcache/**'
- 'pyproject.toml'
- 'setup.py'
- 'requirements/**.txt'
- '.github/workflows/cpu_device.yml'
- '.github/scripts/cpu_device_test.sh'
- '.github/scripts/cpu_server_bench_test.sh'
- '.github/scripts/cpu_vllm_e2e_test.sh'
- '.github/scripts/install_vllm_cpu.sh'
- '.github/scripts/install_lmcache_cpu.sh'
- '.github/scripts/download_model.sh'
- '.github/scripts/run-cpu-e2e-validation.sh'
- '!operator/**'
cpu-device-test:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: CPU device (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-latest]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 1
- name: Setup Python 3.12
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: |
pyproject.toml
requirements/*.txt
- name: Install FFmpeg (macOS)
if: runner.os == 'macOS'
run: |
if ! command -v ffmpeg >/dev/null 2>&1; then
brew install ffmpeg
else
echo "ffmpeg already installed: $(ffmpeg -version | head -n1)"
fi
- name: Cache opt-125m model (GitHub release)
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: opt-125m-gh-v2
- name: Install vLLM CPU (prebuilt nightly from PyPI)
run: bash .github/scripts/install_vllm_cpu.sh
- name: Install lmcache (CPU-only, no vLLM)
run: bash .github/scripts/install_lmcache_cpu.sh
- name: Download facebook/opt-125m (GitHub release)
run: bash .github/scripts/download_opt125m_github.sh
- name: Server bench — LMCache-driven
run: |
LMCACHE_BENCH_TRANSFER_MODE=lmcache_driven \
LMCACHE_HTTP_PORT_BENCH=18080 \
LMCACHE_ZMQ_PORT_BENCH=15555 \
bash .github/scripts/cpu_device_test.sh server_bench
- name: Server bench — Engine-driven
run: |
LMCACHE_BENCH_TRANSFER_MODE=engine_driven \
LMCACHE_HTTP_PORT_BENCH=18081 \
LMCACHE_ZMQ_PORT_BENCH=15556 \
bash .github/scripts/cpu_device_test.sh server_bench
- name: vLLM e2e — Engine-driven (pickle)
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
LMCACHE_E2E_TRANSPORT_MODE=pickle \
LMCACHE_HTTP_PORT_E2E=18082 \
LMCACHE_ZMQ_PORT_E2E=15557 \
VLLM_PORT_E2E=18000 \
bash .github/scripts/cpu_device_test.sh vllm_e2e
# POSIX SHM transport is Linux-only.
- name: vLLM e2e — Engine-driven (shm)
if: runner.os == 'Linux'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
LMCACHE_E2E_TRANSPORT_MODE=shm \
LMCACHE_HTTP_PORT_E2E=18083 \
LMCACHE_ZMQ_PORT_E2E=15558 \
VLLM_PORT_E2E=18001 \
bash .github/scripts/cpu_device_test.sh vllm_e2e
- name: vLLM e2e — Engine-driven
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
LMCACHE_E2E_TRANSPORT_MODE=engine_driven \
LMCACHE_HTTP_PORT_E2E=18084 \
LMCACHE_ZMQ_PORT_E2E=15559 \
VLLM_PORT_E2E=18002 \
bash .github/scripts/cpu_device_test.sh vllm_e2e
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: cpu-device-logs-${{ matrix.os }}
path: |
/tmp/cpu_device_*.log
if-no-files-found: ignore
@@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "actionlint",
"pattern": [
{
"regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$",
"file": 1,
"line": 2,
"column": 3,
"message": 4,
"code": 5
}
]
}
]
}
+16
View File
@@ -0,0 +1,16 @@
{
"problemMatcher": [
{
"owner": "mypy",
"pattern": [
{
"regexp": "^(.+):(\\d+):\\s(error|warning):\\s(.+)$",
"file": 1,
"line": 2,
"severity": 3,
"message": 4
}
]
}
]
}
+221
View File
@@ -0,0 +1,221 @@
name: Build nightly container image of latest code
on:
schedule:
- cron: '30 7 * * *'
workflow_dispatch:
permissions:
contents: read
jobs:
nightly-wheels:
name: Build and publish nightly wheels
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Remove non-release tags
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+$' | xargs -r git tag -d
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Login to DockerHub
if: vars.DOCKERHUB_USERNAME != ''
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Install build tooling
run: |
python -m pip install --upgrade pip
pip install cibuildwheel
- name: Build cu13.0 nightly wheel
env:
CIBW_TEST_COMMAND: "python -c 'import lmcache.c_ops'"
run: |
python -m cibuildwheel --output-dir dist/cu130
- name: Publish cu13.0 wheels to rolling nightly release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release delete nightly --yes --cleanup-tag --repo ${{ github.repository }} 2>/dev/null || true
gh release create nightly \
--repo ${{ github.repository }} \
--title "Nightly $(date +'%Y-%m-%d') · CUDA 13.0" \
--notes "Nightly CUDA 13.0 wheels built from \`dev\` on $(date +'%Y-%m-%d').
\`\`\`
uv pip install lmcache --pre \\
--extra-index-url https://download.pytorch.org/whl/cu130 \\
--find-links https://github.com/${{ github.repository }}/releases/expanded_assets/nightly \\
--index-strategy unsafe-best-match
\`\`\`" \
--prerelease \
dist/cu130/*.whl
- name: Build cu12.9 nightly wheel
env:
CIBW_MANYLINUX_X86_64_IMAGE: docker.io/pytorch/manylinux2_28-builder:cuda12.9
CIBW_ENVIRONMENT: 'TORCH_CUDA_ARCH_LIST="7.0;7.5;8.0;8.6;8.9;9.0;10.0" ENABLE_CXX11_ABI=1 LMCACHE_CUDA_MAJOR=12 PIP_EXTRA_INDEX_URL=https://download.pytorch.org/whl/cu129'
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >-
auditwheel repair
--plat manylinux_2_28_x86_64
--exclude libtorch.so
--exclude libtorch_cuda.so
--exclude libtorch_python.so
--exclude libtorch_cpu.so
--exclude libc10.so
--exclude libc10_cuda.so
--exclude libcudart.so.12
-w {dest_dir} {wheel}
CIBW_TEST_COMMAND: "python -c 'import lmcache.c_ops'"
run: |
python -m cibuildwheel --output-dir dist/cu129
- name: Publish cu12.9 wheels to rolling nightly-cu129 release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release delete nightly-cu129 --yes --cleanup-tag --repo ${{ github.repository }} 2>/dev/null || true
gh release create nightly-cu129 \
--repo ${{ github.repository }} \
--title "Nightly $(date +'%Y-%m-%d') · CUDA 12.9" \
--notes "Nightly CUDA 12.9 wheels built from \`dev\` on $(date +'%Y-%m-%d').
\`\`\`
uv pip install lmcache --pre \\
--extra-index-url https://download.pytorch.org/whl/cu129 \\
--find-links https://github.com/${{ github.repository }}/releases/expanded_assets/nightly-cu129 \\
--index-strategy unsafe-best-match
\`\`\`" \
--prerelease \
dist/cu129/*.whl
nightly-build:
name: Build image
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Login to DockerHub
if: vars.DOCKERHUB_USERNAME != ''
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
- name: Remove non-release tags
run: git tag -l | grep -vE '^v[0-9]+\.[0-9]+\.[0-9]+$' | xargs -r git tag -d
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Get the current date
run: |
echo "NOW=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
- name: Build lmcache/vllm-openai container image (cu13 nightly)
run: |
docker system prune -f --all
docker builder prune -af
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target image-build \
--tag lmcache/vllm-openai:latest-nightly --tag lmcache/vllm-openai:nightly-${{ env.NOW }} \
--file docker/Dockerfile .
- name: Push lmcache/vllm-openai container image to DockerHub
run: |
docker push lmcache/vllm-openai:latest-nightly
docker push lmcache/vllm-openai:nightly-${{ env.NOW }}
- name: Build lmcache/standalone container image (cu13 nightly)
run: |
docker system prune -f --all
docker builder prune -af
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target lmcache-final \
--tag lmcache/standalone:nightly --tag lmcache/standalone:nightly-${{ env.NOW }} \
--file docker/Dockerfile.standalone .
- name: Push lmcache/standalone container image to DockerHub
run: |
docker push lmcache/standalone:nightly
docker push lmcache/standalone:nightly-${{ env.NOW }}
- name: Build lmcache/vllm-openai container image (cu12.9 nightly)
run: |
docker system prune -f --all
docker builder prune -af
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 --build-arg UBUNTU_VERSION=24.04 \
--target image-build \
--tag lmcache/vllm-openai:latest-nightly-cu129 --tag lmcache/vllm-openai:nightly-${{ env.NOW }}-cu129 \
--file docker/Dockerfile .
- name: Push lmcache/vllm-openai cu12.9 nightly image to DockerHub
run: |
docker push lmcache/vllm-openai:latest-nightly-cu129
docker push lmcache/vllm-openai:nightly-${{ env.NOW }}-cu129
- name: Build lmcache/standalone container image (cu12.9 nightly)
run: |
docker system prune -f --all
docker builder prune -af
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 --build-arg UBUNTU_VERSION=24.04 \
--target lmcache-final \
--tag lmcache/standalone:nightly-cu129 --tag lmcache/standalone:nightly-${{ env.NOW }}-cu129 \
--file docker/Dockerfile.standalone .
- name: Push lmcache/standalone cu12.9 nightly image to DockerHub
run: |
docker push lmcache/standalone:nightly-cu129
docker push lmcache/standalone:nightly-${{ env.NOW }}-cu129
+76
View File
@@ -0,0 +1,76 @@
name: "Operator: CI"
on:
push:
branches:
- dev
- "release-**"
pull_request:
branches:
- dev
- "release-**"
permissions:
contents: read
defaults:
run:
shell: bash
working-directory: operator
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
operator: ${{ steps.filter.outputs.operator == 'true' }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push'
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: github.event_name == 'push' || github.event_name == 'pull_request'
id: filter
with:
filters: |
operator:
- 'operator/**'
- '.github/workflows/operator_ci.yml'
ci:
needs: changes
if: needs.changes.outputs.operator == 'true'
name: Format, lint, and test
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: operator/go.mod
- name: Check formatting
run: |
test -z "$(gofmt -l .)" || { gofmt -d .; exit 1; }
- name: Check linter configuration
run: make lint-config
- name: Lint
run: make lint
- name: Test
run: |
go mod tidy
make test
+80
View File
@@ -0,0 +1,80 @@
name: "Operator: Nightly"
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
env:
IMAGE_NAME: lmcache/lmcache-operator
permissions:
contents: write
defaults:
run:
shell: bash
working-directory: operator
jobs:
nightly:
name: Build, push, and release nightly
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Compute nightly tag
id: tag
run: |
DATE=$(date -u +%Y-%m-%d)
echo "TAG=nightly-${DATE}" >> "$GITHUB_OUTPUT"
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: operator
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.TAG }}
${{ env.IMAGE_NAME }}:nightly
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: operator/go.mod
- name: Build installer manifest
run: make build-installer IMG=${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.TAG }}
- name: Update nightly release
uses: softprops/action-gh-release@v2
with:
tag_name: operator-nightly-latest
name: "Operator Nightly Latest (${{ steps.tag.outputs.TAG }})"
prerelease: true
make_latest: false
body: |
Automated nightly operator build from `dev` branch.
**Image:** `${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.TAG }}`
```bash
kubectl apply -f https://github.com/${{ github.repository }}/releases/download/operator-nightly-latest/install.yaml
```
files: operator/dist/install.yaml
+100
View File
@@ -0,0 +1,100 @@
name: "Operator: Release"
on:
release:
types: [published]
env:
IMAGE_NAME: lmcache/lmcache-operator
permissions:
contents: write
defaults:
run:
shell: bash
working-directory: operator
jobs:
release:
name: Build, push, and release
# Filter out main lmcache `v*` releases — only fire for operator-prefixed tags.
if: startsWith(github.ref_name, 'operator-v')
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract version from tag
id: version
run: |
# Strip "operator-" prefix from tag to get version (e.g. operator-v0.4.8rc1 -> v0.4.8rc1).
VERSION="${GITHUB_REF_NAME#operator-}"
echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
# Pre-releases (rc/alpha/beta) get the floating ":latest-rc" tag and must
# never move the stable ":latest" image tag or the "operator-latest" alias.
# Detect from the version string rather than the GitHub "pre-release" flag
# so behavior is robust whether or not that box is ticked in the UI.
if [[ "${VERSION}" =~ (rc|alpha|beta) ]]; then
echo "FLOATING_TAG=latest-rc" >> "$GITHUB_OUTPUT"
echo "IS_PRERELEASE=true" >> "$GITHUB_OUTPUT"
else
echo "FLOATING_TAG=latest" >> "$GITHUB_OUTPUT"
echo "IS_PRERELEASE=false" >> "$GITHUB_OUTPUT"
fi
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: operator
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
${{ env.IMAGE_NAME }}:${{ steps.version.outputs.FLOATING_TAG }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: operator/go.mod
- name: Build installer manifest
run: make build-installer IMG=${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
- name: Attach installer to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: operator/dist/install.yaml
- name: Update latest release alias
# Skip for pre-releases: an rc/alpha/beta must not repoint the stable
# "operator-latest" alias (or its install.yaml) at a pre-release build.
if: steps.version.outputs.IS_PRERELEASE == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: operator-latest
name: "Operator Latest (${{ steps.version.outputs.VERSION }})"
make_latest: false
body: |
Points to the latest stable operator release: `${{ steps.version.outputs.VERSION }}`
**Image:** `${{ env.IMAGE_NAME }}:latest`
```bash
kubectl apply -f https://github.com/${{ github.repository }}/releases/download/operator-latest/install.yaml
```
files: operator/dist/install.yaml
+52
View File
@@ -0,0 +1,52 @@
name: PR Full Build
# Build all release artifacts (sdist + CUDA wheel, CLI wheel, cu129 wheel)
# on PRs carrying the `full` label. The `full` label is added automatically
# by automerge-labeler.yml when auto-merge is enabled on a code-change PR,
# so this acts as a pre-merge sanity check that wheels still build.
#
# No tests, no code-quality checks, no publishing — those are covered by
# test.yml / code_quality_checks.yml (per-PR) and publish.yml (on merge).
on:
pull_request:
branches:
- dev
- "release-**"
types: [opened, synchronize, reopened, labeled]
# Cancel older in-flight runs on the same PR. github.ref is
# refs/pull/<N>/merge on pull_request events, so each PR gets its own group.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# Each build runs independently — a failure in one does not skip the
# others, matching publish.yml's isolation model.
build-main:
name: Build main
if: contains(github.event.pull_request.labels.*.name, 'full')
uses: ./.github/workflows/build_main_artifacts.yml
build-cli:
name: Build CLI
if: contains(github.event.pull_request.labels.*.name, 'full')
uses: ./.github/workflows/build_cli_artifacts.yml
build-cpu:
name: Build CPU
if: contains(github.event.pull_request.labels.*.name, 'full')
uses: ./.github/workflows/build_cpu_artifacts.yml
build-cu129:
name: Build cu129
if: contains(github.event.pull_request.labels.*.name, 'full')
uses: ./.github/workflows/build_cu129_artifacts.yml
# Fork PRs won't expose DOCKERHUB_TOKEN even with `inherit`; the login
# step in build_cu129_artifacts.yml guards on the token being non-empty.
secrets: inherit
+531
View File
@@ -0,0 +1,531 @@
name: Build and Publish to PyPI
on:
# Trigger the workflow on push to the dev and any release branches,
# and on release tag pushes / GitHub release publication.
# Per-PR verification lives in test.yml, code_quality_checks.yml, and
# pr_full_build.yml — this workflow is the release pipeline.
push:
branches:
- dev
- "release-**"
tags:
- "v*"
release:
types:
- published
env:
LC_ALL: en_US.UTF-8
# Cancel superseded runs on the same ref, but never abort a tag/release publish.
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') && github.event_name != 'release' }}
defaults:
run:
shell: bash
permissions:
contents: read
pull-requests: read
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
# Skip the whole pipeline for operator releases (tags like `operator-v*`).
# operator_release.yml handles those; this workflow is lmcache-only.
lmcache: ${{ !startsWith(github.ref_name, 'operator-') && (github.event_name == 'release' || startsWith(github.ref, 'refs/tags/') || steps.filter.outputs.lmcache == 'true') }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
id: filter
with:
filters: |
lmcache:
- '**.py'
- 'pyproject.toml'
- 'pyproject_cli.toml'
- 'requirements/**.txt'
- 'docker/**'
- '.github/workflows/test.yml'
- '.github/workflows/code_quality_checks.yml'
- '.github/workflows/publish.yml'
- '.github/workflows/build_main_artifacts.yml'
- '.github/workflows/build_cli_artifacts.yml'
- '.github/workflows/build_cu129_artifacts.yml'
- '!operator/**'
# Build each artifact group in its own reusable workflow so that a
# failure in one (e.g. cu129) does not block publishing of the others.
build-main:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build main
uses: ./.github/workflows/build_main_artifacts.yml
build-cli:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build CLI
uses: ./.github/workflows/build_cli_artifacts.yml
build-cu129:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build cu129
uses: ./.github/workflows/build_cu129_artifacts.yml
# DockerHub creds needed for the cu12.9 manylinux image pull;
# reusable workflows do not auto-inherit secrets.
secrets: inherit
# Run tests and code quality checks before publishing
test:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Run tests
uses: ./.github/workflows/test.yml
code-quality:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Run code quality checks
uses: ./.github/workflows/code_quality_checks.yml
# Push to Test PyPI when:
# - a new GitHub release is published
# - a commit lands on dev (PR merged)
publish-test-pypi:
name: Publish packages to test.pypi.org
# Require upstream jobs to have succeeded so a cancelled/skipped build
# doesn't trigger a publish that fails on the missing artifact.
if: |
needs.build-main.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' &&
github.event_name == 'push' && github.ref == 'refs/heads/dev'
permissions:
contents: read
# see https://docs.pypi.org/trusted-publishers/
id-token: write
runs-on: ubuntu-latest
needs: [changes, build-main, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-artifacts
path: dist
- name: Upload to Test PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
publish-cli-test-pypi:
name: Publish lmcache-cli packages to test.pypi.org
if: |
needs.build-cli.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' &&
github.event_name == 'push' && github.ref == 'refs/heads/dev'
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
needs: [changes, build-cli, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch CLI release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cli-artifacts
path: dist
- name: Upload lmcache-cli to Test PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
# Push to PyPI (production) when:
# - a new GitHub release is created
publish-pypi:
name: Publish release to pypi.org
if: |
needs.build-main.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
# see https://docs.pypi.org/trusted-publishers/
id-token: write
# allow gh release upload
contents: write
runs-on: ubuntu-latest
needs: [changes, build-main, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-artifacts
path: dist
- name: Upload release artifacts to GitHub release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload '${{ github.ref_name }}' dist/* --repo '${{ github.repository }}'
- name: Upload to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
verbose: true
publish-cli-pypi:
name: Publish lmcache-cli release to pypi.org
if: |
needs.build-cli.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
id-token: write
contents: write
runs-on: ubuntu-latest
needs: [changes, build-cli, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch CLI release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cli-artifacts
path: dist
- name: Upload lmcache-cli release artifacts to GitHub release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload '${{ github.ref_name }}' dist/* --repo '${{ github.repository }}'
- name: Upload lmcache-cli to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
verbose: true
# Publish cu12.9 wheel to GitHub Release (not PyPI) when a release is published
publish-cu129-github-release:
name: Publish cu129 wheel to GitHub Release
if: |
needs.build-cu129.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
contents: write
runs-on: ubuntu-latest
needs: [changes, build-cu129, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch cu12.9 release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cu129-artifacts
path: dist
- name: Publish cu12.9 wheel to dedicated GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create "${{ github.ref_name }}-cu129" \
--repo ${{ github.repository }} \
--latest=false \
--title "Release ${{ github.ref_name }} · CUDA 12.9" \
--notes "CUDA 12.9 wheel for LMCache ${{ github.ref_name }}.
\`\`\`
VERSION=${{ github.ref_name }}
uv pip install lmcache==${VERSION#v} \\
--extra-index-url https://download.pytorch.org/whl/cu129 \\
--find-links https://github.com/${{ github.repository }}/releases/expanded_assets/${VERSION}-cu129 \\
--index-strategy unsafe-best-match
\`\`\`" \
dist/*.whl
# Build container image and push to DockerHub when:
# - a new stable GitHub release is created (non-RC)
publish-image:
name: Publish image to DockerHub
if: |
needs.publish-pypi.result == 'success' &&
!cancelled() &&
github.repository_owner == 'LMCache' && github.event.action == 'published' &&
!contains(github.ref_name, 'rc')
runs-on: ubuntu-latest
needs: [publish-pypi, publish-cu129-github-release]
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Resolve the release tag that triggered this run
run: |
echo "LATEST_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV"
- name: Build lmcache/vllm-openai container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_TAG }} \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target image-release \
--tag lmcache/vllm-openai:latest --tag lmcache/vllm-openai:${{ env.LATEST_TAG }} \
--file docker/Dockerfile .
# `lmcache --help` exercises eager CLI subcommand discovery,
# so a missing runtime dep fails the build before the image ships.
- name: Smoke test lmcache/vllm-openai (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }} --help
- name: Push lmcache/vllm-openai container image to DockerHub
run: |
docker push lmcache/vllm-openai:latest
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/vllm-openai:lightweight
run: |
docker build \
--tag lmcache/vllm-openai:lightweight --tag lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight \
--file docker/Dockerfile.lightweight .
- name: Smoke test lmcache/vllm-openai:lightweight
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight --help
- name: Push lmcache/vllm-openai:lightweight image to DockerHub
run: |
docker push lmcache/vllm-openai:lightweight
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/standalone container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target lmcache-final \
--tag lmcache/standalone:latest --tag lmcache/standalone:${{ env.LATEST_TAG }} \
--tag lmcache/standalone:latest-cu130 --tag lmcache/standalone:${{ env.LATEST_TAG }}-cu130 \
--file docker/Dockerfile.standalone .
- name: Smoke test lmcache/standalone (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/standalone:${{ env.LATEST_TAG }} --help
- name: Push lmcache/standalone container image to DockerHub
run: |
docker push lmcache/standalone:latest
docker push lmcache/standalone:${{ env.LATEST_TAG }}
docker push lmcache/standalone:latest-cu130
docker push lmcache/standalone:${{ env.LATEST_TAG }}-cu130
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/vllm-openai container image (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_TAG }} \
--target image-release-cu129 \
--tag lmcache/vllm-openai:latest-cu129 --tag lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129 \
--file docker/Dockerfile .
- name: Smoke test lmcache/vllm-openai (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129 --help
- name: Push lmcache/vllm-openai cu129 container image to DockerHub
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker push lmcache/vllm-openai:latest-cu129
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/standalone container image (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 \
--target lmcache-final \
--tag lmcache/standalone:latest-cu129 --tag lmcache/standalone:${{ env.LATEST_TAG }}-cu129 \
--file docker/Dockerfile.standalone .
- name: Smoke test lmcache/standalone (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker run --rm --entrypoint lmcache lmcache/standalone:${{ env.LATEST_TAG }}-cu129 --help
- name: Push lmcache/standalone cu129 container image to DockerHub
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker push lmcache/standalone:latest-cu129
docker push lmcache/standalone:${{ env.LATEST_TAG }}-cu129
# Build RC container image and push to DockerHub when:
# - a new RC GitHub release is created (e.g. v0.4.8rc1)
# Only pushes lmcache/vllm-openai:<tag> — does not touch latest or other image variants.
publish-image-rc:
name: Publish RC image to DockerHub
if: |
needs.publish-pypi.result == 'success' &&
!cancelled() &&
github.repository_owner == 'LMCache' && github.event.action == 'published' &&
contains(github.ref_name, 'rc')
runs-on: ubuntu-latest
needs: [publish-pypi]
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Get the release tag
run: |
REF="${{ github.ref_name }}"
[[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+rc[0-9]+$ ]] && echo "LATEST_RC_TAG=$REF" >> "$GITHUB_ENV"
- name: Build lmcache/vllm-openai container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_RC_TAG }} \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target image-release \
--tag lmcache/vllm-openai:${{ env.LATEST_RC_TAG }} \
--file docker/Dockerfile .
- name: Smoke test lmcache/vllm-openai (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_RC_TAG }} --help
- name: Push lmcache/vllm-openai RC image to DockerHub
run: |
docker push lmcache/vllm-openai:${{ env.LATEST_RC_TAG }}
+84
View File
@@ -0,0 +1,84 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '23 8 * * 6'
push:
branches: [ "dev" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
+51
View File
@@ -0,0 +1,51 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
env:
LC_ALL: en_US.UTF-8
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: true
egress-policy: audit
- name: "Stale Action"
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
stale-issue-label: 'stale'
stale-issue-message: >
This issue has been automatically marked as stale because it has not had activity within 60 days.
It will be automatically closed if no further activity occurs within 30 days.
close-issue-message: >
This issue has been automatically closed due to inactivity.
Please feel free to reopen if you feel it is still relevant!
days-before-issue-stale: 60
days-before-issue-close: 30
stale-pr-label: 'stale'
stale-pr-message: >
This pull request has been automatically marked as stale because it has not had activity within 60 days.
It will be automatically closed if no further activity occurs within 30 days.
close-pr-message: >
This pull request has been automatically closed due to inactivity.
Please feel free to reopen if you intend to continue working on it!
days-before-pr-stale: 60
days-before-pr-close: 30
operations-per-run: 300
+147
View File
@@ -0,0 +1,147 @@
name: Sync Torch Version
on:
workflow_dispatch:
schedule:
- cron: "0 2 * * 1"
permissions:
contents: write
pull-requests: write
concurrency:
group: sync-torch-version
cancel-in-progress: false
jobs:
sync:
name: Sync torch pin with vLLM
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out dev branch
uses: actions/checkout@v4
with:
ref: dev
token: ${{ github.token }}
fetch-depth: 0
- name: Sync torch version with vLLM
id: sync
shell: bash
run: |
set -Eeuo pipefail
readonly UPSTREAM_URL="https://raw.githubusercontent.com/vllm-project/vllm/main/pyproject.toml"
readonly TARGET_FILE="pyproject.toml"
tmp_upstream="$(mktemp)"
tmp_output="$(mktemp)"
cleanup() {
rm -f "$tmp_upstream" "$tmp_output"
}
trap cleanup EXIT
echo "Fetching upstream pyproject.toml from vLLM..."
curl \
--fail \
--show-error \
--silent \
--location \
--retry 3 \
--retry-delay 2 \
--connect-timeout 10 \
"$UPSTREAM_URL" \
-o "$tmp_upstream"
extract_torch_version() {
local file="$1"
sed -nE 's/.*torch[[:space:]]*==[[:space:]]*([0-9]+(\.[0-9]+){1,2}).*/\1/p' "$file"
}
mapfile -t upstream_matches < <(extract_torch_version "$tmp_upstream")
mapfile -t current_matches < <(extract_torch_version "$TARGET_FILE")
if [[ "${#upstream_matches[@]}" -ne 1 ]]; then
echo "Expected exactly one torch pin in upstream file, found ${#upstream_matches[@]}"
printf 'Matches: %s\n' "${upstream_matches[@]:-<none>}"
exit 1
fi
if [[ "${#current_matches[@]}" -ne 1 ]]; then
echo "Expected exactly one torch pin in ${TARGET_FILE}, found ${#current_matches[@]}"
printf 'Matches: %s\n' "${current_matches[@]:-<none>}"
exit 1
fi
upstream="${upstream_matches[0]}"
current="${current_matches[0]}"
echo "Current torch version : $current"
echo "Upstream torch version: $upstream"
{
echo "current=$current"
echo "upstream=$upstream"
} >> "$GITHUB_OUTPUT"
if [[ "$upstream" == "$current" ]]; then
echo "Torch version already up to date."
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
awk -v new_ver="$upstream" '
BEGIN { updated = 0 }
{
if ($0 ~ /torch[[:space:]]*==[[:space:]]*[0-9]+(\.[0-9]+){1,2}/) {
sub(/torch[[:space:]]*==[[:space:]]*[0-9]+(\.[0-9]+){1,2}/, "torch==" new_ver)
updated++
}
print
}
END {
if (updated != 1) {
printf("Expected to update exactly one torch pin, updated %d\n", updated) > "/dev/stderr"
exit 1
}
}
' "$TARGET_FILE" > "$tmp_output"
mv "$tmp_output" "$TARGET_FILE"
if git diff --quiet -- "$TARGET_FILE"; then
echo "No file change detected after update."
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Create pull request
if: steps.sync.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ github.token }}
branch: automation/sync-torch-version
base: dev
delete-branch: true
signoff: true
add-paths: |
pyproject.toml
commit-message: "[Build] sync torch version with vLLM (${{
steps.sync.outputs.upstream }})"
title: "[Build] sync torch version with vLLM (${{
steps.sync.outputs.upstream }})"
body: |
Auto-sync the pinned torch version in `pyproject.toml` with vLLM.
- Current: `${{ steps.sync.outputs.current }}`
- Upstream: `${{ steps.sync.outputs.upstream }}`
This PR was generated automatically by GitHub Actions.
labels: |
dependencies
automation
+170
View File
@@ -0,0 +1,170 @@
name: Test
on:
workflow_call:
workflow_dispatch:
pull_request:
branches:
- "dev"
- "release-**"
env:
LC_ALL: en_US.UTF-8
PYTHONHASHSEED: 0
defaults:
run:
shell: bash
# Cancel older in-flight runs on the same PR / branch.
concurrency:
group: test-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
lmcache: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'release' || steps.filter.outputs.lmcache == 'true' }}
raw_block: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'release' || steps.filter.outputs.raw_block == 'true' }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push'
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: github.event_name == 'push' || github.event_name == 'pull_request'
id: filter
with:
filters: |
lmcache:
- '**.py'
- 'pyproject.toml'
- 'requirements/**.txt'
- '.github/workflows/test.yml'
- '!operator/**'
raw_block:
- 'rust/raw_block/**'
- 'lmcache/v1/storage_backend/raw_block/**'
- 'lmcache/v1/distributed/l2_adapters/raw_block_l2_adapter.py'
- 'lmcache/v1/multiprocess/**'
- 'tests/v1/storage_backend/test_raw_block*.py'
- 'tests/v1/storage_backend/raw_block_test_utils.py'
- 'tests/v1/multiprocess/test_raw_block_l2_adapter.py'
- '.github/workflows/test.yml'
test:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: "test: ${{ matrix.python }} on ${{ matrix.platform }}"
runs-on: "${{ matrix.platform }}"
strategy:
matrix:
python:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
platform:
- "ubuntu-latest"
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# https://github.com/actions/checkout/issues/249
fetch-depth: 0
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: ${{ matrix.python }}
cache: pip
cache-dependency-path: |
**/pyproject.toml
**/requirements/*.txt
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install vllm
python -m pip install -r requirements/test.txt
python -m pip install -r requirements/common.txt
- name: "Run non-CUDA unit tests"
run: |
pytest --ignore=tests/disagg \
--ignore=tests/v1/multiprocess/ \
--ignore=tests/v1/distributed/ \
--ignore=tests/v1/mp_observability/ \
--ignore=tests/skipped \
--ignore=tests/cli/commands/trace \
--ignore=tests/v1/storage_backend/test_eic.py \
--ignore=tests/v1/storage_backend/test_p2p_backend_with_controller.py
raw-block-tests:
needs: changes
if: needs.changes.outputs.raw_block == 'true'
name: raw-block temp-file tests
runs-on: ubuntu-latest
env:
LMCACHE_LOG_LEVEL: CRITICAL
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Setup Python 3.11
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.11"
cache: pip
cache-dependency-path: |
**/pyproject.toml
**/requirements/*.txt
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install maturin
python -m pip install -r requirements/test.txt
python -m pip install -r requirements/common.txt
- name: Build raw-block Rust extension
run: |
python -m maturin build --release \
--manifest-path rust/raw_block/Cargo.toml \
--out .pytest-tmp/raw-block-dist
python -m pip install .pytest-tmp/raw-block-dist/*.whl
- name: Run raw-block tests
run: |
pytest -q --basetemp=.pytest-tmp/raw-block \
tests/v1/storage_backend/test_raw_block_device.py \
tests/v1/storage_backend/test_raw_block_core.py \
tests/v1/multiprocess/test_raw_block_l2_adapter.py
+109
View File
@@ -0,0 +1,109 @@
name: Update Chinese Documentation Translations
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch:
inputs:
max_entries:
description: "Maximum entries to translate in this run"
required: false
default: "50"
concurrency:
group: update-chinese-docs
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
jobs:
translate-docs:
name: Update Chinese docs translations
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: dev
- name: Setup Python 3.13
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.13"
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/docs.txt
- name: Reuse translations from open PR
env:
GH_TOKEN: ${{ github.token }}
run: |
# Reuse the automation branch only while its PR is open; a closed PR's
# branch is stale and would overwrite human edits merged to dev.
open_prs=$(gh pr list --repo "$GITHUB_REPOSITORY" \
--head automation/update-chinese-docs --state open \
--json number --jq 'length' || echo 0)
if [ "${open_prs:-0}" -gt 0 ] && \
git fetch origin automation/update-chinese-docs 2>/dev/null; then
echo "Open translation PR found; reusing its translations."
git checkout FETCH_HEAD -- docs/source/locale/zh_CN/LC_MESSAGES/ || true
else
echo "No open translation PR; using dev as the translation base."
fi
- name: Update gettext catalogs
run: |
sphinx-build -b gettext docs/source docs/build/gettext
sphinx-intl update -p docs/build/gettext -l zh_CN -d docs/source/locale -j 1
- name: Delete translations for renamed or removed docs
run: |
find docs/source/locale/zh_CN/LC_MESSAGES -name '*.po' | while read -r po; do
rel="${po#docs/source/locale/zh_CN/LC_MESSAGES/}"
if [ ! -f "docs/build/gettext/${rel%.po}.pot" ]; then
echo "deleting $po (no matching source doc)"
git rm -fq --ignore-unmatch "$po"
fi
done
- name: Translate missing Chinese entries
env:
TRANSLATION_API_BASE_URL: ${{ secrets.TRANSLATION_API_BASE_URL }}
TRANSLATION_API_KEY: ${{ secrets.TRANSLATION_API_KEY }}
TRANSLATION_MODEL: ${{ secrets.TRANSLATION_MODEL }}
TRANSLATION_MAX_ENTRIES: ${{ inputs.max_entries || '500' }}
run: |
python tools/translate_docs_zh.py
- name: Validate Chinese docs build
run: |
SPHINX_LANGUAGE=zh_CN sphinx-build -b html docs/source docs/build/html/zh_CN
- name: Open or update translation PR
uses: peter-evans/create-pull-request@v8
with:
token: ${{ github.token }}
branch: automation/update-chinese-docs
base: dev
delete-branch: true
signoff: true
add-paths: |
docs/source/locale/zh_CN/LC_MESSAGES
commit-message: "Update Chinese documentation translations"
title: "Update Chinese documentation translations"
body: |
Automated weekly update for Chinese documentation translations.
Generated by `.github/workflows/translate_doc_zh.yml`.
labels: |
documentation
automation