vLLM Compatibility Test #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This workflow tests FlexKV's compatibility with the latest vLLM release. | |
| # It verifies that all vLLM internal APIs used by FlexKVConnectorV1 are still | |
| # importable and structurally compatible, so we can detect breaking changes | |
| # in vLLM as early as possible. | |
| name: vLLM Compatibility Test | |
| on: | |
| push: | |
| branches: ["main", "dev"] | |
| paths: | |
| - "flexkv/integration/vllm/**" | |
| pull_request: | |
| branches: ["main", "dev"] | |
| paths: | |
| - "flexkv/integration/vllm/**" | |
| schedule: | |
| # Run every day at 02:00 UTC to catch upstream vLLM changes early | |
| - cron: "0 2 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| vllm_version: | |
| description: "vLLM version to test against (e.g. 0.8.0), leave empty for latest" | |
| required: false | |
| default: "" | |
| jobs: | |
| vllm-compat-test: | |
| name: vLLM API Compatibility Check (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11"] | |
| steps: | |
| - name: Checkout FlexKV | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| - name: Install vLLM (CPU-only, no CUDA needed for import checks) | |
| run: | | |
| pip install --upgrade pip | |
| if [ -n "${{ github.event.inputs.vllm_version }}" ]; then | |
| echo "Installing vLLM==${{ github.event.inputs.vllm_version }}" | |
| pip install "vllm==${{ github.event.inputs.vllm_version }}" --extra-index-url https://download.pytorch.org/whl/cpu | |
| else | |
| echo "Installing latest vLLM" | |
| pip install vllm --extra-index-url https://download.pytorch.org/whl/cpu | |
| fi | |
| python -c "import vllm; print(f'vLLM version: {vllm.__version__}')" | |
| - name: Check 1 - vLLM internal API imports | |
| # Verifies that all vLLM internal symbols imported by flexkv_connector.py | |
| # are still accessible. Catches module renames, class deletions, etc. | |
| run: | | |
| python - <<'EOF' | |
| import sys | |
| failed = [] | |
| def check_import(module_path, symbol=None): | |
| try: | |
| mod = __import__(module_path, fromlist=[symbol] if symbol else []) | |
| if symbol: | |
| getattr(mod, symbol) | |
| print(f" OK {module_path}" + (f".{symbol}" if symbol else "")) | |
| except (ImportError, AttributeError) as e: | |
| print(f" FAIL {module_path}" + (f".{symbol}" if symbol else "") + f": {e}") | |
| failed.append(f"{module_path}" + (f".{symbol}" if symbol else "")) | |
| print("=== Check 1: vLLM API imports used by FlexKVConnectorV1 ===\n") | |
| # Core connector base classes | |
| check_import("vllm.distributed.kv_transfer.kv_connector.v1.base", "KVConnectorBase_V1") | |
| check_import("vllm.distributed.kv_transfer.kv_connector.v1.base", "KVConnectorMetadata") | |
| check_import("vllm.distributed.kv_transfer.kv_connector.v1.base", "KVConnectorRole") | |
| # Metrics | |
| check_import("vllm.distributed.kv_transfer.kv_connector.v1.metrics", "KVConnectorStats") | |
| # Config | |
| check_import("vllm.config", "VllmConfig") | |
| # Logger | |
| check_import("vllm.logger", "init_logger") | |
| # Scheduler output | |
| check_import("vllm.v1.core.sched.output", "SchedulerOutput") | |
| # KV connector output | |
| check_import("vllm.v1.outputs", "KVConnectorOutput") | |
| # TYPE_CHECKING-only imports (verify they still exist) | |
| check_import("vllm.v1.attention.backend", "AttentionMetadata") | |
| check_import("vllm.distributed.kv_events", "KVCacheEvent") | |
| check_import("vllm.forward_context", "ForwardContext") | |
| check_import("vllm.v1.core.kv_cache_manager", "KVCacheBlocks") | |
| check_import("vllm.v1.kv_cache_interface", "KVCacheConfig") | |
| check_import("vllm.v1.request", "Request") | |
| print() | |
| if failed: | |
| print(f"FAILED: {len(failed)} API(s) are no longer available:") | |
| for f in failed: | |
| print(f" - {f}") | |
| sys.exit(1) | |
| else: | |
| print("All imports OK.\n") | |
| EOF | |
| - name: Check 2 - KVConnectorBase_V1 abstract methods are all implemented | |
| # Verifies that FlexKVConnectorV1 implements every abstract method | |
| # required by the base class. Catches cases where vLLM adds new | |
| # abstract methods that the connector has not yet implemented. | |
| run: | | |
| python - <<'EOF' | |
| import sys | |
| import inspect | |
| print("=== Check 2: Abstract method coverage ===\n") | |
| from vllm.distributed.kv_transfer.kv_connector.v1.base import KVConnectorBase_V1 | |
| # Collect all abstract methods declared in the base class | |
| abstract_methods = { | |
| name | |
| for name, method in inspect.getmembers(KVConnectorBase_V1, predicate=inspect.isfunction) | |
| if getattr(method, "__isabstractmethod__", False) | |
| } | |
| print(f"Base class abstract methods ({len(abstract_methods)}):") | |
| for m in sorted(abstract_methods): | |
| print(f" - {m}") | |
| # Parse FlexKVConnectorV1Impl from FlexKV's own adapter file | |
| # (no GPU / C++ extension needed — pure AST analysis) | |
| import ast, pathlib | |
| adapter_path = pathlib.Path("flexkv/integration/vllm/vllm_v1_adapter.py") | |
| if not adapter_path.exists(): | |
| print(f"FAILED: Cannot find adapter file at {adapter_path}") | |
| sys.exit(1) | |
| tree = ast.parse(adapter_path.read_text()) | |
| implemented = set() | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.ClassDef) and node.name == "FlexKVConnectorV1Impl": | |
| for item in node.body: | |
| if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): | |
| implemented.add(item.name) | |
| print(f"\nFlexKVConnectorV1Impl implemented methods ({len(implemented)}):") | |
| for m in sorted(implemented): | |
| print(f" - {m}") | |
| missing = abstract_methods - implemented | |
| print() | |
| if missing: | |
| print(f"FAILED: FlexKVConnectorV1Impl is missing {len(missing)} abstract method(s):") | |
| for m in sorted(missing): | |
| print(f" - {m}") | |
| print("\nvLLM may have added new abstract methods to KVConnectorBase_V1.") | |
| print("Please implement the missing methods in FlexKVConnectorV1Impl.") | |
| sys.exit(1) | |
| else: | |
| print("All abstract methods are implemented.\n") | |
| EOF | |
| - name: Check 3 - Key method signatures are compatible | |
| # Verifies that the signatures of key methods in KVConnectorBase_V1 | |
| # have not changed in a way that would break FlexKVConnectorV1. | |
| # Catches parameter additions/removals/renames in the base class. | |
| run: | | |
| python - <<'EOF' | |
| import sys | |
| import inspect | |
| print("=== Check 3: Key method signature compatibility ===\n") | |
| from vllm.distributed.kv_transfer.kv_connector.v1.base import KVConnectorBase_V1 | |
| # Methods that FlexKVConnectorV1 overrides - check their base signatures | |
| methods_to_check = [ | |
| "start_load_kv", | |
| "wait_for_layer_load", | |
| "save_kv_layer", | |
| "wait_for_save", | |
| "get_finished", | |
| "register_kv_caches", | |
| "get_num_new_matched_tokens", | |
| "update_state_after_alloc", | |
| "build_connector_meta", | |
| "update_connector_output", | |
| "request_finished", | |
| "take_events", | |
| "get_kv_connector_stats", | |
| "get_block_ids_with_load_errors", | |
| "shutdown", | |
| ] | |
| failed = [] | |
| for method_name in methods_to_check: | |
| method = getattr(KVConnectorBase_V1, method_name, None) | |
| if method is None: | |
| print(f" FAIL {method_name}: method no longer exists in KVConnectorBase_V1") | |
| failed.append(method_name) | |
| else: | |
| sig = inspect.signature(method) | |
| params = list(sig.parameters.keys()) | |
| print(f" OK {method_name}{sig}") | |
| print() | |
| if failed: | |
| print(f"FAILED: {len(failed)} method(s) no longer exist in KVConnectorBase_V1:") | |
| for f in failed: | |
| print(f" - {f}") | |
| print("\nThe base class interface has changed. Please update FlexKVConnectorV1.") | |
| sys.exit(1) | |
| else: | |
| print("All method signatures are present.\n") | |
| EOF | |
| - name: Detect CUDA availability | |
| id: cuda_check | |
| run: | | |
| if command -v nvidia-smi &> /dev/null && nvidia-smi &> /dev/null; then | |
| echo "cuda_available=true" >> "$GITHUB_OUTPUT" | |
| echo "CUDA detected: $(nvidia-smi --query-gpu=name --format=csv,noheader | head -1)" | |
| else | |
| echo "cuda_available=false" >> "$GITHUB_OUTPUT" | |
| echo "No CUDA detected — Check 4 will be skipped." | |
| fi | |
| - name: Check 4 - Install FlexKV and verify connector import | |
| if: steps.cuda_check.outputs.cuda_available == 'true' | |
| # This step actually installs FlexKV and verifies that | |
| # FlexKVConnectorV1Impl can be imported successfully. This catches | |
| # any import-time errors in FlexKV's own integration code (e.g. | |
| # missing vLLM symbols that are only discovered at import time | |
| # inside flexkv package itself). | |
| # Requires CUDA environment to build C++ extensions properly. | |
| run: | | |
| # Install system dependencies required for building FlexKV | |
| sudo apt-get install -y libxxhash-dev liburing-dev || true | |
| # Install FlexKV (disable metrics to avoid prometheus-cpp dependency in CI) | |
| FLEXKV_ENABLE_METRICS=0 pip install -e . --no-build-isolation | |
| python - <<'EOF' | |
| import sys | |
| print("=== Check 4: FlexKV install and connector import ===\n") | |
| # Verify FlexKV itself is importable | |
| try: | |
| import flexkv | |
| print(f" OK flexkv imported (version: {getattr(flexkv, '__version__', 'unknown')})") | |
| except ImportError as e: | |
| print(f" FAIL flexkv import failed: {e}") | |
| sys.exit(1) | |
| # Verify the vLLM v1 adapter (the actual implementation class) is importable | |
| try: | |
| from flexkv.integration.vllm.vllm_v1_adapter import FlexKVConnectorV1Impl | |
| print(f" OK FlexKVConnectorV1Impl imported from flexkv.integration.vllm.vllm_v1_adapter") | |
| except ImportError as e: | |
| print(f" FAIL FlexKVConnectorV1Impl import failed: {e}") | |
| print() | |
| print("This means FlexKV's integration code references a vLLM symbol that no longer exists.") | |
| print("Please update flexkv/integration/vllm/vllm_v1_adapter.py to match the new vLLM API.") | |
| sys.exit(1) | |
| print() | |
| print("FlexKV installation and connector import OK.\n") | |
| EOF | |
| - name: Report details on failure | |
| if: failure() | |
| run: | | |
| echo "==========================================" | |
| echo "Compatibility check failed!" | |
| echo "==========================================" | |
| python -c "import vllm; print(f'Tested against vLLM {vllm.__version__}')" || true | |
| pip show vllm | grep -E "^(Name|Version|Home-page)" || true | |
| echo "" | |
| echo "Please check the vLLM changelog for breaking changes:" | |
| echo " https://github.com/vllm-project/vllm/blob/main/CHANGELOG.md" | |
| echo " https://github.com/vllm-project/vllm/releases" |