Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

98 lines
4.3 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Tokenized-payload prep for parallel-sampling (n>1) fan-out.
The scheduler treats each replica as a standalone rid (the n>1 expansion
happens at the AsyncLLM frontend, not on the scheduler), so there is no
per-replica scheduler state here. What we need to centralize is the
tokenized-payload mutation the fan-out loop performs on every replica:
rid regeneration, the ``max_new_tokens=0`` warmup variant, and the plain
replica copy.
The two functions below are pure (no engine state, no I/O) and are
called from ``AsyncLLM._handle_batch_request`` once for the prefix
warmup and ``parallel_sample_num`` times for the replica expansion.
"""
from __future__ import annotations
import copy
from tokenspeed.runtime.engine.io_struct import (
EmbeddingReqInput,
GenerateReqInput,
TokenizedEmbeddingReqInput,
TokenizedGenerateReqInput,
)
def _own_multimodal_inputs(tokenized_copy) -> None:
"""Give this fan-out copy its own multimodal feature tensors.
``MultimodalInputs.publish_shm_features`` rewrites ``item.feature``
in place from tensor to SHM handle, and consumers later unlink the
segment. The prefix warmup and each n>1 replica are shallow
``copy.copy`` of the same tokenized payload, so without an independent
copy they would publish only once (the 2nd+ ``publish`` is a no-op
because the feature is already a handle) and share the SHM segment.
When the first replica's scheduler step consumes-and-unlinks, the
later replicas hit ``FileNotFoundError`` on their own attach.
"""
mm = getattr(tokenized_copy, "multimodal_inputs", None)
if mm is not None:
tokenized_copy.multimodal_inputs = copy.deepcopy(mm)
def prepare_prefix_warmup(
tmp_obj: GenerateReqInput | EmbeddingReqInput,
tokenized_obj: TokenizedGenerateReqInput | TokenizedEmbeddingReqInput,
) -> TokenizedGenerateReqInput | TokenizedEmbeddingReqInput:
"""Build the prefix-warmup variant used before parallel-sampling
fan-out. Mutates ``tmp_obj`` to receive a fresh rid; returns a
copy of ``tokenized_obj`` with ``max_new_tokens`` forced to 0
and streaming disabled so the scheduler caches the common
prefix before the replicas are dispatched.
"""
tokenized_copy = copy.copy(tokenized_obj)
_own_multimodal_inputs(tokenized_copy)
tokenized_copy.rid = tmp_obj.regenerate_rid()
tokenized_copy.sampling_params = copy.copy(tokenized_copy.sampling_params)
tokenized_copy.sampling_params.max_new_tokens = 0
tokenized_copy.stream = False
return tokenized_copy
def prepare_parallel_sampling_replica(
tmp_obj: GenerateReqInput | EmbeddingReqInput,
tokenized_obj: TokenizedGenerateReqInput | TokenizedEmbeddingReqInput,
) -> TokenizedGenerateReqInput | TokenizedEmbeddingReqInput:
"""Build one tokenized replica for parallel-sampling fan-out.
Mutates ``tmp_obj`` to receive a fresh rid; returns a copy of
``tokenized_obj`` sharing that rid. The rest of the tokenized
payload (sampling_params, input_ids, etc.) is unchanged because
the replicas share everything except their request identity.
"""
tokenized_copy = copy.copy(tokenized_obj)
_own_multimodal_inputs(tokenized_copy)
tokenized_copy.rid = tmp_obj.regenerate_rid()
return tokenized_copy