Add vLLM v0.18.1 source tree with KV transfer abort fix
third_party/vllm/ now tracked in git for direct patch management.
Based on vLLM v0.18.1 release with one patch applied:
vllm/v1/core/sched/scheduler.py:
Replace fatal assert with graceful skip when KV transfer callback
arrives for an already-aborted request during PD disaggregated serving.
Future vLLM modifications should be made directly in third_party/vllm/
and committed normally. The patches/ directory is kept as documentation
of what changed from upstream.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
233
third_party/vllm/tools/vllm-rocm/generate-rocm-wheels-root-index.sh
vendored
Executable file
233
third_party/vllm/tools/vllm-rocm/generate-rocm-wheels-root-index.sh
vendored
Executable file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
#
|
||||
# Generate S3 PyPI Root Index for Latest Version
|
||||
#
|
||||
# Creates a PEP 503 compatible index.html at rocm/ pointing to the latest
|
||||
# semantic version's packages. This enables users to install with:
|
||||
# uv pip install vllm --extra-index-url s3://vllm-wheels/rocm
|
||||
#
|
||||
# Usage:
|
||||
# generate-root-index.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --dry-run Preview changes without uploading
|
||||
# --version VER Use specific version instead of auto-detecting latest
|
||||
#
|
||||
# Environment variables:
|
||||
# S3_BUCKET - Bucket name (default: vllm-wheels)
|
||||
# VARIANT - ROCm variant (default: rocm700)
|
||||
# DRY_RUN - Set to 1 for preview mode (same as --dry-run)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ======== Configuration ========
|
||||
BUCKET="${S3_BUCKET:-vllm-wheels}"
|
||||
VARIANT="${VARIANT:-rocm700}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
FORCE_VERSION=""
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--version)
|
||||
FORCE_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Working directory for generated files
|
||||
WORK_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
echo "========================================"
|
||||
echo "Generate Root Index for Latest Version"
|
||||
echo "========================================"
|
||||
echo "S3 Bucket: $BUCKET"
|
||||
echo "ROCm Variant: $VARIANT"
|
||||
echo "Dry Run: $DRY_RUN"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# ======== Step 1: Find latest semantic version ========
|
||||
|
||||
echo "Step 1: Finding latest semantic version..."
|
||||
|
||||
# List all directories under rocm/
|
||||
aws s3api list-objects-v2 \
|
||||
--bucket "$BUCKET" \
|
||||
--prefix "rocm/" \
|
||||
--delimiter "/" \
|
||||
--query 'CommonPrefixes[].Prefix' \
|
||||
--output text | tr '\t' '\n' > "$WORK_DIR/all_prefixes.txt"
|
||||
|
||||
# Filter for semantic versions (x.y.z pattern)
|
||||
grep -oE 'rocm/[0-9]+\.[0-9]+\.[0-9]+/' "$WORK_DIR/all_prefixes.txt" | \
|
||||
sed 's|rocm/||; s|/||' | \
|
||||
sort -V > "$WORK_DIR/versions.txt" || true
|
||||
|
||||
if [[ ! -s "$WORK_DIR/versions.txt" ]]; then
|
||||
echo "ERROR: No semantic versions found under s3://$BUCKET/rocm/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found versions:"
|
||||
cat "$WORK_DIR/versions.txt"
|
||||
echo ""
|
||||
|
||||
if [[ -n "$FORCE_VERSION" ]]; then
|
||||
LATEST_VERSION="$FORCE_VERSION"
|
||||
echo "Using forced version: $LATEST_VERSION"
|
||||
else
|
||||
LATEST_VERSION=$(tail -1 "$WORK_DIR/versions.txt")
|
||||
echo "Latest version (auto-detected): $LATEST_VERSION"
|
||||
fi
|
||||
|
||||
# Verify the version exists
|
||||
if ! grep -qx "$LATEST_VERSION" "$WORK_DIR/versions.txt"; then
|
||||
echo "ERROR: Version $LATEST_VERSION not found in bucket"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ======== Step 2: List packages from latest version ========
|
||||
|
||||
echo ""
|
||||
echo "Step 2: Listing packages from rocm/$LATEST_VERSION/$VARIANT/..."
|
||||
|
||||
VERSION_PREFIX="rocm/$LATEST_VERSION/$VARIANT/"
|
||||
|
||||
# List package directories
|
||||
aws s3api list-objects-v2 \
|
||||
--bucket "$BUCKET" \
|
||||
--prefix "$VERSION_PREFIX" \
|
||||
--delimiter "/" \
|
||||
--query 'CommonPrefixes[].Prefix' \
|
||||
--output text | tr '\t' '\n' > "$WORK_DIR/package_prefixes.txt" || true
|
||||
|
||||
if [[ ! -s "$WORK_DIR/package_prefixes.txt" ]]; then
|
||||
echo "ERROR: No packages found under s3://$BUCKET/$VERSION_PREFIX"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract package names
|
||||
sed "s|${VERSION_PREFIX}||; s|/||g" "$WORK_DIR/package_prefixes.txt" | \
|
||||
grep -v '^$' > "$WORK_DIR/packages.txt"
|
||||
|
||||
echo "Found packages:"
|
||||
cat "$WORK_DIR/packages.txt"
|
||||
echo ""
|
||||
|
||||
# ======== Step 3: Generate root index.html ========
|
||||
|
||||
echo "Step 3: Generating root index.html..."
|
||||
|
||||
mkdir -p "$WORK_DIR/output"
|
||||
|
||||
{
|
||||
cat <<'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="pypi:repository-version" content="1.0">
|
||||
</head>
|
||||
<body>
|
||||
EOF
|
||||
|
||||
while read -r pkg; do
|
||||
echo " <a href=\"$pkg/\">$pkg</a><br>"
|
||||
done < "$WORK_DIR/packages.txt"
|
||||
|
||||
cat <<'EOF'
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
} > "$WORK_DIR/output/index.html"
|
||||
|
||||
echo "Generated root index.html:"
|
||||
cat "$WORK_DIR/output/index.html"
|
||||
echo ""
|
||||
|
||||
# ======== Step 4: Copy and adjust package index files ========
|
||||
|
||||
echo "Step 4: Copying and adjusting package index files..."
|
||||
|
||||
while read -r pkg; do
|
||||
echo "Processing package: $pkg"
|
||||
|
||||
# Download existing index.html from versioned path
|
||||
SOURCE_INDEX="s3://$BUCKET/$VERSION_PREFIX$pkg/index.html"
|
||||
|
||||
mkdir -p "$WORK_DIR/output/$pkg"
|
||||
|
||||
if aws s3 cp "$SOURCE_INDEX" "$WORK_DIR/output/$pkg/index.html" 2>/dev/null; then
|
||||
# Adjust relative paths:
|
||||
# Original: href="../../../{commit}/wheel.whl" (from rocm/0.13.0/rocm710/vllm/)
|
||||
# New: href="../{commit}/wheel.whl" (from rocm/vllm/)
|
||||
sed -i 's|href="\.\./\.\./\.\./|href="../|g' "$WORK_DIR/output/$pkg/index.html"
|
||||
echo " - Downloaded and adjusted: $pkg/index.html"
|
||||
else
|
||||
echo " - WARNING: Could not download index for $pkg"
|
||||
fi
|
||||
done < "$WORK_DIR/packages.txt"
|
||||
|
||||
echo ""
|
||||
|
||||
# ======== Step 5: Upload to S3 ========
|
||||
|
||||
echo "Step 5: Uploading to s3://$BUCKET/rocm/..."
|
||||
echo ""
|
||||
|
||||
# List what would be uploaded
|
||||
echo "Files to upload:"
|
||||
find "$WORK_DIR/output" -name "*.html" -type f | while read -r file; do
|
||||
rel_path="${file#"$WORK_DIR"/output/}"
|
||||
echo " rocm/$rel_path"
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo "DRY RUN - Skipping upload"
|
||||
echo ""
|
||||
echo "Preview of generated files:"
|
||||
echo "----------------------------------------"
|
||||
echo "rocm/index.html:"
|
||||
cat "$WORK_DIR/output/index.html"
|
||||
echo ""
|
||||
echo "----------------------------------------"
|
||||
echo "Sample package index (first package):"
|
||||
FIRST_PKG=$(head -1 "$WORK_DIR/packages.txt")
|
||||
if [[ -f "$WORK_DIR/output/$FIRST_PKG/index.html" ]]; then
|
||||
echo "rocm/$FIRST_PKG/index.html:"
|
||||
cat "$WORK_DIR/output/$FIRST_PKG/index.html"
|
||||
fi
|
||||
else
|
||||
# Upload all generated files
|
||||
aws s3 cp --recursive "$WORK_DIR/output/" "s3://$BUCKET/rocm/" \
|
||||
--content-type "text/html"
|
||||
|
||||
echo "Upload complete!"
|
||||
fi
|
||||
|
||||
# ======== Summary ========
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Root Index Generation Complete!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Latest version: $LATEST_VERSION"
|
||||
echo "Packages indexed: $(wc -l < "$WORK_DIR/packages.txt")"
|
||||
echo ""
|
||||
echo "Install command:"
|
||||
echo " uv pip install vllm --extra-index-url https://wheels.vllm.ai/rocm/"
|
||||
echo "========================================"
|
||||
222
third_party/vllm/tools/vllm-rocm/pin_rocm_dependencies.py
vendored
Normal file
222
third_party/vllm/tools/vllm-rocm/pin_rocm_dependencies.py
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""
|
||||
Pin vLLM dependencies to exact versions of custom ROCm wheels.
|
||||
|
||||
This script modifies vLLM's requirements files to replace version constraints
|
||||
with exact versions of custom-built ROCm wheels (torch, triton, torchvision, amdsmi).
|
||||
|
||||
This ensures that 'pip install vllm' automatically installs the correct custom wheels
|
||||
instead of allowing pip to download different versions from PyPI.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import regex as re
|
||||
|
||||
|
||||
def extract_version_from_wheel(wheel_name: str) -> str:
|
||||
"""
|
||||
Extract version from wheel filename.
|
||||
|
||||
Example:
|
||||
torch-2.9.0a0+git1c57644-cp312-cp312-linux_x86_64.whl -> 2.9.0a0+git1c57644
|
||||
triton-3.4.0-cp312-cp312-linux_x86_64.whl -> 3.4.0
|
||||
"""
|
||||
# Wheel format:
|
||||
# {distribution}-{version}(-{build tag})?-{python}-{abi}-{platform}.whl
|
||||
parts = wheel_name.replace(".whl", "").split("-")
|
||||
|
||||
if len(parts) < 5:
|
||||
raise ValueError(f"Invalid wheel filename format: {wheel_name}")
|
||||
|
||||
# Version is the second part
|
||||
version = parts[1]
|
||||
return version
|
||||
|
||||
|
||||
def get_custom_wheel_versions(install_dir: str) -> dict[str, str]:
|
||||
"""
|
||||
Read /install directory and extract versions of custom wheels.
|
||||
|
||||
Returns:
|
||||
Dict mapping package names to exact versions
|
||||
"""
|
||||
install_path = Path(install_dir)
|
||||
if not install_path.exists():
|
||||
print(f"ERROR: Install directory not found: {install_dir}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
versions = {}
|
||||
|
||||
# Map wheel prefixes to package names
|
||||
# IMPORTANT: Use dashes to avoid matching substrings
|
||||
# (e.g., 'torch' would match 'torchvision')
|
||||
# ORDER MATTERS: This order is preserved when pinning dependencies
|
||||
# in requirements files
|
||||
package_mapping = [
|
||||
("torch-", "torch"), # Match torch- (not torchvision)
|
||||
("triton-", "triton"), # Match triton- (not triton_kernels)
|
||||
("triton_kernels-", "triton-kernels"), # Match triton_kernels-
|
||||
("torchvision-", "torchvision"), # Match torchvision-
|
||||
("torchaudio-", "torchaudio"), # Match torchaudio-
|
||||
("amdsmi-", "amdsmi"), # Match amdsmi-
|
||||
("flash_attn-", "flash-attn"), # Match flash_attn-
|
||||
("amd_aiter-", "amd-aiter"), # Match amd_aiter-
|
||||
]
|
||||
|
||||
for wheel_file in install_path.glob("*.whl"):
|
||||
wheel_name = wheel_file.name
|
||||
|
||||
for prefix, package_name in package_mapping:
|
||||
if wheel_name.startswith(prefix):
|
||||
try:
|
||||
version = extract_version_from_wheel(wheel_name)
|
||||
versions[package_name] = version
|
||||
print(f"Found {package_name}=={version}", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(
|
||||
f"WARNING: Could not extract version from {wheel_name}: {e}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
break
|
||||
|
||||
# Return versions in the order defined by package_mapping
|
||||
ordered_versions = {}
|
||||
for _, package_name in package_mapping:
|
||||
if package_name in versions:
|
||||
ordered_versions[package_name] = versions[package_name]
|
||||
return ordered_versions
|
||||
|
||||
|
||||
def pin_dependencies_in_requirements(requirements_path: str, versions: dict[str, str]):
|
||||
"""
|
||||
Insert custom wheel pins at the TOP of requirements file.
|
||||
|
||||
This ensures that when setup.py processes the file line-by-line,
|
||||
custom wheels (torch, triton, etc.) are encountered FIRST, before
|
||||
any `-r common.txt` includes that might pull in other dependencies.
|
||||
|
||||
Creates:
|
||||
# Custom ROCm wheel pins (auto-generated)
|
||||
torch==2.9.0a0+git1c57644
|
||||
triton==3.4.0
|
||||
torchvision==0.23.0a0+824e8c8
|
||||
amdsmi==26.1.0+5df6c765
|
||||
|
||||
-r common.txt
|
||||
... rest of file ...
|
||||
"""
|
||||
requirements_file = Path(requirements_path)
|
||||
|
||||
if not requirements_file.exists():
|
||||
print(
|
||||
f"ERROR: Requirements file not found: {requirements_path}", file=sys.stderr
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Backup original file
|
||||
backup_file = requirements_file.with_suffix(requirements_file.suffix + ".bak")
|
||||
with open(requirements_file) as f:
|
||||
original_lines = f.readlines()
|
||||
|
||||
# Write backup
|
||||
with open(backup_file, "w") as f:
|
||||
f.writelines(original_lines)
|
||||
|
||||
# Build header with pinned custom wheels
|
||||
header_lines = [
|
||||
"# Custom ROCm wheel pins (auto-generated by pin_rocm_dependencies.py)\n",
|
||||
"# These must come FIRST to ensure correct dependency resolution\n",
|
||||
]
|
||||
|
||||
for package_name, exact_version in versions.items():
|
||||
header_lines.append(f"{package_name}=={exact_version}\n")
|
||||
|
||||
header_lines.append("\n") # Blank line separator
|
||||
|
||||
# Filter out any existing entries for custom packages from original file
|
||||
filtered_lines = []
|
||||
removed_packages = []
|
||||
|
||||
for line in original_lines:
|
||||
stripped = line.strip()
|
||||
should_keep = True
|
||||
|
||||
# Check if this line is for one of our custom packages
|
||||
if stripped and not stripped.startswith("#") and not stripped.startswith("-"):
|
||||
for package_name in versions:
|
||||
# Handle both hyphen and underscore variations
|
||||
pattern_name = package_name.replace("-", "[-_]")
|
||||
pattern = rf"^{pattern_name}\s*[=<>]=?\s*[\d.a-zA-Z+]+"
|
||||
|
||||
if re.match(pattern, stripped, re.IGNORECASE):
|
||||
removed_packages.append(f"{package_name}: {stripped}")
|
||||
should_keep = False
|
||||
break
|
||||
|
||||
if should_keep:
|
||||
filtered_lines.append(line)
|
||||
|
||||
# Combine: header + filtered original content
|
||||
final_lines = header_lines + filtered_lines
|
||||
|
||||
# Write modified content
|
||||
with open(requirements_file, "w") as f:
|
||||
f.writelines(final_lines)
|
||||
|
||||
# Print summary
|
||||
print("\n✓ Inserted custom wheel pins at TOP of requirements:", file=sys.stderr)
|
||||
for package_name, exact_version in versions.items():
|
||||
print(f" - {package_name}=={exact_version}", file=sys.stderr)
|
||||
|
||||
if removed_packages:
|
||||
print("\n✓ Removed old package entries:", file=sys.stderr)
|
||||
for pkg in removed_packages:
|
||||
print(f" - {pkg}", file=sys.stderr)
|
||||
|
||||
print(f"\n✓ Patched requirements file: {requirements_path}", file=sys.stderr)
|
||||
print(f" Backup saved: {backup_file}", file=sys.stderr)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print(
|
||||
f"Usage: {sys.argv[0]} <install_dir> <requirements_file>", file=sys.stderr
|
||||
)
|
||||
print(
|
||||
f"Example: {sys.argv[0]} /install /app/vllm/requirements/rocm.txt",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
install_dir = sys.argv[1]
|
||||
requirements_path = sys.argv[2]
|
||||
|
||||
print("=" * 70, file=sys.stderr)
|
||||
print("Pinning vLLM dependencies to custom ROCm wheel versions", file=sys.stderr)
|
||||
print("=" * 70, file=sys.stderr)
|
||||
|
||||
# Get versions from custom wheels
|
||||
print(f"\nScanning {install_dir} for custom wheels...", file=sys.stderr)
|
||||
versions = get_custom_wheel_versions(install_dir)
|
||||
|
||||
if not versions:
|
||||
print("\nERROR: No custom wheels found in /install!", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Pin dependencies in requirements file
|
||||
print(f"\nPatching {requirements_path}...", file=sys.stderr)
|
||||
pin_dependencies_in_requirements(requirements_path, versions)
|
||||
|
||||
print("\n" + "=" * 70, file=sys.stderr)
|
||||
print("✓ Dependency pinning complete!", file=sys.stderr)
|
||||
print("=" * 70, file=sys.stderr)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user