Source code for tensorrt_llm._torch.async_llm

#!/usr/bin/env python
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from collections.abc import Sequence
from typing import Any, List, Optional

from ..llmapi.llm import LLM
from ..llmapi.llm_args import ExecutorMemoryType, RayPlacementConfig, RuntimeMemoryStatus


[docs] class AsyncLLM(LLM): """LLM runtime with asynchronous setup, generation, and memory control. AsyncLLM uses the Ray orchestrator. Weight-update methods remain available for compatibility with existing reinforcement-learning integrations. """
[docs] def __init__( self, placement_groups: Optional[List[Any]] = None, placement_bundle_indices: Optional[List[List[int]]] = None, per_worker_gpu_share: Optional[float] = None, *args, **kwargs, ): kwargs["orchestrator_type"] = "ray" kwargs["ray_placement_config"] = RayPlacementConfig( defer_workers_init=True, placement_groups=placement_groups, placement_bundle_indices=placement_bundle_indices, per_worker_gpu_share=per_worker_gpu_share, ) if "ray_worker_extension_cls" not in kwargs: kwargs["ray_worker_extension_cls"] = "tensorrt_llm.llmapi.rlhf_utils.WorkerExtension" super().__init__(*args, **kwargs) self._async_initialized = False self._paused = False
[docs] async def setup_async(self): """Setup the LLM asynchronously.""" if not self._async_initialized: await self._executor.init_workers_async() await self._executor.setup_engine_remote_async() self._async_initialized = True return self
[docs] async def get_memory_status(self) -> RuntimeMemoryStatus: """Return the authoritative runtime-memory checkpoint status.""" self._check_runtime_memory_enabled() replies = await self.collective_rpc("get_memory_status") statuses = [RuntimeMemoryStatus.model_validate(reply) for reply in replies] if not statuses: raise RuntimeError("No worker returned runtime-memory status.") expected = statuses[0] if any(status != expected for status in statuses[1:]): raise RuntimeError( "Runtime-memory state diverged across workers: " f"{[status.model_dump(mode='json') for status in statuses]}" ) return expected
[docs] async def release( self, tags: Sequence[ExecutorMemoryType | str] | None = None, ) -> None: """Release the GPU memory used by the LLM asynchronously. Args: tags: Memory types to release. When omitted, release every sleep-managed runtime memory type. """ self._check_runtime_memory_enabled() default_tags = [ memory_type for memory_type in ExecutorMemoryType if memory_type not in (ExecutorMemoryType.INIT_KV_CACHE, ExecutorMemoryType.INIT_EXTRA_RESOURCES) ] normalized = self._normalize_runtime_memory_tags(tags, default_tags=default_tags) await self.collective_rpc("sleep", args=([tag.value for tag in normalized],))
[docs] async def resume( self, tags: Sequence[ExecutorMemoryType | str] | None = None, ) -> None: """Resume the GPU memory used by the LLM asynchronously. Args: tags: Memory types to restore. When omitted, restore every currently parked memory type. """ self._check_runtime_memory_enabled() status = await self.get_memory_status() if status.state not in ("running", "parked"): raise RuntimeError(f"Cannot resume runtime memory while state is '{status.state}'.") normalized = self._normalize_runtime_memory_tags(tags, default_tags=status.parked_tags) if normalized: await self.collective_rpc("wakeup", args=([tag.value for tag in normalized],))
[docs] async def update_weights(self, weights: dict[str, str]): """Update the weights of the LLM asynchronously. Args: weights: Dictionary mapping device UUIDs to IPC handles for weight tensors. """ await self.collective_rpc("update_weights", args=(weights,))
[docs] async def collective_rpc( self, method: str, args: tuple[Any, ...] = (), kwargs: Optional[dict] = None, unique_reply_rank: Optional[int] = None, # TODO: deprecate this in the future target_ranks: int | list[int] | None = None, ) -> list[Any]: """Execute an asynchronous RPC call on all GPU workers. Currently, this is only supported for RayExecutor. Args: method (str): The name of the worker method to execute. args (tuple[Any, ...]): Positional arguments to pass to the worker method. Defaults to (). kwargs (dict, optional): Keyword arguments to pass to the worker method. Defaults to None. unique_reply_rank (int, optional): The rank of the worker that will be used to send the reply. target_ranks (int | list[int] | None): The ranks of the workers that will be used to send the reply. Returns: list[Any]: A list of results from each worker. """ return await self._executor.collective_rpc_async( method, args, kwargs, unique_reply_rank=unique_reply_rank, target_ranks=target_ranks )
[docs] def generate_async(self, *args, **kwargs): if self._paused: raise RuntimeError( "AsyncLLM is paused. Call resume_generation() before submitting new requests." ) return super().generate_async(*args, **kwargs)
[docs] async def pause_generation(self) -> None: """Abort all in-flight requests and block new ones until resume_generation() is called. Sends abort signals then drains the executor. It only returns once the engine has no active or queued requests. """ self._paused = True self._executor.abort_all_requests() await self.collective_rpc("wait_for_engine_idle")
[docs] async def resume_generation(self) -> None: """Allow new generation requests after a pause_generation() call.""" self._paused = False
def __await__(self): return self.setup_async().__await__() def __enter__(self): raise RuntimeError("Please use 'async with AsyncLLM' instead") async def __aenter__(self): await self.setup_async() return super().__enter__() async def __aexit__(self, exc_type, exc_val, exc_tb): return super().__exit__(exc_type, exc_val, exc_tb)