-
Notifications
You must be signed in to change notification settings - Fork 2.7k
[None][feat] Support num_postprocess_workers > 0 under the Ray Orchestration #18399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -255,9 +255,20 @@ def set_result_queue(self, queue): | |
| assert self.frontend_result_queues is None | ||
| self.result_queue = queue | ||
|
|
||
| def set_postproc_queues(self, queues: List["IpcQueue"]): | ||
| """ Set the IPC queues for feeding post-processing processes. """ | ||
| assert self.result_queue is None | ||
| def set_postproc_queues(self, | ||
| queues: List["IpcQueue"], | ||
| *, | ||
| coexist_with_result_queue: bool = False): | ||
| """ Set the IPC queues for feeding post-processing processes. | ||
|
|
||
| coexist_with_result_queue: the classic proxy gives each PostprocWorker | ||
| its own push lane to the frontend, so a result_queue must not exist | ||
| there. Under RPC/Ray orchestration the finished PostprocWorker.Output | ||
| records are collected back INTO the result queue (the single RPC | ||
| response stream), so both queues legitimately coexist. | ||
| """ | ||
| if not coexist_with_result_queue: | ||
| assert self.result_queue is None | ||
| assert self.frontend_result_queues is None | ||
| self.postproc_queues = queues | ||
|
|
||
|
|
@@ -1465,14 +1476,51 @@ def _get_logprobs(worker, | |
| return logprobs_result | ||
|
|
||
|
|
||
| def _send_rsp_to_postproc( | ||
| worker, response: Union[tllm.Response, ResponseWrapper, ErrorResponse], | ||
| postproc_batches: Optional[List[List["PostprocWorker.Input"]]]): | ||
| """Shard a raw response to the postproc workers (batched or direct put).""" | ||
| sampling_params, postproc_params, disaggregated_params = ( | ||
| _get_params_for_first_rsp(worker, response.client_id)) | ||
| inp = PostprocWorker.Input( | ||
| response, | ||
| # sampling_params is necessary for creating fake GenerationResult | ||
| # instances in the postproc processes. They are for incremental | ||
| # detokenize. They should be transmitted only once for each | ||
| # Request. | ||
| sampling_params=sampling_params, | ||
| postproc_params=postproc_params, | ||
| disaggregated_params=disaggregated_params, | ||
| streaming=worker._results.get(response.client_id, None)._streaming) | ||
|
|
||
| # Group the responses into buckets for the postprocessing steps. | ||
| # Bucketing is used instead of random dispatching because the | ||
| # incremental detokenization during postprocessing relies on the | ||
| # prior CompletionOutput of a given request. | ||
| pid = response.client_id % worker.postproc_config.num_postprocess_workers | ||
|
|
||
| if postproc_batches is None: | ||
| worker.postproc_queues[pid].put(inp) | ||
| else: | ||
| postproc_batches[pid].append(inp) | ||
|
|
||
|
|
||
| def _send_rsp( | ||
| worker, | ||
| response: Union[tllm.Response, ResponseWrapper, ErrorResponse], | ||
| postproc_batches: Optional[List[List["PostprocWorker.Input"]]] = None, | ||
| rsp_batch: Optional[List[tllm.Response]] = None): | ||
| # if postproc_batches is set, append to batch instead of putting to IpcQueue | ||
|
|
||
| if worker.frontend_result_queues is not None: | ||
| # Postprocess parallelism takes priority over the direct result routes: | ||
| # under RPC/Ray orchestration the worker holds a result_queue (the RPC | ||
| # response stream feed) AND postproc input queues at the same time, and | ||
| # raw responses must go to the postproc workers first — their finished | ||
| # Output records re-enter the result_queue via the collector thread | ||
| # (see RpcWorkerMixin.init_postproc_workers). | ||
| if postproc_batches is not None: | ||
| _send_rsp_to_postproc(worker, response, postproc_batches) | ||
|
Comment on lines
+1521
to
+1522
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Route When postprocessing is enabled, this branch sends Handle 🤖 Prompt for AI Agents |
||
| elif worker.frontend_result_queues is not None: | ||
| # Route to the origin frontend's result lane; None/out-of-range ids | ||
| # fall back to lane 0 (see frontend_lane_index). | ||
| if rsp_batch is not None: | ||
|
|
@@ -1487,29 +1535,7 @@ def _send_rsp( | |
| else: | ||
| worker.result_queue.put(response) | ||
| else: | ||
| sampling_params, postproc_params, disaggregated_params = ( | ||
| _get_params_for_first_rsp(worker, response.client_id)) | ||
| inp = PostprocWorker.Input( | ||
| response, | ||
| # sampling_params is necessary for creating fake GenerationResult | ||
| # instances in the postproc processes. They are for incremental | ||
| # detokenize. They should be transmitted only once for each | ||
| # Request. | ||
| sampling_params=sampling_params, | ||
| postproc_params=postproc_params, | ||
| disaggregated_params=disaggregated_params, | ||
| streaming=worker._results.get(response.client_id, None)._streaming) | ||
|
|
||
| pid = response.client_id % worker.postproc_config.num_postprocess_workers | ||
|
|
||
| if not postproc_batches: | ||
| # Group the responses into buckets for the postprocessing steps. | ||
| # Bucketing is used instead of random dispatching because the | ||
| # incremental detokenization during postprocessing relies on the | ||
| # prior CompletionOutput of a given request. | ||
| worker.postproc_queues[pid].put(inp) | ||
| else: | ||
| postproc_batches[pid].append(inp) | ||
| _send_rsp_to_postproc(worker, response, None) | ||
|
|
||
| # Eliminate the finished GenerationRequest instances timely, which may | ||
| # take considerable memory. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -154,6 +154,23 @@ class CompletionOutput: | |
| # the result of result_handler passed to postprocess workers | ||
| _postprocess_result: Any = None | ||
|
|
||
| def __getstate__(self): | ||
| # _incremental_states holds a tokenizers.DecodeStream (a Rust object, | ||
| # not picklable) used for process-local incremental detokenization. | ||
| # Receivers never need it, so drop it when this output crosses a | ||
| # process boundary — e.g. a PostprocWorker streaming a raw | ||
| # CompletionOutput (no postproc_params) back over IPC. | ||
| # slots=True dataclass: object.__getstate__ returns | ||
| # (dict_or_None, slots_dict). | ||
| state = object.__getstate__(self) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target source ---'
sed -n '120,205p' tensorrt_llm/executor/result.py
printf '%s\n' '--- direct Python-version declarations ---'
rg -n --hidden -g '!*.pyc' -g '!node_modules' -g '!build' -g '!dist' \
'python_requires|requires-python|Python 3\.10|python-version|3\.10|3\.11' \
pyproject.toml setup.cfg setup.py tox.ini .github tensorrt_llm 2>/dev/null | head -200
printf '%s\n' '--- scoped repository guidance ---'
find /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761 -maxdepth 2 -type f -name '*.md' -printRepository: NVIDIA/TensorRT-LLM Length of output: 9547 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- class declaration and imports ---'
sed -n '1,145p' tensorrt_llm/executor/result.py
printf '%s\n' '--- CompletionOutput and postprocessing boundary references ---'
rg -n -C 4 --glob '*.py' \
'CompletionOutput|PostprocWorker|postproc_params|pickle|dumps|send\(' \
tensorrt_llm/executor tests 2>/dev/null | head -300
printf '%s\n' '--- relevant repository convention ---'
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/repo-wide.mdRepository: NVIDIA/TensorRT-LLM Length of output: 34389 🌐 Web query:
💡 Result: A default implementation of the object.getstate method was introduced in Python 3.11 [1][2]. Prior to this change, in Python 3.10 and earlier, the object class did not provide a default getstate method [3]. If a class did not define its own getstate, the pickling process would default to using the instance's dict [3]. The addition of this default implementation in Python 3.11 serves several purposes: 1. Standardized Behavior: It provides a consistent, C-implemented mechanism for determining the pickleable state of an object [4][5]. 2. Improved Support for slots: A primary motivation for this change was to enable the automatic pickling and copying of instance attributes implemented as slots in subclasses of certain builtin types (such as bytearray, set, frozenset, collections.OrderedDict, collections.deque, weakref.WeakSet, and datetime.tzinfo), which previously had limited or no support for such attributes [4][2]. 3. Pickling Logic: Depending on the object's structure (presence of dict or slots), the default implementation returns different states, such as None for objects with neither, or tuples of dictionaries for objects using slots [6]. This change had the unintended side effect of impacting some existing projects that did not expect object.getstate to exist, as its presence can affect how code detects or handles pickling logic [5][2]. Projects encountering issues often require workarounds if they specifically relied on the absence of this method to determine behavior [5][2]. Citations:
Keep serialization compatible with Python 3.10. The package supports Python 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| if isinstance(state, tuple) and len(state) == 2: | ||
| d, slots = state | ||
| if slots and '_incremental_states' in slots: | ||
| slots = dict(slots) | ||
| slots['_incremental_states'] = None | ||
| return (d, slots) | ||
| return state | ||
|
|
||
| @property | ||
| def length(self) -> int: | ||
| return len(self.token_ids) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,87 @@ def init_rpc_worker(self, rank: int, rpc_addr: Optional[str], hmac_key: bytes): | |
|
|
||
| self.rpc_server = None | ||
| self.rpc_addr = rpc_addr | ||
| self._postproc_pool = None | ||
| self._postproc_input_queues = None | ||
| self._postproc_collector = None | ||
| self._postproc_collector_thread = None | ||
|
|
||
| def init_postproc_workers(self): | ||
| """Spawn local PostprocWorker processes feeding the RPC response stream. | ||
|
|
||
| The classic (MPI proxy) path gives each PostprocWorker a dedicated | ||
| push lane straight to the frontend process. Under RPC/Ray | ||
| orchestration no such lane exists: every record must travel the single | ||
| RPC response stream. Instead of teaching PostprocWorker a new | ||
| transport, its push pipe is pointed at a local collector socket whose | ||
| consumer thread enqueues the already-final ``PostprocWorker.Output`` | ||
| batches into ``_response_queue`` — the same queue ``fetch_responses`` | ||
| drains — so the stream, the proxy demux, and the client stay on the | ||
| code they already run for the classic postproc path. | ||
|
|
||
| Call on the response-producing rank only (rank 0), after | ||
| ``init_rpc_worker``. | ||
| """ | ||
| import threading | ||
| from concurrent.futures import ProcessPoolExecutor | ||
|
|
||
| import zmq | ||
|
|
||
| from .ipc import IpcQueue | ||
| from .postproc_worker import PostprocWorker, postproc_worker_main | ||
|
|
||
| num = self.postproc_config.num_postprocess_workers | ||
| assert num > 0 | ||
|
|
||
| self._postproc_input_queues = [ | ||
| IpcQueue(is_server=True, name=f"rpc_worker_postproc_input_{i}") for i in range(num) | ||
| ] | ||
| self._postproc_collector = IpcQueue( | ||
| is_server=True, socket_type=zmq.PULL, name="rpc_worker_postproc_collector" | ||
| ) | ||
| # Both the result_queue (RPC stream feed) and the postproc input | ||
| # queues are live on this worker — see set_postproc_queues docstring. | ||
| self.set_postproc_queues(self._postproc_input_queues, coexist_with_result_queue=True) | ||
|
|
||
| # fork (default), matching the classic path. spawn is NOT usable | ||
| # here: the spawn bootstrap re-imports the Ray worker's __main__, | ||
| # which deadlocks inside a Ray actor (verified empirically). | ||
| self._postproc_pool = ProcessPoolExecutor(max_workers=num) | ||
| for i in range(num): | ||
| self._postproc_pool.submit( | ||
| postproc_worker_main, | ||
| self._postproc_input_queues[i].address, | ||
| [self._postproc_collector.address], | ||
| self.postproc_config.postprocess_tokenizer_dir, | ||
| PostprocWorker.default_record_creator, | ||
| self.postproc_config.post_processor_hook, | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ) | ||
|
Comment on lines
+103
to
+110
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🌐 Web query:
💡 Result: Yes, for a concurrent.futures.ProcessPoolExecutor, exceptions raised by a task in a worker process are captured by the executor and stored within the associated Future object [1][2][3]. These exceptions remain on the Future object until they are explicitly retrieved or re-raised [2][3]. Specifically: 1. Calling Future.result will re-raise the exception if the task failed, allowing it to be caught by a try/except block in the main thread [2][4][3]. 2. Calling Future.exception will return the exception object itself (or None if no exception occurred) without re-raising it [2][5][6]. If you do not call either of these methods, the exception remains stored within the Future object and will not be raised automatically in the main process, which can lead to silent task failures if not handled correctly [1][2][3]. Citations:
🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- repository convention and learning files ---'
find /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- target file outline ---'
ast-grep outline tensorrt_llm/executor/rpc_worker_mixin.py
printf '%s\n' '--- target implementation ---'
sed -n '1,190p' tensorrt_llm/executor/rpc_worker_mixin.py
printf '%s\n' '--- relevant symbols and call sites ---'
rg -n -C 3 'init_postproc_workers|postproc_worker_main|_collect_postproc_outputs|_error_queue|_response_queue|_postproc_pool' tensorrt_llm/executorRepository: NVIDIA/TensorRT-LLM Length of output: 33912 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- applicable repository conventions and learnings ---'
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/repo-wide.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/py.md
printf '%s\n' '--- postprocessing worker contract ---'
sed -n '250,370p' tensorrt_llm/executor/postproc_worker.py
printf '%s\n' '--- response routing contract ---'
sed -n '1480,1545p' tensorrt_llm/executor/base_worker.py
printf '%s\n' '--- error queue initialization and handling ---'
sed -n '80,115p' tensorrt_llm/executor/executor.py
sed -n '285,380p' tensorrt_llm/executor/executor.py
printf '%s\n' '--- RPC worker initialization and shutdown callers ---'
sed -n '245,280p' tensorrt_llm/executor/ray/gpu_worker.py
sed -n '325,345p' tensorrt_llm/executor/ray/gpu_worker.pyRepository: NVIDIA/TensorRT-LLM Length of output: 22768 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- postprocessing construction and failure boundaries ---'
sed -n '1,270p' tensorrt_llm/executor/postproc_worker.py
printf '%s\n' '--- postprocessing configuration and queue setup ---'
rg -n -C 5 'class PostprocWorkerConfig|def set_postproc_queues|postproc_queues|postproc_config.enabled|def await_responses|def process_and_handle_responses' tensorrt_llm/executor
printf '%s\n' '--- RPC server start and request acceptance ordering ---'
sed -n '1,180p' tensorrt_llm/executor/ray/gpu_worker.py
sed -n '1,190p' tensorrt_llm/executor/rpc.pyRepository: NVIDIA/TensorRT-LLM Length of output: 32303 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- exception decorator ---'
rg -n -C 8 'def print_traceback_on_error|print_traceback_on_error' tensorrt_llm/llmapi tensorrt_llm/executor
printf '%s\n' '--- RPC implementation files ---'
find tensorrt_llm/executor -maxdepth 2 -type f -iname '*rpc*' -print
printf '%s\n' '--- RPC server lifecycle and request dispatch ---'
rg -n -C 8 'class RPCServer|def bind|def start|def submit|enqueue_request|fetch_responses' tensorrt_llm/executor --glob '*rpc*' --glob '*.py'
printf '%s\n' '--- exact postprocessing setup and response-fetch flow ---'
sed -n '1140,1185p' tensorrt_llm/executor/base_worker.py
sed -n '1325,1375p' tensorrt_llm/executor/base_worker.pyRepository: NVIDIA/TensorRT-LLM Length of output: 50375 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- decorator behavior ---'
sed -n '45,65p' tensorrt_llm/llmapi/utils.py
printf '%s\n' '--- RPC server implementation ---'
sed -n '1,230p' tensorrt_llm/executor/rpc/rpc_server.py
printf '%s\n' '--- response handler branch for postproc outputs and errors ---'
sed -n '1160,1215p' tensorrt_llm/executor/base_worker.py
sed -n '450,540p' tensorrt_llm/executor/result.pyRepository: NVIDIA/TensorRT-LLM Length of output: 16645 Propagate postprocessing worker failures.
🤖 Prompt for AI Agents |
||
|
|
||
| def _collect_postproc_outputs(): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Add return annotations to the new procedures. Add 📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| while not self.shutdown_event.is_set(): | ||
| batch = self._postproc_collector.get() | ||
| if batch is None: | ||
| break | ||
| # fetch_responses drains _response_queue batch-wise; Output | ||
| # batches ride the RPC stream exactly like final responses. | ||
| self._response_queue.put(batch) | ||
|
|
||
| self._postproc_collector_thread = threading.Thread( | ||
| target=_collect_postproc_outputs, name="rpc_worker_postproc_collector", daemon=True | ||
| ) | ||
| self._postproc_collector_thread.start() | ||
|
|
||
| def shutdown_postproc_workers(self): | ||
| """Best-effort teardown of the local postproc pool (idempotent).""" | ||
| if self._postproc_input_queues: | ||
| for q in self._postproc_input_queues: | ||
| try: | ||
| q.put(None) # PostprocWorker mainloop exits on None | ||
| except Exception: | ||
| pass | ||
| if self._postproc_pool is not None: | ||
| self._postproc_pool.shutdown(wait=False) | ||
| self._postproc_pool = None | ||
|
|
||
| def start_rpc_server(self): | ||
| if self.rank == 0: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| """Postprocess parallelism under the Ray orchestrator. | ||
|
|
||
| The rank-0 RayGPUWorker spawns a local PostprocWorker pool; finished | ||
| ``PostprocWorker.Output`` records re-enter the RPC response stream via the | ||
| collector thread (RpcWorkerMixin.init_postproc_workers). These tests pin the | ||
| two user-visible contracts: | ||
|
|
||
| 1. workers>0 produces token-for-token identical text to the inline | ||
| (workers=0) path, sync and streaming; | ||
| 2. the stream/proxy demux finalizes Output records (no leaked results, | ||
| no hang on completion). | ||
| """ | ||
|
|
||
| import asyncio | ||
| import os | ||
|
|
||
| from utils.llm_data import llm_models_root | ||
|
|
||
| from tensorrt_llm import LLM | ||
| from tensorrt_llm.llmapi import KvCacheConfig, SamplingParams | ||
|
|
||
| PROMPTS = [ | ||
| "Hello, my name is", | ||
| "The president of the United States is", | ||
| "The capital of France is", | ||
| "The future of AI is", | ||
| ] | ||
|
|
||
|
|
||
| def _model_path() -> str: | ||
| override = os.environ.get("POSTPROC_TEST_MODEL") | ||
| if override: | ||
| return override | ||
| return str(llm_models_root() / "llama-models-v2/TinyLlama-1.1B-Chat-v1.0") | ||
|
|
||
|
|
||
| def _make_llm(num_postprocess_workers: int) -> LLM: | ||
| model = _model_path() | ||
| extra = {} | ||
| if num_postprocess_workers > 0: | ||
| extra = dict( | ||
| num_postprocess_workers=num_postprocess_workers, postprocess_tokenizer_dir=model | ||
| ) | ||
| return LLM( | ||
| model=model, | ||
| kv_cache_config=KvCacheConfig(enable_block_reuse=False, max_tokens=16384), | ||
| **extra, | ||
| ) | ||
|
|
||
|
|
||
| def _generate_sync(num_postprocess_workers: int) -> list[str]: | ||
| sampling_params = SamplingParams(temperature=0, max_tokens=32) | ||
| with _make_llm(num_postprocess_workers) as llm: | ||
| outputs = llm.generate(PROMPTS, sampling_params) | ||
| return [output.outputs[0].text for output in outputs] | ||
|
|
||
|
|
||
| def test_postproc_workers_match_inline(): | ||
| """Greedy outputs with workers=2 must equal the inline workers=0 path.""" | ||
| baseline = _generate_sync(0) | ||
| assert all(text for text in baseline) | ||
| with_postproc = _generate_sync(2) | ||
| assert with_postproc == baseline | ||
|
|
||
|
|
||
| def test_streaming_control_no_postproc(): | ||
| """Control: streaming with workers=0 must work (isolates any pre-existing | ||
| ray-streaming issue from the postproc path).""" | ||
| _streaming_body(num_postprocess_workers=0) | ||
|
|
||
|
|
||
| def test_postproc_workers_streaming(): | ||
| """Streaming rides the same Output records; final text must match sync.""" | ||
| _streaming_body(num_postprocess_workers=2) | ||
|
Comment on lines
+58
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
for bucket in test-db qa; do
root="tests/integration/test_lists/$bucket"
if [ -d "$root" ]; then
rg -a -n \
'test_postproc_workers|test_postproc_workers_match_inline|test_streaming_control_no_postproc|test_postproc_workers_streaming' \
"$root" || true
fi
doneRepository: NVIDIA/TensorRT-LLM Length of output: 157 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository guidance ---'
find /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761 -maxdepth 2 -type f -name '*.md' -print | sort
printf '%s\n' '--- reviewed file ---'
cat -n tests/unittest/_torch/ray_orchestrator/single_gpu/test_postproc_workers.py
printf '%s\n' '--- file status ---'
git status --short -- tests/unittest/_torch/ray_orchestrator/single_gpu/test_postproc_workers.py
printf '%s\n' '--- test-list directories ---'
for root in tests/integration/test_lists/test-db tests/integration/test_lists/qa; do
if [ -d "$root" ]; then
printf '%s\n' "$root"
find "$root" -maxdepth 2 -type f -print | sort | head -80
else
printf '%s\n' "$root (absent)"
fi
doneRepository: NVIDIA/TensorRT-LLM Length of output: 13037 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- test conventions ---'
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests-integration-test-lists.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests-integration-test-lists-test-db.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests-integration-test-lists-qa.md
printf '%s\n' '--- scoped Python/test learnings ---'
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/py.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/tests.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/tests-integration-test-lists.md
printf '%s\n' '--- representative test-list entries and unit-test references ---'
rg -a -n -m 20 \
'unittest|tests/unittest|test_[A-Za-z0-9_]+\.py|pytest' \
tests/integration/test_lists/test-db tests/integration/test_lists/qaRepository: NVIDIA/TensorRT-LLM Length of output: 50376 🏁 Script executed: #!/bin/bash
set -euo pipefail
target='unittest/_torch/ray_orchestrator/single_gpu/test_postproc_workers.py'
printf '%s\n' '--- exact and directory registrations ---'
for root in tests/integration/test_lists/test-db tests/integration/test_lists/qa; do
printf '%s\n' "$root"
rg -a -n -F \
-e "$target" \
-e 'unittest/_torch/ray_orchestrator' \
-e 'ray_orchestrator' \
"$root" || true
done
printf '%s\n' '--- neighboring Ray orchestrator tests ---'
find tests/unittest/_torch/ray_orchestrator -maxdepth 3 -type f -name 'test_*.py' -print | sort
printf '%s\n' '--- neighboring list entries ---'
rg -a -n 'ray|orchestrator|single_gpu' tests/integration/test_lists/test-db tests/integration/test_lists/qa \
| head -120 || trueRepository: NVIDIA/TensorRT-LLM Length of output: 23070 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- l0_h100 Ray section ---'
sed -n '245,275p' tests/integration/test_lists/test-db/l0_h100.yml
printf '%s\n' '--- neighboring source headers and signatures ---'
for file in \
tests/unittest/_torch/ray_orchestrator/single_gpu/test_llm_sleep.py \
tests/unittest/_torch/ray_orchestrator/single_gpu/test_llm_update_weights.py \
tests/unittest/_torch/ray_orchestrator/single_gpu/test_cache_transceiver_comm.py; do
printf '%s\n' "--- $file"
sed -n '1,35p' "$file"
doneRepository: NVIDIA/TensorRT-LLM Length of output: 6015 Register the new Ray tests in 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
|
|
||
| def _streaming_body(num_postprocess_workers: int): | ||
| sampling_params = SamplingParams(temperature=0, max_tokens=32) | ||
|
|
||
| with _make_llm(num_postprocess_workers) as llm: | ||
| sync_texts = [output.outputs[0].text for output in llm.generate(PROMPTS, sampling_params)] | ||
|
|
||
| async def collect(prompt: str) -> str: | ||
| final = None | ||
| async for output in llm.generate_async(prompt, sampling_params, streaming=True): | ||
| final = output | ||
| return final.outputs[0].text | ||
|
|
||
| async def run_all() -> list[str]: | ||
| return await asyncio.gather(*[collect(p) for p in PROMPTS]) | ||
|
|
||
| streamed_texts = asyncio.run(run_all()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- applicable repository knowledge files ---'
find /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761 -type f -name '*.md' -maxdepth 3 -print
printf '%s\n' '--- changed test ---'
cat -n tests/unittest/_torch/ray_orchestrator/single_gpu/test_postproc_workers.py
printf '%s\n' '--- related test-list entries ---'
rg -n -i 'postproc_workers|ray_orchestrator|single_gpu' tests/integration/test_lists || true
printf '%s\n' '--- file status ---'
git status --short -- tests/unittest/_torch/ray_orchestrator/single_gpu/test_postproc_workers.py tests/integration/test_listsRepository: NVIDIA/TensorRT-LLM Length of output: 27864 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- LLM.generate_async definitions and call sites ---'
rg -n -g '*.py' '(^|[[:space:]])(async[[:space:]]+def|def)[[:space:]]+generate_async|generate_async[[:space:]]*=' tensorrt_llm tests | head -120
printf '%s\n' '--- test conventions ---'
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/conventions/tests-integration-test-lists.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/tests.md
cat /tmp/coderabbit-repo-knowledge/nvidia-tensorrt-llm-053b2761/learnings/tests-integration-test-lists.mdRepository: NVIDIA/TensorRT-LLM Length of output: 5495 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- LLM.generate_async implementation ---'
sed -n '700,825p' tensorrt_llm/llmapi/llm.py
printf '%s\n' '--- directly referenced async-generation helpers ---'
rg -n -g '*.py' 'generate_async\(|streaming|AsyncIterator|async for' tensorrt_llm/llmapi/llm.py tensorrt_llm/llmapi | head -180Repository: NVIDIA/TensorRT-LLM Length of output: 10264 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- generate_async return path ---'
sed -n '825,875p' tensorrt_llm/llmapi/llm.py
printf '%s\n' '--- RequestOutput definitions and iteration methods ---'
rg -n -g '*.py' 'class RequestOutput|def __aiter__|async def __anext__|def __iter__|def __next__|_streaming' tensorrt_llm/llmapi tensorrt_llm/executor | head -160Repository: NVIDIA/TensorRT-LLM Length of output: 9019 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- RequestOutput construction ---'
sed -n '60,115p' tensorrt_llm/llmapi/llm.py
printf '%s\n' '--- generation-result iteration ---'
sed -n '1115,1170p' tensorrt_llm/executor/result.py
sed -n '1195,1245p' tensorrt_llm/executor/result.py
printf '%s\n' '--- streaming status fields used by the iterators ---'
sed -n '880,1010p' tensorrt_llm/executor/result.pyRepository: NVIDIA/TensorRT-LLM Length of output: 11915 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- GenerationResultBase result stepping and completion ---'
sed -n '250,380p' tensorrt_llm/executor/result.py
sed -n '1000,1165p' tensorrt_llm/executor/result.py
printf '%s\n' '--- async queue timeout contract ---'
rg -n -g '*.py' 'class AsyncQueue|async def get|def get\(' tensorrt_llm | head -80Repository: NVIDIA/TensorRT-LLM Length of output: 19202 Bound the asynchronous collection time.
🤖 Prompt for AI Agents |
||
|
|
||
| assert streamed_texts == sync_texts | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add required method annotations.
tensorrt_llm/executor/base_worker.py#L258-L261: uselist["IpcQueue"]and add-> None.tensorrt_llm/executor/rpc_worker_mixin.py#L61-L61: add-> Nonetoinit_postproc_workers.tensorrt_llm/executor/rpc_worker_mixin.py#L131-L131: add-> Nonetoshutdown_postproc_workers.As per coding guidelines: “Annotate every function” and “prefer built-in generic types.”
📍 Affects 2 files
tensorrt_llm/executor/base_worker.py#L258-L261(this comment)tensorrt_llm/executor/rpc_worker_mixin.py#L61-L61tensorrt_llm/executor/rpc_worker_mixin.py#L131-L131🤖 Prompt for AI Agents
Source: Coding guidelines