diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a18f4be..0cae80c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: --index-url https://download.pytorch.org/whl/cpu \ "torch==${TORCH_VERSION}" - name: Test maintained Python trees - run: python -m pytest spark_transport runtime/exl3-r7 runtime/glm53-flash runtime/deepseek0731-gb10 runtime/qwen38 runtime/test_public_overlay.py performance/harnesses scripts -q -rs + run: python -m pytest spark_transport runtime/exl3-r7 runtime/glm53-flash runtime/glm53-flash-adaptive-mtp-python-overlay runtime/glm53-flash-dflash7-python-overlay runtime/deepseek0731-gb10 runtime/qwen38 runtime/test_public_overlay.py performance/harnesses scripts -q -rs docs-links: name: docs links diff --git a/AGENTS.md b/AGENTS.md index bd107a27..c8bd8c26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,7 +104,7 @@ test imports torch: python -m pip install -r requirements-dev.txt python -m pip install --index-url https://download.pytorch.org/whl/cpu "torch==2.11.0" ruff check --select E,F,W --ignore E501 spark_transport runtime scripts performance -python -m pytest spark_transport runtime/exl3-r7 runtime/glm53-flash runtime/deepseek0731-gb10 runtime/qwen38 runtime/test_public_overlay.py performance/harnesses scripts -q -rs +python -m pytest spark_transport runtime/exl3-r7 runtime/glm53-flash runtime/glm53-flash-adaptive-mtp-python-overlay runtime/glm53-flash-dflash7-python-overlay runtime/deepseek0731-gb10 runtime/qwen38 runtime/test_public_overlay.py performance/harnesses scripts -q -rs ``` The test suite is CPU-only contract coverage. It does not validate CUDA, diff --git a/docs/GLM53_B12X_KDA_ADAPTIVE_MTP_SPARKCACHE_TP4_QUICKSTART.md b/docs/GLM53_B12X_KDA_ADAPTIVE_MTP_SPARKCACHE_TP4_QUICKSTART.md index 2cd87dd2..07e09958 100644 --- a/docs/GLM53_B12X_KDA_ADAPTIVE_MTP_SPARKCACHE_TP4_QUICKSTART.md +++ b/docs/GLM53_B12X_KDA_ADAPTIVE_MTP_SPARKCACHE_TP4_QUICKSTART.md @@ -1,9 +1,10 @@ # Serve GLM-5.3 with adaptive MTP, live-tensor B12X KDA, and SparkCache -Status: **implemented, not qualified**. This guide builds vLLM commit -`0b67266a0f37d6146a8403fb8482403c62f412d5` and the SparkCache overlay from -commit `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`, Git tree -`94c236b9dfbf5f70075eb47877fd9caaa5d8c249`, for four DGX Spark systems at +Status: **implemented, not qualified**. This guide retains vLLM native +extensions from `da4d7be6c97434f6942292ed8abbf4b32dc44355`, overlays Python +source `0b67266a0f37d6146a8403fb8482403c62f412d5`, and installs SparkCache +commit `c56f77f97b3da907d32e888d82046359a62f0f88`, Git tree +`deac36758f86695cd13f07b2870c2e49842aed9c`, for four DGX Spark systems at TP4/DCP1. The adaptive-MTP composition has GPU-free contract coverage but no four-rank persistent-restore or performance qualification. @@ -13,69 +14,59 @@ TP4 makes the pinned vLLM loader select `nogds=True`, so model loading uses pipelined host I/O without GPU Direct Storage. The profile reserves 20 GiB of FP8 KV per rank and enables SparkCache CUDA -restore, tail-only publication, shared restore trunks, and bounded shared GPU -prefix leases. Image construction and distribution do not require stopping an -existing service. Do not run the launch command until all four ranks have the -same verified image ID. +restore, tail-only copy-on-write publication, shared-segment restore, and +bounded shared GPU prefix leases. The vLLM overlay emits only hash-proven +recurrent replay boundaries; SparkCache keeps publication pending while a +request has no hand-off and rejects incomplete, contradictory, or changed +evidence. Image construction and distribution +do not require stopping an existing service. Do not run the launch command +until all four ranks have the same verified image ID. -## Build the runtime and SparkCache overlay +## Build the exact Python-overlay image -Use Linux ARM64 with Docker BuildKit and at least 250 GiB of free local -storage. Clone both repositories beside each other: +Use Linux ARM64 with Docker BuildKit and enough space for the exact source +trees, B12X wheel, SparkCache CUDA placement library, and derived image: ```bash git clone https://github.com/FujitsuPolycom/sparkring.git sparkring git -C sparkring checkout --detach -git clone https://github.com/FujitsuPolycom/sparkcache.git sparkcache -git -C sparkcache checkout --detach 5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3 - -IMAGE='sparkring-glm53-runtime:b12x-kda-adaptive-mtp-0b67266a-arm64' \ -BUILD_RECEIPT="$PWD/glm53-b12x-kda-adaptive-mtp-runtime-receipt.json" \ -bash sparkring/runtime/glm53-flash-b12x-kda-adaptive-mtp/build-image.sh - -runtime_image='sparkring-glm53-runtime:b12x-kda-adaptive-mtp-0b67266a-arm64' -runtime_image_id="$(docker image inspect --format '{{.Id}}' "${runtime_image}")" -python sparkcache/deploy/glm53_flash/build_image.py \ - --repository "$PWD/sparkcache" \ - --containerfile deploy/glm53_flash/Containerfile.b12x-kda-adaptive-mtp \ - --base-image "${runtime_image}" \ - --base-image-id "${runtime_image_id}" \ - --source-sha256 bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63 \ - --sparkcache-revision 5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3 \ - --output-image sparkring-glm53-sparkcache:b12x-kda-adaptive-mtp-0b67266a-arm64 +cd sparkring +IMAGE='sparkring-glm53-sparkcache:vllm-python-0b67266-native-da4d7be-b12x-b1d541f-arm64' \ +BUILD_RECEIPT="$PWD/glm53-public-python-overlay-image-receipt.json" \ +bash runtime/glm53-flash-adaptive-mtp-python-overlay/build-image.sh ``` -Record immutable local identities: - -```bash -sparkcache_image='sparkring-glm53-sparkcache:b12x-kda-adaptive-mtp-0b67266a-arm64' -sparkcache_image_id="$(docker image inspect --format '{{.Id}}' "${sparkcache_image}")" -cuda_placement_sha256="$(docker run --rm --entrypoint sha256sum "${sparkcache_image}" \ - /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so \ - | cut -d ' ' -f1)" -test "${#cuda_placement_sha256}" -eq 64 -``` - -The runtime builder verifies the complete first-parent vLLM history from -`da4d7be` through adaptive MTP and the three live-tensor B12X KDA commits. The -SparkCache build verifies LF Linux preimages, four exact patches, and eleven -postimage source files. The pinned SparkCache source accepts the canonical -`spark_cache_cuda_*` keys in the profile directly; no legacy-key translation -is part of this composition. +The builder verifies the 31-file Python overlay, retained native ELF and +dispatch manifests, B12X `b1d541f`, SparkCache clean source SHA-256 +`788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98`, +four SparkCache patches, recurrent-boundary producer patch SHA-256 +`5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0`, +and lease contract SHA-256 +`f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811`. +The receipt binds these sources to the local image ID. The builder does not +push the image or contact serving hosts. + +`runtime/glm53-flash-b12x-kda-adaptive-mtp/` remains an exact full-source +builder for its older SparkCache contract. It does not apply the recurrent +producer after SparkCache's vLLM patches, so it is unsupported with SparkCache +`c56f77f` and must not be substituted in this guide. ## Resolve the TP4 profile ```bash -profile_template='sparkring/scripts/config/glm53-flash-b12x-kda-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json' -site_template='sparkring/scripts/config/glm53-flash-b12x-kda-adaptive-mtp-tp4-site.example.yaml' -python sparkring/scripts/prepare_glm53_b12x_kda_adaptive_mtp_profile.py \ +receipt="$PWD/glm53-public-python-overlay-image-receipt.json" +image='sparkring-glm53-sparkcache:vllm-python-0b67266-native-da4d7be-b12x-b1d541f-arm64' +profile_template='scripts/config/glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json' +site_template='scripts/config/glm53-flash-b12x-kda-adaptive-mtp-tp4-site.example.yaml' +python scripts/prepare_glm53_public_python_overlay_profile.py \ --profile-template "${profile_template}" \ --site-template "${site_template}" \ - --image "${sparkcache_image}" \ - --image-id "${sparkcache_image_id}" \ - --parent-image "${runtime_image}" \ - --parent-image-id "${runtime_image_id}" \ - --cuda-placement-library-sha256 "${cuda_placement_sha256}" \ + --image "${image}" \ + --image-id "$(jq -r .image_id "${receipt}")" \ + --cuda-placement-library-sha256 "$(jq -r .artifacts.sparkcache_cuda_placement_sha256 "${receipt}")" \ + --native-elf-manifest-sha256 "$(jq -r .runtime_contract.native_elf_manifest_sha256 "${receipt}")" \ + --native-dispatch-manifest-sha256 "$(jq -r .runtime_contract.native_dispatch_manifest_sha256 "${receipt}")" \ + --source-receipt-sha256 "$(jq -r .artifacts.source_receipt_sha256 "${receipt}")" \ --profile-output profile.json \ --site-output site.yaml ``` @@ -86,7 +77,9 @@ SparkCache directory. Do not change MTP depths, the observation window, loader queue, source identities, or attestation command. The one-shot clear token is recorded only after a successful SparkCache-owned -cache removal. Restarting this unchanged profile does not clear again. +cache removal. Restarting this unchanged profile does not clear again. The +resolved profile requires the recurrent-boundary patch label before any +container starts. `--prefill-schedule-interval` is not part of this implemented profile. Test interval `8` as a separate research-only profile so its mixed prefill/decode @@ -95,14 +88,14 @@ tradeoff cannot be confused with adaptive-MTP or SparkCache results. ## Verify and launch ```bash -python sparkring/scripts/preflight.py \ +python scripts/preflight.py \ --site site.yaml --strict-placeholders --json preflight.json -python sparkring/scripts/sparkring_generic_launcher.py \ +python scripts/sparkring_generic_launcher.py \ --site site.yaml --profile profile.json plan > start-plan.json -python sparkring/scripts/sparkring_generic_launcher.py \ +python scripts/sparkring_generic_launcher.py \ --site site.yaml --profile profile.json \ --execute \ - --confirmation START_GLM53_FLASH_MTP5_ADAPTIVE_FASTSAFETENSORS_TP4 \ + --confirmation START_GLM53_FLASH_PUBLIC_PYTHON_OVERLAY_MTP5_ADAPTIVE_FASTSAFETENSORS_TP4 \ start ``` @@ -110,18 +103,19 @@ The final command changes the four-rank serving deployment. Tail rank zero: ```bash ssh operator@rank0.example.net \ - 'docker logs --follow --tail 120 glm53-flash-b12x-kda-mtp5-adaptive-fastsafetensors-sparkcache-tp4-r0 2>&1' + 'docker logs --follow --tail 120 glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-r0 2>&1' ``` Wait for health, then run the exact semantic request: ```bash api_endpoint='http://rank0.example.net:8015' -served_model='glm-5.3-flash-nvfp4-b12x-kda-mtp5-adaptive-fastsafetensors-0b67266a-tp4' +served_model='glm-5.3-flash-nvfp4-python-overlay-0b67266-on-da4d7be-b12x-b1d541f-mtp5-adaptive-tp4' until curl --fail --silent "${api_endpoint}/health" >/dev/null; do sleep 5; done -python sparkcache/deploy/glm53_flash/qualification_request.py \ - --endpoint "${api_endpoint}" --model "${served_model}" \ - --kind semantic --output semantic.json +curl --fail --silent --show-error "${api_endpoint}/v1/completions" \ + -H 'Content-Type: application/json' \ + -d "{\"model\":\"${served_model}\",\"prompt\":\"The capital of France is\",\"max_tokens\":16,\"temperature\":0}" \ + > semantic.json ``` Construction support does not prove four-rank serving. Qualification requires @@ -133,10 +127,13 @@ NCCL error, or traceback. The overlay does not change SparkCache wire fields, digest salts, 256-token geometry, or stored object schemas. Its embedded-MTP digest is SHA-256 over -`glm53-embedded-mtp-runtime-v1`, the target identity, the full vLLM commit, -maximum depth five, and `adaptive:3:32`, separated by zero bytes. - -Including the vLLM revision gives this runtime a distinct draft-state cache -identity from the e105 adaptive-MTP profile. Stored entries therefore -recompute instead of crossing the KDA source boundary without byte-equivalence -evidence. +`glm53-embedded-mtp-composed-runtime-v1`, the target identity, the overlaid +vLLM Python commit, retained native vLLM commit, B12X commit, maximum depth +five, and `adaptive:3:32`, separated by zero bytes. + +Including both retained-native and overlaid-Python revisions gives this runtime +a distinct draft-state cache identity from the e105 adaptive-MTP and older +full-source profiles. Stored entries therefore recompute instead of crossing a +runtime boundary without byte-equivalence evidence. SparkCache `49c517e` does +not change wire fields, digest salts, or 256-token geometry; the new lease +contract accepts the producer postimages needed to prove recurrent publication. diff --git a/docs/GLM53_DFLASH7_PYTHON_OVERLAY_SPARKCACHE_TP4_QUICKSTART.md b/docs/GLM53_DFLASH7_PYTHON_OVERLAY_SPARKCACHE_TP4_QUICKSTART.md index 7fe55780..9681155d 100644 --- a/docs/GLM53_DFLASH7_PYTHON_OVERLAY_SPARKCACHE_TP4_QUICKSTART.md +++ b/docs/GLM53_DFLASH7_PYTHON_OVERLAY_SPARKCACHE_TP4_QUICKSTART.md @@ -1,14 +1,17 @@ # Serve GLM-5.3 with external DFlash7 and the exact Python-overlay runtime -Status: **implemented**. The image builder, profile resolver, and four-rank -dry-run contract pass without GPUs. Local image ID +Status: **implemented, not qualified** for the current build contract. The +image builder, profile resolver, and four-rank dry-run contract pass without +GPUs. Historical local image ID `sha256:eef863d8bc578815a80b0e2d9f0d745102b6363415225101fd92171a2e5a55cb` is **qualified** only for the TP4/DCP1 startup, health, semantic generation, arbitrary page-boundary replay, and 131,072- and 262,144-token restore cases in the [bounded validation record](../performance/records/glm53-flash/dflash7-python-overlay-pr30-live-validation.md). -The exact image has no retained C2/C8/C16 or DFlash response-quality evidence. -A rebuilt image has a different identity and requires its own live checks. +That historical image used SparkCache `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`; +it is not an artifact of the current contract below. It has no retained +C2/C8/C16 or DFlash response-quality evidence. A current build has a different +identity and requires its own live checks. ## Runtime contract @@ -17,7 +20,8 @@ A rebuilt image has a different identity and requires its own live checks. | vLLM native extensions and wheel metadata | `da4d7be6c97434f6942292ed8abbf4b32dc44355` | | vLLM Python source | `0b67266a0f37d6146a8403fb8482403c62f412d5`, tree `ba9484ccb33aa56e90ff2f447f15ca9b9da97639` | | B12X | `b1d541f9e71a35f030d45fae437630fff7507c2a`, tree `c69cdec1c59a08e8e0e549f930fa8abcfb5134ae` | -| SparkCache reconstructed-page placement, canonical CUDA configuration, and bounded page-delta reads | `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`, tree `94c236b9dfbf5f70075eb47877fd9caaa5d8c249`, clean source SHA-256 `bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63` | +| SparkCache shared-segment restore, tail-only copy-on-write publication, canonical CUDA configuration, and bounded page-delta reads | `c56f77f97b3da907d32e888d82046359a62f0f88`, tree `deac36758f86695cd13f07b2870c2e49842aed9c`, clean source SHA-256 `788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98` | +| Recurrent replay-boundary producer | Patch SHA-256 `5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0`; produces the four postimages accepted by SparkCache lease contract SHA-256 `f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811` | | DFlash draft-loader separation | Patch SHA-256 `39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279`, postimage SHA-256 `98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4` | | Unused DeepEP removal | Distribution `deep_ep==2.0.0+local`, removal receipt SHA-256 `65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3` | | Target | `local-inference-lab/GLM-5.3-Flash-NVFP4@520de24eabf507659eaef7c70f14fd584527facc` | @@ -40,7 +44,9 @@ bash runtime/glm53-flash-dflash7-python-overlay/build-image.sh The builder verifies the public da4 image, the 31-file Python overlay, retained native ELFs and dispatch operators, B12X, SparkCache clean source, the CUDA -placement library, four exact vLLM patches, and the eleven-file lease contract. +placement library, four SparkCache vLLM patches, the recurrent-boundary +producer patch, and the eleven-file lease contract. The resulting profile also +requires the producer patch label before starting any container. The base-image inspection must identify exactly one installed distribution, `deep_ep==2.0.0+local`, as the owner of the `deep_ep` module. The derived image uninstalls that exact distribution and verifies that `deep_ep` is absent. The @@ -62,7 +68,7 @@ Two profiles share the same image and DFlash7 cache identity: | Profile | Status | Loader behavior | |---|---|---| | `glm53-flash-dflash7-python-overlay-safetensors-sparkcache-tp4-dcp1.example.json` | **implemented**, not qualified | Uses global safetensors for target and draft. This follows the qualified-compatible loader shape but still requires live qualification on the composed 0b image. | -| `glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json` | **qualified** only for the recorded image and bounded cases | Uses global fastsafetensors with queue size one for the target and `draft_load_config={"load_format":"safetensors"}` for DFlash. | +| `glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json` | **implemented**, not qualified for the current build | Uses global fastsafetensors with queue size one for the target and `draft_load_config={"load_format":"safetensors"}` for DFlash. The historical receipt remains evidence only for its recorded image. | The image applies an exact-input vLLM patch that passes `SpeculativeConfig.draft_load_config` to the DFlash model loader. The image @@ -74,9 +80,11 @@ The all-safetensors profile remains unqualified. The fastsafetensors result belongs only to the image ID and cases named above; it does not transfer to a rebuild. -SparkCache commit `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3` -accepts the canonical CUDA keys used by both profiles. No legacy-key rewrite -is part of this path. +SparkCache commit `c56f77f97b3da907d32e888d82046359a62f0f88` +accepts the canonical CUDA keys used by both profiles. It consumes the +hash-proven recurrent hand-off emitted by this exact vLLM overlay and cancels +publication when the metadata is absent, incomplete, or contradictory. +No legacy-key rewrite is part of this path. ## Resolve the profile and inspect the plan @@ -156,7 +164,7 @@ content, writes the token's completion marker only after successful removal, and treats later starts with the same token as no-ops. Change the token only when another intentional cache reset is required. -`--prefill-schedule-interval` is not part of the qualified DFlash7 profile. +`--prefill-schedule-interval` is not part of the current DFlash7 profiles. Test interval `8` in a separate research-only profile so its mixed prefill/decode tradeoff is measured independently. @@ -171,13 +179,11 @@ different cache root and one-shot clear token so loader observations remain isolated. The pinned SparkCache source combines canonical CUDA configuration names, -replacement of a partial terminal HMA page when an authenticated cache -boundary falls inside that page, and an eight-worker reader for authenticated -page-delta chunks. The reader preserves manifest descriptor order after -concurrent reads. Moving from SparkCache commit -`5d571018de5b63a9a90e5c11e6d6e86bbff4a957` to -`5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3` does not change the namespace. -Checkpoint identities, page-delta wire schemas, record vocabulary, digest -salts, parallel geometry, vLLM patches, the lease contract, and the CUDA -placement ABI are unchanged. Compatible `page-tail-cow-v1` entries remain -eligible. Null-block publication failures remain unsupported. +authenticated shared-segment restore, an eight-worker ordered page-delta +reader, and tail-only copy-on-write publication. Cache identities, digest +salts, 256-token chunk geometry, page-delta wire bytes, and the CUDA placement +ABI are unchanged. The source change does not change the namespace, so +compatible `page-tail-cow-v1` entries remain eligible. +The vLLM lease-contract bytes do change to accept the recurrent-boundary +postimages. Missing or malformed boundary evidence is a cache miss and +recomputation, never an unverified publication. diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/Containerfile b/runtime/glm53-flash-adaptive-mtp-python-overlay/Containerfile index 3b43225e..599a70c6 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/Containerfile +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/Containerfile @@ -85,6 +85,10 @@ RUN python3 "${PYTHON_OVERLAY_ROOT}/overlay_contract.py" \ --stage target COPY bundle/runtime/patches/010-dflash-draft-load-config.patch \ ${PYTHON_OVERLAY_ROOT}/patches/010-dflash-draft-load-config.patch +COPY bundle/runtime/patches/011-recurrent-boundary-contract.patch \ + ${PYTHON_OVERLAY_ROOT}/patches/011-recurrent-boundary-contract.patch +COPY bundle/runtime/patches/012-recurrent-boundary-contract-tests.patch \ + ${PYTHON_OVERLAY_ROOT}/patches/012-recurrent-boundary-contract-tests.patch RUN root=/usr/local/lib/python3.12/dist-packages; \ target="${root}/vllm/v1/worker/gpu/spec_decode/dflash/utils.py"; \ before="$(sha256sum "${target}" | cut -d' ' -f1)"; \ @@ -147,6 +151,19 @@ RUN set -eu; \ patch --batch --forward -p1 -d "${root}" \ < /opt/sparkcache-vllm-patches/041-sparkcache-shared-prefix-attach.patch; \ test "$(sha256sum "${scheduler}" | cut -d' ' -f1)" = 6d397c97f31e67a75efc01b5ddd89fa58db425de14fa43965ef2d6146b6b9bdb; \ + recurrent_patch="${PYTHON_OVERLAY_ROOT}/patches/011-recurrent-boundary-contract.patch"; \ + recurrent_tests="${PYTHON_OVERLAY_ROOT}/patches/012-recurrent-boundary-contract-tests.patch"; \ + test "$(sha256sum "${recurrent_patch}" | cut -d' ' -f1)" = 5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0; \ + test "$(sha256sum "${recurrent_tests}" | cut -d' ' -f1)" = fd6ebb99c60f7d242d7594bc5de4136f73768ccfa7327cef562c241126d3581c; \ + test "$(sha256sum "${root}/vllm/v1/core/kv_cache_manager.py" | cut -d' ' -f1)" = ee03dc9ce2b720c0be6e9f572d23580ba96eff68fe3406250557e83071654af0; \ + test "$(sha256sum "${root}/vllm/v1/core/sched/output.py" | cut -d' ' -f1)" = 65235eba652e5a3ccee18bf3cbfeac9bf4da8fb9c61e961580f612cfb7e593bc; \ + test "$(sha256sum "${root}/vllm/v1/core/sched/scheduler.py" | cut -d' ' -f1)" = 6d397c97f31e67a75efc01b5ddd89fa58db425de14fa43965ef2d6146b6b9bdb; \ + test "$(sha256sum "${root}/vllm/v1/core/single_type_kv_cache_manager.py" | cut -d' ' -f1)" = e4b1c5c38b63b708fd55aa40a9ab0d008b266d006a63dcfcef55890ac1371cb8; \ + patch --batch --forward -p1 -d "${root}" < "${recurrent_patch}"; \ + test "$(sha256sum "${root}/vllm/v1/core/kv_cache_manager.py" | cut -d' ' -f1)" = c5b83d382c96b2bf8c466a993ed77123a14a971e2661797128533319388d0b5f; \ + test "$(sha256sum "${root}/vllm/v1/core/sched/output.py" | cut -d' ' -f1)" = 9911b3f9d21815a185285852b5a6176e5484e1ab0ff5c30f7caaa68ea0fab543; \ + test "$(sha256sum "${root}/vllm/v1/core/sched/scheduler.py" | cut -d' ' -f1)" = 260f36ce8fabf70c193b20009ea465eea7b1b6c8e9fb72f2307a01ba8fcf7b2a; \ + test "$(sha256sum "${root}/vllm/v1/core/single_type_kv_cache_manager.py" | cut -d' ' -f1)" = f67a1850a7e0288baaa6d42e7ec55b22b09c156720767e23acaabedcae333c8a; \ python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py \ --vllm-root "${root}" \ --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json; \ @@ -177,6 +194,7 @@ LABEL org.opencontainers.image.title="SparkRing GLM-5.3 adaptive-MTP Python over org.sparkring.vllm.python-overlay-manifest-sha256="${OVERLAY_MANIFEST_SHA256}" \ org.sparkring.vllm.dflash-draft-loader-patch-sha256="39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279" \ org.sparkring.vllm.dflash-draft-loader-postimage-sha256="98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4" \ + org.sparkring.vllm.recurrent-boundary-patch-sha256="5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0" \ org.sparkring.vllm.native-elf-manifest-sha256="${NATIVE_ELF_MANIFEST_SHA256}" \ org.sparkring.vllm.native-dispatch-manifest-sha256="${NATIVE_DISPATCH_MANIFEST_SHA256}" \ org.jovian.b12x.commit="${B12X_COMMIT}" \ @@ -185,6 +203,6 @@ LABEL org.opencontainers.image.title="SparkRing GLM-5.3 adaptive-MTP Python over org.sparkcache.source-tree="${SPARKCACHE_TREE}" \ org.sparkcache.source-sha256="${SPARKCACHE_SOURCE_SHA256}" \ org.sparkcache.cuda-placement-library-sha256="${SPARKCACHE_CUDA_PLACEMENT_SHA256}" \ - org.sparkcache.vllm-contract-sha256="6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024" \ + org.sparkcache.vllm-contract-sha256="f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811" \ org.sparkcache.deployment-profile="glm53-flash-adaptive-mtp-python-overlay" \ org.sparkring.source-receipt-sha256="${SOURCE_RECEIPT_SHA256}" diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/README.md b/runtime/glm53-flash-adaptive-mtp-python-overlay/README.md index d5770107..22140c7c 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/README.md +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/README.md @@ -25,12 +25,24 @@ files. Construction fails if any retained artifact changes. The output labels identify the retained native commit and overlaid Python commit separately; the image is not described as a source-built vLLM 0b67266 wheel. +The composed vLLM source also provides the opt-in +`SchedulerOutput.recurrent_boundary_blocks` interface for connectors that need +the exact Mamba replay-boundary state. Entries are emitted only after the block +hash proves both the KV group and token boundary. The scheduler stops aligned +prefill at the recurrent boundary so that page is materialized and hashed; +callers which overshoot it receive no unsafe substitute. Partial-tail +copy-on-write handoffs remain backward compatible. Aligned handoffs require the connector to +advertise `supports_recurrent_boundary_blocks`; other connectors retain no +additional recurrent page. Pins live through the worker execution fence and +are released during request cleanup. + The SparkCache source that routes reconstructed opaque pages through the SM121 -placement library is commit -`5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`, Git tree -`94c236b9dfbf5f70075eb47877fd9caaa5d8c249`. The builder verifies clean +placement library, restores shared segment objects, and publishes only the +copy-on-write tail is commit +`c56f77f97b3da907d32e888d82046359a62f0f88`, Git tree +`deac36758f86695cd13f07b2870c2e49842aed9c`. The builder verifies clean deployable-source SHA-256 -`bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63` +`788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98` before generating the SparkCache CUDA placement library. It applies the VMM exemption, load-failure recovery, shared-prefix retention, and follower-attachment patches in order, then runs the eleven-file lease-contract verifier. The diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/overlay_contract.py b/runtime/glm53-flash-adaptive-mtp-python-overlay/overlay_contract.py index ba492fa0..c421379a 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/overlay_contract.py +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/overlay_contract.py @@ -116,6 +116,13 @@ def final_file_hashes(pins: dict[str, Any]) -> dict[str, str]: result[target] = _sha256( patch["postimage_sha256"], f"{target} patch postimage" ) + for patch in pins["vllm"].get("composed_runtime_patches", ()): + for target_record in patch["targets"]: + target = safe_relative_path(target_record["path"]).as_posix() + result[target] = _sha256( + target_record["postimage_sha256"], + f"{target} composed patch postimage", + ) return result @@ -201,6 +208,62 @@ def validate_optional_load_config_fallback(source: str) -> None: ) +def validate_recurrent_boundary_sources(root: Path) -> None: + """Require exact-boundary selection and the SchedulerOutput hand-off.""" + + output_source = (root / "vllm/v1/core/sched/output.py").read_text( + encoding="utf-8" + ) + scheduler_source = (root / "vllm/v1/core/sched/scheduler.py").read_text( + encoding="utf-8" + ) + manager_source = ( + root / "vllm/v1/core/single_type_kv_cache_manager.py" + ).read_text(encoding="utf-8") + cache_source = (root / "vllm/v1/core/kv_cache_manager.py").read_text( + encoding="utf-8" + ) + required = { + "SchedulerOutput field": ( + "recurrent_boundary_blocks: " + "dict[str, list[tuple[int, int, int]]] | None = None", + output_source, + ), + "scheduler hand-off": ( + "recurrent_boundary_blocks=pending_recurrent_boundary_blocks", + scheduler_source, + ), + "connector capability gate": ( + '"supports_recurrent_boundary_blocks"', + scheduler_source, + ), + "consumer-compatible free fence": ( + "kv_transfer_config.is_kv_consumer", + scheduler_source, + ), + "overlapping-step free fence": ( + "multiple_inflight_batches and (", + scheduler_source, + ), + "prompt-minus-one replay rule": ( + "(request.num_prompt_tokens - 1) // self.block_pool.hash_block_size", + manager_source, + ), + "exact token-boundary proof": ( + "block.block_hash_num_tokens != replay_boundary", + manager_source, + ), + "exact group proof": ( + "get_group_id(block.block_hash) != self.kv_cache_group_id", + manager_source, + ), + "request-lifetime pin": ("self._pin_recurrent_boundary", cache_source), + } + for label, (fragment, source) in required.items(): + if fragment not in source: + raise ContractError(f"recurrent-boundary source omits {label}") + + def verify_vllm_runtime_patch_files( root: Path, pins: dict[str, Any], @@ -226,6 +289,20 @@ def verify_vllm_runtime_patch_files( "sha256": observed, } ) + for record in pins["vllm"].get("composed_runtime_patches", ()): + for target_record in record["targets"]: + relative = safe_relative_path(target_record["path"]) + path = root / relative + observed = sha256_file(path) + if observed != target_record["postimage_sha256"]: + raise ContractError( + f"vLLM composed patch postimage mismatch for {relative}: " + f"expected {target_record['postimage_sha256']}, got {observed}" + ) + compile(path.read_bytes(), str(path), "exec") + verified.append({"path": relative.as_posix(), "sha256": observed}) + if pins["vllm"].get("composed_runtime_patches"): + validate_recurrent_boundary_sources(root) loader = root / "vllm/model_executor/model_loader/__init__.py" validate_optional_load_config_fallback(loader.read_text(encoding="utf-8")) return verified diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/011-recurrent-boundary-contract.patch b/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/011-recurrent-boundary-contract.patch new file mode 100644 index 00000000..615edd58 --- /dev/null +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/011-recurrent-boundary-contract.patch @@ -0,0 +1,246 @@ +diff --git a/vllm/v1/core/kv_cache_manager.py b/vllm/v1/core/kv_cache_manager.py +index 645af9c87..80d4500ff 100644 +--- a/vllm/v1/core/kv_cache_manager.py ++++ b/vllm/v1/core/kv_cache_manager.py +@@ -1155,13 +1155,66 @@ class KVCacheManager: + block, + boundary_tokens, + ) in mgr.take_pending_partial_tail_offloads(): +- self.block_pool.touch((block,)) +- self._partial_tail_pins.setdefault(req_id, []).append(block) ++ block = self._pin_recurrent_boundary(req_id, block, boundary_tokens) + offloads.setdefault(req_id, []).append( + (group_id, block.block_id, boundary_tokens) + ) + return offloads + ++ def take_recurrent_boundary_blocks( ++ self, ++ partial_tail_offloads: dict[str, list[tuple[int, int, int]]] | None = None, ++ ) -> dict[str, list[tuple[int, int, int]]]: ++ """Drain hash-proven recurrent boundaries for connector metadata. ++ ++ The returned union preserves the partial-tail entries already exposed ++ to connectors and adds full Mamba pages at the scheduler replay ++ boundary. Pins use the existing request-cleanup lifetime. ++ """ ++ boundaries = { ++ req_id: list(entries) ++ for req_id, entries in (partial_tail_offloads or {}).items() ++ } ++ for mgr in self.coordinator.single_type_managers: ++ for ( ++ req_id, ++ group_id, ++ block, ++ boundary_tokens, ++ ) in mgr.take_pending_aligned_recurrent_boundaries(): ++ block = self._pin_recurrent_boundary(req_id, block, boundary_tokens) ++ entry = (group_id, block.block_id, boundary_tokens) ++ request_entries = boundaries.setdefault(req_id, []) ++ if entry not in request_entries: ++ request_entries.append(entry) ++ return boundaries ++ ++ def _pin_recurrent_boundary( ++ self, ++ request_id: str, ++ block: KVCacheBlock, ++ boundary_tokens: int, ++ ) -> KVCacheBlock: ++ """Pin one exact boundary without duplicating a prior hand-off.""" ++ assert not block.is_null ++ assert block.block_hash is not None ++ assert block.block_hash_num_tokens == boundary_tokens ++ pins = self._partial_tail_pins.setdefault(request_id, []) ++ for pinned in pins: ++ if ( ++ pinned.block_hash == block.block_hash ++ and pinned.block_hash_num_tokens == boundary_tokens ++ ): ++ return pinned ++ self.block_pool.touch((block,)) ++ pins.append(block) ++ return block ++ ++ def discard_aligned_recurrent_boundaries(self) -> None: ++ """Drop aligned hand-offs when the producer connector did not opt in.""" ++ for mgr in self.coordinator.single_type_managers: ++ mgr.take_pending_aligned_recurrent_boundaries() ++ + def new_step_starts(self) -> None: + """Notify the coordinator that a new step is starting.""" + self.coordinator.new_step_starts() +diff --git a/vllm/v1/core/sched/output.py b/vllm/v1/core/sched/output.py +index ad09529e8..f3a363085 100644 +--- a/vllm/v1/core/sched/output.py ++++ b/vllm/v1/core/sched/output.py +@@ -280,6 +280,13 @@ class SchedulerOutput: + # tail (mamba "align" CoW target). None unless partial hash hits are active. + partial_tail_offloads: dict[str, list[tuple[int, int, int]]] | None = None + ++ # Hash-proven recurrent-state blocks for external KV connectors: ++ # {request_id: [(group_id, block_id, boundary_tokens), ...]}. The union ++ # includes full aligned Mamba pages and the partial-tail CoW targets above. ++ # Consumers opting into aligned boundaries must finish their worker-side ++ # snapshot before request cleanup releases the scheduler pin. ++ recurrent_boundary_blocks: dict[str, list[tuple[int, int, int]]] | None = None ++ + # Dynamic speculative decoding: optimal K chosen by scheduler. + # Number of spec tokens to schedule for the next step. + num_spec_tokens_to_schedule: int | None = None +diff --git a/vllm/v1/core/sched/scheduler.py b/vllm/v1/core/sched/scheduler.py +index 562c240f4..3d1e13665 100644 +--- a/vllm/v1/core/sched/scheduler.py ++++ b/vllm/v1/core/sched/scheduler.py +@@ -165,7 +165,14 @@ class Scheduler(SchedulerInterface): + # Connector can reallocate and fill those blocks via a load that + # isn't ordered against that write, so defer freeing them. + multiple_inflight_batches = self.vllm_config.max_concurrent_batches > 1 +- if multiple_inflight_batches and kv_transfer_config.is_kv_consumer: ++ if multiple_inflight_batches and ( ++ kv_transfer_config.is_kv_consumer ++ or bool( ++ getattr( ++ self.connector, "supports_recurrent_boundary_blocks", False ++ ) ++ ) ++ ): + self.defer_block_free = True + + self.requires_kv_delivery = self.connector.requires_kv_delivery +@@ -1331,6 +1338,7 @@ class Scheduler(SchedulerInterface): + # pin); the manager drops stale entries when the request's blocks are + # popped for free. + pending_partial_tail_offloads = None ++ pending_recurrent_boundary_blocks = None + if ( + self.connector is not None + and self.vllm_config.kv_transfer_config is not None +@@ -1339,6 +1347,19 @@ class Scheduler(SchedulerInterface): + pending_partial_tail_offloads = ( + self.kv_cache_manager.take_partial_tail_offloads() or None + ) ++ if bool( ++ getattr(self.connector, "supports_recurrent_boundary_blocks", False) ++ ): ++ # Opting in requires the connector's worker snapshot to finish ++ # before request cleanup releases the boundary pins. ++ pending_recurrent_boundary_blocks = ( ++ self.kv_cache_manager.take_recurrent_boundary_blocks( ++ pending_partial_tail_offloads ++ ) ++ or None ++ ) ++ else: ++ self.kv_cache_manager.discard_aligned_recurrent_boundaries() + + kv_cache_block_copies, cow_retained_blocks = ( + self.kv_cache_manager.take_kv_cache_block_copies() +@@ -1391,6 +1412,7 @@ class Scheduler(SchedulerInterface): + new_block_ids_to_zero=self._get_new_block_ids_to_zero(), + kv_cache_block_copies=pending_kv_cache_block_copies, + partial_tail_offloads=pending_partial_tail_offloads, ++ recurrent_boundary_blocks=pending_recurrent_boundary_blocks, + num_spec_tokens_to_schedule=num_spec_tokens_to_schedule, + ec_manager_metadata=self.encoder_cache_manager.get_manager_metadata(), + ) +diff --git a/vllm/v1/core/single_type_kv_cache_manager.py b/vllm/v1/core/single_type_kv_cache_manager.py +index ceb56a598..aef3efe8e 100644 +--- a/vllm/v1/core/single_type_kv_cache_manager.py ++++ b/vllm/v1/core/single_type_kv_cache_manager.py +@@ -13,6 +13,7 @@ from vllm.v1.core.kv_cache_utils import ( + BlockHashListWithBlockSize, + BlockHashWithGroupId, + KVCacheBlock, ++ get_group_id, + resolve_block_hashes, + ) + from vllm.v1.kv_cache_interface import ( +@@ -121,6 +122,9 @@ class SingleTypeKVCacheManager(ABC): + self._pending_partial_tail_offloads: list[ + tuple[str, int, KVCacheBlock, int] + ] = [] ++ self._pending_aligned_recurrent_boundaries: list[ ++ tuple[str, int, KVCacheBlock, int] ++ ] = [] + + @classmethod + def _get_num_evictable_blocks(cls, blocks: Sequence[KVCacheBlock]): +@@ -399,6 +403,14 @@ class SingleTypeKVCacheManager(ABC): + self._pending_partial_tail_offloads = [] + return pending + ++ def take_pending_aligned_recurrent_boundaries( ++ self, ++ ) -> list[tuple[str, int, KVCacheBlock, int]]: ++ """Drain full-page Mamba boundary hand-offs.""" ++ pending = self._pending_aligned_recurrent_boundaries ++ self._pending_aligned_recurrent_boundaries = [] ++ return pending ++ + def _apply_cow( + self, + request_id: str, +@@ -1697,6 +1709,11 @@ class MambaManager(SingleTypeKVCacheManager): + for entry in self._pending_partial_tail_offloads + if entry[0] != request_id + ] ++ self._pending_aligned_recurrent_boundaries = [ ++ entry ++ for entry in self._pending_aligned_recurrent_boundaries ++ if entry[0] != request_id ++ ] + return super().pop_blocks_for_free(request_id) + + def get_num_skipped_tokens(self, num_computed_tokens: int) -> int: +@@ -1720,6 +1737,7 @@ class MambaManager(SingleTypeKVCacheManager): + partial_hash = self._cache_partial_tail_block(request, num_tokens) + if partial_hash is not None: + self.cached_blocks_this_step.add(partial_hash) ++ self._queue_aligned_recurrent_boundary(request, num_tokens) + if num_cached_blocks_after > num_cached_blocks_before: + for block in self.req_to_blocks[request.request_id][ + num_cached_blocks_before:num_cached_blocks_after +@@ -1778,6 +1796,41 @@ class MambaManager(SingleTypeKVCacheManager): + self._producer_partial_tail_reqs[request.request_id] = num_tokens + return partial_hash + ++ def _queue_aligned_recurrent_boundary( ++ self, ++ request: Request, ++ num_tokens: int, ++ ) -> None: ++ """Queue a full Mamba page only when its hash proves the replay boundary.""" ++ if num_tokens <= 0 or num_tokens % self.block_size != 0: ++ return ++ replay_boundary = ( ++ (request.num_prompt_tokens - 1) // self.block_pool.hash_block_size ++ ) * self.block_pool.hash_block_size ++ if num_tokens != replay_boundary: ++ return ++ ++ block_idx = num_tokens // self.block_size - 1 ++ blocks = self.req_to_blocks[request.request_id] ++ if block_idx >= len(blocks): ++ return ++ block = blocks[block_idx] ++ if ( ++ block.is_null ++ or block.block_hash is None ++ or block.block_hash_num_tokens != replay_boundary ++ or get_group_id(block.block_hash) != self.kv_cache_group_id ++ ): ++ return ++ self._pending_aligned_recurrent_boundaries.append( ++ ( ++ request.request_id, ++ self.kv_cache_group_id, ++ block, ++ replay_boundary, ++ ) ++ ) ++ + + class CrossAttentionManager(SingleTypeKVCacheManager): + """Manager for cross-attention KV cache in encoder-decoder models.""" diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/012-recurrent-boundary-contract-tests.patch b/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/012-recurrent-boundary-contract-tests.patch new file mode 100644 index 00000000..b49a90ae --- /dev/null +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/patches/012-recurrent-boundary-contract-tests.patch @@ -0,0 +1,479 @@ +diff --git a/tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py b/tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py +index eff889b98..4f4975379 100644 +--- a/tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py ++++ b/tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py +@@ -4,6 +4,7 @@ + "align") models: scheduler chunk splitting, partial tail registration, CoW + on partial hits, and same-step deferral.""" + ++import inspect + from math import lcm + from types import SimpleNamespace + from unittest.mock import MagicMock +@@ -12,6 +13,17 @@ import pytest + import torch + + from tests.v1.core.test_prefix_caching import make_kv_cache_manager, make_request ++from vllm.config import ( ++ CacheConfig, ++ DeviceConfig, ++ KVTransferConfig, ++ ModelConfig, ++ SchedulerConfig, ++ SpeculativeConfig, ++ VllmConfig, ++) ++from vllm.distributed.kv_transfer.kv_connector.factory import KVConnectorFactory ++from vllm.distributed.kv_transfer.kv_connector.v1.base import SupportsHMA + from vllm.utils.hashing import sha256 + from vllm.v1.core.kv_cache_utils import ( + KVCacheBlockCopy, +@@ -20,6 +32,7 @@ from vllm.v1.core.kv_cache_utils import ( + init_none_hash, + ) + from vllm.v1.core.sched.scheduler import Scheduler ++from vllm.v1.core.single_type_kv_cache_manager import register_all_kvcache_specs + from vllm.v1.kv_cache_interface import ( + FullAttentionSpec, + KVCacheConfig, +@@ -27,6 +40,9 @@ from vllm.v1.kv_cache_interface import ( + MambaSpec, + SlidingWindowSpec, + ) ++from vllm.v1.outputs import ModelRunnerOutput ++from vllm.v1.request import RequestStatus ++from vllm.v1.structured_output import StructuredOutputManager + + + @pytest.fixture(autouse=True) +@@ -69,6 +85,14 @@ def test_capable_connector_uses_divergent_partial_hit_lookup(): + manager.get_computed_blocks.assert_not_called() + + ++@pytest.mark.skip_global_cleanup ++def test_recurrent_boundary_capability_defers_overlapping_block_free(): ++ source = inspect.getsource(Scheduler.__init__) ++ assert "multiple_inflight_batches and (" in source ++ assert "kv_transfer_config.is_kv_consumer" in source ++ assert '"supports_recurrent_boundary_blocks"' in source ++ ++ + def make_full_mamba_manager( + *, + dcp_world_size: int, +@@ -856,6 +880,415 @@ def test_take_partial_tail_offloads_empty_without_partial_tail(): + assert manager.take_partial_tail_offloads() == {} + + ++@pytest.mark.parametrize("prompt_tokens", [6992, 7168]) ++@pytest.mark.skip_global_cleanup ++def test_aligned_recurrent_boundary_ignores_seven_speculative_slots( ++ prompt_tokens: int, ++): ++ """The GLM aligned boundary is selected by its exact hash, not by scanning ++ the later running and DFlash speculative slots.""" ++ hash_block_size = 256 ++ mamba_block_size = 2304 ++ boundary_tokens = 6912 ++ kv_cache_config = KVCacheConfig( ++ num_blocks=128, ++ kv_cache_tensors=[], ++ kv_cache_groups=[ ++ KVCacheGroupSpec( ++ ["full"], ++ FullAttentionSpec( ++ block_size=hash_block_size, ++ num_kv_heads=1, ++ head_size=1, ++ dtype=torch.float32, ++ ), ++ ), ++ KVCacheGroupSpec( ++ ["mamba"], ++ MambaSpec( ++ block_size=mamba_block_size, ++ shapes=(1, 1), ++ dtypes=(torch.float32,), ++ mamba_cache_mode="align", ++ num_speculative_blocks=7, ++ ), ++ ), ++ ], ++ ) ++ manager = make_kv_cache_manager( ++ kv_cache_config=kv_cache_config, ++ max_model_len=8192, ++ enable_caching=True, ++ scheduler_block_size=mamba_block_size, ++ hash_block_size=hash_block_size, ++ ) ++ request = make_request( ++ "aligned", ++ list(range(prompt_tokens)), ++ hash_block_size, ++ sha256, ++ ) ++ computed_blocks, num_computed, _ = manager.get_computed_blocks(request) ++ assert manager.allocate_slots( ++ request, boundary_tokens, num_computed, computed_blocks ++ ) is not None ++ ++ assert manager.take_partial_tail_offloads() == {} ++ boundaries = manager.take_recurrent_boundary_blocks() ++ assert list(boundaries) == ["aligned"] ++ assert len(boundaries["aligned"]) == 1 ++ group_id, block_id, emitted_boundary = boundaries["aligned"][0] ++ assert (group_id, emitted_boundary) == (1, boundary_tokens) ++ ++ mamba_blocks = manager.get_blocks("aligned").blocks[1] ++ assert len(mamba_blocks) == 3 + 7 ++ boundary_block = mamba_blocks[2] ++ assert block_id == boundary_block.block_id ++ assert block_id not in {block.block_id for block in mamba_blocks[3:]} ++ assert boundary_block.block_hash_num_tokens == boundary_tokens ++ assert get_group_id(boundary_block.block_hash) == group_id ++ ++ # Finish the prompt, then schedule one decode token. The second allocation ++ # runs remove_skipped_blocks, which nulls arithmetic slot 2. The hand-off ++ # must keep naming and pinning the exact cached boundary block rather than ++ # selecting a later running or speculative slot. ++ request.num_computed_tokens = boundary_tokens ++ assert manager.allocate_slots( ++ request, prompt_tokens - boundary_tokens ++ ) is not None ++ request.num_computed_tokens = prompt_tokens ++ request.append_output_token_ids([prompt_tokens]) ++ assert manager.allocate_slots(request, 1) is not None ++ ++ assert mamba_blocks[2].is_null ++ assert boundary_block.ref_cnt == 1 ++ assert block_id not in { ++ block.block_id for block in mamba_blocks[3:] if not block.is_null ++ } ++ assert boundaries["aligned"] == [(1, block_id, boundary_tokens)] ++ ++ manager.free(request) ++ assert boundary_block.ref_cnt == 0 ++ ++ ++@pytest.mark.parametrize("prompt_tokens", [6992, 7168]) ++@pytest.mark.skip_global_cleanup ++def test_crossed_aligned_boundary_refuses_unmaterialized_handoff( ++ prompt_tokens: int, ++): ++ """A caller that skips the required 6,912-token scheduler stop has no ++ hash-proven recurrent page to publish and must fail closed.""" ++ hash_block_size = 256 ++ mamba_block_size = 2304 ++ kv_cache_config = KVCacheConfig( ++ num_blocks=128, ++ kv_cache_tensors=[], ++ kv_cache_groups=[ ++ KVCacheGroupSpec( ++ ["full"], ++ FullAttentionSpec( ++ block_size=hash_block_size, ++ num_kv_heads=1, ++ head_size=1, ++ dtype=torch.float32, ++ ), ++ ), ++ KVCacheGroupSpec( ++ ["mamba"], ++ MambaSpec( ++ block_size=mamba_block_size, ++ shapes=(1, 1), ++ dtypes=(torch.float32,), ++ mamba_cache_mode="align", ++ num_speculative_blocks=7, ++ ), ++ ), ++ ], ++ ) ++ manager = make_kv_cache_manager( ++ kv_cache_config=kv_cache_config, ++ max_model_len=8192, ++ enable_caching=True, ++ scheduler_block_size=mamba_block_size, ++ hash_block_size=hash_block_size, ++ ) ++ request = make_request( ++ "crossed-aligned", ++ list(range(prompt_tokens)), ++ hash_block_size, ++ sha256, ++ ) ++ computed_blocks, num_computed, _ = manager.get_computed_blocks(request) ++ ++ assert manager.allocate_slots( ++ request, prompt_tokens, num_computed, computed_blocks ++ ) is not None ++ assert manager.take_partial_tail_offloads() == {} ++ assert manager.take_recurrent_boundary_blocks() == {} ++ assert "crossed-aligned" not in manager._partial_tail_pins ++ ++ mamba_blocks = manager.get_blocks("crossed-aligned").blocks[1] ++ assert all(block.is_null for block in mamba_blocks[:3]) ++ manager.free(request) ++ ++ ++@pytest.mark.skip_global_cleanup ++def test_recurrent_boundary_union_preserves_partial_tail_handoff(): ++ hash_block_size = 2 ++ manager = make_full_mamba_manager( ++ dcp_world_size=1, ++ hash_block_size=hash_block_size, ++ full_block_size=hash_block_size, ++ mamba_block_size=2 * hash_block_size, ++ ) ++ request = make_request("partial", [0, 0, 1, 1, 2, 2], 2, sha256) ++ computed_blocks, num_computed, _ = manager.get_computed_blocks(request) ++ assert manager.allocate_slots( ++ request, 6, num_computed, computed_blocks ++ ) is not None ++ request.num_computed_tokens = 6 ++ request.append_output_token_ids([3]) ++ assert manager.allocate_slots(request, 1) is not None ++ ++ partial = manager.take_partial_tail_offloads() ++ assert manager.take_recurrent_boundary_blocks(partial) == partial ++ ++ ++@pytest.mark.skip_global_cleanup ++def test_aligned_recurrent_boundary_can_be_discarded_without_a_pin(): ++ manager = make_full_mamba_manager( ++ dcp_world_size=1, ++ hash_block_size=2, ++ full_block_size=2, ++ mamba_block_size=4, ++ ) ++ request = make_request("no-capability", [0, 0, 1, 1, 2], 2, sha256) ++ computed_blocks, num_computed, _ = manager.get_computed_blocks(request) ++ assert manager.allocate_slots(request, 4, num_computed, computed_blocks) is not None ++ boundary = manager.get_blocks("no-capability").blocks[1][0] ++ ref_cnt = boundary.ref_cnt ++ ++ manager.discard_aligned_recurrent_boundaries() ++ ++ assert manager.take_recurrent_boundary_blocks() == {} ++ assert boundary.ref_cnt == ref_cnt ++ ++ ++class _RecurrentProducerConnector(SupportsHMA): ++ """Minimal scheduler-side producer at the real KV connector seam.""" ++ ++ supports_divergent_local_hybrid_hits = False ++ supports_recurrent_boundary_blocks = True ++ requires_kv_delivery = False ++ ++ def bind_gpu_block_pool(self, block_pool) -> None: ++ self.block_pool = block_pool ++ ++ def on_new_request(self, request) -> None: ++ pass ++ ++ def get_num_new_matched_tokens(self, request, num_computed_tokens): ++ return 0, False ++ ++ def update_state_after_alloc(self, request, blocks, num_external_tokens) -> None: ++ pass ++ ++ def build_connector_meta(self, scheduler_output): ++ return None ++ ++ def get_kv_connector_stats(self): ++ return None ++ ++ def take_events(self): ++ return () ++ ++ def request_finished_all_groups(self, request, block_ids): ++ return False, None ++ ++ ++def _make_glm_recurrent_scheduler( ++ monkeypatch: pytest.MonkeyPatch, ++) -> Scheduler: ++ """Build a CPU-only Scheduler with the live GLM recurrent geometry. ++ ++ The ngram fixture is switched to multi-module MTP after construction. This ++ avoids loading a drafter while exercising the same ``use_eagle`` prefill ++ split and seven-slot lookahead path used by the GLM DFlash/MTP runtimes. ++ """ ++ monkeypatch.setenv("VLLM_ALLOW_LONG_MAX_MODEL_LEN", "1") ++ hash_block_size = 256 ++ mamba_block_size = 2304 ++ connector = _RecurrentProducerConnector() ++ monkeypatch.setattr( ++ KVConnectorFactory, ++ "create_connector", ++ lambda **_kwargs: connector, ++ ) ++ model_config = ModelConfig( ++ model="facebook/opt-125m", ++ trust_remote_code=True, ++ dtype="float16", ++ seed=42, ++ skip_tokenizer_init=True, ++ max_model_len=16384, ++ ) ++ speculative_config = SpeculativeConfig(model="ngram", num_speculative_tokens=7) ++ speculative_config.method = "mtp" ++ vllm_config = VllmConfig( ++ scheduler_config=SchedulerConfig( ++ max_num_seqs=4, ++ max_num_batched_tokens=8192, ++ max_model_len=16384, ++ enable_chunked_prefill=True, ++ is_encoder_decoder=False, ++ watermark=0.0, ++ ), ++ model_config=model_config, ++ speculative_config=speculative_config, ++ cache_config=CacheConfig( ++ block_size=hash_block_size, ++ enable_prefix_caching=True, ++ mamba_cache_mode="align", ++ prefix_match_unit=hash_block_size, ++ ), ++ kv_transfer_config=KVTransferConfig( ++ kv_connector="SimpleCPUOffloadConnector", ++ kv_role="kv_producer", ++ ), ++ device_config=DeviceConfig(device="cpu"), ++ ) ++ vllm_config.cache_config.num_gpu_blocks = 256 ++ kv_cache_config = KVCacheConfig( ++ num_blocks=256, ++ kv_cache_tensors=[], ++ kv_cache_groups=[ ++ KVCacheGroupSpec( ++ ["full"], ++ FullAttentionSpec( ++ block_size=hash_block_size, ++ num_kv_heads=1, ++ head_size=1, ++ dtype=torch.float32, ++ ), ++ ), ++ KVCacheGroupSpec( ++ ["mamba"], ++ MambaSpec( ++ block_size=mamba_block_size, ++ shapes=(1, 1), ++ dtypes=(torch.float32,), ++ mamba_cache_mode="align", ++ num_speculative_blocks=7, ++ ), ++ ), ++ ], ++ ) ++ register_all_kvcache_specs(vllm_config) ++ return Scheduler( ++ vllm_config=vllm_config, ++ kv_cache_config=kv_cache_config, ++ structured_output_manager=StructuredOutputManager(vllm_config), ++ block_size=mamba_block_size, ++ hash_block_size=hash_block_size, ++ ) ++ ++ ++def _empty_model_output(request_id: str) -> ModelRunnerOutput: ++ return ModelRunnerOutput( ++ req_ids=[request_id], ++ req_id_to_index={request_id: 0}, ++ sampled_token_ids=[[]], ++ logprobs=None, ++ prompt_logprobs_dict={}, ++ pooler_output=[], ++ ) ++ ++ ++@pytest.mark.skip_global_cleanup ++def test_aligned_glm_boundary_is_in_the_allocating_scheduler_output( ++ monkeypatch: pytest.MonkeyPatch, ++) -> None: ++ scheduler = _make_glm_recurrent_scheduler(monkeypatch) ++ request = make_request("aligned-output", list(range(6992)), 256, sha256) ++ scheduler.add_request(request) ++ ++ first = scheduler.schedule() ++ assert first.num_scheduled_tokens == {"aligned-output": 6656} ++ assert first.recurrent_boundary_blocks is None ++ scheduler.update_from_output(first, _empty_model_output("aligned-output")) ++ ++ output = scheduler.schedule() ++ ++ assert output.num_scheduled_tokens == {"aligned-output": 256} ++ assert request.num_computed_tokens == 6912 ++ assert output.partial_tail_offloads is None ++ assert output.recurrent_boundary_blocks is not None ++ ((group_id, block_id, boundary_tokens),) = output.recurrent_boundary_blocks[ ++ "aligned-output" ++ ] ++ assert (group_id, boundary_tokens) == (1, 6912) ++ boundary_block = scheduler.kv_cache_manager.block_pool.blocks[block_id] ++ assert boundary_block.block_hash_num_tokens == boundary_tokens ++ assert get_group_id(boundary_block.block_hash) == group_id ++ assert scheduler.kv_cache_manager._partial_tail_pins["aligned-output"] == [ ++ boundary_block ++ ] ++ ++ scheduler.update_from_output(output, _empty_model_output("aligned-output")) ++ scheduler.finish_requests("aligned-output", RequestStatus.FINISHED_ABORTED) ++ assert "aligned-output" not in scheduler.kv_cache_manager._partial_tail_pins ++ assert boundary_block.ref_cnt == 0 ++ ++ ++@pytest.mark.skip_global_cleanup ++def test_partial_glm_boundary_is_in_next_scheduler_output_after_cow( ++ monkeypatch: pytest.MonkeyPatch, ++) -> None: ++ scheduler = _make_glm_recurrent_scheduler(monkeypatch) ++ request = make_request("partial-output", list(range(8193)), 256, sha256) ++ scheduler.add_request(request) ++ ++ first = scheduler.schedule() ++ assert first.num_scheduled_tokens == {"partial-output": 7936} ++ assert first.kv_cache_block_copies is None ++ assert first.partial_tail_offloads is None ++ assert first.recurrent_boundary_blocks is None ++ scheduler.update_from_output(first, _empty_model_output("partial-output")) ++ ++ boundary = scheduler.schedule() ++ assert boundary.num_scheduled_tokens == {"partial-output": 256} ++ assert request.num_computed_tokens == 8192 ++ assert boundary.kv_cache_block_copies is None ++ assert boundary.partial_tail_offloads is None ++ assert boundary.recurrent_boundary_blocks is None ++ scheduler.update_from_output(boundary, _empty_model_output("partial-output")) ++ ++ second = scheduler.schedule() ++ ++ assert second.num_scheduled_tokens == {"partial-output": 1} ++ assert second.kv_cache_block_copies is not None ++ assert second.partial_tail_offloads is not None ++ assert second.recurrent_boundary_blocks == second.partial_tail_offloads ++ ((group_id, block_id, boundary_tokens),) = second.recurrent_boundary_blocks[ ++ "partial-output" ++ ] ++ assert (group_id, boundary_tokens) == (1, 8192) ++ cow_copy = next( ++ copy ++ for copy in second.kv_cache_block_copies ++ if copy.dst_block_id == block_id ++ ) ++ assert cow_copy.src_block_id != block_id ++ cow_block = scheduler.kv_cache_manager.block_pool.blocks[block_id] ++ assert scheduler.kv_cache_manager._partial_tail_pins["partial-output"] == [ ++ cow_block ++ ] ++ ++ scheduler.update_from_output(second, _empty_model_output("partial-output")) ++ scheduler.finish_requests("partial-output", RequestStatus.FINISHED_ABORTED) ++ assert "partial-output" not in scheduler.kv_cache_manager._partial_tail_pins ++ assert cow_block.ref_cnt == 0 ++ + def test_truncate_computed_blocks_preserves_sparse_prefix_positions(): + """truncate_computed_blocks slices each group by its own block size, + keeps null placeholders in the retained prefix, and leaves the original diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/pins.json b/runtime/glm53-flash-adaptive-mtp-python-overlay/pins.json index 6cf2a799..c470f63a 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/pins.json +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/pins.json @@ -39,6 +39,43 @@ "contract": "DFlash passes SpeculativeConfig.draft_load_config to get_model; None retains the target LoadConfig fallback." } ], + "composed_runtime_patches": [ + { + "status": "implemented", + "path": "runtime/glm53-flash-adaptive-mtp-python-overlay/patches/011-recurrent-boundary-contract.patch", + "sha256": "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0", + "contract": "SchedulerOutput.recurrent_boundary_blocks exposes only hash-proven aligned Mamba pages and partial-tail CoW targets; request cleanup releases their pins.", + "targets": [ + { + "path": "vllm/v1/core/kv_cache_manager.py", + "preimage_sha256": "ee03dc9ce2b720c0be6e9f572d23580ba96eff68fe3406250557e83071654af0", + "postimage_sha256": "c5b83d382c96b2bf8c466a993ed77123a14a971e2661797128533319388d0b5f" + }, + { + "path": "vllm/v1/core/sched/output.py", + "preimage_sha256": "65235eba652e5a3ccee18bf3cbfeac9bf4da8fb9c61e961580f612cfb7e593bc", + "postimage_sha256": "9911b3f9d21815a185285852b5a6176e5484e1ab0ff5c30f7caaa68ea0fab543" + }, + { + "path": "vllm/v1/core/sched/scheduler.py", + "preimage_sha256": "6d397c97f31e67a75efc01b5ddd89fa58db425de14fa43965ef2d6146b6b9bdb", + "postimage_sha256": "260f36ce8fabf70c193b20009ea465eea7b1b6c8e9fb72f2307a01ba8fcf7b2a" + }, + { + "path": "vllm/v1/core/single_type_kv_cache_manager.py", + "preimage_sha256": "e4b1c5c38b63b708fd55aa40a9ab0d008b266d006a63dcfcef55890ac1371cb8", + "postimage_sha256": "f67a1850a7e0288baaa6d42e7ec55b22b09c156720767e23acaabedcae333c8a" + } + ], + "test_patch": { + "path": "runtime/glm53-flash-adaptive-mtp-python-overlay/patches/012-recurrent-boundary-contract-tests.patch", + "sha256": "fd6ebb99c60f7d242d7594bc5de4136f73768ccfa7327cef562c241126d3581c", + "target": "tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py", + "preimage_sha256": "2f58e600fd39413b385b9e96b3c642a9ffb051bedcfc247a700fb532b35ec185", + "postimage_sha256": "1a43d1a4bd43ee81dd89975e23ea6b0333e287d53c7a7e8c21f1554a6ca8b32a" + } + } + ], "native_source_objects": { "csrc": "9ada29088768f1bc08dadd2eed3c9738eb9ac8a1", "cmake": "5e5bbdbe1c1b3a479656d8d6a41cc32a1982c43d", @@ -77,9 +114,9 @@ }, "sparkcache": { "repository": "https://github.com/FujitsuPolycom/sparkcache.git", - "commit": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", - "source_tree_sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", + "commit": "c56f77f97b3da907d32e888d82046359a62f0f88", + "tree": "deac36758f86695cd13f07b2870c2e49842aed9c", + "source_tree_sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", "cuda_config_schema": "canonical-v1", "canonical_cuda_config_keys": [ "spark_cache_cuda_restore", @@ -90,7 +127,7 @@ ], "contract": { "path": "sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json", - "sha256": "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024", + "sha256": "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811", "files": 11 }, "patches": [ diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/prepare_context.py b/runtime/glm53-flash-adaptive-mtp-python-overlay/prepare_context.py index 0167b0dc..5bdf79b8 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/prepare_context.py +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/prepare_context.py @@ -10,6 +10,7 @@ import os import shutil import subprocess +import sys from pathlib import Path from typing import Any, Iterable @@ -206,6 +207,84 @@ def verify_vllm_runtime_patches( ) +def verify_composed_runtime_patches( + source: Path, + pins: dict[str, Any], + patch_root: Path, + sparkcache: Path, +) -> None: + """Verify patches whose preimages include the SparkCache vLLM chain.""" + + contract = sparkcache / pins["sparkcache"]["contract"]["path"] + if sha256_file(contract) != pins["sparkcache"]["contract"]["sha256"]: + raise PrepareError("SparkCache vLLM contract differs from its pin") + applied_sparkcache: list[Path] = [] + applied_composed: list[Path] = [] + try: + for record in pins["sparkcache"]["patches"]: + patch = sparkcache / record["path"] + run(("git", "-C", str(source), "apply", str(patch))) + applied_sparkcache.append(patch) + + for record in pins["vllm"].get("composed_runtime_patches", ()): + patch = patch_root / Path(record["path"]).name + if sha256_file(patch) != record["sha256"]: + raise PrepareError(f"vLLM composed patch mismatch: {record['path']}") + for target_record in record["targets"]: + target = source / target_record["path"] + if sha256_file(target) != target_record["preimage_sha256"]: + raise PrepareError( + "vLLM composed patch preimage mismatch: " + f"{target_record['path']}" + ) + run(("git", "-C", str(source), "apply", "--check", str(patch))) + run(("git", "-C", str(source), "apply", str(patch))) + applied_composed.append(patch) + for target_record in record["targets"]: + target = source / target_record["path"] + if sha256_file(target) != target_record["postimage_sha256"]: + raise PrepareError( + "vLLM composed patch postimage mismatch: " + f"{target_record['path']}" + ) + + test_record = record["test_patch"] + test_patch = patch_root / Path(test_record["path"]).name + test_target = source / test_record["target"] + if sha256_file(test_patch) != test_record["sha256"]: + raise PrepareError("vLLM recurrent-boundary test patch mismatch") + if sha256_file(test_target) != test_record["preimage_sha256"]: + raise PrepareError("vLLM recurrent-boundary test preimage mismatch") + run(("git", "-C", str(source), "apply", "--check", str(test_patch))) + run(("git", "-C", str(source), "apply", str(test_patch))) + try: + if sha256_file(test_target) != test_record["postimage_sha256"]: + raise PrepareError("vLLM recurrent-boundary test postimage mismatch") + finally: + run(("git", "-C", str(source), "apply", "--reverse", str(test_patch))) + verifier = sparkcache / "sparkcache/runtime_patches/verify_lease_contract.py" + run( + ( + sys.executable, + str(verifier), + "--vllm-root", + str(source), + "--contract", + str(contract), + ) + ) + finally: + for patch in reversed(applied_composed): + run(("git", "-C", str(source), "apply", "--reverse", str(patch))) + for patch in reversed(applied_sparkcache): + run(("git", "-C", str(source), "apply", "--reverse", str(patch))) + verify_git_source( + source, + commit=pins["vllm"]["python_commit"], + tree=pins["vllm"]["python_tree"], + ) + + def copy_overlay(source: Path, destination: Path, manifest: dict[str, Any]) -> None: target = manifest["target"]["commit"] for record in manifest["files"]: @@ -305,6 +384,7 @@ def prepare(output: Path, *, repository_root: Path = ROOT) -> dict[str, Any]: path = sparkcache / patch["path"] if sha256_file(path) != patch["sha256"]: raise PrepareError(f"SparkCache patch mismatch: {patch['path']}") + verify_composed_runtime_patches(vllm, pins, patch_root, sparkcache) contract = sparkcache / pins["sparkcache"]["contract"]["path"] if sha256_file(contract) != pins["sparkcache"]["contract"]["sha256"]: raise PrepareError("SparkCache vLLM contract differs from its pin") @@ -320,7 +400,10 @@ def prepare(output: Path, *, repository_root: Path = ROOT) -> dict[str, Any]: ): copy_file(HERE / filename, runtime / filename) runtime_patches = [] - for patch in pins["vllm"].get("runtime_patches", ()): + patch_records = list(pins["vllm"].get("runtime_patches", ())) + for patch in pins["vllm"].get("composed_runtime_patches", ()): + patch_records.extend((patch, patch["test_patch"])) + for patch in patch_records: name = Path(patch["path"]).name copy_file(patch_root / name, runtime / "patches" / name) runtime_patches.append(f"bundle/runtime/patches/{name}") @@ -360,6 +443,9 @@ def prepare(output: Path, *, repository_root: Path = ROOT) -> dict[str, Any]: }, }, "vllm_runtime_patches": pins["vllm"].get("runtime_patches", []), + "vllm_composed_runtime_patches": pins["vllm"].get( + "composed_runtime_patches", [] + ), "files": {relative: sha256_file(output / relative) for relative in receipt_inputs}, } (output / "receipt.json").write_text( @@ -390,11 +476,21 @@ def verify_context(context: Path) -> dict[str, Any]: "runtime_patches", [] ): raise PrepareError("prepared context vLLM runtime patch receipt differs") + if receipt.get("vllm_composed_runtime_patches") != pins["vllm"].get( + "composed_runtime_patches", [] + ): + raise PrepareError("prepared context composed vLLM patch receipt differs") verify_vllm_runtime_patches( context / "bundle/sources/vllm", pins, context / "bundle/runtime/patches", ) + verify_composed_runtime_patches( + context / "bundle/sources/vllm", + pins, + context / "bundle/runtime/patches", + context / "bundle/sources/sparkcache", + ) verify_git_source( context / "bundle/sources/b12x", commit=pins["b12x"]["commit"], diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/test_public_python_overlay.py b/runtime/glm53-flash-adaptive-mtp-python-overlay/test_public_python_overlay.py index 3e9d12d3..49ff22c5 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/test_public_python_overlay.py +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/test_public_python_overlay.py @@ -2,6 +2,7 @@ import hashlib import importlib.util +import inspect import json import re from dataclasses import dataclass @@ -51,11 +52,14 @@ def test_overlay_pins_public_base_and_mixed_vllm_provenance() -> None: assert pins["b12x"]["commit"] != pins["b12x"]["base_commit"] sparkcache = pins["sparkcache"] assert sparkcache["commit"] == ( - "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3" + "c56f77f97b3da907d32e888d82046359a62f0f88" ) - assert sparkcache["tree"] == "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" + assert sparkcache["tree"] == "deac36758f86695cd13f07b2870c2e49842aed9c" assert sparkcache["source_tree_sha256"] == ( - "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63" + "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98" + ) + assert sparkcache["contract"]["sha256"] == ( + "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811" ) assert sparkcache["cuda_config_schema"] == "canonical-v1" assert pins["dependencies"]["torch"] == "2.13.0+cu130" @@ -104,6 +108,60 @@ def test_dflash_loader_patch_binds_exact_0b_preimage_and_postimage() -> None: ]["sha256"] +def test_recurrent_boundary_patch_binds_composed_source_and_exact_tests() -> None: + pins = json.loads(PINS.read_text(encoding="utf-8")) + (record,) = pins["vllm"]["composed_runtime_patches"] + assert prepare.sha256_file(HERE / "patches" / Path(record["path"]).name) == ( + record["sha256"] + ) + test_record = record["test_patch"] + assert prepare.sha256_file( + HERE / "patches" / Path(test_record["path"]).name + ) == test_record["sha256"] + assert {target["path"] for target in record["targets"]} == { + "vllm/v1/core/kv_cache_manager.py", + "vllm/v1/core/sched/output.py", + "vllm/v1/core/sched/scheduler.py", + "vllm/v1/core/single_type_kv_cache_manager.py", + } + + +def test_recurrent_boundary_source_validator_is_fail_closed(tmp_path: Path) -> None: + sources = { + "vllm/v1/core/sched/output.py": ( + "recurrent_boundary_blocks: " + "dict[str, list[tuple[int, int, int]]] | None = None\n" + ), + "vllm/v1/core/sched/scheduler.py": ( + "recurrent_boundary_blocks=pending_recurrent_boundary_blocks\n" + 'getattr(self.connector, "supports_recurrent_boundary_blocks", False)\n' + "kv_transfer_config.is_kv_consumer\n" + "multiple_inflight_batches and (\n" + ), + "vllm/v1/core/single_type_kv_cache_manager.py": ( + "(request.num_prompt_tokens - 1) // self.block_pool.hash_block_size\n" + "block.block_hash_num_tokens != replay_boundary\n" + "get_group_id(block.block_hash) != self.kv_cache_group_id\n" + ), + "vllm/v1/core/kv_cache_manager.py": "self._pin_recurrent_boundary\n", + } + for relative, source in sources.items(): + path = tmp_path / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(source, encoding="utf-8") + contract.validate_recurrent_boundary_sources(tmp_path) + + manager = tmp_path / "vllm/v1/core/single_type_kv_cache_manager.py" + manager.write_text( + sources["vllm/v1/core/single_type_kv_cache_manager.py"].replace( + "request.num_prompt_tokens - 1", "request.num_prompt_tokens" + ), + encoding="utf-8", + ) + with pytest.raises(contract.ContractError, match="prompt-minus-one"): + contract.validate_recurrent_boundary_sources(tmp_path) + + def test_dflash_loader_contract_honors_explicit_draft_load_config() -> None: contract.validate_dflash_loader_source( """ @@ -257,6 +315,40 @@ def test_image_verifier_reads_the_clean_sparkcache_source_receipt() -> None: assert 'artifacts["sparkcache_source_tree_sha256"]' in verifier +def test_image_verifier_accepts_dflash_member_and_four_recurrent_postimages() -> None: + pins = json.loads(PINS.read_text(encoding="utf-8")) + report = { + "vllm_runtime_patches": [ + { + "path": record["target"], + "sha256": record["postimage_sha256"], + } + for record in pins["vllm"]["runtime_patches"] + ] + + [ + { + "path": target["path"], + "sha256": target["postimage_sha256"], + } + for record in pins["vllm"]["composed_runtime_patches"] + for target in record["targets"] + ] + } + assert len(report["vllm_runtime_patches"]) == 5 + verify.verify_runtime_patch_report(report, pins) + + missing_dflash = { + "vllm_runtime_patches": report["vllm_runtime_patches"][1:] + } + with pytest.raises(verify.VerifyError, match="exact DFlash"): + verify.verify_runtime_patch_report(missing_dflash, pins) + + wrong_recurrent = json.loads(json.dumps(report)) + wrong_recurrent["vllm_runtime_patches"][-1]["sha256"] = "0" * 64 + with pytest.raises(verify.VerifyError, match="exact recurrent"): + verify.verify_runtime_patch_report(wrong_recurrent, pins) + + def test_build_prepares_context_below_the_temporary_workspace() -> None: script = (HERE / "build-image.sh").read_text(encoding="utf-8") preparer = (HERE / "prepare_context.py").read_text(encoding="utf-8") @@ -274,8 +366,52 @@ def test_sparkcache_patches_and_contract_run_after_the_python_overlay() -> None: patch_030 = recipe.index("030-sparkcache-hma-load-failure.patch") patch_040 = recipe.index("040-sparkcache-shared-prefix-lease.patch") patch_041 = recipe.index("041-sparkcache-shared-prefix-attach.patch") - lease = recipe.index("verify_lease_contract.py") - assert overlay < dflash_patch < patch_020 < patch_030 < patch_040 < patch_041 < lease + recurrent = recipe.index("011-recurrent-boundary-contract.patch", patch_041) + final_lease = recipe.index("verify_lease_contract.py", recurrent) + assert recipe.count("verify_lease_contract.py") == 1 + assert ( + overlay + < dflash_patch + < patch_020 + < patch_030 + < patch_040 + < patch_041 + < recurrent + < final_lease + ) + + +def test_pr35_lease_verifier_runs_only_after_recurrent_symbols_exist() -> None: + recipe = (HERE / "Containerfile").read_text(encoding="utf-8") + recurrent_patch = (HERE / "patches/011-recurrent-boundary-contract.patch").read_text( + encoding="utf-8" + ) + recurrent_apply = recipe.index( + 'patch --batch --forward -p1 -d "${root}" < "${recurrent_patch}"' + ) + lease_verify = recipe.index("verify_lease_contract.py") + assert recurrent_apply < lease_verify + contract = json.loads(PINS.read_text(encoding="utf-8"))["sparkcache"]["contract"] + assert contract["sha256"] == ( + "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811" + ) + for definition in ( + "def take_recurrent_boundary_blocks(", + "recurrent_boundary_blocks:", + "def take_pending_aligned_recurrent_boundaries(", + ): + assert definition in recurrent_patch + + +def test_context_preparer_runs_the_real_lease_verifier_on_composed_source() -> None: + source = inspect.getsource(prepare.verify_composed_runtime_patches) + recurrent_apply = source.index('run(("git", "-C", str(source), "apply", str(patch)))') + exact_postimage = source.index('target_record["postimage_sha256"]') + verifier = source.index("verify_lease_contract.py") + recurrent_reverse = source.index("for patch in reversed(applied_composed)") + assert recurrent_apply < exact_postimage < verifier < recurrent_reverse + assert '"--vllm-root"' in source + assert "str(contract)" in source def test_output_labels_do_not_claim_a_source_built_0b_wheel() -> None: @@ -294,3 +430,6 @@ def test_output_labels_do_not_claim_a_source_built_0b_wheel() -> None: assert labels["org.sparkring.vllm.dflash-draft-loader-postimage-sha256"] == ( "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4" ) + assert labels["org.sparkring.vllm.recurrent-boundary-patch-sha256"] == ( + "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0" + ) diff --git a/runtime/glm53-flash-adaptive-mtp-python-overlay/verify_image.py b/runtime/glm53-flash-adaptive-mtp-python-overlay/verify_image.py index 870bc4dc..8646814a 100644 --- a/runtime/glm53-flash-adaptive-mtp-python-overlay/verify_image.py +++ b/runtime/glm53-flash-adaptive-mtp-python-overlay/verify_image.py @@ -120,6 +120,12 @@ def expected_output_labels(pins: dict[str, Any]) -> dict[str, str]: labels["org.sparkring.vllm.dflash-draft-loader-postimage-sha256"] = patch[ "postimage_sha256" ] + composed = pins["vllm"].get("composed_runtime_patches", ()) + if len(composed) != 1: + raise VerifyError("runtime contract requires one recurrent-boundary patch") + labels["org.sparkring.vllm.recurrent-boundary-patch-sha256"] = composed[0][ + "sha256" + ] cleanup = pins.get("runtime_cleanup", {}).get("deep_ep") if cleanup is not None: labels["org.sparkring.runtime.removed-deep-ep-distribution"] = ( @@ -251,6 +257,46 @@ def verify_runtime_cleanup(artifacts: dict[str, Any], pins: dict[str, Any]) -> N raise VerifyError("the attested deep_ep distribution remains installed") +def verify_runtime_patch_report(runtime: dict[str, Any], pins: dict[str, Any]) -> None: + """Require the exact DFlash member and exact recurrent postimage set.""" + + records = runtime.get("vllm_runtime_patches") + if not isinstance(records, list) or any( + not isinstance(record, dict) + or set(record) != {"path", "sha256"} + or not isinstance(record["path"], str) + or not isinstance(record["sha256"], str) + for record in records + ): + raise VerifyError("runtime patch verification report is malformed") + observed = {record["path"]: record["sha256"] for record in records} + if len(observed) != len(records): + raise VerifyError("runtime patch verification report contains duplicate paths") + + expected_dflash = { + record["target"]: record["postimage_sha256"] + for record in pins["vllm"].get("runtime_patches", ()) + } + observed_dflash = { + path: observed[path] for path in expected_dflash if path in observed + } + if observed_dflash != expected_dflash: + raise VerifyError("runtime did not verify the exact DFlash draft-loader patch") + + expected_recurrent = { + target["path"]: target["postimage_sha256"] + for record in pins["vllm"].get("composed_runtime_patches", ()) + for target in record["targets"] + } + observed_recurrent = { + path: observed[path] for path in expected_recurrent if path in observed + } + if observed_recurrent != expected_recurrent: + raise VerifyError("runtime did not verify the exact recurrent postimage set") + if set(observed) != set(expected_dflash) | set(expected_recurrent): + raise VerifyError("runtime patch verification report contains unexpected paths") + + def verify_image(engine: str, image: str, pins_path: Path = PINS) -> dict[str, Any]: pins = load_pins(pins_path) inspection = inspect_image(engine, image) @@ -259,15 +305,7 @@ def verify_image(engine: str, image: str, pins_path: Path = PINS) -> dict[str, A artifacts = artifact_probe(engine, image) if runtime.get("vllm_python_files_verified") != 31: raise VerifyError("runtime did not verify all 31 vLLM Python overlay files") - expected_runtime_patches = [ - { - "path": record["target"], - "sha256": record["postimage_sha256"], - } - for record in pins["vllm"].get("runtime_patches", ()) - ] - if runtime.get("vllm_runtime_patches") != expected_runtime_patches: - raise VerifyError("runtime did not verify the DFlash draft-loader patch") + verify_runtime_patch_report(runtime, pins) if artifacts["sparkcache_contract_sha256"] != pins["sparkcache"]["contract"]["sha256"]: raise VerifyError("installed SparkCache lease contract differs from its pin") if artifacts["sparkcache_source_tree_sha256"] != pins["sparkcache"][ diff --git a/runtime/glm53-flash-dflash7-python-overlay/README.md b/runtime/glm53-flash-dflash7-python-overlay/README.md index 8a62da2f..28a7c6ff 100644 --- a/runtime/glm53-flash-dflash7-python-overlay/README.md +++ b/runtime/glm53-flash-dflash7-python-overlay/README.md @@ -1,12 +1,13 @@ # GLM-5.3 DFlash7 public-base Python overlay -Status: **implemented**. The builder constructs and verifies an ARM64 image. -Local image ID +Status: **implemented, not qualified** for the current source contract. The +builder constructs and verifies an ARM64 image. Historical local image ID `sha256:eef863d8bc578815a80b0e2d9f0d745102b6363415225101fd92171a2e5a55cb` is **qualified** only for the bounded four-rank cases recorded in `performance/records/glm53-flash/dflash7-python-overlay-pr30-live-validation.md`. -Qualification does not transfer to a rebuild or to the all-safetensors -profile. +That image used SparkCache `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`, +not the current source below. Qualification does not transfer to a rebuild or +to either current profile. The image combines these exact roles: @@ -15,10 +16,11 @@ The image combines these exact roles: - the 31-file vLLM Python delta at `0b67266a0f37d6146a8403fb8482403c62f412d5`; - B12X `b1d541f9e71a35f030d45fae437630fff7507c2a`; -- SparkCache reconstructed-page placement and bounded page-delta-read source - `5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3`, Git tree - `94c236b9dfbf5f70075eb47877fd9caaa5d8c249`, and deployable source SHA-256 - `bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63`; +- SparkCache reconstructed-page placement, shared-segment restore, bounded + page-delta reads, and tail-only copy-on-write publication source + `c56f77f97b3da907d32e888d82046359a62f0f88`, Git tree + `deac36758f86695cd13f07b2870c2e49842aed9c`, and deployable source SHA-256 + `788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98`; - external BF16 DFlash2 weights with SHA-256 `b33c03475ba7322cf398828f2d8d1be376df30dc05c6b40c28c8ea8da23e410b`. @@ -46,9 +48,9 @@ operator-mounted and are verified by the runtime profile. Two executable profiles use external DFlash at depth seven and TP4, FP8 target KV, 256-token vLLM blocks, 32 sequences, and SparkCache page-tail copy-on-write -publication with CUDA restore. The all-safetensors profile is implemented but -unqualified. The mixed-loader profile is qualified only for the exact image -and bounded cases named above. The conservative profile uses global +publication with CUDA restore. Both current profiles are implemented but +unqualified. The historical mixed-loader image is qualified only for the +bounded cases named above. The conservative profile uses global safetensors. The mixed profile uses global fastsafetensors for the target and an exact `draft_load_config` selecting safetensors for DFlash. The image applies and verifies the draft-loader patch before installing SparkCache patches. See @@ -59,18 +61,50 @@ No legacy-key compatibility profile or translation is required by these profiles. The pinned SparkCache source at -`5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3` accepts canonical CUDA -configuration keys, replaces a partial terminal HMA -page when the authenticated cache boundary falls inside that page, and reads -authenticated page-delta chunks with a bounded eight-worker pool while -preserving descriptor order. It does not change cache identity, page-tail wire -schemas, record geometry, vLLM patch bytes, the lease contract, or the CUDA -placement ABI. Compatible entries produced by the source at commit -`5d571018de5b63a9a90e5c11e6d6e86bbff4a957` remain in the same namespace. -Null-block publication failures remain unsupported by this source contract. +`c56f77f97b3da907d32e888d82046359a62f0f88` accepts canonical CUDA +configuration keys, restores authenticated shared segment objects, reads page +deltas with a bounded worker pool, and publishes only the copy-on-write tail. +Its recurrent publication path requires the hash-proven boundary hand-off +produced by this vLLM overlay and fails closed when that evidence is missing or +contradictory. It does not change cache identity, page-tail wire schemas, +record geometry, or the CUDA placement ABI. Compatible `page-tail-cow-v1` +entries remain eligible; unverified state is recomputed. Both profiles preserve B12X compute backends and the pinned PYNCCL/NCCL library. They disable unsupported symmetric-memory and FlashInfer all-reduce probes, disable the all-reduce RMS fusion, select language-model-only serving, and leave Torch thread selection unset. ModelOpt and FP8 KV warnings remain visible because they describe supported-runtime limitations rather than unused optional backends. + +## Recurrent replay-boundary hand-off + +Status: **implemented** in both sides of this exact composition. The pinned +SparkCache connector advertises `supports_recurrent_boundary_blocks`, validates +the hand-off against request and recurrent topology, waits through outputs with +no per-request entry, and recomputes instead of publishing malformed or +conflicting evidence. Live qualification remains +required for any rebuilt image. + +`SchedulerOutput.recurrent_boundary_blocks` has this schema: + +```text +dict[str, list[tuple[int, int, int]]] | None +request_id -> [(group_id, block_id, boundary_tokens), ...] +``` + +Each entry identifies a Mamba `align` block whose prefix-cache hash covers +exactly `boundary_tokens`. The replay boundary is the greatest 256-token hash +boundary below the prompt end. A full 2,304-token recurrent page is admitted +only when the scheduler stops at that boundary and its stored hash token count +and group ID both match. An overshooting direct manager call emits no mapping. +The scheduler never substitutes a later running-state or DFlash speculative +slot. Existing partial-tail copy-on-write targets remain available through +`partial_tail_offloads` and are also included in the new field. + +The scheduler pins an admitted block before worker execution. A connector that +opts in must finish its worker-side snapshot before request cleanup; overlapping +scheduler steps defer request-block recycling until their execution fence has +completed. Request cleanup releases the pin, including cancellation paths. +Connectors without the capability receive no aligned-boundary metadata and +retain no additional recurrent block. The interface does not change +SparkCache cache identities or on-disk namespaces. diff --git a/runtime/glm53-flash-dflash7-python-overlay/pins.json b/runtime/glm53-flash-dflash7-python-overlay/pins.json index 5d9923d3..5ab89a0c 100644 --- a/runtime/glm53-flash-dflash7-python-overlay/pins.json +++ b/runtime/glm53-flash-dflash7-python-overlay/pins.json @@ -39,6 +39,43 @@ "contract": "DFlash passes SpeculativeConfig.draft_load_config to get_model; None retains the target LoadConfig fallback." } ], + "composed_runtime_patches": [ + { + "status": "implemented", + "path": "runtime/glm53-flash-adaptive-mtp-python-overlay/patches/011-recurrent-boundary-contract.patch", + "sha256": "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0", + "contract": "SchedulerOutput.recurrent_boundary_blocks exposes only hash-proven aligned Mamba pages and partial-tail CoW targets; request cleanup releases their pins.", + "targets": [ + { + "path": "vllm/v1/core/kv_cache_manager.py", + "preimage_sha256": "ee03dc9ce2b720c0be6e9f572d23580ba96eff68fe3406250557e83071654af0", + "postimage_sha256": "c5b83d382c96b2bf8c466a993ed77123a14a971e2661797128533319388d0b5f" + }, + { + "path": "vllm/v1/core/sched/output.py", + "preimage_sha256": "65235eba652e5a3ccee18bf3cbfeac9bf4da8fb9c61e961580f612cfb7e593bc", + "postimage_sha256": "9911b3f9d21815a185285852b5a6176e5484e1ab0ff5c30f7caaa68ea0fab543" + }, + { + "path": "vllm/v1/core/sched/scheduler.py", + "preimage_sha256": "6d397c97f31e67a75efc01b5ddd89fa58db425de14fa43965ef2d6146b6b9bdb", + "postimage_sha256": "260f36ce8fabf70c193b20009ea465eea7b1b6c8e9fb72f2307a01ba8fcf7b2a" + }, + { + "path": "vllm/v1/core/single_type_kv_cache_manager.py", + "preimage_sha256": "e4b1c5c38b63b708fd55aa40a9ab0d008b266d006a63dcfcef55890ac1371cb8", + "postimage_sha256": "f67a1850a7e0288baaa6d42e7ec55b22b09c156720767e23acaabedcae333c8a" + } + ], + "test_patch": { + "path": "runtime/glm53-flash-adaptive-mtp-python-overlay/patches/012-recurrent-boundary-contract-tests.patch", + "sha256": "fd6ebb99c60f7d242d7594bc5de4136f73768ccfa7327cef562c241126d3581c", + "target": "tests/v1/core/prefix_cache/test_partial_prefix_cache_hits.py", + "preimage_sha256": "2f58e600fd39413b385b9e96b3c642a9ffb051bedcfc247a700fb532b35ec185", + "postimage_sha256": "1a43d1a4bd43ee81dd89975e23ea6b0333e287d53c7a7e8c21f1554a6ca8b32a" + } + } + ], "native_source_objects": { "csrc": "9ada29088768f1bc08dadd2eed3c9738eb9ac8a1", "cmake": "5e5bbdbe1c1b3a479656d8d6a41cc32a1982c43d", @@ -77,9 +114,9 @@ }, "sparkcache": { "repository": "https://github.com/FujitsuPolycom/sparkcache.git", - "commit": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", - "source_tree_sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", + "commit": "c56f77f97b3da907d32e888d82046359a62f0f88", + "tree": "deac36758f86695cd13f07b2870c2e49842aed9c", + "source_tree_sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", "cuda_config_schema": "canonical-v1", "canonical_cuda_config_keys": [ "spark_cache_cuda_restore", @@ -90,7 +127,7 @@ ], "contract": { "path": "sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json", - "sha256": "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024", + "sha256": "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811", "files": 11 }, "patches": [ diff --git a/runtime/glm53-flash-dflash7-python-overlay/test_dflash7_python_overlay.py b/runtime/glm53-flash-dflash7-python-overlay/test_dflash7_python_overlay.py index 7c842b05..2b7c4159 100644 --- a/runtime/glm53-flash-dflash7-python-overlay/test_dflash7_python_overlay.py +++ b/runtime/glm53-flash-dflash7-python-overlay/test_dflash7_python_overlay.py @@ -38,13 +38,13 @@ def test_pins_bind_the_exact_dflash7_composition() -> None: "b1d541f9e71a35f030d45fae437630fff7507c2a" ) assert pins["sparkcache"]["commit"] == ( - "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3" + "c56f77f97b3da907d32e888d82046359a62f0f88" ) assert pins["sparkcache"]["tree"] == ( - "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" + "deac36758f86695cd13f07b2870c2e49842aed9c" ) assert pins["sparkcache"]["source_tree_sha256"] == ( - "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63" + "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98" ) assert pins["sparkcache"]["cuda_config_schema"] == "canonical-v1" assert pins["sparkcache"]["canonical_cuda_config_keys"] == [ @@ -106,13 +106,13 @@ def test_verifier_requires_the_dflash7_deployment_label() -> None: ) assert labels["org.sparkcache.cuda-config-schema"] == "canonical-v1" assert labels["org.sparkcache.source-revision"] == ( - "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3" + "c56f77f97b3da907d32e888d82046359a62f0f88" ) assert labels["org.sparkcache.source-tree"] == ( - "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" + "deac36758f86695cd13f07b2870c2e49842aed9c" ) assert labels["org.sparkcache.source-sha256"] == ( - "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63" + "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98" ) assert labels["org.jovian.vllm.commit"] != labels[ "org.sparkring.vllm.python.commit" @@ -120,6 +120,9 @@ def test_verifier_requires_the_dflash7_deployment_label() -> None: assert labels["org.sparkring.vllm.dflash-draft-loader-patch-sha256"] == ( "39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279" ) + assert labels["org.sparkring.vllm.recurrent-boundary-patch-sha256"] == ( + "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0" + ) assert labels["org.sparkring.runtime.removed-deep-ep-distribution"] == ( "deep_ep==2.0.0+local" ) diff --git a/scripts/config/glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json b/scripts/config/glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json index 69e50207..46bffa6b 100644 --- a/scripts/config/glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json +++ b/scripts/config/glm53-flash-dflash7-python-overlay-fastsafetensors-sparkcache-tp4-dcp1.example.json @@ -150,9 +150,9 @@ "max_num_seqs": "32", "sparkcache_publication_schema": "tail-cow-v1", "sparkcache_effective_publication_schema": "page-tail-cow-v1", - "sparkcache_source_sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "sparkcache_source_revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "sparkcache_source_tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", + "sparkcache_source_sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "sparkcache_source_revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "sparkcache_source_tree": "deac36758f86695cd13f07b2870c2e49842aed9c", "vllm_native_revision": "da4d7be6c97434f6942292ed8abbf4b32dc44355", "vllm_python_revision": "0b67266a0f37d6146a8403fb8482403c62f412d5", "vllm_python_tree": "ba9484ccb33aa56e90ff2f447f15ca9b9da97639", @@ -174,6 +174,7 @@ "org.sparkring.vllm.python-overlay-manifest-sha256": "e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a", "org.sparkring.vllm.dflash-draft-loader-patch-sha256": "39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279", "org.sparkring.vllm.dflash-draft-loader-postimage-sha256": "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4", + "org.sparkring.vllm.recurrent-boundary-patch-sha256": "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0", "org.sparkring.vllm.native-elf-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256", "org.sparkring.vllm.native-dispatch-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256", "org.sparkring.b12x.tree": "c69cdec1c59a08e8e0e549f930fa8abcfb5134ae", @@ -182,10 +183,10 @@ "org.sparkcache.deployment-profile": "glm53-flash-dflash7-python-overlay", "org.sparkcache.cuda-config-schema": "canonical-v1", "org.sparkcache.cuda-placement-library-sha256": "REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256", - "org.sparkcache.source-revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "org.sparkcache.source-tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", - "org.sparkcache.source-sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "org.sparkcache.vllm-contract-sha256": "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024", + "org.sparkcache.source-revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "org.sparkcache.source-tree": "deac36758f86695cd13f07b2870c2e49842aed9c", + "org.sparkcache.source-sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "org.sparkcache.vllm-contract-sha256": "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811", "org.sparkring.runtime.removed-deep-ep-distribution": "deep_ep==2.0.0+local", "org.sparkring.runtime.deep-ep-removal-receipt-sha256": "65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3", "org.sparkring.source-receipt-sha256": "REPLACE_WITH_SOURCE_RECEIPT_SHA256", @@ -196,6 +197,6 @@ "attestation_hook": [ "/bin/sh", "-ec", - "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test -f /dflash-draft/config.json && test -f /dflash-draft/model.safetensors && test \"$(sha256sum /dflash-draft/config.json | cut -d ' ' -f1)\" = c4aeac0101196a6e26705b34c45230bcd0c7c68ee2d2d1efdb242087f3712573 && test \"$(sha256sum /dflash-draft/model.safetensors | cut -d ' ' -f1)\" = b33c03475ba7322cf398828f2d8d1be376df30dc05c6b40c28c8ea8da23e410b && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = 6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/deep-ep-removal-receipt.json | cut -d ' ' -f1)\" = 65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3 && python3 -c 'import importlib.util; assert importlib.util.find_spec(\"deep_ep\") is None' && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" + "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test -f /dflash-draft/config.json && test -f /dflash-draft/model.safetensors && test \"$(sha256sum /dflash-draft/config.json | cut -d ' ' -f1)\" = c4aeac0101196a6e26705b34c45230bcd0c7c68ee2d2d1efdb242087f3712573 && test \"$(sha256sum /dflash-draft/model.safetensors | cut -d ' ' -f1)\" = b33c03475ba7322cf398828f2d8d1be376df30dc05c6b40c28c8ea8da23e410b && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = 788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/deep-ep-removal-receipt.json | cut -d ' ' -f1)\" = 65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3 && python3 -c 'import importlib.util; assert importlib.util.find_spec(\"deep_ep\") is None' && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" ] } diff --git a/scripts/config/glm53-flash-dflash7-python-overlay-safetensors-sparkcache-tp4-dcp1.example.json b/scripts/config/glm53-flash-dflash7-python-overlay-safetensors-sparkcache-tp4-dcp1.example.json index fc294973..f96d6734 100644 --- a/scripts/config/glm53-flash-dflash7-python-overlay-safetensors-sparkcache-tp4-dcp1.example.json +++ b/scripts/config/glm53-flash-dflash7-python-overlay-safetensors-sparkcache-tp4-dcp1.example.json @@ -147,9 +147,9 @@ "max_num_seqs": "32", "sparkcache_publication_schema": "tail-cow-v1", "sparkcache_effective_publication_schema": "page-tail-cow-v1", - "sparkcache_source_sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "sparkcache_source_revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "sparkcache_source_tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", + "sparkcache_source_sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "sparkcache_source_revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "sparkcache_source_tree": "deac36758f86695cd13f07b2870c2e49842aed9c", "vllm_native_revision": "da4d7be6c97434f6942292ed8abbf4b32dc44355", "vllm_python_revision": "0b67266a0f37d6146a8403fb8482403c62f412d5", "vllm_python_tree": "ba9484ccb33aa56e90ff2f447f15ca9b9da97639", @@ -171,6 +171,7 @@ "org.sparkring.vllm.python-overlay-manifest-sha256": "e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a", "org.sparkring.vllm.dflash-draft-loader-patch-sha256": "39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279", "org.sparkring.vllm.dflash-draft-loader-postimage-sha256": "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4", + "org.sparkring.vllm.recurrent-boundary-patch-sha256": "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0", "org.sparkring.vllm.native-elf-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256", "org.sparkring.vllm.native-dispatch-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256", "org.sparkring.b12x.tree": "c69cdec1c59a08e8e0e549f930fa8abcfb5134ae", @@ -179,10 +180,10 @@ "org.sparkcache.deployment-profile": "glm53-flash-dflash7-python-overlay", "org.sparkcache.cuda-config-schema": "canonical-v1", "org.sparkcache.cuda-placement-library-sha256": "REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256", - "org.sparkcache.source-revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "org.sparkcache.source-tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", - "org.sparkcache.source-sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "org.sparkcache.vllm-contract-sha256": "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024", + "org.sparkcache.source-revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "org.sparkcache.source-tree": "deac36758f86695cd13f07b2870c2e49842aed9c", + "org.sparkcache.source-sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "org.sparkcache.vllm-contract-sha256": "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811", "org.sparkring.runtime.removed-deep-ep-distribution": "deep_ep==2.0.0+local", "org.sparkring.runtime.deep-ep-removal-receipt-sha256": "65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3", "org.sparkring.source-receipt-sha256": "REPLACE_WITH_SOURCE_RECEIPT_SHA256", @@ -193,6 +194,6 @@ "attestation_hook": [ "/bin/sh", "-ec", - "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test -f /dflash-draft/config.json && test -f /dflash-draft/model.safetensors && test \"$(sha256sum /dflash-draft/config.json | cut -d ' ' -f1)\" = c4aeac0101196a6e26705b34c45230bcd0c7c68ee2d2d1efdb242087f3712573 && test \"$(sha256sum /dflash-draft/model.safetensors | cut -d ' ' -f1)\" = b33c03475ba7322cf398828f2d8d1be376df30dc05c6b40c28c8ea8da23e410b && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = 6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/deep-ep-removal-receipt.json | cut -d ' ' -f1)\" = 65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3 && python3 -c 'import importlib.util; assert importlib.util.find_spec(\"deep_ep\") is None' && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" + "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test -f /dflash-draft/config.json && test -f /dflash-draft/model.safetensors && test \"$(sha256sum /dflash-draft/config.json | cut -d ' ' -f1)\" = c4aeac0101196a6e26705b34c45230bcd0c7c68ee2d2d1efdb242087f3712573 && test \"$(sha256sum /dflash-draft/model.safetensors | cut -d ' ' -f1)\" = b33c03475ba7322cf398828f2d8d1be376df30dc05c6b40c28c8ea8da23e410b && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = 788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/deep-ep-removal-receipt.json | cut -d ' ' -f1)\" = 65514f44829e7d176b0b2cacc9559ed22724e525b7041a8bcd4d2e02d1f372e3 && python3 -c 'import importlib.util; assert importlib.util.find_spec(\"deep_ep\") is None' && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" ] } diff --git a/scripts/config/glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json b/scripts/config/glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json index 8c4d7310..757f99bc 100644 --- a/scripts/config/glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json +++ b/scripts/config/glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json @@ -118,9 +118,9 @@ "weight_loader_tp_nogds": "true", "sparkcache_publication_schema": "tail-cow-v1", "sparkcache_effective_publication_schema": "page-tail-cow-v1", - "sparkcache_source_sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "sparkcache_source_revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "sparkcache_source_tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", + "sparkcache_source_sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "sparkcache_source_revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "sparkcache_source_tree": "deac36758f86695cd13f07b2870c2e49842aed9c", "vllm_native_revision": "da4d7be6c97434f6942292ed8abbf4b32dc44355", "vllm_python_revision": "0b67266a0f37d6146a8403fb8482403c62f412d5", "vllm_python_tree": "ba9484ccb33aa56e90ff2f447f15ca9b9da97639", @@ -139,6 +139,7 @@ "org.sparkring.vllm.python-overlay-manifest-sha256": "e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a", "org.sparkring.vllm.dflash-draft-loader-patch-sha256": "39b567013ee7aed79f63200ed460129587933dc77fb430decdf19f78178de279", "org.sparkring.vllm.dflash-draft-loader-postimage-sha256": "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4", + "org.sparkring.vllm.recurrent-boundary-patch-sha256": "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0", "org.sparkring.vllm.native-elf-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256", "org.sparkring.vllm.native-dispatch-manifest-sha256": "REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256", "org.sparkring.b12x.tree": "c69cdec1c59a08e8e0e549f930fa8abcfb5134ae", @@ -146,10 +147,10 @@ "org.sparkring.base.image-id": "sha256:7e8c0ebcb2001efb4cdab0ec9d20d53972e62db3688230044e22e61ffb1d35d5", "org.sparkcache.deployment-profile": "glm53-flash-adaptive-mtp-python-overlay", "org.sparkcache.cuda-placement-library-sha256": "REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256", - "org.sparkcache.source-revision": "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3", - "org.sparkcache.source-tree": "94c236b9dfbf5f70075eb47877fd9caaa5d8c249", - "org.sparkcache.source-sha256": "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63", - "org.sparkcache.vllm-contract-sha256": "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024", + "org.sparkcache.source-revision": "c56f77f97b3da907d32e888d82046359a62f0f88", + "org.sparkcache.source-tree": "deac36758f86695cd13f07b2870c2e49842aed9c", + "org.sparkcache.source-sha256": "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98", + "org.sparkcache.vllm-contract-sha256": "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811", "org.sparkring.source-receipt-sha256": "REPLACE_WITH_SOURCE_RECEIPT_SHA256", "org.sparkring.nccl.commit": "73cf112295c33aee2b895f329f592f2a9b4b0f97", "org.sparkring.nccl.patch-sha256": "6709063fa1c25055ae77a9397dea5d89643f8211d25e7990bdd11597d08c0dde", @@ -158,6 +159,6 @@ "attestation_hook": [ "/bin/sh", "-ec", - "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = 6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" + "test -f /models/target/config.json && test -f /models/target/model.safetensors.index.json && test \"$(sha256sum /models/target/config.json | cut -d ' ' -f1)\" = 676382abd1e90a6c85f0c8f33d45441ecd45fd514fd7b63ce5610e732d8e4996 && test \"$(sha256sum /models/target/model.safetensors.index.json | cut -d ' ' -f1)\" = 0d1d9e6b226e76520e182de10d4e7194cc885c5cb1bf885bb90de1916ce312cb && test \"$(sha256sum /opt/sparkring/nccl/libnccl.so.2 | cut -d ' ' -f1)\" = 5f1c3f10d5ace66d4ba584415bbfe42b6ac1a0a9116a3b81dcbe50516ad924b3 && grep -q '\"tail-cow-v1\"' /opt/sparkcache-src/sparkcache/spark_context_cache_config.py && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/vllm-python-overlay.json | cut -d ' ' -f1)\" = e5e528288b173399611a4930fecc4182b7208bc1564881d52ca5d2c5c4ae0f6a && test \"$(sha256sum /opt/sparkring/runtime/python-overlay/source-receipt.json | cut -d ' ' -f1)\" = REPLACE_WITH_SOURCE_RECEIPT_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_elf_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_ELF_MANIFEST_SHA256 && test \"$(python3 -c 'import json; print(json.load(open(\"/opt/sparkring/runtime/python-overlay/retained-native.json\", encoding=\"utf-8\"))[\"native_dispatch_manifest_sha256\"])')\" = REPLACE_WITH_VLLM_NATIVE_DISPATCH_MANIFEST_SHA256 && test \"$(cat /opt/sparkring/runtime/python-overlay/sparkcache-source-tree.sha256)\" = 788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json | cut -d ' ' -f1)\" = f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811 && test \"$(sha256sum /opt/sparkcache-src/sparkcache/native/build-cuda/libspark_cache_placement.so | cut -d ' ' -f1)\" = REPLACE_WITH_CUDA_PLACEMENT_LIBRARY_SHA256 && python3 /opt/sparkcache-src/sparkcache/runtime_patches/verify_lease_contract.py --vllm-root /usr/local/lib/python3.12/dist-packages --contract /opt/sparkcache-src/sparkcache/runtime_patches/vllm-kv-block-lease-contract-glm53-b12x-kda-adaptive-mtp.json" ] } diff --git a/scripts/prepare_glm53_dflash7_python_overlay_profile.py b/scripts/prepare_glm53_dflash7_python_overlay_profile.py index 2c501028..bd837983 100644 --- a/scripts/prepare_glm53_dflash7_python_overlay_profile.py +++ b/scripts/prepare_glm53_dflash7_python_overlay_profile.py @@ -42,13 +42,16 @@ DFLASH_LOADER_POSTIMAGE_SHA256 = ( "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4" ) -SPARKCACHE_COMMIT = "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3" -SPARKCACHE_TREE = "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" +RECURRENT_BOUNDARY_PATCH_SHA256 = ( + "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0" +) +SPARKCACHE_COMMIT = "c56f77f97b3da907d32e888d82046359a62f0f88" +SPARKCACHE_TREE = "deac36758f86695cd13f07b2870c2e49842aed9c" SPARKCACHE_SOURCE_SHA256 = ( - "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63" + "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98" ) LEASE_CONTRACT_SHA256 = ( - "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024" + "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811" ) TARGET_IDENTITY = "a35e6bf2875c1875609b8deaec404c07c6cc80259e4222fc0b51e649498bd6b9" DFLASH_CONFIG_SHA256 = ( @@ -246,6 +249,9 @@ def resolve( "org.sparkring.vllm.dflash-draft-loader-postimage-sha256": ( DFLASH_LOADER_POSTIMAGE_SHA256 ), + "org.sparkring.vllm.recurrent-boundary-patch-sha256": ( + RECURRENT_BOUNDARY_PATCH_SHA256 + ), "org.jovian.b12x.commit": B12X_COMMIT, "org.sparkring.b12x.tree": B12X_TREE, "org.opencontainers.image.base.name": PUBLIC_BASE, diff --git a/scripts/prepare_glm53_public_python_overlay_profile.py b/scripts/prepare_glm53_public_python_overlay_profile.py index 67556755..a99f9066 100644 --- a/scripts/prepare_glm53_public_python_overlay_profile.py +++ b/scripts/prepare_glm53_public_python_overlay_profile.py @@ -53,13 +53,16 @@ DFLASH_LOADER_POSTIMAGE_SHA256 = ( "98acbae2b3bb4482d83f9637c163ce7c92707ccdf6561b7e431f23337f151cf4" ) -SPARKCACHE_COMMIT = "5ec6a9953ad5d39120298bbfc26e95a6fa4b1dc3" -SPARKCACHE_TREE = "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" +RECURRENT_BOUNDARY_PATCH_SHA256 = ( + "5a6561a5bbab990dcd03bfd6a485ea26c3b5a578c2fd61b76305767b16dbfba0" +) +SPARKCACHE_COMMIT = "c56f77f97b3da907d32e888d82046359a62f0f88" +SPARKCACHE_TREE = "deac36758f86695cd13f07b2870c2e49842aed9c" SPARKCACHE_SOURCE_SHA256 = ( - "bc238f96e550c7ec27d4081dd1f2e741d404aaf5c8572d89ccc5e76812be4d63" + "788686e858ba4af01f535e95122c7650f412fddc40cd221a0924f4ce2b32ff98" ) LEASE_CONTRACT_SHA256 = ( - "6defde9551cbb586fd09bb2d3020495531b6573397875a767eaae1dbad126024" + "f36ed14eaf1f97a5dffa94bda8151b1e0fa182afc0d121b757b70bebc6a43811" ) TARGET_IDENTITY = "a35e6bf2875c1875609b8deaec404c07c6cc80259e4222fc0b51e649498bd6b9" MTP_CACHE_IDENTITY_SHA256 = ( @@ -235,6 +238,9 @@ def resolve( "org.sparkring.vllm.dflash-draft-loader-postimage-sha256": ( DFLASH_LOADER_POSTIMAGE_SHA256 ), + "org.sparkring.vllm.recurrent-boundary-patch-sha256": ( + RECURRENT_BOUNDARY_PATCH_SHA256 + ), "org.jovian.b12x.commit": B12X_COMMIT, "org.sparkring.b12x.tree": B12X_TREE, "org.opencontainers.image.base.name": PUBLIC_BASE, diff --git a/scripts/test_glm53_flash_profile.py b/scripts/test_glm53_flash_profile.py index c4edb2be..3e55c246 100644 --- a/scripts/test_glm53_flash_profile.py +++ b/scripts/test_glm53_flash_profile.py @@ -507,4 +507,11 @@ def test_ci_runs_glm53_runtime_contracts() -> None: workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text( encoding="utf-8" ) - assert "runtime/exl3-r7 runtime/glm53-flash runtime/deepseek0731-gb10" in workflow + for runtime_path in ( + "runtime/exl3-r7", + "runtime/glm53-flash", + "runtime/glm53-flash-adaptive-mtp-python-overlay", + "runtime/glm53-flash-dflash7-python-overlay", + "runtime/deepseek0731-gb10", + ): + assert runtime_path in workflow diff --git a/scripts/test_prepare_glm53_b12x_kda_adaptive_mtp_profile.py b/scripts/test_prepare_glm53_b12x_kda_adaptive_mtp_profile.py index 761a556a..e05006e3 100644 --- a/scripts/test_prepare_glm53_b12x_kda_adaptive_mtp_profile.py +++ b/scripts/test_prepare_glm53_b12x_kda_adaptive_mtp_profile.py @@ -240,14 +240,15 @@ def test_resolver_normalizes_legacy_cuda_restore_aliases_and_rejects_conflicts() def test_quickstart_names_the_executable_builder_and_profile_contracts() -> None: guide = QUICKSTART.read_text(encoding="utf-8") - assert "runtime/glm53-flash-b12x-kda-adaptive-mtp/build-image.sh" in guide - assert str(PROFILE.relative_to(ROOT)).replace("\\", "/") in guide + assert "runtime/glm53-flash-b12x-kda-adaptive-mtp/build-image.sh" not in guide + assert str(PROFILE.relative_to(ROOT)).replace("\\", "/") not in guide assert str(SITE.relative_to(ROOT)).replace("\\", "/") in guide - assert "prepare_glm53_b12x_kda_adaptive_mtp_profile.py" in guide - assert SPARKCACHE_COMMIT in guide - assert SPARKCACHE_TREE in guide - assert SPARKCACHE_SOURCE_SHA256 in guide + assert "prepare_glm53_b12x_kda_adaptive_mtp_profile.py" not in guide + assert "unsupported with SparkCache" in guide assert VLLM_COMMIT in guide - assert "START_GLM53_FLASH_MTP5_ADAPTIVE_FASTSAFETENSORS_TP4" in guide + assert ( + "START_GLM53_FLASH_PUBLIC_PYTHON_OVERLAY_MTP5_ADAPTIVE_FASTSAFETENSORS_TP4" + in guide + ) assert "--cuda-placement-library-sha256" in guide assert "--native-library-sha256" not in guide diff --git a/scripts/test_prepare_glm53_dflash7_python_overlay_profile.py b/scripts/test_prepare_glm53_dflash7_python_overlay_profile.py index 2cf0ac94..3ed326fb 100644 --- a/scripts/test_prepare_glm53_dflash7_python_overlay_profile.py +++ b/scripts/test_prepare_glm53_dflash7_python_overlay_profile.py @@ -15,6 +15,7 @@ DEEP_EP_REMOVAL_RECEIPT_SHA256, DFLASH_LOADER_PATCH_SHA256, DFLASH_LOADER_POSTIMAGE_SHA256, + RECURRENT_BOUNDARY_PATCH_SHA256, DFLASH_WEIGHTS_SHA256, SPARKCACHE_COMMIT, SPARKCACHE_SOURCE_SHA256, @@ -182,6 +183,9 @@ def test_resolved_profile_requires_dflash7_image_labels() -> None: assert labels["org.sparkring.vllm.dflash-draft-loader-postimage-sha256"] == ( DFLASH_LOADER_POSTIMAGE_SHA256 ) + assert labels["org.sparkring.vllm.recurrent-boundary-patch-sha256"] == ( + RECURRENT_BOUNDARY_PATCH_SHA256 + ) assert labels["org.sparkring.runtime.removed-deep-ep-distribution"] == ( DEEP_EP_DISTRIBUTION ) @@ -225,6 +229,13 @@ def test_resolver_rejects_mtp_or_noncanonical_cuda_restore() -> None: with pytest.raises(ResolveError, match="OMP_NUM_THREADS"): resolve(changed, copy.deepcopy(site), **kwargs) + changed = copy.deepcopy(profile) + changed["required_image_labels"].pop( + "org.sparkring.vllm.recurrent-boundary-patch-sha256" + ) + with pytest.raises(ResolveError, match="recurrent-boundary-patch-sha256"): + resolve(changed, copy.deepcopy(site), **kwargs) + changed = copy.deepcopy(profile) changed["environment"].pop("VLLM_ALLREDUCE_USE_SYMM_MEM") with pytest.raises(ResolveError, match="VLLM_ALLREDUCE_USE_SYMM_MEM"): diff --git a/scripts/test_prepare_glm53_public_python_overlay_profile.py b/scripts/test_prepare_glm53_public_python_overlay_profile.py index 52fca05d..21f289d2 100644 --- a/scripts/test_prepare_glm53_public_python_overlay_profile.py +++ b/scripts/test_prepare_glm53_public_python_overlay_profile.py @@ -13,9 +13,13 @@ DFLASH_LOADER_PATCH_SHA256, DFLASH_LOADER_POSTIMAGE_SHA256, LEASE_CONTRACT_SHA256, + RECURRENT_BOUNDARY_PATCH_SHA256, MTP_CACHE_IDENTITY_SHA256, OVERLAY_MANIFEST_SHA256, PUBLIC_BASE, + SPARKCACHE_COMMIT, + SPARKCACHE_SOURCE_SHA256, + SPARKCACHE_TREE, VLLM_NATIVE_COMMIT, VLLM_PYTHON_COMMIT, ResolveError, @@ -31,6 +35,7 @@ / "glm53-flash-public-python-overlay-mtp5-adaptive-fastsafetensors-sparkcache-tp4-dcp1.example.json" ) SITE = CONFIG / "glm53-flash-b12x-kda-adaptive-mtp-tp4-site.example.yaml" +QUICKSTART = ROOT / "docs/GLM53_B12X_KDA_ADAPTIVE_MTP_SPARKCACHE_TP4_QUICKSTART.md" IMAGE_ID = "sha256:" + "ab" * 32 CUDA_PLACEMENT_LIBRARY = "1a" * 32 NATIVE_ELF = "2b" * 32 @@ -138,6 +143,9 @@ def test_resolver_requires_mixed_provenance_and_all_artifact_hashes() -> None: assert labels["org.sparkring.vllm.dflash-draft-loader-postimage-sha256"] == ( DFLASH_LOADER_POSTIMAGE_SHA256 ) + assert labels["org.sparkring.vllm.recurrent-boundary-patch-sha256"] == ( + RECURRENT_BOUNDARY_PATCH_SHA256 + ) assert labels["org.sparkring.vllm.native-elf-manifest-sha256"] == NATIVE_ELF assert labels["org.sparkring.vllm.native-dispatch-manifest-sha256"] == ( NATIVE_DISPATCH @@ -147,7 +155,7 @@ def test_resolver_requires_mixed_provenance_and_all_artifact_hashes() -> None: ) assert "org.sparkcache.native-library-sha256" not in labels assert labels["org.sparkcache.source-tree"] == ( - "94c236b9dfbf5f70075eb47877fd9caaa5d8c249" + "deac36758f86695cd13f07b2870c2e49842aed9c" ) assert labels["org.sparkcache.vllm-contract-sha256"] == LEASE_CONTRACT_SHA256 assert labels["org.sparkring.source-receipt-sha256"] == SOURCE_RECEIPT @@ -183,6 +191,13 @@ def test_resolver_rejects_snapshot_publication_or_source_built_labels() -> None: with pytest.raises(ResolveError, match="org.jovian.vllm.commit"): resolve(changed, copy.deepcopy(site), **arguments) + changed = copy.deepcopy(profile) + changed["required_image_labels"].pop( + "org.sparkring.vllm.recurrent-boundary-patch-sha256" + ) + with pytest.raises(ResolveError, match="recurrent-boundary-patch-sha256"): + resolve(changed, copy.deepcopy(site), **arguments) + changed = copy.deepcopy(profile) transfer = json.loads(_argument(changed, "--kv-transfer-config")) transfer["kv_connector_extra_config"].pop( @@ -265,3 +280,15 @@ def test_generic_launcher_builds_a_four_rank_dry_run( rendered = json.dumps(plan) assert profile["container_name"] in rendered assert profile["image"] in rendered + + +def test_quickstart_uses_the_recurrent_capable_python_overlay() -> None: + guide = QUICKSTART.read_text(encoding="utf-8") + assert "runtime/glm53-flash-adaptive-mtp-python-overlay/build-image.sh" in guide + assert str(PROFILE.relative_to(ROOT)).replace("\\", "/") in guide + assert "prepare_glm53_public_python_overlay_profile.py" in guide + assert SPARKCACHE_COMMIT in guide + assert SPARKCACHE_TREE in guide + assert SPARKCACHE_SOURCE_SHA256 in guide + assert RECURRENT_BOUNDARY_PATCH_SHA256 in guide + assert "runtime/glm53-flash-b12x-kda-adaptive-mtp/build-image.sh" not in guide