main
py 168 lines 5.25 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """End-to-end tests for the vLLM RL pause/resume lifecycle."""
4
5 import os
6 import threading
7 from typing import Any
8 from unittest.mock import patch
9
10 import pytest
11 import requests
12
13 from tests.entrypoints.serve.dev.rlhf.conftest import (
14 cached_tokens,
15 completion_with_cache_details,
16 gen,
17 golden_output,
18 is_paused,
19 ok,
20 pause,
21 resume,
22 server,
23 start_stream,
24 )
25
26
27 @pytest.fixture(scope="module", params=[False, True], ids=["MRV1", "MRV2"])
28 def use_v2(request):
29 return request.param
30
31
32 @pytest.fixture(scope="module")
33 def server_url(use_v2):
34 env_vars = {
35 "VLLM_USE_V2_MODEL_RUNNER": "1" if use_v2 else "0",
36 }
37
38 with (
39 patch.dict(os.environ, env_vars),
40 server(
41 extra_args=[
42 "--enable-prefix-caching",
43 "--enable-prompt-tokens-details",
44 ]
45 ) as url,
46 ):
47 yield url
48
49
50 @pytest.fixture(autouse=True)
51 def restore_unpaused_state(server_url):
52 assert resume(server_url) == 200
53 yield
54 assert resume(server_url) == 200
55
56
57 class TestPauseResume:
58 def test_state_and_idempotency_across_cycles(self, server_url):
59 assert not is_paused(server_url)
60
61 assert resume(server_url) == 200
62 assert resume(server_url) == 200
63 assert not is_paused(server_url)
64
65 for _ in range(2):
66 for mode in ("abort", "wait", "keep"):
67 assert pause(server_url, mode=mode) == 200
68 assert pause(server_url, mode=mode) == 200
69 assert is_paused(server_url)
70
71 assert resume(server_url) == 200
72 assert resume(server_url) == 200
73 assert not is_paused(server_url)
74
75 def test_invalid_mode_preserves_state(self, server_url):
76 for paused in (False, True):
77 if paused:
78 assert pause(server_url) == 200
79 assert is_paused(server_url) is paused
80
81 response = requests.post(
82 f"{server_url}/pause",
83 params={"mode": "invalid"},
84 timeout=10,
85 )
86 assert response.status_code == 400
87 assert response.json()["error"]["param"] == "query.mode"
88 assert is_paused(server_url) is paused
89
90 assert resume(server_url) == 200
91
92 @pytest.mark.parametrize(
93 ("mode", "max_tokens", "inflight_finish_reason"),
94 [
95 pytest.param("abort", 256, "abort", id="abort"),
96 pytest.param("wait", 256, "length", id="wait"),
97 pytest.param("keep", 256, "length", id="keep"),
98 ],
99 )
100 def test_mode_request_lifecycle(
101 self,
102 server_url,
103 mode,
104 max_tokens,
105 inflight_finish_reason,
106 ):
107 inflight, inflight_thread = start_stream(server_url, max_tokens)
108 new_result: dict[str, Any] = {}
109 new_done = threading.Event()
110
111 def _new_request():
112 new_result["response"] = gen(server_url, max_tokens=4, timeout=60)
113 new_done.set()
114
115 new_thread = threading.Thread(target=_new_request)
116 try:
117 assert pause(server_url, mode=mode) == 200
118 assert is_paused(server_url)
119
120 if mode in ("abort", "wait"):
121 assert inflight.done.is_set()
122 else:
123 chunks_after_pause = len(inflight.chunks)
124 assert not inflight.done.wait(timeout=5)
125 assert len(inflight.chunks) == chunks_after_pause, (
126 "in-flight request continued generating in keep mode"
127 )
128
129 new_thread.start()
130 assert not new_done.wait(timeout=0.3), (
131 "new request completed while generation was paused"
132 )
133 finally:
134 assert resume(server_url) == 200
135 inflight_thread.join(timeout=30)
136 if new_thread.ident is not None:
137 new_thread.join(timeout=30)
138
139 assert not inflight_thread.is_alive()
140 assert inflight.error is None
141 assert inflight.finish_reason == inflight_finish_reason
142 assert not new_thread.is_alive()
143 assert ok(new_result.get("response"))
144
145 def test_clear_cache_preserves_output_and_controls_prefix_cache(self, server_url):
146 prompt = (
147 "Paris is the capital of France. Berlin is the capital of Germany. "
148 ) * 20
149
150 assert pause(server_url, clear_cache=True) == 200
151 assert resume(server_url) == 200
152 baseline = completion_with_cache_details(server_url, prompt)
153 warmed = completion_with_cache_details(server_url, prompt)
154
155 assert cached_tokens(baseline) == 0
156 assert cached_tokens(warmed) > 0
157
158 assert pause(server_url, clear_cache=False) == 200
159 assert resume(server_url) == 200
160 preserved = completion_with_cache_details(server_url, prompt)
161 assert golden_output(preserved) == golden_output(baseline)
162 assert cached_tokens(preserved) > 0
163
164 assert pause(server_url, clear_cache=True) == 200
165 assert resume(server_url) == 200
166 cleared = completion_with_cache_details(server_url, prompt)
167 assert golden_output(cleared) == golden_output(baseline)
168 assert cached_tokens(cleared) == 0