A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[V0 Deprecation] Remove MQLLMEngine (#25019)
Signed-off-by: Woosuk Kwon <woosuk.kwon@berkeley.edu> Signed-off-by: Woosuk Kwon <woosuk@thinkingmachines.ai>
Woosuk Kwon committed
Sep 16, 2025 at 21:29 UTC
5801e4977679895d89493e84088e7936f528285f
15 files changed
+12
-1969
.buildkite/test-pipeline.yaml
-2
@@ -46,7 +46,6 @@ steps:
46
mirror_hardwares: [amdexperimental]
47
source_file_dependencies:
48
- vllm/
49
- - tests/mq_llm_engine
49
- tests/async_engine
50
- tests/test_inputs.py
51
- tests/test_outputs.py
@@ -57,7 +56,6 @@ steps:
56
- tests/transformers_utils
57
commands:
58
- python3 standalone_tests/lazy_imports.py
60
- - pytest -v -s mq_llm_engine # MQLLMEngine
59
- pytest -v -s async_engine # AsyncLLMEngine
60
- pytest -v -s test_inputs.py
61
- pytest -v -s test_outputs.py
tests/entrypoints/openai/test_lora_resolvers.py
+2
-2
@@ -10,7 +10,6 @@ from unittest.mock import MagicMock
10
import pytest
11
12
from vllm.config.multimodal import MultiModalConfig
13
-from vllm.engine.multiprocessing.client import MQLLMEngineClient
13
from vllm.entrypoints.openai.protocol import CompletionRequest, ErrorResponse
14
from vllm.entrypoints.openai.serving_completion import OpenAIServingCompletion
15
from vllm.entrypoints.openai.serving_models import (BaseModelPath,
@@ -18,6 +17,7 @@ from vllm.entrypoints.openai.serving_models import (BaseModelPath,
17
from vllm.lora.request import LoRARequest
18
from vllm.lora.resolver import LoRAResolver, LoRAResolverRegistry
19
from vllm.transformers_utils.tokenizer import get_tokenizer
20
+from vllm.v1.engine.async_llm import AsyncLLM
21
22
MODEL_NAME = "openai-community/gpt2"
23
BASE_MODEL_PATHS = [BaseModelPath(name=MODEL_NAME, model_path=MODEL_NAME)]
@@ -82,7 +82,7 @@ def register_mock_resolver():
82
@pytest.fixture
83
def mock_serving_setup():
84
"""Provides a mocked engine and serving completion instance."""
85
- mock_engine = MagicMock(spec=MQLLMEngineClient)
85
+ mock_engine = MagicMock(spec=AsyncLLM)
86
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
87
mock_engine.errored = False
88
tests/entrypoints/openai/test_serving_chat.py
+7
-7
@@ -13,12 +13,12 @@ import pytest
13
import pytest_asyncio
14
15
from vllm.config.multimodal import MultiModalConfig
16
-from vllm.engine.multiprocessing.client import MQLLMEngineClient
16
from vllm.entrypoints.openai.protocol import ChatCompletionRequest
17
from vllm.entrypoints.openai.serving_chat import OpenAIServingChat
18
from vllm.entrypoints.openai.serving_models import (BaseModelPath,
19
OpenAIServingModels)
20
from vllm.transformers_utils.tokenizer import get_tokenizer
21
+from vllm.v1.engine.async_llm import AsyncLLM
22
23
from ...utils import RemoteOpenAIServer
24
@@ -276,7 +276,7 @@ def test_async_serving_chat_init():
276
277
@pytest.mark.asyncio
278
async def test_serving_chat_returns_correct_model_name():
279
- mock_engine = MagicMock(spec=MQLLMEngineClient)
279
+ mock_engine = MagicMock(spec=AsyncLLM)
280
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
281
mock_engine.errored = False
282
@@ -312,7 +312,7 @@ async def test_serving_chat_returns_correct_model_name():
312
313
@pytest.mark.asyncio
314
async def test_serving_chat_should_set_correct_max_tokens():
315
- mock_engine = MagicMock(spec=MQLLMEngineClient)
315
+ mock_engine = MagicMock(spec=AsyncLLM)
316
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
317
mock_engine.errored = False
318
@@ -355,7 +355,7 @@ async def test_serving_chat_should_set_correct_max_tokens():
355
}
356
357
# Reinitialize the engine with new settings
358
- mock_engine = MagicMock(spec=MQLLMEngineClient)
358
+ mock_engine = MagicMock(spec=AsyncLLM)
359
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
360
mock_engine.errored = False
361
@@ -410,7 +410,7 @@ async def test_serving_chat_should_set_correct_max_tokens():
410
}
411
412
# Reinitialize the engine with new settings
413
- mock_engine = MagicMock(spec=MQLLMEngineClient)
413
+ mock_engine = MagicMock(spec=AsyncLLM)
414
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
415
mock_engine.errored = False
416
@@ -467,7 +467,7 @@ async def test_serving_chat_could_load_correct_generation_config():
467
"repetition_penalty": 1.05
468
}
469
470
- mock_engine = MagicMock(spec=MQLLMEngineClient)
470
+ mock_engine = MagicMock(spec=AsyncLLM)
471
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
472
mock_engine.errored = False
473
@@ -523,7 +523,7 @@ async def test_serving_chat_did_set_correct_cache_salt(model_type):
523
mock_model_config = MockModelConfig()
524
mock_model_config.hf_config.model_type = model_type
525
526
- mock_engine = MagicMock(spec=MQLLMEngineClient)
526
+ mock_engine = MagicMock(spec=AsyncLLM)
527
mock_engine.get_tokenizer.return_value = get_tokenizer(MODEL_NAME)
528
mock_engine.errored = False
529
tests/mq_llm_engine/__init__.py
tests/mq_llm_engine/conftest.py
deleted
-12
@@ -1,12 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-import pytest
4
-
5
-
6
-@pytest.fixture(scope="function", autouse=True)
7
-def use_v0_only(monkeypatch):
8
- """
9
- Since this module is V0 only, set VLLM_USE_V1=0 for
10
- all tests in the module.
11
- """
12
- monkeypatch.setenv('VLLM_USE_V1', '0')
tests/mq_llm_engine/test_abort.py
deleted
-69
@@ -1,69 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-"""Test that aborting is handled properly."""
4
-
5
-import asyncio
6
-import tempfile
7
-import uuid
8
-
9
-import pytest
10
-
11
-from tests.mq_llm_engine.utils import RemoteMQLLMEngine, generate
12
-from vllm.engine.arg_utils import AsyncEngineArgs
13
-
14
-MODEL = "google/gemma-1.1-2b-it"
15
-ENGINE_ARGS = AsyncEngineArgs(model=MODEL)
16
-RAISED_ERROR = KeyError
17
-RAISED_VALUE = "foo"
18
-EXPECTED_TOKENS = 250
19
-
20
-
21
-@pytest.fixture(scope="function")
22
-def tmp_socket():
23
- with tempfile.TemporaryDirectory() as td:
24
- yield f"ipc://{td}/{uuid.uuid4()}"
25
-
26
-
27
-@pytest.mark.asyncio
28
-async def test_abort(tmp_socket):
29
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
30
- ipc_path=tmp_socket) as engine:
31
-
32
- client = await engine.make_client()
33
-
34
- request_id_to_be_aborted = "request-aborted"
35
- request_ids_a = [f"request-a-{idx}" for idx in range(10)]
36
- request_ids_b = [f"request-b-{idx}" for idx in range(10)]
37
-
38
- # Requests started before one to be aborted.
39
- tasks = []
40
- for request_id in request_ids_a:
41
- tasks.append(
42
- asyncio.create_task(
43
- generate(client, request_id, EXPECTED_TOKENS)))
44
-
45
- # Aborted.
46
- task_aborted = asyncio.create_task(
47
- generate(client, request_id_to_be_aborted, EXPECTED_TOKENS))
48
-
49
- # Requests started after one to be aborted.
50
- for request_id in request_ids_b:
51
- tasks.append(
52
- asyncio.create_task(
53
- generate(client, request_id, EXPECTED_TOKENS)))
54
-
55
- # Actually abort.
56
- await asyncio.sleep(0.5)
57
- await client.abort(request_id_to_be_aborted)
58
-
59
- # Confirm that we got all the EXPECTED tokens from the requests.
60
- for task in tasks:
61
- count, request_id = await task
62
- assert count == EXPECTED_TOKENS, (
63
- f"{request_id} generated only {count} tokens")
64
-
65
- # Cancel task (this will hang indefinitely if not).
66
- task_aborted.cancel()
67
-
68
- # Shutdown.
69
- client.close()
tests/mq_llm_engine/test_error_handling.py
deleted
-376
@@ -1,376 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-"""Test that various errors are handled properly."""
4
-
5
-import asyncio
6
-import tempfile
7
-import time
8
-import uuid
9
-from unittest.mock import Mock
10
-
11
-import pytest
12
-
13
-from tests.mq_llm_engine.utils import RemoteMQLLMEngine
14
-from vllm import SamplingParams
15
-from vllm.engine.arg_utils import AsyncEngineArgs
16
-from vllm.engine.llm_engine import LLMEngine
17
-from vllm.engine.multiprocessing import MQEngineDeadError
18
-from vllm.engine.multiprocessing.engine import MQLLMEngine
19
-from vllm.entrypoints.openai.api_server import build_async_engine_client
20
-from vllm.entrypoints.openai.cli_args import make_arg_parser
21
-from vllm.lora.request import LoRARequest
22
-from vllm.sequence import SequenceGroupMetadata
23
-from vllm.usage.usage_lib import UsageContext
24
-from vllm.utils import FlexibleArgumentParser
25
-
26
-MODEL = "google/gemma-1.1-2b-it"
27
-ENGINE_ARGS = AsyncEngineArgs(model=MODEL, enforce_eager=True)
28
-RAISED_ERROR = KeyError
29
-RAISED_VALUE = "foo"
30
-
31
-
32
-@pytest.fixture(scope="function")
33
-def tmp_socket():
34
- with tempfile.TemporaryDirectory() as td:
35
- yield f"ipc://{td}/{uuid.uuid4()}"
36
-
37
-
38
-def run_with_evil_forward(engine_args: AsyncEngineArgs, ipc_path: str):
39
- # Make engine.
40
- engine = MQLLMEngine.from_engine_args(
41
- engine_args=engine_args,
42
- usage_context=UsageContext.UNKNOWN_CONTEXT,
43
- ipc_path=ipc_path)
44
-
45
- # Raise error during first forward pass.
46
- engine.engine.model_executor.execute_model = Mock(
47
- side_effect=RAISED_ERROR(RAISED_VALUE))
48
-
49
- # Run engine.
50
- engine.start()
51
-
52
-
53
-@pytest.mark.asyncio
54
-async def test_evil_forward(tmp_socket):
55
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
56
- ipc_path=tmp_socket,
57
- run_fn=run_with_evil_forward) as engine:
58
-
59
- client = await engine.make_client()
60
-
61
- # Server should be healthy after initial probe.
62
- await asyncio.sleep(2.0)
63
- await client.check_health()
64
-
65
- # Throws an error that should get ENGINE_DEAD_ERROR.
66
- with pytest.raises(MQEngineDeadError):
67
- async for _ in client.generate(prompt="Hello my name is",
68
- sampling_params=SamplingParams(),
69
- request_id=str(uuid.uuid4())):
70
- pass
71
- assert client.errored
72
-
73
- await asyncio.sleep(1.0)
74
- with pytest.raises(RAISED_ERROR):
75
- await client.check_health()
76
- assert client.errored
77
-
78
- # Shutdown.
79
- client.close()
80
-
81
-
82
-def run_with_evil_model_executor_health(engine_args: AsyncEngineArgs,
83
- ipc_path: str):
84
- # Make engine.
85
- engine = MQLLMEngine.from_engine_args(
86
- engine_args=engine_args,
87
- usage_context=UsageContext.UNKNOWN_CONTEXT,
88
- ipc_path=ipc_path)
89
-
90
- # Raise error during first forward pass.
91
- engine.engine.model_executor.check_health = Mock(side_effect=RAISED_ERROR)
92
-
93
- # Run engine.
94
- engine.start()
95
-
96
-
97
-@pytest.mark.asyncio
98
-async def test_failed_health_check(tmp_socket):
99
- with RemoteMQLLMEngine(
100
- engine_args=ENGINE_ARGS,
101
- ipc_path=tmp_socket,
102
- run_fn=run_with_evil_model_executor_health) as engine:
103
-
104
- client = await engine.make_client()
105
- assert client.is_running
106
-
107
- # Health probe should throw RAISED_ERROR.
108
- await asyncio.sleep(15.)
109
-
110
- with pytest.raises(RAISED_ERROR):
111
- await client.check_health()
112
- assert client.errored
113
-
114
- # Generate call should throw ENGINE_DEAD_ERROR
115
- with pytest.raises(MQEngineDeadError):
116
- async for _ in client.generate(prompt="Hello my name is",
117
- sampling_params=SamplingParams(),
118
- request_id=str(uuid.uuid4())):
119
- pass
120
-
121
- client.close()
122
-
123
-
124
-def run_with_evil_abort(engine_args: AsyncEngineArgs, ipc_path: str):
125
- # Make engine.
126
- engine = MQLLMEngine.from_engine_args(
127
- engine_args=engine_args,
128
- usage_context=UsageContext.UNKNOWN_CONTEXT,
129
- ipc_path=ipc_path)
130
-
131
- # Raise error during abort call.
132
- engine.engine.abort_request = Mock(side_effect=RAISED_ERROR)
133
-
134
- # Run engine.
135
- engine.start()
136
-
137
-
138
-@pytest.mark.asyncio
139
-async def test_failed_abort(tmp_socket):
140
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
141
- ipc_path=tmp_socket,
142
- run_fn=run_with_evil_abort) as engine:
143
-
144
- client = await engine.make_client()
145
- assert client.is_running
146
-
147
- # First check health should work.
148
- await client.check_health()
149
-
150
- # Trigger an abort on the client side.
151
- # This request ID does not exist, and will cause the engine to error
152
- await client.abort(request_id="foo")
153
-
154
- # Future generation requests will now fail
155
- # with reference to the original KeyError("foo")
156
- with pytest.raises(MQEngineDeadError) as execinfo:
157
- async for _ in client.generate(
158
- prompt="Hello my name is",
159
- sampling_params=SamplingParams(max_tokens=10),
160
- request_id=str(uuid.uuid4())):
161
- pass
162
- assert "KeyError" in repr(execinfo.value)
163
- assert client.errored
164
-
165
- # This should raise the original error.
166
- with pytest.raises(RAISED_ERROR):
167
- await client.check_health()
168
-
169
- client.close()
170
-
171
-
172
-@pytest.mark.asyncio
173
-async def test_batch_error(tmp_socket):
174
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
175
- ipc_path=tmp_socket,
176
- run_fn=run_with_evil_abort) as engine:
177
-
178
- client = await engine.make_client()
179
- assert client.is_running
180
-
181
- # First check health should work.
182
- await client.check_health()
183
-
184
- # Batch of requests
185
- async def do_generate(client):
186
- # min_tokens=2048 to keep busy the engine busy
187
- # to get enough time to get process a request
188
- # that will crash the engine
189
- params = SamplingParams(min_tokens=2048, max_tokens=2048)
190
- async for _ in client.generate(prompt="Hello my name is",
191
- sampling_params=params,
192
- request_id=str(uuid.uuid4())):
193
- pass
194
-
195
- tasks = [asyncio.create_task(do_generate(client)) for _ in range(10)]
196
-
197
- # This request will force a processing batch to raise
198
- # an exception and next the engine get errored
199
- await client.abort(request_id="foo")
200
-
201
- # The batch of those request failed, then they
202
- # should get the same exception as a MQEngineDeadError.
203
- errors = await asyncio.gather(*tasks, return_exceptions=True)
204
- for e in errors:
205
- assert isinstance(e, MQEngineDeadError)
206
- assert "KeyError" in repr(e)
207
-
208
- client.close()
209
-
210
-
211
-@pytest.mark.asyncio
212
-async def test_bad_request(tmp_socket):
213
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
214
- ipc_path=tmp_socket) as engine:
215
-
216
- client = await engine.make_client()
217
-
218
- # Invalid request should fail, but not crash the server.
219
- with pytest.raises(ValueError):
220
- async for _ in client.generate(prompt="Hello my name is",
221
- sampling_params=SamplingParams(),
222
- request_id="abcd-1",
223
- lora_request=LoRARequest(
224
- "invalid-lora", 1,
225
- "invalid-path")):
226
- pass
227
-
228
- # This request should be okay.
229
- async for _ in client.generate(prompt="Hello my name is",
230
- sampling_params=SamplingParams(),
231
- request_id="abcd-2"):
232
- pass
233
-
234
- # Shutdown.
235
- client.close()
236
-
237
-
238
-@pytest.mark.asyncio
239
-async def test_mp_crash_detection(monkeypatch: pytest.MonkeyPatch):
240
- with monkeypatch.context() as m:
241
-
242
- parser = FlexibleArgumentParser(
243
- description="vLLM's remote OpenAI server.")
244
- parser = make_arg_parser(parser)
245
- args = parser.parse_args([])
246
-
247
- # When LLMEngine is loaded, it will crash.
248
- def mock_init():
249
- raise ValueError
250
-
251
- m.setattr(LLMEngine, "__init__", mock_init)
252
-
253
- start = time.perf_counter()
254
- async with build_async_engine_client(args):
255
- pass
256
- end = time.perf_counter()
257
-
258
- assert end - start < 100, (
259
- "Expected vLLM to gracefully shutdown in <100s "
260
- "if there is an error in the startup.")
261
-
262
-
263
-@pytest.mark.asyncio
264
-async def test_mp_cuda_init():
265
- # it should not crash, when cuda is initialized
266
- # in the API server process
267
- import torch
268
- torch.cuda.init()
269
- parser = FlexibleArgumentParser(description="vLLM's remote OpenAI server.")
270
- parser = make_arg_parser(parser)
271
- args = parser.parse_args([])
272
-
273
- async with build_async_engine_client(args):
274
- pass
275
-
276
-
277
-@pytest.mark.asyncio
278
-async def test_engine_process_death(tmp_socket):
279
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
280
- ipc_path=tmp_socket) as engine:
281
-
282
- client = await engine.make_client()
283
- assert client.is_running
284
-
285
- # kill the engine process
286
- engine.proc.kill()
287
-
288
- # Generate call should fail
289
- with pytest.raises(MQEngineDeadError):
290
- async for _ in client.generate(prompt="Hello my name is",
291
- sampling_params=SamplingParams(),
292
- request_id=str(uuid.uuid4())):
293
- pass
294
-
295
- # And the health check should show the engine is dead
296
- with pytest.raises(RuntimeError, match="Engine process .* died"):
297
- await client.check_health()
298
-
299
- client.close()
300
-
301
-
302
-def run_with_evil_input_processing(engine_args: AsyncEngineArgs,
303
- ipc_path: str):
304
- """Simulate an exception while preparing inputs for the model.
305
- In the wild, this could be something like a multimodal input processor
306
- failing on invalid image data."""
307
-
308
- # Make engine.
309
- engine = MQLLMEngine.from_engine_args(
310
- engine_args=engine_args,
311
- usage_context=UsageContext.UNKNOWN_CONTEXT,
312
- ipc_path=ipc_path)
313
-
314
- runner = engine.engine.model_executor.driver_worker.worker.model_runner
315
-
316
- # Raise error in the model runner when adding a sequence group.
317
- # See class ModelInputForGPUBuilder
318
- def raiser(_, seq_group_metadata: SequenceGroupMetadata):
319
- if seq_group_metadata.request_id.startswith("evil"):
320
- raise RAISED_ERROR(RAISED_VALUE)
321
-
322
- runner.builder.per_seq_group_compute_fns.append(raiser)
323
-
324
- # Run engine.
325
- engine.start()
326
-
327
-
328
-@pytest.mark.asyncio
329
-async def test_failed_inputs(tmp_socket):
330
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
331
- ipc_path=tmp_socket,
332
- run_fn=run_with_evil_input_processing) as engine:
333
-
334
- client = await engine.make_client()
335
- assert client.is_running
336
-
337
- # Engine should be healthy
338
- await client.check_health()
339
-
340
- async def run_failing_request():
341
- async for _ in client.generate(
342
- prompt="Hello my name is",
343
- sampling_params=SamplingParams(max_tokens=10),
344
- request_id="evil" + str(uuid.uuid4())):
345
- pass
346
-
347
- async def run_passing_request():
348
- async for _ in client.generate(
349
- prompt="Hello my name is",
350
- sampling_params=SamplingParams(max_tokens=10),
351
- request_id=str(uuid.uuid4())):
352
- pass
353
-
354
- passing_tasks = [
355
- asyncio.create_task(run_passing_request()) for _ in range(10)
356
- ]
357
- failing_tasks = [
358
- asyncio.create_task(run_failing_request()) for _ in range(10)
359
- ]
360
- await asyncio.gather(*failing_tasks, return_exceptions=True)
361
- await asyncio.gather(*passing_tasks)
362
-
363
- # All the bad inputs should have raised
364
- for task in failing_tasks:
365
- with pytest.raises(RAISED_ERROR):
366
- task.result()
367
-
368
- # But all good inputs should have still succeeded
369
- for task in passing_tasks:
370
- task.result()
371
-
372
- # And the engine should remain healthy
373
- assert not client.errored
374
- await client.check_health()
375
-
376
- client.close()
tests/mq_llm_engine/test_load.py
deleted
-59
@@ -1,59 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-"""Test that the MQLLMEngine is able to handle 10k concurrent requests."""
4
-
5
-import asyncio
6
-import tempfile
7
-import uuid
8
-
9
-import pytest
10
-
11
-from tests.mq_llm_engine.utils import RemoteMQLLMEngine, generate
12
-from vllm.engine.arg_utils import AsyncEngineArgs
13
-
14
-MODEL = "google/gemma-1.1-2b-it"
15
-NUM_EXPECTED_TOKENS = 10
16
-NUM_REQUESTS = 10000
17
-
18
-# Scenarios to test for num generated token.
19
-ENGINE_ARGS = AsyncEngineArgs(model=MODEL)
20
-
21
-
22
-@pytest.fixture(scope="function")
23
-def tmp_socket():
24
- with tempfile.TemporaryDirectory() as td:
25
- yield f"ipc://{td}/{uuid.uuid4()}"
26
-
27
-
28
-@pytest.mark.asyncio
29
-async def test_load(tmp_socket):
30
- with RemoteMQLLMEngine(engine_args=ENGINE_ARGS,
31
- ipc_path=tmp_socket) as engine:
32
-
33
- client = await engine.make_client()
34
-
35
- request_ids = [f"request-{i}" for i in range(NUM_REQUESTS)]
36
-
37
- # Create concurrent requests.
38
- tasks = []
39
- for request_id in request_ids:
40
- tasks.append(
41
- asyncio.create_task(
42
- generate(client, request_id, NUM_EXPECTED_TOKENS)))
43
-
44
- # Confirm that we got all the EXPECTED tokens from the requests.
45
- failed_request_id = None
46
- tokens = None
47
- for task in tasks:
48
- num_generated_tokens, request_id = await task
49
- if (num_generated_tokens != NUM_EXPECTED_TOKENS
50
- and failed_request_id is None):
51
- failed_request_id = request_id
52
- tokens = num_generated_tokens
53
-
54
- assert failed_request_id is None, (
55
- f"{failed_request_id} generated {tokens} but "
56
- f"expected {NUM_EXPECTED_TOKENS}")
57
-
58
- # Shutdown.
59
- client.close()
tests/mq_llm_engine/utils.py
deleted
-81
@@ -1,81 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import asyncio
5
-import multiprocessing
6
-from typing import Callable, Union
7
-
8
-from vllm import SamplingParams
9
-from vllm.engine.arg_utils import AsyncEngineArgs
10
-from vllm.engine.multiprocessing.client import MQLLMEngineClient
11
-from vllm.engine.multiprocessing.engine import MQLLMEngine
12
-from vllm.outputs import RequestOutput
13
-from vllm.usage.usage_lib import UsageContext
14
-
15
-
16
-async def generate(
17
- client: MQLLMEngineClient,
18
- request_id: str,
19
- num_tokens: int,
20
- return_output: bool = False) -> Union[RequestOutput, tuple[int, str]]:
21
-
22
- final_output = None
23
- count = 0
24
- async for out in client.generate(
25
- request_id=request_id,
26
- prompt="Hello my name is Robert and",
27
- sampling_params=SamplingParams(max_tokens=num_tokens,
28
- temperature=0)):
29
-
30
- count += 1
31
- final_output = out
32
- await asyncio.sleep(0.)
33
-
34
- if return_output:
35
- return final_output
36
-
37
- # Confirm we generated all the tokens we expected.
38
- return count, request_id
39
-
40
-
41
-def run_normal(engine_args: AsyncEngineArgs, ipc_path: str):
42
- # Make engine.
43
- engine = MQLLMEngine.from_engine_args(
44
- engine_args=engine_args,
45
- usage_context=UsageContext.UNKNOWN_CONTEXT,
46
- ipc_path=ipc_path)
47
-
48
- # Run engine.
49
- engine.start()
50
-
51
-
52
-class RemoteMQLLMEngine:
53
-
54
- def __init__(self,
55
- engine_args: AsyncEngineArgs,
56
- ipc_path: str,
57
- run_fn: Callable = run_normal) -> None:
58
-
59
- self.engine_args = engine_args
60
- self.ipc_path = ipc_path
61
- context = multiprocessing.get_context("spawn")
62
- self.proc = context.Process(target=run_fn,
63
- args=(engine_args, ipc_path))
64
- self.proc.start()
65
-
66
- def __enter__(self):
67
- return self
68
-
69
- def __exit__(self, exc_type, exc_value, traceback):
70
- self.proc.kill()
71
-
72
- async def make_client(self) -> MQLLMEngineClient:
73
- engine_config = self.engine_args.create_engine_config()
74
- client = MQLLMEngineClient(self.ipc_path, engine_config, self.proc.pid)
75
- while True:
76
- try:
77
- await client.setup()
78
- break
79
- except TimeoutError:
80
- assert self.proc.is_alive()
81
- return client
vllm/engine/multiprocessing/__init__.py
deleted
-145
@@ -1,145 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import uuid
5
-from dataclasses import dataclass, field
6
-from enum import Enum
7
-from typing import List, Mapping, Optional, Union
8
-
9
-from vllm import PoolingParams
10
-from vllm.inputs import PromptType
11
-from vllm.lora.request import LoRARequest
12
-from vllm.outputs import RequestOutput
13
-from vllm.sampling_params import SamplingParams
14
-from vllm.utils import Device
15
-
16
-VLLM_RPC_SUCCESS_STR = "SUCCESS"
17
-
18
-IPC_INPUT_EXT = "_input_socket"
19
-IPC_OUTPUT_EXT = "_output_socket"
20
-IPC_HEALTH_EXT = "_health_socket"
21
-IPC_DATA_EXT = "_data_socket"
22
-
23
-
24
-class MQEngineDeadError(RuntimeError):
25
- pass
26
-
27
-
28
-@dataclass
29
-class RPCProcessRequest:
30
- prompt: PromptType
31
- params: Union[SamplingParams, PoolingParams]
32
- request_id: str
33
- lora_request: Optional[LoRARequest] = None
34
- trace_headers: Optional[Mapping[str, str]] = None
35
- priority: int = 0
36
-
37
- def __init__(
38
- self,
39
- prompt: PromptType,
40
- params: Union[SamplingParams, PoolingParams],
41
- request_id: str,
42
- lora_request: Optional[LoRARequest] = None,
43
- trace_headers: Optional[Mapping[str, str]] = None,
44
- priority: int = 0,
45
- ) -> None:
46
- super().__init__()
47
-
48
- self.prompt = prompt
49
- self.params = params
50
- self.request_id = request_id
51
- self.lora_request = lora_request
52
- self.trace_headers = trace_headers
53
- self.priority = priority
54
-
55
-
56
-@dataclass
57
-class RPCError:
58
- request_id: Optional[str]
59
- is_engine_errored: bool
60
- exception: BaseException
61
-
62
-
63
-@dataclass
64
-class RPCAbortRequest:
65
- request_id: str
66
-
67
-
68
-class RPCStartupRequest(Enum):
69
- IS_SERVER_READY = 1
70
-
71
-
72
-@dataclass
73
-class RPCStartupResponse:
74
- tracing_enabled: bool
75
-
76
-
77
-class RPCUProfileRequest(Enum):
78
- START_PROFILE = 1
79
- STOP_PROFILE = 2
80
-
81
-
82
-class RPCResetMultiModalCacheRequest(Enum):
83
- RESET = 1
84
-
85
-
86
-@dataclass
87
-class RPCResetPrefixCacheRequest:
88
- device: Device
89
-
90
-
91
-class RPCSleepRequest(Enum):
92
- SLEEP_LEVEL_1 = 1
93
- SLEEP_LEVEL_2 = 2
94
-
95
-
96
-@dataclass
97
-class RPCWakeUpRequest:
98
- tags: Optional[list[str]] = None
99
-
100
-
101
-@dataclass
102
-class RPCIsSleepingRequest:
103
- # Set the default value of request_id to a new UUID
104
- request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
105
-
106
-
107
-@dataclass
108
-class RPCIsSleepingResponse:
109
- request_id: str
110
- is_sleeping: bool
111
-
112
-
113
-@dataclass
114
-class RPCLoadAdapterRequest:
115
- lora_request: LoRARequest
116
- # Set the default value of request_id to a new UUID
117
- request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
118
-
119
-
120
-@dataclass
121
-class RPCAdapterLoadedResponse:
122
- request_id: str
123
- lora_loaded: bool
124
-
125
-
126
-RPC_REQUEST_T = Union[RPCProcessRequest, RPCAbortRequest, RPCStartupRequest,
127
- RPCUProfileRequest, RPCLoadAdapterRequest,
128
- RPCResetMultiModalCacheRequest,
129
- RPCResetPrefixCacheRequest, RPCSleepRequest,
130
- RPCWakeUpRequest, RPCIsSleepingRequest]
131
-
132
-REQUEST_OUTPUTS_T = Union[List[RequestOutput], RPCAdapterLoadedResponse,
133
- RPCIsSleepingResponse, RPCError]
134
-
135
-
136
-def ENGINE_DEAD_ERROR(
137
- error: Optional[BaseException] = None) -> MQEngineDeadError:
138
- if error is None:
139
- return MQEngineDeadError(
140
- "Engine loop is not running. Inspect the stacktrace to "
141
- "find the original error")
142
-
143
- return MQEngineDeadError(
144
- "Engine loop is not running. Inspect the stacktrace to "
145
- f"find the original error: {repr(error)}.")
vllm/engine/multiprocessing/client.py
deleted
-643
@@ -1,643 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import asyncio
5
-import copy
6
-import pickle
7
-from contextlib import contextmanager, suppress
8
-from typing import (Any, AsyncGenerator, Dict, Iterable, Iterator, List,
9
- Mapping, Optional, Union)
10
-
11
-import cloudpickle
12
-import psutil
13
-import zmq
14
-import zmq.asyncio
15
-from zmq import Frame # type: ignore[attr-defined]
16
-from zmq.asyncio import Socket
17
-
18
-from vllm import PoolingParams
19
-from vllm.config import DecodingConfig, ModelConfig, VllmConfig
20
-from vllm.core.scheduler import SchedulerOutputs
21
-# yapf conflicts with isort for this block
22
-# yapf: disable
23
-from vllm.engine.multiprocessing import (ENGINE_DEAD_ERROR, IPC_DATA_EXT,
24
- IPC_HEALTH_EXT, IPC_INPUT_EXT,
25
- IPC_OUTPUT_EXT, RPC_REQUEST_T,
26
- VLLM_RPC_SUCCESS_STR, RPCAbortRequest,
27
- RPCAdapterLoadedResponse, RPCError,
28
- RPCIsSleepingRequest,
29
- RPCIsSleepingResponse,
30
- RPCLoadAdapterRequest,
31
- RPCProcessRequest,
32
- RPCResetMultiModalCacheRequest,
33
- RPCResetPrefixCacheRequest,
34
- RPCSleepRequest, RPCStartupRequest,
35
- RPCStartupResponse,
36
- RPCUProfileRequest, RPCWakeUpRequest)
37
-from vllm.engine.protocol import EngineClient
38
-# yapf: enable
39
-from vllm.envs import VLLM_RPC_TIMEOUT
40
-from vllm.inputs import PromptType
41
-from vllm.inputs.preprocess import InputPreprocessor
42
-from vllm.logger import init_logger
43
-from vllm.lora.request import LoRARequest
44
-from vllm.model_executor.layers.sampler import SamplerOutput
45
-from vllm.outputs import PoolingRequestOutput, RequestOutput
46
-from vllm.sampling_params import SamplingParams
47
-from vllm.transformers_utils.tokenizer_group import init_tokenizer_from_configs
48
-from vllm.utils import Device
49
-
50
-logger = init_logger(__name__)
51
-
52
-
53
-class MQClientClosedError(Exception):
54
- """Exception class raised when the client is used post-close.
55
-
56
- The client can be closed, which closes the ZMQ context. This normally
57
- happens on server shutdown. In some cases, methods like abort and
58
- do_log_stats will still be called and then try to open a socket, which
59
- causes a ZMQError and creates a huge stack trace.
60
- So, we throw this error such that we can suppress it.
61
- """
62
-
63
-
64
-class MQLLMEngineClient(EngineClient):
65
- """A client wrapper for MQLLMEngine that conforms to the
66
- EngineClient protocol.
67
-
68
- MQLLMEngine and MQLLMEngineClient are intended to run in separate
69
- processes communicating via zeromq ipc sockets.
70
-
71
- The entrypoint to MQLLMEngineClient is through the generate()
72
- method. On generate() MQLLMEngine does three things:
73
- - Creates an asyncio output queue
74
- - Sends a RPCGenerateRequest to the MQLLMEngine via zmq
75
- - Pulls RequestOutputs from its queue and yields them
76
-
77
- MQLLMEngine runs two background loops:
78
- - output_loop: the output loop pulls List[RequestOutput]
79
- from the MQLLMEngine via zmq (each list is the output
80
- of one engine_step in the LLMEngine). It then parses
81
- the list and pushes individual request_outputs into
82
- the corresponding output_queue such that they can be
83
- consumed by the .generate() method.
84
- - health_loop: the health loop queries the health socket
85
- every N seconds, confirming the engine is healthy
86
- """
87
-
88
- def __init__(self, ipc_path: str, engine_config: VllmConfig,
89
- engine_pid: int):
90
- self.context = zmq.asyncio.Context()
91
- self._errored_with: Optional[BaseException] = None
92
-
93
- # Get the configs.
94
- self.vllm_config = engine_config
95
- self.model_config = engine_config.model_config
96
- self.decoding_config = engine_config.decoding_config
97
-
98
- if self.vllm_config.model_config.skip_tokenizer_init:
99
- self.tokenizer = None
100
-
101
- else:
102
- # Create the tokenizer group.
103
- self.tokenizer = init_tokenizer_from_configs(
104
- model_config=self.model_config,
105
- scheduler_config=engine_config.scheduler_config,
106
- lora_config=engine_config.lora_config)
107
-
108
- self.input_preprocessor = InputPreprocessor(self.model_config,
109
- self.tokenizer)
110
-
111
- # Send RPCGenerateRequest to the MQLLMEngine.
112
- self.input_socket: Socket = self.context.socket(zmq.constants.PUSH)
113
- self.input_socket.connect(f"{ipc_path}{IPC_INPUT_EXT}")
114
-
115
- # Receive streams of RequestOutput from the MQLLMEngine.
116
- self.output_socket: Socket = self.context.socket(zmq.constants.PULL)
117
- self.output_socket.connect(f"{ipc_path}{IPC_OUTPUT_EXT}")
118
-
119
- # IPC path for acking heartbeats.
120
- self.heartbeat_socket: Socket = self.context.socket(zmq.constants.PULL)
121
- self.heartbeat_socket.connect(f"{ipc_path}{IPC_HEALTH_EXT}")
122
-
123
- # IPC path for the data socket.
124
- self.data_ipc_path = f"{ipc_path}{IPC_DATA_EXT}"
125
-
126
- # Stream for each individual request.
127
- self.output_queues: Dict[str, asyncio.Queue] = {}
128
-
129
- # Loop to handle output of the LLMEngine periodically.
130
- # Started after the MQLLMEngine is ready so that we can
131
- # build the Client in an executor to enable clean shutdown.
132
- self.output_loop: Optional[asyncio.Task] = None
133
-
134
- # Loop to check health of the LLMEngine periodically.
135
- # Started after the MQLLMEngine is ready.
136
- self.health_loop: Optional[asyncio.Task] = None
137
- self._engine_process = psutil.Process(engine_pid)
138
-
139
- @staticmethod
140
- def is_unsupported_config(vllm_config: VllmConfig):
141
- # Pipeline parallel not yet supported
142
- return vllm_config.parallel_config.pipeline_parallel_size > 1
143
-
144
- @contextmanager
145
- def get_data_socket(self) -> Iterator[Socket]:
146
- socket = self.context.socket(zmq.constants.DEALER)
147
- try:
148
- socket.connect(self.data_ipc_path)
149
- yield socket
150
- finally:
151
- socket.close(linger=0)
152
-
153
- async def run_heartbeat_loop(self, timeout: int):
154
- """Background loop that continually checks to ensure the engine process
155
- is still alive.
156
- """
157
- try:
158
- while True:
159
- # Check if the engine process is running:
160
- if not self._engine_process.is_running() or (
161
- self._engine_process.status() == psutil.STATUS_ZOMBIE):
162
- # NB: is_running() returns True for zombies
163
- self._set_errored(
164
- RuntimeError(
165
- f"Engine process (pid {self._engine_process.pid}) "
166
- "died."))
167
- break
168
-
169
- if await self.heartbeat_socket.poll(timeout=timeout):
170
- # Heartbeat received- check the message
171
- await self._check_success(
172
- error_message="Heartbeat failed.",
173
- socket=self.heartbeat_socket)
174
-
175
- logger.debug("Heartbeat successful.")
176
-
177
- except asyncio.CancelledError:
178
- logger.debug("Shutting down MQLLMEngineClient check health loop.")
179
-
180
- except psutil.NoSuchProcess:
181
- self._set_errored(
182
- RuntimeError(
183
- f"Engine process (pid {self._engine_process.pid}) died."))
184
-
185
- except Exception as e:
186
- self._set_errored(e)
187
-
188
- async def run_output_handler_loop(self):
189
- """Get RequestOutputs from Engine and stream to Request Queues"""
190
-
191
- try:
192
- while True:
193
- # Poll, checking for ENGINE_DEAD
194
- while await self.output_socket.poll(timeout=VLLM_RPC_TIMEOUT
195
- ) == 0:
196
- logger.debug("Waiting for output from MQLLMEngine.")
197
-
198
- # If errored, alert all running requests.
199
- if self.errored:
200
- for queue_j in tuple(self.output_queues.values()):
201
- queue_j.put_nowait(
202
- ENGINE_DEAD_ERROR(self._errored_with))
203
- return
204
-
205
- message: Frame = await self.output_socket.recv(copy=False)
206
- request_outputs = pickle.loads(message.buffer)
207
-
208
- is_error = isinstance(request_outputs,
209
- (BaseException, RPCError))
210
- if is_error:
211
- if isinstance(request_outputs, RPCError):
212
- rpc_error: RPCError = request_outputs
213
- request_id = rpc_error.request_id
214
- exception = rpc_error.exception
215
- is_engine_errored = rpc_error.is_engine_errored
216
- else:
217
- # MPLLMEngine should always return an RPCError to
218
- # the output_socket when an issue arises.
219
- # If we are here, we are in a bad state and
220
- # should shut down the server.
221
- error: BaseException = request_outputs
222
- logger.error(
223
- "Received Exception %s rather than RPCError from "
224
- "MPLLMEngine. This should never happen.", error)
225
- request_id = None
226
- exception = error
227
- is_engine_errored = True
228
-
229
- # Set to error state only on engine critical error
230
- # (and record only the first one)
231
- if is_engine_errored and not self._errored_with:
232
- self._errored_with = exception
233
- # If engine is errored, no matter the type of exception
234
- # it will no longer be able to receive new requests,
235
- # therefore we have to inform that the current
236
- # processed requests failed as well. Send back a dead
237
- # engine error give this feedback and also give a
238
- # 'hint' to the server to shut down next.
239
- exception = self.dead_error
240
-
241
- if request_id is None:
242
- # If request_id is None, then the engine raised an
243
- # exception for a batch, and we may not know the
244
- # request that caused it, neither if it was actually
245
- # caused by any of them (e.g. CUDA OOM). Therefore we
246
- # broadcast the same exception for all requests.
247
- for queue_i in tuple(self.output_queues.values()):
248
- queue_i.put_nowait(exception)
249
- else:
250
- queue = self.output_queues.get(request_id)
251
- if queue is not None:
252
- queue.put_nowait(exception)
253
- # Put each output into the appropriate queue.
254
- elif isinstance(
255
- request_outputs,
256
- (RPCAdapterLoadedResponse, RPCIsSleepingResponse)):
257
- self._add_output(request_outputs)
258
- else:
259
- for request_output in request_outputs:
260
- self._add_output(request_output)
261
-
262
- except asyncio.CancelledError:
263
- logger.debug("Shutting down MQLLMEngineClient output handler.")
264
-
265
- def _add_output(self, request_output: Union[RequestOutput,
266
- RPCAdapterLoadedResponse,
267
- RPCIsSleepingResponse]):
268
- queue = self.output_queues.get(request_output.request_id)
269
- if queue is not None:
270
- queue.put_nowait(request_output)
271
-
272
- async def setup(self):
273
- """Set up the client before it starts sending server requests."""
274
-
275
- # Start output_loop
276
- if self.output_loop is None:
277
- # only generate once to avoid multiple concurrent output_loops
278
- # this will lead to race conditions and wrong orders of tokens
279
- # returned by the engine
280
- # setup will be called multiple times during the startup of
281
- # the engine
282
- self.output_loop = asyncio.create_task(
283
- self.run_output_handler_loop())
284
-
285
- with self.get_data_socket() as socket:
286
- # Wait until server is ready.
287
- response = await self._wait_for_server_rpc(socket)
288
-
289
- self.tracing_flag = response.tracing_enabled
290
-
291
- # Start health_loop.
292
- if self.health_loop is None:
293
- self.health_loop = asyncio.create_task(
294
- self.run_heartbeat_loop(timeout=VLLM_RPC_TIMEOUT))
295
-
296
- def close(self):
297
- """Destroy the ZeroMQ Context."""
298
- # Close all sockets and terminate the context.
299
- self.context.destroy(linger=0)
300
-
301
- # Cancel background tasks.
302
- if self.health_loop is not None:
303
- self.health_loop.cancel()
304
- if self.output_loop is not None:
305
- self.output_loop.cancel()
306
-
307
- def _set_errored(self, e: BaseException):
308
- logger.exception(repr(e))
309
- if self._errored_with is None:
310
- self._errored_with = e
311
-
312
- @staticmethod
313
- async def _send_get_data_rpc_request(request: RPCStartupRequest,
314
- expected_type: Any,
315
- error_message: str,
316
- socket: Socket) -> Any:
317
- """Send an RPC request that is expecting data back."""
318
-
319
- # Ping RPCServer with a request.
320
- await socket.send_multipart((pickle.dumps(request), ), copy=False)
321
-
322
- # Make sure the server responds in time.
323
- if await socket.poll(timeout=VLLM_RPC_TIMEOUT) == 0:
324
- raise TimeoutError("RPCServer didn't reply within "
325
- f"{VLLM_RPC_TIMEOUT} ms")
326
-
327
- # Await the data from the Server.
328
- frame = await socket.recv(copy=False)
329
- data = pickle.loads(frame.buffer)
330
-
331
- if isinstance(data, BaseException):
332
- raise data
333
- elif not isinstance(data, expected_type):
334
- raise ValueError(error_message)
335
-
336
- return data
337
-
338
- @staticmethod
339
- async def _send_one_way_rpc_request(request: RPC_REQUEST_T,
340
- socket: Socket):
341
- """Send one-way RPC request to trigger an action."""
342
-
343
- if socket.closed:
344
- raise MQClientClosedError()
345
-
346
- await socket.send_multipart((pickle.dumps(request), ))
347
-
348
- async def _await_ack(self, error_message: str, socket: Socket):
349
- """Await acknowledgement that a request succeeded."""
350
-
351
- if socket.closed:
352
- raise MQClientClosedError()
353
-
354
- if await socket.poll(timeout=VLLM_RPC_TIMEOUT) == 0:
355
- raise TimeoutError("MQLLMEngine didn't reply within "
356
- f"{VLLM_RPC_TIMEOUT}ms")
357
-
358
- await self._check_success(error_message, socket)
359
-
360
- @staticmethod
361
- async def _check_success(error_message: str, socket: Socket):
362
- """Confirm that socket has a VLLM_RPC_SUCCESS_STR message"""
363
-
364
- if socket.closed:
365
- raise MQClientClosedError()
366
-
367
- frame = await socket.recv(copy=False)
368
- response = pickle.loads(frame.buffer)
369
-
370
- # Raise error if unsuccessful
371
- if isinstance(response, BaseException):
372
- raise response
373
- elif (not isinstance(response, str)
374
- or response != VLLM_RPC_SUCCESS_STR):
375
- raise ValueError(error_message)
376
-
377
- async def get_input_preprocessor(self) -> InputPreprocessor:
378
- return self.input_preprocessor
379
-
380
- async def get_tokenizer(self, lora_request: Optional[LoRARequest] = None):
381
- if self.tokenizer is None:
382
- return None
383
- else:
384
- return await self.tokenizer.get_lora_tokenizer_async(lora_request)
385
-
386
- async def get_vllm_config(self) -> VllmConfig:
387
- return self.vllm_config
388
-
389
- async def get_decoding_config(self) -> DecodingConfig:
390
- return self.decoding_config
391
-
392
- async def get_model_config(self) -> ModelConfig:
393
- return self.model_config
394
-
395
- async def is_tracing_enabled(self) -> bool:
396
- return self.tracing_flag
397
-
398
- async def _wait_for_server_rpc(self, socket: Socket) -> RPCStartupResponse:
399
- """Wait for the RPCServer to start up."""
400
-
401
- return await self._send_get_data_rpc_request(
402
- request=RPCStartupRequest.IS_SERVER_READY,
403
- expected_type=RPCStartupResponse,
404
- error_message="Unable to start RPC Server",
405
- socket=socket)
406
-
407
- async def abort(self, request_id: Union[str, Iterable[str]]):
408
- """Send an ABORT_REQUEST signal to the RPC Server"""
409
-
410
- if not isinstance(request_id, str):
411
- raise RuntimeError("Only single-request abort supported in"
412
- " deprecated V0")
413
-
414
- with suppress(MQClientClosedError):
415
- await self._send_one_way_rpc_request(
416
- request=RPCAbortRequest(request_id), socket=self.input_socket)
417
-
418
- async def do_log_stats(
419
- self,
420
- scheduler_outputs: Optional[SchedulerOutputs] = None,
421
- model_output: Optional[List[SamplerOutput]] = None,
422
- ) -> None:
423
- """
424
- Ignore do_log_stats (handled on MQLLMEngine polling)
425
- """
426
- pass
427
-
428
- async def check_health(self):
429
- """
430
- The check health loop probes the health status of the
431
- Engine's health every N seconds and sets _errored_with
432
- if the engine is unhealthy.
433
- """
434
- if self._errored_with is not None:
435
- raise self._errored_with
436
-
437
- @property
438
- def is_running(self) -> bool:
439
- return not self.errored
440
-
441
- @property
442
- def is_stopped(self) -> bool:
443
- return self.errored
444
-
445
- @property
446
- def errored(self) -> bool:
447
- return self._errored_with is not None
448
-
449
- @property
450
- def dead_error(self) -> BaseException:
451
- return ENGINE_DEAD_ERROR(self._errored_with)
452
-
453
- def generate(
454
- self,
455
- prompt: PromptType,
456
- sampling_params: SamplingParams,
457
- request_id: str,
458
- lora_request: Optional[LoRARequest] = None,
459
- trace_headers: Optional[Mapping[str, str]] = None,
460
- priority: int = 0,
461
- ) -> AsyncGenerator[RequestOutput, None]:
462
- """Generate outputs for a request.
463
-
464
- Generate outputs for a request. This method is a coroutine. It adds the
465
- request into the waiting queue of the LLMEngine and streams the outputs
466
- from the LLMEngine to the caller.
467
-
468
- Args:
469
- prompt: The prompt to the LLM. See
470
- [`PromptType`][vllm.inputs.PromptType] for more details about
471
- the format of each input.
472
- sampling_params: The sampling parameters of the request.
473
- request_id: The unique id of the request.
474
- lora_request: LoRA request to use for generation, if any.
475
- trace_headers: OpenTelemetry trace headers.
476
- priority: Priority of the request (lower means earlier handling).
477
- Any priority other than 0 will lead to an error if the
478
- scheduling policy is not "priority".
479
- """
480
- return self._process_request(prompt, sampling_params, request_id,
481
- lora_request, trace_headers, priority)
482
-
483
- def encode(
484
- self,
485
- prompt: PromptType,
486
- pooling_params: PoolingParams,
487
- request_id: str,
488
- lora_request: Optional[LoRARequest] = None,
489
- trace_headers: Optional[Mapping[str, str]] = None,
490
- priority: int = 0,
491
- tokenization_kwargs: Optional[dict[str, Any]] = None,
492
- ) -> AsyncGenerator[PoolingRequestOutput, None]:
493
- raise NotImplementedError(
494
- "Pooling models are not supported in vLLM V0")
495
-
496
- async def _process_request(
497
- self,
498
- prompt: PromptType,
499
- params: SamplingParams,
500
- request_id: str,
501
- lora_request: Optional[LoRARequest] = None,
502
- trace_headers: Optional[Mapping[str, str]] = None,
503
- priority: int = 0,
504
- ) -> AsyncGenerator[RequestOutput, None]:
505
- """Send an RPCGenerateRequest to the RPCServer and stream responses."""
506
-
507
- # If already dead, error out.
508
- if self._errored_with is not None:
509
- raise ENGINE_DEAD_ERROR(self._errored_with)
510
-
511
- # Ensure the request id is unique among running requests
512
- if request_id in self.output_queues:
513
- raise ValueError(f"Request {request_id} already exists")
514
-
515
- # 1) Create output queue for this request.
516
- queue: asyncio.Queue[Union[RequestOutput,
517
- BaseException]] = asyncio.Queue()
518
- self.output_queues[request_id] = queue
519
-
520
- try:
521
- # 2) Detach logits processors so that they can be pickled
522
- # separately (may require cloudpickle which is slower)
523
- if params.logits_processors:
524
- # Defensive shallow copy
525
- params = copy.copy(params)
526
- logits_processors = params.logits_processors
527
- params.logits_processors = None
528
- lp_bytes = cloudpickle.dumps(logits_processors)
529
- else:
530
- lp_bytes = None
531
-
532
- request_bytes = pickle.dumps(
533
- RPCProcessRequest(
534
- prompt=prompt,
535
- params=params,
536
- request_id=request_id,
537
- lora_request=lora_request,
538
- trace_headers=trace_headers,
539
- priority=priority,
540
- ))
541
-
542
- # 3) Send the RPCGenerateRequest to the MQLLMEngine.
543
- parts = (request_bytes,
544
- lp_bytes) if lp_bytes else (request_bytes, )
545
- await self.input_socket.send_multipart(parts, copy=False)
546
-
547
- # 4) Stream the RequestOutputs from the output queue. Note
548
- # that the output_loop pushes RequestOutput objects to this
549
- # queue after pulling them from the zmq socket.
550
- finished = False
551
- try:
552
- while not finished:
553
- request_output = await queue.get()
554
-
555
- if isinstance(request_output, BaseException):
556
- raise request_output
557
-
558
- finished = request_output.finished
559
- yield request_output
560
- finally:
561
- # Request was canceled by the client.
562
- if not finished and not self.errored:
563
- await self.abort(request_id)
564
- finally:
565
- self.output_queues.pop(request_id)
566
-
567
- async def start_profile(self) -> None:
568
- """Start profiling the engine"""
569
-
570
- await self._send_one_way_rpc_request(
571
- request=RPCUProfileRequest.START_PROFILE, socket=self.input_socket)
572
-
573
- async def stop_profile(self) -> None:
574
- """Stop profiling the engine"""
575
-
576
- await self._send_one_way_rpc_request(
577
- request=RPCUProfileRequest.STOP_PROFILE, socket=self.input_socket)
578
-
579
- async def reset_mm_cache(self) -> None:
580
- """Reset the multi-modal cache"""
581
-
582
- await self._send_one_way_rpc_request(
583
- request=RPCResetMultiModalCacheRequest.RESET,
584
- socket=self.input_socket)
585
-
586
- async def reset_prefix_cache(self,
587
- device: Optional[Device] = None) -> None:
588
- """Reset the prefix cache"""
589
-
590
- await self._send_one_way_rpc_request(
591
- request=RPCResetPrefixCacheRequest(device),
592
- socket=self.input_socket)
593
-
594
- async def sleep(self, level: int = 1) -> None:
595
- """Sleep the engine for a given level"""
596
- return await self._send_one_way_rpc_request(
597
- request=RPCSleepRequest(level), socket=self.input_socket)
598
-
599
- async def wake_up(self, tags: Optional[list[str]] = None) -> None:
600
- """Wake up the engine"""
601
- return await self._send_one_way_rpc_request(
602
- request=RPCWakeUpRequest(tags), socket=self.input_socket)
603
-
604
- async def is_sleeping(self) -> bool:
605
- """Check whether the engine is sleeping"""
606
- request = RPCIsSleepingRequest()
607
-
608
- queue: asyncio.Queue[Union[BaseException,
609
- RPCIsSleepingResponse]] = asyncio.Queue()
610
- self.output_queues[request.request_id] = queue
611
-
612
- request_bytes = pickle.dumps(request)
613
- await self.input_socket.send_multipart((request_bytes, ), copy=False)
614
-
615
- request_output = await queue.get()
616
- self.output_queues.pop(request.request_id)
617
-
618
- if isinstance(request_output, BaseException):
619
- raise request_output
620
- return request_output.is_sleeping
621
-
622
- async def add_lora(self, lora_request: LoRARequest) -> bool:
623
- """Load a new LoRA adapter into the engine for future requests."""
624
- # Uses the same I/O as generate requests
625
- request = RPCLoadAdapterRequest(lora_request)
626
-
627
- # Create output queue for this request.
628
- queue: asyncio.Queue[Union[
629
- BaseException, RPCAdapterLoadedResponse]] = asyncio.Queue()
630
- self.output_queues[request.request_id] = queue
631
-
632
- # Send the request
633
- request_bytes = pickle.dumps(request)
634
- await self.input_socket.send_multipart((request_bytes, ), copy=False)
635
-
636
- # Wait for the response
637
- request_output = await queue.get()
638
- self.output_queues.pop(request.request_id)
639
-
640
- # Raise on error, otherwise happily return None
641
- if isinstance(request_output, BaseException):
642
- raise request_output
643
- return request_output.lora_loaded
vllm/engine/multiprocessing/engine.py
deleted
-470
@@ -1,470 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import pickle
5
-import signal
6
-from contextlib import contextmanager
7
-from typing import Iterator, List, Optional, Union
8
-
9
-import cloudpickle
10
-import zmq
11
-
12
-from vllm import AsyncEngineArgs, SamplingParams
13
-from vllm.config import VllmConfig
14
-from vllm.engine.llm_engine import LLMEngine
15
-# yapf conflicts with isort for this block
16
-# yapf: disable
17
-from vllm.engine.multiprocessing import (ENGINE_DEAD_ERROR, IPC_DATA_EXT,
18
- IPC_HEALTH_EXT, IPC_INPUT_EXT,
19
- IPC_OUTPUT_EXT, REQUEST_OUTPUTS_T,
20
- VLLM_RPC_SUCCESS_STR, RPCAbortRequest,
21
- RPCAdapterLoadedResponse, RPCError,
22
- RPCIsSleepingRequest,
23
- RPCIsSleepingResponse,
24
- RPCLoadAdapterRequest,
25
- RPCProcessRequest,
26
- RPCResetMultiModalCacheRequest,
27
- RPCResetPrefixCacheRequest,
28
- RPCSleepRequest, RPCStartupRequest,
29
- RPCStartupResponse,
30
- RPCUProfileRequest, RPCWakeUpRequest)
31
-# yapf: enable
32
-from vllm.logger import init_logger
33
-from vllm.outputs import RequestOutput
34
-from vllm.transformers_utils.config import (
35
- maybe_register_config_serialize_by_value)
36
-from vllm.usage.usage_lib import UsageContext
37
-from vllm.utils import deprecate_kwargs
38
-from vllm.worker.model_runner_base import InputProcessingError
39
-
40
-logger = init_logger(__name__)
41
-
42
-POLLING_TIMEOUT_MS = 10000
43
-HEALTHY_RESPONSE = (pickle.dumps(VLLM_RPC_SUCCESS_STR), )
44
-
45
-
46
-class MQLLMEngine:
47
- """A multiprocessing wrapper for
48
- [`LLMEngine`][vllm.engine.llm_engine.LLMEngine].
49
-
50
- This class is used to wrap the
51
- [`LLMEngine`][vllm.engine.llm_engine.LLMEngine] class to enable use
52
- in concurrent manner. It runs a background loop and uses zeromq to
53
- receive new requests and stream outputs incrementally via ipc.
54
-
55
- The [`LLMEngine`][vllm.engine.llm_engine.LLMEngine] generate or encode
56
- process is kicked off when a new RPCProcessRequest is received by the
57
- input_socket.
58
-
59
- The self.engine_loop checks the input_socket for new requests,
60
- adds them to the LLMEngine if there are any, calls the internal
61
- [`LLMEngine.step()`][vllm.engine.llm_engine.LLMEngine.step], and sends
62
- the RequestOutputs back over the output_socket.
63
-
64
- If use_async_sockets is set, the logic associated with reading new
65
- requests from the socket and sending data to the socket is passed
66
- as a callback to the llm_engine, which calls the logic asynchronously
67
- such that the IPC can be overlapped with the GPU.
68
-
69
- Args:
70
- ipc_path: Base path for zeromq interprocess messaging
71
- use_async_sockets: Whether to make send/recv async with GPU
72
- log_requests: Whether to log the requests.
73
- *args: Arguments for [`LLMEngine`][vllm.engine.llm_engine.LLMEngine].
74
- **kwargs: Arguments for [`LLMEngine`][vllm.engine.llm_engine.LLMEngine].
75
- """
76
-
77
- def __init__(self,
78
- ipc_path: str,
79
- use_async_sockets: bool,
80
- *args,
81
- log_requests: bool = True,
82
- **kwargs) -> None:
83
- # For MQLLMEngine, we can use cached outputs, since each new request
84
- # output is immediately pickled and send over the socket, which frees
85
- # the python object to be reused again.
86
- kwargs['use_cached_outputs'] = True
87
-
88
- self.engine = LLMEngine(*args, **kwargs)
89
- self.log_requests = log_requests
90
-
91
- self.use_async_sockets = use_async_sockets
92
- if self.use_async_sockets:
93
- self.engine.process_request_outputs_callback = \
94
- self._async_socket_engine_callback
95
-
96
- self.ctx = zmq.Context() # type: ignore[attr-defined]
97
-
98
- # Receive input from the client.
99
- self.input_socket = self.ctx.socket(zmq.constants.PULL)
100
- self.input_socket.bind(f"{ipc_path}{IPC_INPUT_EXT}")
101
-
102
- # Send output stream back to client.
103
- self.output_socket = self.ctx.socket(zmq.constants.PUSH)
104
- self.output_socket.bind(f"{ipc_path}{IPC_OUTPUT_EXT}")
105
-
106
- # Send heartbeats back to client.
107
- self.heartbeat_socket = self.ctx.socket(zmq.constants.PUSH)
108
- self.heartbeat_socket.bind(f"{ipc_path}{IPC_HEALTH_EXT}")
109
-
110
- # IPC path for the data socket.
111
- self.data_ipc_path = f"{ipc_path}{IPC_DATA_EXT}"
112
-
113
- # Error state.
114
- self._errored_with: Optional[BaseException] = None
115
-
116
- @property
117
- def dead_error(self) -> BaseException:
118
- if self._errored_with is not None:
119
- return ENGINE_DEAD_ERROR(self._errored_with)
120
- else:
121
- return ENGINE_DEAD_ERROR()
122
-
123
- @classmethod
124
- @deprecate_kwargs(
125
- "disable_log_requests",
126
- additional_message=("This argument will have no effect. "
127
- "Use `enable_log_requests` instead."),
128
- )
129
- def from_vllm_config(
130
- cls,
131
- vllm_config: VllmConfig,
132
- usage_context: UsageContext,
133
- enable_log_requests: bool,
134
- disable_log_stats: bool,
135
- ipc_path: str,
136
- disable_log_requests: bool = True, # Deprecated, will be removed
137
- ) -> "MQLLMEngine":
138
- # Setup plugins for each process
139
- from vllm.plugins import load_general_plugins
140
- load_general_plugins()
141
-
142
- use_async_sockets = vllm_config.model_config.use_async_output_proc
143
-
144
- return cls(
145
- vllm_config=vllm_config,
146
- executor_class=LLMEngine._get_executor_cls(vllm_config),
147
- ipc_path=ipc_path,
148
- usage_context=usage_context,
149
- use_async_sockets=use_async_sockets,
150
- log_requests=enable_log_requests,
151
- log_stats=(not disable_log_stats),
152
- )
153
-
154
- @staticmethod
155
- def from_engine_args(engine_args: AsyncEngineArgs,
156
- usage_context: UsageContext, ipc_path: str):
157
- """Creates an MQLLMEngine from the engine arguments."""
158
-
159
- vllm_config = engine_args.create_engine_config(usage_context)
160
- return MQLLMEngine.from_vllm_config(
161
- ipc_path=ipc_path,
162
- vllm_config=vllm_config,
163
- usage_context=usage_context,
164
- enable_log_requests=engine_args.enable_log_requests,
165
- disable_log_stats=engine_args.disable_log_stats,
166
- )
167
-
168
- def start(self):
169
- try:
170
- try:
171
- logger.debug("Starting Startup Loop.")
172
- self.run_startup_loop()
173
- logger.debug("Starting Engine Loop.")
174
- self.run_engine_loop()
175
- except Exception as e:
176
- logger.exception(repr(e))
177
- except KeyboardInterrupt:
178
- logger.debug("Shutting down MQLLMEngine.")
179
- finally:
180
- logger.debug("MQLLMEngine is shut down.")
181
- self.cleanup()
182
-
183
- def cleanup(self):
184
- """Cleanup zeromq state on shutdown."""
185
- # Closes all sockets and destroys context.
186
- self.ctx.destroy(linger=0)
187
- del self.engine
188
-
189
- @contextmanager
190
- def make_data_socket(
191
- self) -> Iterator[zmq.Socket]: # type: ignore[name-defined]
192
- socket = self.ctx.socket(zmq.constants.ROUTER)
193
- try:
194
- socket.bind(self.data_ipc_path)
195
- yield socket
196
- finally:
197
- socket.close(linger=0)
198
-
199
- def run_startup_loop(self) -> None:
200
- """Startup loop for sending data from Engine -> Client."""
201
-
202
- with self.make_data_socket() as socket:
203
- response: Union[RPCStartupResponse, BaseException]
204
- try:
205
- identity, message = socket.recv_multipart(copy=False)
206
- request: RPCStartupRequest = pickle.loads(message.buffer)
207
-
208
- # Handle the query from the Client.
209
- if request == RPCStartupRequest.IS_SERVER_READY:
210
- tracing_enabled = self.engine.is_tracing_enabled()
211
- response = RPCStartupResponse(
212
- tracing_enabled=tracing_enabled)
213
-
214
- except Exception as e:
215
- response = e
216
-
217
- socket.send_multipart((identity, pickle.dumps(response)),
218
- copy=False)
219
-
220
- def run_engine_loop(self):
221
- """Core busy loop of the LLMEngine."""
222
-
223
- while True:
224
- if not self.engine.has_unfinished_requests():
225
- # Poll until there is work to do.
226
- while self.input_socket.poll(timeout=POLLING_TIMEOUT_MS) == 0:
227
- # When there's no work, check on engine health and send
228
- # health status back to client
229
- self._health_check()
230
- self.engine.do_log_stats()
231
- logger.debug("Waiting for new requests in engine loop.")
232
-
233
- # Handle any input from the client.
234
- self.handle_new_input()
235
-
236
- # Engine step.
237
- request_outputs = self.engine_step()
238
-
239
- # Send request outputs (if async, done in engine_step callback).
240
- if not self.use_async_sockets:
241
- self._send_outputs(request_outputs)
242
-
243
- def engine_step(self) -> List[RequestOutput]:
244
- """Engine step wrapper with error handling."""
245
- try:
246
- return self.engine.step()
247
- except SystemExit:
248
- raise
249
- except InputProcessingError as e:
250
- # Special case where we handle an error preparing the inputs for
251
- # a single request in the batch
252
- rpc_err = RPCError(request_id=e.request_id,
253
- is_engine_errored=False,
254
- exception=e.__cause__)
255
- self._send_outputs(rpc_err)
256
- return []
257
- except BaseException as e:
258
- self._set_errored(e)
259
- rpc_err = RPCError(request_id=None,
260
- is_engine_errored=True,
261
- exception=e)
262
- self._send_outputs(rpc_err)
263
- raise e
264
-
265
- def handle_new_input(self):
266
- """Handle new input from the socket"""
267
- try:
268
- while self.input_socket.poll(timeout=0) != 0:
269
- frames = self.input_socket.recv_multipart(copy=False)
270
- request = pickle.loads(frames[0].buffer)
271
-
272
- if isinstance(request, RPCProcessRequest):
273
- if len(frames) > 1:
274
- # Use cloudpickle for logits processors
275
- assert isinstance(request.params, SamplingParams)
276
- lprocs = cloudpickle.loads(frames[1].buffer)
277
- request.params.logits_processors = lprocs
278
- self._handle_process_request(request)
279
- elif isinstance(request, RPCAbortRequest):
280
- self._handle_abort_request(request)
281
- elif isinstance(request, RPCUProfileRequest):
282
- if request == RPCUProfileRequest.START_PROFILE:
283
- self.start_profile()
284
- else:
285
- self.stop_profile()
286
- elif isinstance(request, RPCLoadAdapterRequest):
287
- self._handle_load_adapter_request(request)
288
- elif isinstance(request, RPCResetMultiModalCacheRequest):
289
- self.reset_mm_cache()
290
- elif isinstance(request, RPCResetPrefixCacheRequest):
291
- self.reset_prefix_cache()
292
- elif isinstance(request, RPCSleepRequest):
293
- self.sleep(request.value)
294
- elif isinstance(request, RPCWakeUpRequest):
295
- self.wake_up(request.tags)
296
- elif isinstance(request, RPCIsSleepingRequest):
297
- self._handle_is_sleeping_request(request)
298
- else:
299
- raise ValueError("Unknown RPCRequest Type: "
300
- f"{type(request)}")
301
-
302
- except Exception as e:
303
- self._set_errored(e)
304
- self._send_unhealthy(e)
305
- raise e from None
306
-
307
- def _handle_process_request(self, request: RPCProcessRequest):
308
- """Handle RPCProcessRequest by adding it to the LLMEngine."""
309
- request_id = request.request_id
310
-
311
- if self._errored_with is not None:
312
- rpc_err = RPCError(request_id=request_id,
313
- is_engine_errored=True,
314
- exception=ENGINE_DEAD_ERROR(self._errored_with))
315
- self._send_outputs(rpc_err)
316
-
317
- try:
318
- self.engine.add_request(request_id=request_id,
319
- prompt=request.prompt,
320
- params=request.params,
321
- lora_request=request.lora_request,
322
- trace_headers=request.trace_headers,
323
- priority=request.priority)
324
-
325
- if self.log_requests:
326
- logger.info("Added request %s.", request.request_id)
327
-
328
- except Exception as e:
329
- # We do not set self._errored = True here, since the error
330
- # is due to an issue adding this request to the engine,
331
- # rather than an issue with the engine itself.
332
- logger.debug("Failed to add request %s to engine. %s",
333
- request.request_id, e)
334
- is_errored = self._errored_with is not None
335
- rpc_err = RPCError(request_id=request_id,
336
- is_engine_errored=is_errored,
337
- exception=e)
338
- self._send_outputs(rpc_err)
339
-
340
- # Remove request from the engine.
341
- self.engine.abort_request(request_id)
342
-
343
- def _handle_abort_request(self, request: RPCAbortRequest):
344
- self.engine.abort_request(request.request_id)
345
- if self.log_requests:
346
- logger.info("Aborted request %s.", request.request_id)
347
-
348
- def _handle_load_adapter_request(self, request: RPCLoadAdapterRequest):
349
- try:
350
- lora_loaded = self.engine.add_lora(request.lora_request)
351
- except BaseException as e:
352
- # Send back an error if the adater fails to load
353
- rpc_err = RPCError(request_id=request.request_id,
354
- is_engine_errored=False,
355
- exception=e)
356
- self._send_outputs(rpc_err)
357
- return
358
- # Otherwise, send back the successful load message
359
- self._send_outputs(
360
- RPCAdapterLoadedResponse(request_id=request.request_id,
361
- lora_loaded=lora_loaded))
362
-
363
- def _handle_is_sleeping_request(self, request: RPCIsSleepingRequest):
364
- is_sleeping = self.is_sleeping()
365
- self._send_outputs(
366
- RPCIsSleepingResponse(request_id=request.request_id,
367
- is_sleeping=is_sleeping))
368
-
369
- def _health_check(self):
370
- # Send unhealthy if engine has already errored
371
- if self._errored_with is not None:
372
- self._send_unhealthy(self._errored_with)
373
- try:
374
- self.engine.check_health()
375
- self._send_healthy()
376
- except Exception as e:
377
- self._set_errored(e)
378
- self._send_unhealthy(e)
379
-
380
- def _send_outputs(self, outputs: REQUEST_OUTPUTS_T):
381
- """Send outputs back to the engine client. These can be:
382
- - Exceptions
383
- - A list of generation outputs
384
- - A response from loading a lora adapter
385
- """
386
- if outputs:
387
- try:
388
- from ray.exceptions import RayTaskError
389
-
390
- # RayTaskError might not pickelable here. We need to unpack the
391
- # underlying exception as the real exception in the output.
392
- if (isinstance(outputs, RPCError)
393
- and isinstance(outputs.exception, RayTaskError)):
394
- outputs.exception = outputs.exception.cause
395
- except ImportError:
396
- pass
397
-
398
- output_bytes = pickle.dumps(outputs)
399
- self.output_socket.send_multipart((output_bytes, ), copy=False)
400
-
401
- def _send_healthy(self):
402
- """Send HEALTHY message to RPCClient."""
403
- if not self.heartbeat_socket.closed:
404
- self.heartbeat_socket.send_multipart(HEALTHY_RESPONSE, copy=False)
405
-
406
- def _send_unhealthy(self, error: BaseException):
407
- """Send UNHEALTHY message to RPCClient."""
408
- if not self.heartbeat_socket.closed:
409
- error_bytes = pickle.dumps(error)
410
- self.heartbeat_socket.send_multipart((error_bytes, ), copy=False)
411
-
412
- def _async_socket_engine_callback(self,
413
- request_outputs: REQUEST_OUTPUTS_T):
414
- """Callback used by engine to make socket handling async with GPU."""
415
- self._send_outputs(request_outputs)
416
- self.handle_new_input()
417
-
418
- def _set_errored(self, e: BaseException):
419
- """Log and set errored status if this is the first issue."""
420
- if self._errored_with is None:
421
- self._errored_with = e
422
-
423
- def start_profile(self) -> None:
424
- self.engine.start_profile()
425
-
426
- def stop_profile(self) -> None:
427
- self.engine.stop_profile()
428
-
429
- def reset_mm_cache(self) -> bool:
430
- return self.engine.reset_mm_cache()
431
-
432
- def reset_prefix_cache(self) -> bool:
433
- return self.engine.reset_prefix_cache()
434
-
435
- def sleep(self, level: int = 1) -> None:
436
- self.engine.sleep(level)
437
-
438
- def wake_up(self, tags: Optional[list[str]] = None) -> None:
439
- self.engine.wake_up(tags)
440
-
441
- def is_sleeping(self) -> bool:
442
- return self.engine.is_sleeping()
443
-
444
-
445
-def signal_handler(*_) -> None:
446
- raise KeyboardInterrupt("MQLLMEngine terminated")
447
-
448
-
449
-def run_mp_engine(vllm_config: VllmConfig, usage_context: UsageContext,
450
- ipc_path: str, disable_log_stats: bool,
451
- enable_log_requests: bool, engine_alive):
452
- try:
453
- # Ensure we can serialize transformer config before spawning
454
- maybe_register_config_serialize_by_value()
455
-
456
- engine = MQLLMEngine.from_vllm_config(
457
- vllm_config=vllm_config,
458
- usage_context=usage_context,
459
- disable_log_stats=disable_log_stats,
460
- enable_log_requests=enable_log_requests,
461
- ipc_path=ipc_path)
462
-
463
- signal.signal(signal.SIGTERM, signal_handler)
464
-
465
- engine.start()
466
-
467
- except BaseException as e:
468
- logger.exception(e)
469
- engine_alive.value = False
470
- raise e from None
vllm/entrypoints/launcher.py
-2
@@ -12,7 +12,6 @@ from fastapi import FastAPI, Request, Response
12
13
from vllm import envs
14
from vllm.engine.async_llm_engine import AsyncEngineDeadError
15
-from vllm.engine.multiprocessing import MQEngineDeadError
15
from vllm.engine.protocol import EngineClient
16
from vllm.entrypoints.constants import (H11_MAX_HEADER_COUNT_DEFAULT,
17
H11_MAX_INCOMPLETE_EVENT_SIZE_DEFAULT)
@@ -156,7 +155,6 @@ def _add_shutdown_handlers(app: FastAPI, server: uvicorn.Server) -> None:
155
156
@app.exception_handler(RuntimeError)
157
@app.exception_handler(AsyncEngineDeadError)
159
- @app.exception_handler(MQEngineDeadError)
158
@app.exception_handler(EngineDeadError)
159
@app.exception_handler(EngineGenerateError)
160
async def runtime_exception_handler(request: Request, __):
vllm/entrypoints/openai/api_server.py
+2
-100
@@ -2,7 +2,6 @@
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
import asyncio
5
-import atexit
5
import gc
6
import importlib
7
import inspect
@@ -17,7 +16,6 @@ import uuid
16
from argparse import Namespace
17
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable
18
from contextlib import asynccontextmanager
20
-from functools import partial
19
from http import HTTPStatus
20
from typing import Annotated, Any, Callable, Optional
21
@@ -42,8 +40,6 @@ import vllm.envs as envs
40
from vllm.config import VllmConfig
41
from vllm.engine.arg_utils import AsyncEngineArgs
42
from vllm.engine.async_llm_engine import AsyncLLMEngine # type: ignore
45
-from vllm.engine.multiprocessing.client import MQLLMEngineClient
46
-from vllm.engine.multiprocessing.engine import run_mp_engine
43
from vllm.engine.protocol import EngineClient
44
from vllm.entrypoints.chat_utils import (load_chat_template,
45
resolve_hf_chat_template,
@@ -102,13 +98,10 @@ from vllm.entrypoints.utils import (cli_env_setup, load_aware_call,
98
log_non_default_args, with_cancellation)
99
from vllm.logger import init_logger
100
from vllm.reasoning import ReasoningParserManager
105
-from vllm.transformers_utils.config import (
106
- maybe_register_config_serialize_by_value)
101
from vllm.transformers_utils.tokenizer import MistralTokenizer
102
from vllm.usage.usage_lib import UsageContext
103
from vllm.utils import (Device, FlexibleArgumentParser, decorate_logs,
110
- get_open_zmq_ipc_path, is_valid_ipv6_address,
111
- set_ulimit)
104
+ is_valid_ipv6_address, set_ulimit)
105
from vllm.v1.metrics.prometheus import get_prometheus_registry
106
from vllm.version import __version__ as VLLM_VERSION
107
@@ -237,8 +230,7 @@ async def build_async_engine_client_from_engine_args(
230
async_llm.shutdown()
231
232
# V0 AsyncLLM.
240
- elif (MQLLMEngineClient.is_unsupported_config(vllm_config)
241
- or disable_frontend_multiprocessing):
233
+ else:
234
235
engine_client: Optional[EngineClient] = None
236
try:
@@ -252,96 +244,6 @@ async def build_async_engine_client_from_engine_args(
244
if engine_client and hasattr(engine_client, "shutdown"):
245
engine_client.shutdown()
246
255
- # V0MQLLMEngine.
256
- else:
257
- if "PROMETHEUS_MULTIPROC_DIR" not in os.environ:
258
- # Make TemporaryDirectory for prometheus multiprocessing
259
- # Note: global TemporaryDirectory will be automatically
260
- # cleaned up upon exit.
261
- global prometheus_multiproc_dir
262
- prometheus_multiproc_dir = tempfile.TemporaryDirectory()
263
- os.environ[
264
- "PROMETHEUS_MULTIPROC_DIR"] = prometheus_multiproc_dir.name
265
- else:
266
- logger.warning(
267
- "Found PROMETHEUS_MULTIPROC_DIR was set by user. "
268
- "This directory must be wiped between vLLM runs or "
269
- "you will find inaccurate metrics. Unset the variable "
270
- "and vLLM will properly handle cleanup.")
271
-
272
- # Select random path for IPC.
273
- ipc_path = get_open_zmq_ipc_path()
274
- logger.debug("Multiprocessing frontend to use %s for IPC Path.",
275
- ipc_path)
276
-
277
- # Start RPCServer in separate process (holds the LLMEngine).
278
- # the current process might have CUDA context,
279
- # so we need to spawn a new process
280
- context = multiprocessing.get_context("spawn")
281
-
282
- # Ensure we can serialize transformer config before spawning
283
- maybe_register_config_serialize_by_value()
284
-
285
- # The Process can raise an exception during startup, which may
286
- # not actually result in an exitcode being reported. As a result
287
- # we use a shared variable to communicate the information.
288
- engine_alive = multiprocessing.Value('b', True, lock=False)
289
- engine_process = context.Process(
290
- target=run_mp_engine,
291
- args=(vllm_config, UsageContext.OPENAI_API_SERVER, ipc_path,
292
- engine_args.disable_log_stats,
293
- engine_args.enable_log_requests, engine_alive))
294
- engine_process.start()
295
- engine_pid = engine_process.pid
296
- assert engine_pid is not None, "Engine process failed to start."
297
- logger.info("Started engine process with PID %d", engine_pid)
298
-
299
- def _cleanup_ipc_path():
300
- socket_path = ipc_path.replace("ipc://", "")
301
- if os.path.exists(socket_path):
302
- os.remove(socket_path)
303
-
304
- # Ensure we clean up the local IPC socket file on exit.
305
- atexit.register(_cleanup_ipc_path)
306
-
307
- # Build RPCClient, which conforms to EngineClient Protocol.
308
- build_client = partial(MQLLMEngineClient, ipc_path, vllm_config,
309
- engine_pid)
310
- mq_engine_client = await asyncio.get_running_loop().run_in_executor(
311
- None, build_client)
312
- try:
313
- while True:
314
- try:
315
- await mq_engine_client.setup()
316
- break
317
- except TimeoutError:
318
- if (not engine_process.is_alive()
319
- or not engine_alive.value):
320
- raise RuntimeError(
321
- "Engine process failed to start. See stack "
322
- "trace for the root cause.") from None
323
-
324
- yield mq_engine_client # type: ignore[misc]
325
- finally:
326
- # Ensure rpc server process was terminated
327
- engine_process.terminate()
328
-
329
- # Close all open connections to the backend
330
- mq_engine_client.close()
331
-
332
- # Wait for engine process to join
333
- engine_process.join(4)
334
- if engine_process.exitcode is None:
335
- # Kill if taking longer than 5 seconds to stop
336
- engine_process.kill()
337
-
338
- # Lazy import for prometheus multiprocessing.
339
- # We need to set PROMETHEUS_MULTIPROC_DIR environment variable
340
- # before prometheus_client is imported.
341
- # See https://prometheus.github.io/client_python/multiprocess/
342
- from prometheus_client import multiprocess
343
- multiprocess.mark_process_dead(engine_process.pid)
344
-
247
248
async def validate_json_request(raw_request: Request):
249
content_type = raw_request.headers.get("content-type", "").lower()
vllm/platforms/rocm.py
+1
-1
@@ -191,7 +191,7 @@ class RocmPlatform(Platform):
191
kv_cache_dtype, block_size, use_v1, use_mla,
192
has_sink) -> str:
193
if use_mla:
194
- from vllm.attention.backends.rocm_aiter_mla import (
194
+ from vllm.v1.attention.backends.mla.rocm_aiter_mla import (
195
is_aiter_mla_enabled)
196
197
if selected_backend is None: