Repository navigation
Conversation
…i-K2.5 (#42978) Signed-off-by: Xavier Aguilar <xavier.aguilarfruto@amd.com> Co-authored-by: Douglas Lehr <91553416+dllehr-amd@users.noreply.github.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com> (cherry picked from commit baacbfc)
Signed-off-by: tjtanaa <tunjian.tan@embeddedllm.com> (cherry picked from commit bfe1001)
…erts (#44679) Signed-off-by: Rohan138 <rohanpotdar138@gmail.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com> (cherry picked from commit 5b6b536)
Signed-off-by: Mohammad Miadh Angkad <176301910+mmangkad@users.noreply.github.com> (cherry picked from commit 40e065e)
Signed-off-by: khluu <khluu000@gmail.com> (cherry picked from commit e3e31e5)
…#45586 The behavioral sleep/wake tests from this file are fully superseded by tests/entrypoints/serve/dev/rlhf/test_sleep_wake.py in PR vllm-project#45586, which consolidates all RL CI into a single dedicated PR. Signed-off-by: aoshen02 <aoshen@inferact.ai>
…strumentator crash on serve startup A fresh pip install pulls fastapi 0.137, whose new _IncludedRouter route type (a BaseRoute with no .path) breaks prometheus-fastapi-instrumentator's route iteration -> 'vllm serve' dies at startup with: AttributeError: '_IncludedRouter' object has no attribute 'path' requirements/common.txt pinned fastapi[standard]>=0.115.0 with no upper bound. Cap to <0.137; lift once the instrumentator handles _IncludedRouter (trallnag/prometheus-fastapi-instrumentator#370). Signed-off-by: avesed <trevor.lijunlin@gmail.com>
Adds tests/entrypoints/serve/dev/rlhf/ with two test files covering the
full RL lifecycle via the VLLM_SERVER_DEV_MODE HTTP endpoint surface.
test_sleep_wake.py (6 classes, 26 tests)
TestSleepWakeFlags -- flag / Prometheus metrics smoke
TestPhysicalMemory -- GPU free-bytes quantification per sleep level/tag
TestSchedulerGate -- scheduler must not dispatch during partial/full wake
TestOutputCorrectness -- golden-output roundtrip, multi-cycle stability
TestPauseResume -- /pause /resume /is_paused (all 3 PauseMode values)
TestErrorPaths -- idempotency, wrong order, abort interaction
test_weight_update.py (3 classes, 7 tests)
TestWeightTransferProtocol -- /start /finish framing, prefix-cache flush
TestWeightUpdateWithTensors -- output changes after real tensor push
TestCompoundRLStep -- full RL step: sleep -> update -> wake(weights)
-> wake(kv_cache) -> rollout (PR #44483 scenario)
Key properties:
- Tests drive the production HTTP surface (not internal Python API)
- --enable-sleep-mode required; without it sleep/wake are no-ops
- Self-contained: no tests.utils dependency
- VLLM_TEST_MODEL env var selects model
Directly addresses active bugs:
#44483 -- partial wake IMA (TestSchedulerGate.test_partial_wake_blocks_*)
#45326 -- request hang during sleep (test_sleep_abort_mode_blocks_*)
#45518 -- double-sleep idempotency (TestErrorPaths.test_double_sleep_*)
RFC: #45585
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: aoshen02 <aoshen@inferact.ai>
TestSleepWakeFlags in test_sleep_wake.py covers all assertions from test_sleep.py and more; keeping both would create duplicate coverage. Signed-off-by: aoshen02 <aoshen@inferact.ai>
…rtions 0.55 was below the vLLM E2E test norm (0.75) and made the physical memory assertions too weak (0.5 GiB threshold has too much noise from PyTorch cache churn alone). With 0.75: - KV cache is ~3x larger, making sleep/wake deltas unambiguous - freed_gib threshold: 0.5 -> 1.5 GiB (1B bf16 weights ~2 GiB alone) - recovered_gib threshold: 0.3 -> 1.0 GiB Signed-off-by: aoshen02 <aoshen@inferact.ai>
Add TestWeightReloadCodePaths to test_weight_update.py — exercises the layerwise weight-reload path (initialize → finalize → process_weights_after_loading) across model architecture × quantization combinations using vllm.LLM in-process. Test matrix: - moe-bf16-tiny: TitanML/tiny-mixtral (MoE reload baseline) - moe-bf16-3b: ibm-research/PowerMoE-3b (real MoE kernel reconstruction) - moe-fp8: allenai/OLMoE-1B-7B-0924 with fp8 (#45989 crash path) - moe-mxfp8: allenai/OLMoE-1B-7B-0924 with mxfp8 (#44613 crash path, SM100+) Each case triggers finalize_layerwise_reload which calls process_weights_after_loading on all layers — the exact code path where get_current_vllm_config() crashes during MoE kernel reconstruction if the config context is missing. Verified on GB200 (SM100): 4/4 passed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract duplicated _server(), _gen(), _ok(), sleep/wake/pause/resume helpers, gpu_free_bytes(), sleep_metrics(), and weight-transfer helpers into a single conftest.py shared by all RL lifecycle test modules. Also adds: - dummy_weights=True mode (--load-format dummy) for fast T0 state-machine tests - poll_until() helper as a 200-lie workaround for tests that need to verify state after an operation where the HTTP 200 may precede completion - gen_with_logprobs() for precision tests RFC: vllm-project/vllm#45585 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
17 HTTP-based tests across 6 classes covering the RL lifecycle state-machine invariants. Uses --load-format dummy for fast startup without downloading real weights. Classes: TestSleepingTagsSemantics - sleeping_tags partial/full wake semantics TestPauseStateOrthogonality - pause and sleep are orthogonal dimensions TestWeightUpdateOrdering - start/update/finish protocol ordering TestIdempotency - double sleep/wake/pause engine survival TestInitialState - server starts awake, unpaused, no active update TestConcurrentStateTransitions - concurrent sleep/wake doesn't corrupt state All tests drive the full HTTP endpoint stack with VLLM_SERVER_DEV_MODE=1 and --load-format dummy for speed. References: sglang test_pause_generation_tensor_consistency.py, AReaL tests/test_staleness_manager.py, verl test_vllm_weight_update_utils_on_cpu.py. RFC: vllm-project/vllm#45585 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
64707bc to
6e662b4
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
New test classes covering gaps identified in 12-framework survey:
TestConcurrentRace
- test_concurrent_sleep_and_generate_no_deadlock
10 generate threads racing against 1 sleep/wake thread must not
deadlock. Ref: miles TestSessionConcurrencyContracts.
TestMemoryLeakCycle
- test_no_host_rss_growth_over_10_cycles
10 sleep/wake cycles; CPU RSS growth must be < 10% of baseline.
Ref: ROLL test_vllm_mem_oom.py generate_memory().
TestSamplingNAbort
- test_sampling_n4_abort_engine_survives
sleep(abort) while sampling_n=4 is in-flight; engine must survive.
Ref: ROLL _check_vllm_sampling_n.
TestLogprobsPrecision
- test_logprobs_stable_after_sleep_wake
logprobs before/after sleep/wake must match within 1e-3.
Ref: ROLL tests/distributed/strategy/log_probs/.
TestSleepWakeLatency
- test_sleep_wake_roundtrip_under_10s (single cycle < 10 s)
- test_five_cycles_total_under_60s (5 cycles < 60 s)
Ref: sglang test_update_weights_from_distributed.py assert time < 3s.
TestCPUWeightBackup
- test_weights_cpu_backup_5cycle_output_stable
5× staged wake (weights-only then kv_cache); golden output stable.
Ref: sglang test_release_and_resume_occupation_with_weights_cpu_backup.
All 7 pass in vllm/vllm-openai:v0.23.0 container.
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Codex review identified these real bugs and flakiness risks:
TestConcurrentRace:
- Add is_alive() check after join(timeout=30) — a hanging thread would
previously pass silently, hiding the deadlock the test targets.
- Add `assert not errors` — exceptions in background threads were
swallowed, allowing the test to pass vacuously.
TestMemoryLeakCycle:
- RSS was measuring the pytest runner process, not the vLLM server
subprocess. Switch to GPU free-bytes (via _gpu_free_bytes()) which
directly reflects cumem-managed memory. Threshold: 50 MiB tolerance.
TestSamplingNAbort:
- Assert `"err" not in result` so background thread exceptions are
surfaced rather than silently ignored.
TestLogprobsPrecision:
- Relax tolerance 1e-3 → 1e-2 (BF16 has ~3 significant decimal digits;
1e-3 was tighter than the dtype can guarantee).
- Assert `compared > 0` so an all-None response doesn't pass vacuously.
TestCPUWeightBackup:
- Add --seed 42 to the server args for deterministic greedy decoding.
Without a seed, temperature=0 is not bit-identical across wake cycles
on all hardware; the text comparison would be a flakiness source.
- Add assertion that each cycle's output is non-empty.
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…eview)
TestWeightUpdateProtocolErrors (3 tests):
- test_start_without_init_returns_error_and_engine_survives
In v0.23.0 /start_weight_update requires /init_weight_transfer_engine
(NCCL setup) first; without it returns 500. Verifies informative error
message and engine survival. Ref: AReaL TestConnect.
- test_update_weights_without_start_returns_error
/update_weights outside a start→finish session must error + engine survives.
Ref: ROLL test_collective_rpc.py.
- test_finish_without_start_returns_error_and_engine_survives
Extends existing test_finish_without_start_handled with generate check.
TestCompoundRLStepExtended (2 tests):
- test_five_colocate_rl_steps_output_stable
5 staged sleep/wake cycles with --seed 42 golden output check.
Tests the colocate RL pattern: sleep(1) → wake(weights) → wake(kv_cache).
- test_sleep_staged_wake_latency
Full staged wake cycle must complete in < 15 s.
Ref: sglang test_update_weights_from_distributed.py latency gate.
Codex review fixes applied:
- except (ValueError, KeyError) instead of bare except Exception
- Added engine survival check to test_update_weights_without_start
All 5 tests pass in vllm/vllm-openai:v0.23.0 container.
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
4 tests across 3 classes; auto-skipped when < 2 GPUs.
TestTP2SleepWakeMemory:
- test_tp2_sleep_frees_gpu_memory
Verifies sleep(1) frees > 0.3 GiB on BOTH GPU-0 and GPU-1 (TP=2 shard),
and wake restores > 0.2 GiB on both ranks. Ref: sglang
test_multi_instance_release_memory_occupation.py.
TestTP2OutputCorrectness:
- test_tp2_full_wake_restores_output
sleep/wake roundtrip at TP=2 must not corrupt weight shards.
- test_tp2_three_cycles_stable
3 cycles stable; output matches golden on every cycle.
TestTP2StagedWake:
- test_tp2_staged_wake_keeps_sleeping_between_tags
wake(["weights"]) at TP=2 must leave is_sleeping=True until
wake(["kv_cache"]) completes (#44483 regression at TP=2).
Codex review fixes applied:
- Add _ok guard on golden capture in test_tp2_three_cycles_stable
- Check both GPU-0 and GPU-1 memory in memory test
All 4 pass in vllm/vllm-openai:v0.23.0 container (2 GPU).
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The dev RLHF /sleep and /wake_up endpoints returned a bare Response(200)
with no body, inconsistent with the sibling /pause endpoint which returns
{"status": "paused"}. RL orchestrators (verl, SkyRL, trl) had no
machine-readable confirmation of which transition occurred, how long it
took, or — critically — whether a staged (partial-tag) wake left the engine
still sleeping.
Changes:
- /sleep -> {"status": "sleeping", "level": int, "elapsed_ms": float}
- /wake_up -> {"status": "awake"|"sleeping", "tags_woken": list|None,
"elapsed_ms": float}
status="sleeping" after a partial wake (e.g. tags=["weights"]) because
kv_cache is still unmapped; this lets orchestrators sequence staged wakes.
- Remove the stale FIXME comments. They referred to v0 frontend
multiprocessing; in the v1 engine `await client.sleep()/wake_up()` resolves
on the engine-core response, so the operation is already applied when the
endpoint returns. elapsed_ms therefore measures the real operation time.
Backward compatible: status code stays 200 (callers that only check the
status code are unaffected); only a response body is added. This is the
serve/dev/ experimental namespace.
Tested in vllm/vllm-openai:v0.23.0 (tests/entrypoints/serve/dev/rlhf/
test_response_schema.py, 5 tests); 19 existing sleep/wake/state-machine
tests pass unchanged against the patched engine (no regression).
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add weight version tracking to the dev RLHF API router. This is a key
gap identified in the 12-framework survey (GAP-2 in RFC #45585):
downstream RL frameworks (AReaL, miles, slime, vime) need to know which
version of weights produced each rollout for off-policy detection.
Design (improvement over sglang's weight_version):
weight_gen: monotonic int, auto-incremented on /finish_weight_update.
Cannot be forgotten, cannot go backwards. Enables staleness
arithmetic: staleness = current_gen - sample_gen.
weight_label: optional client-set string (e.g. "step-1500").
Purely informational, decoupled from ordering. Set via
/update_weight_label or via finish_weight_update body.
Both update AFTER the engine confirms finish_weight_update succeeded,
so there is no window where gen is bumped but weights are stale.
Endpoints:
GET /weight_info → {"weight_gen": int, "weight_label": str}
POST /update_weight_label → set label without bumping gen
POST /finish_weight_update now returns weight_gen in response body
Implementation: _WeightVersionState on app.state (thread-safe, no engine
core plumbing needed). Initialized in attach_router().
8 tests pass in vllm/vllm-openai:v0.23.0 container.
RFC: vllm-project/vllm#45585
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… NCCL-free paths test_sleep_wake.py: - test_sleep_level1_frees_gpu_memory: fix re_allocated_gib direction (free_sleeping - free_awake2 > 0.4; after wake, free bytes decrease) - test_pause_blocks_without_releasing_memory: fix is_sleeping assertion (v1 engine: is_sleeping = is_scheduler_paused OR executor.is_sleeping) test_weight_update.py: - test_start_finish_no_tensors_engine_survives: accept 200 or 500 (start_weight_update needs init_weight_transfer_engine / NCCL) - test_finish_without_start_handled: add 500 to allowed status codes - TestCompoundRLStep: use sleep/wake only (remove start/finish that need NCCL) - TestWeightReloadCodePaths: skip gracefully on read-only HF cache Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- _run_reload_test: use exc.errno == errno.EROFS instead of locale-sensitive string matching on OSError.strerror (Codex: str-match is locale-dependent) - TestCompoundRLStep: update class docstring to reflect weight_update calls were removed; stale docstring listed the full RL lifecycle but tests only cover the staged-wake scheduler guard - test_sleep_level1: consolidate 3 lines into 1 comment; trim redundant inline Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…us metrics + RL state machine
Phase 2.5 — Weight checksum (POST /weight_checker):
actions: snapshot | compare | checksum | reset
Worker computes SHA-256 per named parameter via collective_rpc.
New files: rlhf/metrics.py, v1/worker/gpu_worker.compute_weight_checksums,
v1/executor/abstract.compute_weight_checksums,
v1/engine/core.compute_weight_checksums,
v1/engine/core_client.compute_weight_checksums_async,
v1/engine/async_llm.compute_weight_checksums
Phase 3a — Prometheus metrics (vllm:rl_weight_update_*):
vllm:rl_weight_update_total Counter (increments on finish)
vllm:rl_weight_update_duration_seconds Histogram (start→finish latency)
vllm:rl_weight_gen Gauge (mirrors weight_gen)
vllm:rl_weight_update_active Gauge (1 during update, 0 at rest)
New file: rlhf/metrics.py
Phase 3b — RL state machine enforcement (rlhf/rl_state_machine.py):
Enforces ordering: start → update_weights → finish
Double-start → 409 Conflict
Finish-without-start → 409 Conflict
update_weights-without-start → 409 Conflict
New endpoint: GET /weight_update_active → {weight_update_active: bool}
New test files: test_weight_checker.py (13 tests)
test_weight_update_metrics.py (7 tests)
test_state_machine_enforcement.py (9 tests)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… at startup test_weight_checker.py: sleep()/wake() from conftest return int (status_code), not Response → use == 200 comparison, not .status_code api_router.attach_router: initialize RL Prometheus metric label sets at startup so vllm:rl_weight_* appear in /metrics before any weight update occurs (prometheus_client labeled metrics only surface after first label-set creation) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…, compare api_router.py: - start/finish_weight_update: check SM BEFORE engine call (prevent stuck flag); mark SM AFTER engine succeeds; finish uses try/finally so active metric always clears even if bump raises - update_weights: proper try/except around async on_update_weights() rl_state_machine.py: - Replace threading.Lock with asyncio.Lock (event-loop safe for async handlers) - All transition methods are now async (await sm.on_*) - Pre-check `is_updating` property for conflict detection before engine call (avoids double-acquire and allows early 409 before RPC cost) gpu_worker.compute_weight_checksums: - Include named_buffers() alongside named_parameters() (quantized/LoRA weights may be registered as buffers) - Guard exotic dtypes to avoid view(torch.uint8) crash on bool tensors _WeightCheckerState.compare: - Surface tensors in snapshot but missing from current (partial transfer catch) test_weight_update_metrics.py: - Remove Counter from startup assertion: prometheus_client Counter labeled at value=0 may not appear in /metrics until first inc() — behavior is version- dependent. Document this in the test and check separately after updates. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
@aoshen02 Great test matrix — this kind of systematic lifecycle coverage is exactly what sleep mode needs. A few edge cases from our multi-model hot-swap work that aren't in the current matrix:
Happy to contribute these as additional test cases if they fit the scope. |
I see, if needed please find me at slack via aoshen524@gmail.com or add my wechat a18102550020, thanks. |
… wake ordering Add test_cumem_physical.py covering allocator-level edge cases from production multi-model hot-swap work: Tier 2 (Physical — cumem state machine): - Partial wake leaves KV unmapped (precondition for #44395) - sleep() after partial wake skips already-unmapped allocations (prevents double unmap_and_release → CUDA_ERROR_INVALID_VALUE) - Repeated wake of same tag is idempotent (no double create_and_map) - sleep/wake correctness and timing regression with CUDA graph pool Tier 3 (Protocol — cross-tag ordering): - wake(weights) → generate: scheduler must block (KV still unmapped) - wake(kv_cache) → generate: scheduler must block (weights unmapped) - Concurrent wake + generate race does not crash engine These tests exercise the CuMemAllocator VMM state transitions that are not reachable through the HTTP flag-level tests in test_state_machine.py. RFC: vllm-project/vllm#45585 Refs: #44395, #46438
| ) | ||
| except (requests.exceptions.Timeout, | ||
| requests.exceptions.ConnectionError): | ||
| pass # Both acceptable — blocked or crashed safely |
There was a problem hiding this comment.
it seems like if r is normal, we need to fail the testcase, but it doesn't make it here.
There was a problem hiding this comment.
Thanks for the headsup!
Purpose
Implements the test matrix proposed in RFC #45585.
Adds
tests/entrypoints/serve/dev/rlhf/with two files that cover the fullRL lifecycle through the production HTTP endpoint surface (
VLLM_SERVER_DEV_MODE=1).All tests require
--enable-sleep-mode— without itsleep/wake_upare no-opsand the underlying bugs cannot be exercised.
Files
test_sleep_wake.py— 6 classes, 26 testsTestSleepWakeFlagsis_sleepingflag + Prometheusvllm:engine_sleep_statemetrics (supersedestest_sleep.py)TestPhysicalMemoryTestSchedulerGateTestOutputCorrectnesssleep(1)→wake, staged wake, 3-cycle stability, prefix-cache flushTestPauseResume/pause/resume/is_paused— all threePauseModevalues (abort/wait/keep)TestErrorPathstest_weight_update.py— 3 classes, 7 testsTestWeightTransferProtocol/start_weight_update→/finish_weight_updateframing, prefix-cache flush after updateTestWeightUpdateWithTensorsVLLM_TEST_MODEL_ALTis set)TestCompoundRLStepsleep → update → wake(weights) → wake(kv_cache) → rolloutBugs directly addressed
TestSchedulerGate::test_partial_wake_blocks_until_kv_residentTestSchedulerGate::test_sleep_abort_mode_blocks_new_requestsTestErrorPaths::test_double_sleep_idempotentTest plan
Verified on H200, Qwen3-0.6B,
--enforce-eager.Notes
test_sleep.py(existing) is left untouched;TestSleepWakeFlagssupersedes itand it can be removed in a follow-up once this suite is stable in CI.
TestWeightUpdateWithTensorsis skipped by default (requiresVLLM_TEST_MODEL_ALT);intended to run nightly or manually.
running against both patched and un-patched vLLM on H200.