[Bugfix] Fail a duplex session that outgrows max_model_len instead of killing the EngineCore (#7277)
Signed-off-by: Tianyao Wu <rayroy31@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Gao Han <hgaoaf@connect.ust.hk> Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
This commit is contained in:
7 files changed
+582
-3
No files matched your search
@@ -21,6 +21,7 @@ from vllm.v1.engine import EngineCoreOutput, EngineCoreOutputs, FinishReason
|
||||
from vllm.v1.engine.exceptions import EngineDeadError
|
||||
from vllm.v1.metrics.stats import IterationStats
|
||||
|
||||
from vllm_omni.engine import OmniEngineCoreOutput
|
||||
from vllm_omni.engine.messages import (
|
||||
AbortRequestMessage,
|
||||
AbortResultMessage,
|
||||
@@ -1891,3 +1892,61 @@ async def test_abort_retry_does_not_repeat_successful_stage_abort():
|
||||
assert first.physical_abort_calls == 1
|
||||
assert second.physical_abort_calls == 2
|
||||
assert second.bound == set()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_duplex_session_request_error_finish_is_delivered_as_request_error() -> None:
|
||||
"""A session-owned request the scheduler finished with FinishReason.ERROR
|
||||
(e.g. its prompt could not grow past max_model_len) must reach the
|
||||
session's consumer as a request-scoped error, not vanish: its terminal
|
||||
outputs are not processed like other requests, and the output processor
|
||||
may already have dropped the request state."""
|
||||
output_queue: asyncio.Queue = asyncio.Queue()
|
||||
orchestrator = Orchestrator(
|
||||
request_async_queue=asyncio.Queue(),
|
||||
output_async_queue=output_queue,
|
||||
rpc_async_queue=asyncio.Queue(),
|
||||
stage_pools=[],
|
||||
)
|
||||
duplex_state = OrchestratorRequestState(request_id="duplex-req", session_owned=True)
|
||||
plain_state = OrchestratorRequestState(request_id="plain-req")
|
||||
reason = "context_length_exceeded: streaming session prompt would grow to 8220 tokens, above max_model_len 8192"
|
||||
|
||||
await orchestrator._report_duplex_session_request_error(
|
||||
0,
|
||||
0,
|
||||
OmniEngineCoreOutput(
|
||||
request_id="duplex-req", new_token_ids=[], finish_reason=FinishReason.ERROR, stop_reason=reason
|
||||
),
|
||||
duplex_state,
|
||||
)
|
||||
error = output_queue.get_nowait()
|
||||
assert isinstance(error, ErrorMessage)
|
||||
assert error.request_id == "duplex-req"
|
||||
assert error.stage_id == 0
|
||||
assert error.fatal is False
|
||||
assert error.error == reason
|
||||
|
||||
# An ordinary segment stop, a segment-finished error and a request that is
|
||||
# not session-owned produce nothing.
|
||||
await orchestrator._report_duplex_session_request_error(
|
||||
0,
|
||||
0,
|
||||
OmniEngineCoreOutput(request_id="duplex-req", new_token_ids=[], finish_reason=FinishReason.STOP),
|
||||
duplex_state,
|
||||
)
|
||||
await orchestrator._report_duplex_session_request_error(
|
||||
0,
|
||||
0,
|
||||
OmniEngineCoreOutput(
|
||||
request_id="duplex-req", new_token_ids=[], finish_reason=FinishReason.ERROR, is_segment_finished=True
|
||||
),
|
||||
duplex_state,
|
||||
)
|
||||
await orchestrator._report_duplex_session_request_error(
|
||||
0,
|
||||
0,
|
||||
OmniEngineCoreOutput(request_id="plain-req", new_token_ids=[], finish_reason=FinishReason.ERROR),
|
||||
plain_state,
|
||||
)
|
||||
assert output_queue.empty()
|
||||
Reference in new issue
Block a user