Skip to content

Commit aec5b5d

Browse files
akshayjadiyanvDanny McCormick
andauthored
Add embedded NVIDIA Dynamo support to vLLM ModelHandler (#38701)
* Add embedded NVIDIA Dynamo support to vLLM ModelHandler VLLMCompletionsModelHandler and VLLMChatModelHandler gain two keyword-only parameters, use_dynamo (default False) and dynamo_frontend_kwargs. When use_dynamo=True, the handler launches a dynamo.frontend process as the OpenAI-compatible local endpoint plus a separate dynamo.vllm worker, instead of vllm.entrypoints.openai.api_server. The existing native-vLLM path is unchanged when the flag is absent. The example pipeline vllm_text_completion.py gains --use_dynamo and --max_tokens flags. validate_inference_args is now a no-op on both handlers so OpenAI-style request kwargs (e.g. max_tokens) can be passed through RunInference. A new unit-test module covers process-launch behaviour for both paths. This supersedes #36966 (now closed) and rebases the embedded-Dynamo approach onto current master, preserving the recent batching-kwargs additions to the ModelHandler base. Co-authored-by: Danny McCormick <damccorm@google.com> * Harden _VLLMModelServer process lifecycle per code review Apply five robustness fixes flagged on PR #38701: - Track the temporary etcd data dir as self._etcd_data_dir and shutil.rmtree(..., ignore_errors=True) it in _stop_processes so worker restarts don't leak /tmp directories. - Wrap process.terminate() / process.wait() / process.kill() in a single try/except OSError to absorb the ProcessLookupError race when a process exits between poll() and the signal call. - Switch the ETCD_ENDPOINTS removal from `del os.environ[...]` to `os.environ.pop(..., None)` to be idempotent. - Wrap __del__ in try/except Exception so cleanup never raises during interpreter shutdown. - Add the embedded etcd process to the check_connectivity() poll loop so an etcd death fails fast instead of waiting out the 10-minute timeout. * Enable Dataflow IT for embedded Dynamo on T4 Bump vllm.dockerfile.old to apache-beam[gcp]==2.71.0 (and the COPY-from beam_python3.12_sdk image to 2.71.0), install ai-dynamo[vllm], and add the etcd binary required by embedded Dynamo's runtime discovery. Uncomment the Dynamo IT block in common.gradle. Drop the unused machine_type override so it inherits n1-standard-4 from argMap, and switch nvidia-l4 -> nvidia-tesla-t4 to match the existing native vLLM ITs and the local Dataflow validation (per @damccorm review). Validated end-to-end on Dataflow with Qwen/Qwen3-0.6B; the nvext.timing field present on every PredictionResult confirms the Dynamo frontend served the requests. * Trigger Python PostCommit for Dynamo IT Bump the beam_PostCommit_Python trigger file so the postcommit suite (inferencePostCommitITPy312 -> vllmTests) runs the embedded Dynamo IT against the rebuilt apache-beam-testing vLLM image. * fix: run Dynamo vLLM IT separately in py312 PostCommit Split vllmDynamoTests from vllmTests so py312 validates Dynamo without blocking on the pre-existing native opt-125m hang in apache-beam-testing. * Restore full vLLM postcommit suite * fix: fold Dynamo IT back into vllmTests for py312 PostCommit Revert the Option A split now that the native opt-125m vLLM hang is fixed (3.12 PostCommit passed in ~2.5h). vllmTests again runs completion -> chat -> Dynamo as a single suite; the separate vllmDynamoTests task is removed. Bump PostCommit trigger to re-run. --------- Co-authored-by: Danny McCormick <damccorm@google.com>
1 parent 1c4b204 commit aec5b5d

6 files changed

Lines changed: 510 additions & 60 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"comment": "Modify this file in a trivial way to cause this test suite to run.",
3-
"pr": "37345",
4-
"modification": 53
3+
"pr": "38701",
4+
"modification": 55
55
}

sdks/python/apache_beam/examples/inference/vllm_text_completion.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ def parse_known_args(argv):
138138
'Passed to the vLLM OpenAI server as --gpu-memory-utilization '
139139
'(fraction of total GPU memory for KV cache). Lower this if the '
140140
'engine fails to start with CUDA out of memory.'))
141+
parser.add_argument(
142+
'--use_dynamo',
143+
dest='use_dynamo',
144+
action='store_true',
145+
help=(
146+
'Use embedded NVIDIA Dynamo as the vLLM engine. Requires '
147+
'ai-dynamo[vllm] and the etcd binary in the runtime environment. '
148+
'See VLLMCompletionsModelHandler for limitations of embedded mode.'))
149+
parser.add_argument(
150+
'--max_tokens',
151+
dest='max_tokens',
152+
type=int,
153+
default=16,
154+
help='Maximum number of tokens to generate for each example.')
141155
return parser.parse_known_args(argv)
142156

143157

@@ -178,22 +192,26 @@ def run(
178192
build_vllm_server_kwargs(known_args))
179193

180194
model_handler = VLLMCompletionsModelHandler(
181-
model_name=known_args.model, vllm_server_kwargs=effective_vllm_kwargs)
195+
model_name=known_args.model,
196+
vllm_server_kwargs=effective_vllm_kwargs,
197+
use_dynamo=known_args.use_dynamo)
182198
input_examples = COMPLETION_EXAMPLES
183199

184200
if known_args.chat:
185201
model_handler = VLLMChatModelHandler(
186202
model_name=known_args.model,
187203
chat_template_path=known_args.chat_template,
188-
vllm_server_kwargs=dict(effective_vllm_kwargs))
204+
vllm_server_kwargs=dict(effective_vllm_kwargs),
205+
use_dynamo=known_args.use_dynamo)
189206
input_examples = CHAT_EXAMPLES
190207

191208
pipeline = test_pipeline
192209
if not test_pipeline:
193210
pipeline = beam.Pipeline(options=pipeline_options)
194211

195212
examples = pipeline | "Create examples" >> beam.Create(input_examples)
196-
predictions = examples | "RunInference" >> RunInference(model_handler)
213+
predictions = examples | "RunInference" >> RunInference(
214+
model_handler, inference_args={'max_tokens': known_args.max_tokens})
197215
process_output = predictions | "Process Predictions" >> beam.ParDo(
198216
PostProcessor())
199217
_ = process_output | "WriteOutput" >> beam.io.WriteToText(

sdks/python/apache_beam/ml/inference/test_resources/vllm.dockerfile.old

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,22 @@ RUN python3 --version
3434
RUN apt-get install -y curl
3535
RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.12 && pip install --upgrade pip
3636

37-
RUN pip install --no-cache-dir -vvv apache-beam[gcp]==2.58.1
38-
RUN pip install openai vllm
37+
RUN pip install --no-cache-dir -vvv apache-beam[gcp]==2.71.0
38+
RUN pip install --no-cache-dir openai vllm ai-dynamo[vllm]
3939

4040
RUN apt install libcairo2-dev pkg-config python3-dev -y
4141
RUN pip install pycairo
4242

43+
# etcd binary required by embedded NVIDIA Dynamo for runtime discovery.
44+
ENV ETCD_VERSION=v3.5.13
45+
RUN curl -L https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz -o /tmp/etcd.tar.gz && \
46+
tar xzf /tmp/etcd.tar.gz -C /tmp && \
47+
mv /tmp/etcd-${ETCD_VERSION}-linux-amd64/etcd /usr/local/bin/etcd && \
48+
chmod +x /usr/local/bin/etcd && \
49+
rm -rf /tmp/etcd*
50+
4351
# Copy the Apache Beam worker dependencies from the Beam Python 3.12 SDK image.
44-
COPY --from=apache/beam_python3.12_sdk:2.58.1 /opt/apache/beam /opt/apache/beam
52+
COPY --from=apache/beam_python3.12_sdk:2.71.0 /opt/apache/beam /opt/apache/beam
4553

4654
# Set the entrypoint to Apache Beam SDK worker launcher.
4755
ENTRYPOINT [ "/opt/apache/beam/boot" ]

0 commit comments

Comments
 (0)