Squashed migration rebased onto current main. Collapses the 46-commit dev/migrate-MR-v2 history (10 merge commits + superseded pre-rebase work) into a single commit, so the diff reflects only the genuine MR-V2 feature (worker_v2/ runners + model_states, omni schedulers, stage engine pipeline, pooling_output serialization bridge). Content already merged to main separately (step_audio2, tts_adapters, npu/_310p, etc.) drops out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import torch
|
|
|
|
from vllm_omni.core.sched.output import OmniCachedRequestData
|
|
from vllm_omni.model_executor.models.output_templates import OmniOutput
|
|
from vllm_omni.worker_v2.omni_generation_model_runner import OmniGenerationModelRunner
|
|
|
|
|
|
class _Array:
|
|
def __init__(self):
|
|
self.np = [0]
|
|
self.writes = []
|
|
self.applied = False
|
|
|
|
def stage_write_elem(self, idx, value):
|
|
self.writes.append((idx, value))
|
|
|
|
def apply_write(self):
|
|
self.applied = True
|
|
|
|
|
|
class _TokenIds:
|
|
def __init__(self):
|
|
self.writes = []
|
|
|
|
def stage_write(self, idx, start, values):
|
|
self.writes.append((idx, start, list(values)))
|
|
|
|
|
|
class _ReqStates:
|
|
def __init__(self, has_request=True):
|
|
self.req_id_to_index = {"r1": 0} if has_request else {}
|
|
self.prompt_len = SimpleNamespace(np=[0])
|
|
self.prefill_len = SimpleNamespace(np=[0])
|
|
self.total_len = _Array()
|
|
self.all_token_ids = _TokenIds()
|
|
self.num_computed_tokens = _Array()
|
|
self.num_computed_prefill_tokens = [7]
|
|
self.applied = False
|
|
self.removed = []
|
|
|
|
def apply_staged_writes(self):
|
|
self.applied = True
|
|
|
|
def remove_request(self, req_id):
|
|
self.removed.append(req_id)
|
|
self.req_id_to_index.pop(req_id, None)
|
|
|
|
|
|
class _IntermediateBuffer:
|
|
def __init__(self):
|
|
self.buffers = [{"stale": "value", "req_id": "r1"}]
|
|
|
|
def remove_request(self, idx):
|
|
self.buffers[idx] = {}
|
|
|
|
|
|
def test_async_chunk_update_clears_stale_intermediate_buffer():
|
|
runner = object.__new__(OmniGenerationModelRunner)
|
|
runner.req_states = _ReqStates()
|
|
runner.model_state = SimpleNamespace(intermediate_buffer=_IntermediateBuffer())
|
|
cached = OmniCachedRequestData(
|
|
req_ids=["r1"],
|
|
resumed_req_ids=set(),
|
|
new_token_ids=[],
|
|
all_token_ids=[],
|
|
new_block_ids=[],
|
|
num_computed_tokens=[],
|
|
num_output_tokens=[],
|
|
prompt_token_ids={"r1": [1, 2, 3]},
|
|
additional_information={"r1": {"fresh": "value"}},
|
|
)
|
|
|
|
runner._handle_async_chunk_updates(SimpleNamespace(scheduled_cached_reqs=cached))
|
|
|
|
assert runner.model_state.intermediate_buffer.buffers[0] == {}
|
|
assert runner.req_states.prompt_len.np[0] == 3
|
|
assert runner.req_states.prefill_len.np[0] == 3
|
|
assert runner.req_states.all_token_ids.writes == [(0, 0, [1, 2, 3])]
|
|
assert runner.req_states.num_computed_prefill_tokens[0] == 0
|
|
assert runner.req_states.applied is True
|
|
|
|
|
|
def test_async_chunk_update_readds_released_cached_request():
|
|
runner = object.__new__(OmniGenerationModelRunner)
|
|
runner.req_states = _ReqStates(has_request=False)
|
|
runner.model_state = SimpleNamespace(intermediate_buffer=_IntermediateBuffer())
|
|
added = []
|
|
runner.add_requests = lambda scheduler_output: added.extend(scheduler_output.scheduled_new_reqs)
|
|
cached = OmniCachedRequestData(
|
|
req_ids=["r1"],
|
|
resumed_req_ids=set(),
|
|
new_token_ids=[],
|
|
all_token_ids={"r1": [1, 2, 3]},
|
|
new_block_ids=[([7],)],
|
|
num_computed_tokens=[0],
|
|
num_output_tokens=[0],
|
|
prompt_token_ids={"r1": [1, 2, 3]},
|
|
additional_information={"r1": {"fresh": "value"}},
|
|
)
|
|
|
|
runner._handle_async_chunk_updates(SimpleNamespace(scheduled_cached_reqs=cached))
|
|
|
|
assert len(added) == 1
|
|
assert added[0].req_id == "r1"
|
|
assert added[0].prompt_token_ids == [1, 2, 3]
|
|
assert added[0].prefill_token_ids == [1, 2, 3]
|
|
assert added[0].block_ids == ([7],)
|
|
assert added[0].additional_information == {"fresh": "value"}
|
|
|
|
|
|
def _make_runner_for_sample(async_chunk):
|
|
runner = object.__new__(OmniGenerationModelRunner)
|
|
runner._gen_model_output = OmniOutput(
|
|
text_hidden_states=torch.zeros(1, 1),
|
|
multimodal_outputs={"audio": [torch.zeros(1)]},
|
|
)
|
|
runner._gen_input_batch = SimpleNamespace(
|
|
num_reqs=1,
|
|
idx_mapping_np=[0],
|
|
req_ids=["r1"],
|
|
)
|
|
runner._gen_kv_connector_output = None
|
|
runner.execute_model_state = object()
|
|
runner.model_config = SimpleNamespace(async_chunk=async_chunk)
|
|
runner.req_states = _ReqStates()
|
|
runner.model_state = SimpleNamespace(
|
|
intermediate_buffer=_IntermediateBuffer(),
|
|
remove_request=lambda idx: runner.model_state.intermediate_buffer.remove_request(idx),
|
|
)
|
|
removed = []
|
|
runner._remove_request = lambda req_id: removed.append(req_id) or True
|
|
return runner, removed
|
|
|
|
|
|
def test_generation_sample_keeps_runner_slot_without_async_chunk():
|
|
runner, removed = _make_runner_for_sample(async_chunk=False)
|
|
|
|
output = runner.sample_tokens()
|
|
|
|
assert output.req_ids == ["r1"]
|
|
assert removed == []
|
|
assert runner.model_state.intermediate_buffer.buffers[0] == {"stale": "value", "req_id": "r1"}
|
|
|
|
|
|
def test_generation_sample_releases_runner_slot_after_chunk_output():
|
|
runner, removed = _make_runner_for_sample(async_chunk=True)
|
|
|
|
output = runner.sample_tokens()
|
|
|
|
assert output.req_ids == ["r1"]
|
|
assert removed == ["r1"]
|
|
assert runner.model_state.intermediate_buffer.buffers[0] == {}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_async_chunk_update_clears_stale_intermediate_buffer()
|
|
test_async_chunk_update_readds_released_cached_request()
|
|
test_generation_sample_keeps_runner_slot_without_async_chunk()
|
|
test_generation_sample_releases_runner_slot_after_chunk_output()
|