Skip to content

Commit 3dd3ee5

Browse files
Yadan-WeiYadan Wei
andauthored
ci(vllm): add nemotron-nano-12b-v2 smoke test + split sagemaker endpoint config (#6349)
* ci(vllm): add nemotron-nano-12b-v2 to smoke tests Adds NVIDIA-Nemotron-Nano-12B-v2 (Mamba2-Transformer hybrid, 12B params) to the vLLM smoke-test matrix on the g6e.xl (L40S 48GB) fleet. Requires vllm>=0.10.1 for hybrid Mamba2 support; the DLC vllm:server-cuda-v2 image ships vllm 0.24.0+amzn2023 which satisfies this. Uses --mamba_ssm_cache_dtype float32 (required by the model card to preserve accuracy) and --trust-remote-code for the custom configuration_nemotron_h.py. Verified end-to-end on g6e.2xlarge: model loads as NemotronHForCausalLM, Mamba2 SSD Triton kernels warm up, KV cache pool 369K tokens, both /think reasoning-on and /no_think reasoning-off modes return correct output. Steady-state GPU memory ~42 GiB out of 46 GiB. Signed-off-by: Yadan Wei <yadanwei@amazon.com> * ci(vllm): split sagemaker endpoint tests into vllm-sagemaker-tests.yml Extracts the sagemaker: section from vllm-model-tests.yml into a dedicated vllm-sagemaker-tests.yml so smoke-test and endpoint-test configs evolve independently. Each file now owns its own s3_prefix and test_fixtures_prefix. - test_sm_model_serving.py now reads from vllm-sagemaker-tests.yml - vllm.pr-amzn2023.yml and vllm.pr-ubuntu.yml add the new file to the top-level paths trigger and to sagemaker-test-change, so edits to endpoint entries fire the sagemaker suite (not the smoke suite) - parse_model_config.py and vllm.tests-model.yml need no changes; they only read smoke-test:, and its file path is unchanged Signed-off-by: Yadan Wei <yadanwei@amazon.com> * ci(vllm): rename vllm-sagemaker-tests to vllm-sagemaker-endpoint-tests Everything under .github/config/model-tests/ is a model test config. "sagemaker-tests" was ambiguous — the smoke-test file also runs SageMaker-image smoke tests. Rename to make the distinction explicit: this file drives real SageMaker endpoint tests (test_sm_model_serving.py), the sibling drives smoke tests. Follow-up references updated in the sibling YAML comment, both PR workflow triggers, and the sagemaker test loader. Signed-off-by: Yadan Wei <yadanwei@amazon.com> --------- Signed-off-by: Yadan Wei <yadanwei@amazon.com> Co-authored-by: Yadan Wei <yadanwei@amazon.com>
1 parent f98b17f commit 3dd3ee5

7 files changed

Lines changed: 235 additions & 182 deletions

File tree

Lines changed: 17 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
# vLLM Model Test Configuration
2-
# Smoke-test and SageMaker endpoint test suites.
3-
#
4-
# smoke-test: model serving + inference validation
5-
# sagemaker: config-driven real SageMaker endpoint tests
6-
#
7-
# Benchmark (throughput/latency) configs were split out into
8-
# vllm-text-benchmark-tests.yml and vllm-multimodal-benchmark-tests.yml.
1+
# vLLM Model Smoke Test Configuration
92
#
3+
# Smoke tests: model serving + inference validation.
104
# smoke-test has codebuild-fleet and runner-scale-sets sub-keys.
115
# Workflow parsers construct s3_path from s3_prefix + s3_model.
6+
#
7+
# SageMaker endpoint tests live in the sibling vllm-sagemaker-endpoint-tests.yml.
8+
# Benchmark (throughput/latency) configs live in
9+
# vllm-text-benchmark-tests.yml and vllm-multimodal-benchmark-tests.yml.
1210

1311
s3_prefix: "s3://dlc-cicd-models/llm-models"
1412
test_fixtures_prefix: "s3://dlc-cicd-models/test-fixtures"
@@ -39,6 +37,17 @@ smoke-test:
3937
fleet: "x86-g6exl-runner"
4038
extra_args: "--tensor-parallel-size 1 --max-model-len 4096 --dtype bfloat16 --reasoning-parser qwen3"
4139

40+
# NVIDIA Nemotron-Nano-12B-v2: Mamba2-Transformer hybrid (NemotronHForCausalLM),
41+
# 12B params bf16 (~24GB weights). Requires vllm>=0.10.1 for hybrid support.
42+
# Fits a single L40S (g6e.xl, 48GB) at tp=1 with ~42GB steady-state (23% headroom).
43+
# --mamba_ssm_cache_dtype float32 is required by the model card — without it,
44+
# SSM cache defaults to bf16 and accuracy degrades. --trust-remote-code is needed
45+
# to load the custom configuration_nemotron_h.py from the HF repo.
46+
- name: "nemotron-nano-12b-v2"
47+
s3_model: "nemotron-nano-12b-v2.tar.gz"
48+
fleet: "x86-g6exl-runner"
49+
extra_args: "--tensor-parallel-size 1 --max-model-len 4096 --dtype bfloat16 --trust-remote-code --mamba_ssm_cache_dtype float32"
50+
4251
- name: "qwen3-embedding-0.6b"
4352
s3_model: "qwen3-embedding-0.6b.tar.gz"
4453
fleet: "x86-g6xl-runner"
@@ -84,169 +93,3 @@ smoke-test:
8493
validate: "json_field:text"
8594

8695
runner-scale-sets: []
87-
88-
# --- SageMaker Endpoint Tests ---
89-
# Config-driven real endpoint tests. Each entry deploys a model from S3,
90-
# sends requests to the configured route, and validates the response.
91-
# Test script: test/vllm/sagemaker/test_sm_model_serving.py
92-
sagemaker:
93-
# MiniCPM5-1B — dense 1B LlamaForCausalLM, bf16. Standard chat model served via
94-
# the OpenAI /v1/chat/completions route. Validated offline on
95-
# server-sagemaker-cuda-v2 with SM_VLLM_MAX_MODEL_LEN=8192. Hybrid reasoning
96-
# emits a <think> block, so message.content is non-empty; json_field validation
97-
# (non-empty check) mirrors the other entries' validate rule.
98-
- name: "minicpm5-1b"
99-
s3_model: "minicpm5-1b.tar.gz"
100-
instance_type: "ml.g6.xlarge"
101-
required_image_pattern: "amzn2023"
102-
env:
103-
SM_VLLM_MODEL: "/opt/ml/model"
104-
SM_VLLM_MAX_MODEL_LEN: "8192"
105-
SM_VLLM_DTYPE: "bfloat16"
106-
test_cases:
107-
- name: "chat-completion"
108-
route: "/v1/chat/completions"
109-
content_type: "application/json"
110-
request:
111-
body:
112-
model: "/opt/ml/model"
113-
messages:
114-
- role: "user"
115-
content: "What is the capital of France?"
116-
max_tokens: 256
117-
temperature: 0
118-
validate: "json_field:choices.0.message.content"
119-
120-
- name: "voxtral-mini-4b"
121-
s3_model: "voxtral-mini-4b.tar.gz"
122-
instance_type: "ml.g6.xlarge"
123-
required_image_pattern: "amzn2023"
124-
env:
125-
SM_VLLM_MODEL: "/opt/ml/model"
126-
SM_VLLM_TOKENIZER_MODE: "mistral"
127-
SM_VLLM_CONFIG_FORMAT: "mistral"
128-
SM_VLLM_LOAD_FORMAT: "mistral"
129-
SM_VLLM_ENFORCE_EAGER: "true"
130-
SM_VLLM_MAX_MODEL_LEN: "8192"
131-
test_cases:
132-
- name: "transcription-en"
133-
route: "/v1/audio/transcriptions"
134-
content_type: "multipart/form-data"
135-
request:
136-
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
137-
file_field: "file"
138-
file_name: "test.wav"
139-
file_content_type: "audio/wav"
140-
fields:
141-
model: "/opt/ml/model"
142-
validate: "json_field:text"
143-
144-
- name: "granite-speech-4-1-2b"
145-
s3_model: "granite-speech-4.1-2b.tar.gz"
146-
instance_type: "ml.g6.xlarge"
147-
required_image_pattern: "amzn2023"
148-
env:
149-
SM_VLLM_MODEL: "/opt/ml/model"
150-
SM_VLLM_MAX_MODEL_LEN: "2048"
151-
test_cases:
152-
- name: "transcription-en"
153-
route: "/v1/audio/transcriptions"
154-
content_type: "multipart/form-data"
155-
request:
156-
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
157-
file_field: "file"
158-
file_name: "test.wav"
159-
file_content_type: "audio/wav"
160-
fields:
161-
model: "/opt/ml/model"
162-
validate: "json_field:text"
163-
164-
- name: "voxtral-mini-4b-network-isolated"
165-
s3_model: "voxtral-mini-4b-isolated.tar.gz"
166-
instance_type: "ml.g6.xlarge"
167-
required_image_pattern: "amzn2023"
168-
network_isolation: true
169-
env:
170-
SM_VLLM_MODEL: "/opt/ml/model"
171-
SM_VLLM_TOKENIZER_MODE: "mistral"
172-
SM_VLLM_CONFIG_FORMAT: "mistral"
173-
SM_VLLM_LOAD_FORMAT: "mistral"
174-
SM_VLLM_ENFORCE_EAGER: "true"
175-
SM_VLLM_MAX_MODEL_LEN: "8192"
176-
STANDARD_PIP_ARGS: "--no-index --find-links /opt/ml/model/requirements -r /opt/ml/model/requirements.txt"
177-
test_cases:
178-
- name: "transcription-en-isolated"
179-
route: "/v1/audio/transcriptions"
180-
content_type: "multipart/form-data"
181-
request:
182-
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
183-
file_field: "file"
184-
file_name: "test.wav"
185-
file_content_type: "audio/wav"
186-
fields:
187-
model: "/opt/ml/model"
188-
validate: "json_field:text"
189-
190-
- name: "granite-speech-4-1-2b-network-isolated"
191-
s3_model: "granite-speech-4.1-2b-isolated.tar.gz"
192-
instance_type: "ml.g6.xlarge"
193-
required_image_pattern: "amzn2023"
194-
network_isolation: true
195-
env:
196-
SM_VLLM_MODEL: "/opt/ml/model"
197-
SM_VLLM_MAX_MODEL_LEN: "2048"
198-
STANDARD_PIP_ARGS: "--no-index --find-links /opt/ml/model/requirements -r /opt/ml/model/requirements.txt"
199-
test_cases:
200-
- name: "transcription-en-isolated"
201-
route: "/v1/audio/transcriptions"
202-
content_type: "multipart/form-data"
203-
request:
204-
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
205-
file_field: "file"
206-
file_name: "test.wav"
207-
file_content_type: "audio/wav"
208-
fields:
209-
model: "/opt/ml/model"
210-
validate: "json_field:text"
211-
212-
# Qwen3-Reranker-4B — Path C: load as Qwen3ForSequenceClassification so the
213-
# Cohere-compatible /v1/rerank endpoint registers. SM_VLLM_HF_OVERRIDES is
214-
# passed verbatim to vLLM as --hf-overrides. The chat template is read from
215-
# test/vllm/scripts/amzn2023/qwen3_reranker_chat_template.jinja (shared with
216-
# the EC2 reranker scripts), flattened to single-line with {{ '\n' }} escapes
217-
# at test runtime, and injected as SM_VLLM_CHAT_TEMPLATE. vLLM's
218-
# --chat-template falls back to inline jinja for non-path values containing
219-
# '{', '}', or newline; the {{ '\n' }} expressions render to real newlines
220-
# at request time, reproducing the original template byte-for-byte.
221-
- name: "qwen3-reranker-4b"
222-
s3_model: "qwen3-reranker-4b.tar.gz"
223-
instance_type: "ml.g6.xlarge"
224-
required_image_pattern: "amzn2023"
225-
chat_template_file: "test/vllm/scripts/amzn2023/qwen3_reranker_chat_template.jinja"
226-
env:
227-
SM_VLLM_MODEL: "/opt/ml/model"
228-
SM_VLLM_RUNNER: "pooling"
229-
# Plain JSON, no literal-single-quote wrapping. standard-supervisor (>=0.1.15,
230-
# pinned in docker/vllm/Dockerfile.amzn2023) shlex.quote()s each argv element
231-
# before the supervisord round-trip, so the inner double quotes survive intact
232-
# and json.loads succeeds. Wrapping in single quotes here would pass them
233-
# through verbatim and break json.loads on the leading quote.
234-
SM_VLLM_HF_OVERRIDES: '{"architectures":["Qwen3ForSequenceClassification"],"classifier_from_token":["no","yes"],"is_original_qwen3_reranker":true}'
235-
SM_VLLM_DTYPE: "bfloat16"
236-
SM_VLLM_MAX_MODEL_LEN: "10000"
237-
SM_VLLM_GPU_MEMORY_UTILIZATION: "0.85"
238-
test_cases:
239-
- name: "rerank-basic"
240-
route: "/v1/rerank"
241-
content_type: "application/json"
242-
request:
243-
body:
244-
model: "/opt/ml/model"
245-
query: "What is the capital of France?"
246-
documents:
247-
- "Paris is the capital of France."
248-
- "Berlin is the capital of Germany."
249-
- "The Eiffel Tower is in Paris."
250-
- "Tokyo is the capital of Japan."
251-
validate: "json_field:results.0.relevance_score"
252-
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# vLLM SageMaker Endpoint Test Configuration
2+
#
3+
# Config-driven real SageMaker endpoint tests. Each entry deploys a model
4+
# from S3, sends requests to the configured route, and validates the response.
5+
#
6+
# Test script: test/vllm/sagemaker/amzn2023/test_sm_model_serving.py
7+
#
8+
# Smoke-test entries live in the sibling vllm-model-tests.yml.
9+
10+
s3_prefix: "s3://dlc-cicd-models/llm-models"
11+
test_fixtures_prefix: "s3://dlc-cicd-models/test-fixtures"
12+
13+
sagemaker:
14+
# MiniCPM5-1B — dense 1B LlamaForCausalLM, bf16. Standard chat model served via
15+
# the OpenAI /v1/chat/completions route. Validated offline on
16+
# server-sagemaker-cuda-v2 with SM_VLLM_MAX_MODEL_LEN=8192. Hybrid reasoning
17+
# emits a <think> block, so message.content is non-empty; json_field validation
18+
# (non-empty check) mirrors the other entries' validate rule.
19+
- name: "minicpm5-1b"
20+
s3_model: "minicpm5-1b.tar.gz"
21+
instance_type: "ml.g6.xlarge"
22+
required_image_pattern:
23+
os_version: "amzn2023"
24+
env:
25+
SM_VLLM_MODEL: "/opt/ml/model"
26+
SM_VLLM_MAX_MODEL_LEN: "8192"
27+
SM_VLLM_DTYPE: "bfloat16"
28+
test_cases:
29+
- name: "chat-completion"
30+
route: "/v1/chat/completions"
31+
content_type: "application/json"
32+
request:
33+
body:
34+
model: "/opt/ml/model"
35+
messages:
36+
- role: "user"
37+
content: "What is the capital of France?"
38+
max_tokens: 256
39+
temperature: 0
40+
validate: "json_field:choices.0.message.content"
41+
42+
- name: "voxtral-mini-4b"
43+
s3_model: "voxtral-mini-4b.tar.gz"
44+
instance_type: "ml.g6.xlarge"
45+
required_image_pattern:
46+
os_version: "amzn2023"
47+
env:
48+
SM_VLLM_MODEL: "/opt/ml/model"
49+
SM_VLLM_TOKENIZER_MODE: "mistral"
50+
SM_VLLM_CONFIG_FORMAT: "mistral"
51+
SM_VLLM_LOAD_FORMAT: "mistral"
52+
SM_VLLM_ENFORCE_EAGER: "true"
53+
SM_VLLM_MAX_MODEL_LEN: "8192"
54+
test_cases:
55+
- name: "transcription-en"
56+
route: "/v1/audio/transcriptions"
57+
content_type: "multipart/form-data"
58+
request:
59+
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
60+
file_field: "file"
61+
file_name: "test.wav"
62+
file_content_type: "audio/wav"
63+
fields:
64+
model: "/opt/ml/model"
65+
validate: "json_field:text"
66+
67+
- name: "granite-speech-4-1-2b"
68+
s3_model: "granite-speech-4.1-2b.tar.gz"
69+
instance_type: "ml.g6.xlarge"
70+
required_image_pattern:
71+
os_version: "amzn2023"
72+
env:
73+
SM_VLLM_MODEL: "/opt/ml/model"
74+
SM_VLLM_MAX_MODEL_LEN: "2048"
75+
test_cases:
76+
- name: "transcription-en"
77+
route: "/v1/audio/transcriptions"
78+
content_type: "multipart/form-data"
79+
request:
80+
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
81+
file_field: "file"
82+
file_name: "test.wav"
83+
file_content_type: "audio/wav"
84+
fields:
85+
model: "/opt/ml/model"
86+
validate: "json_field:text"
87+
88+
- name: "voxtral-mini-4b-network-isolated"
89+
s3_model: "voxtral-mini-4b-isolated.tar.gz"
90+
instance_type: "ml.g6.xlarge"
91+
required_image_pattern:
92+
os_version: "amzn2023"
93+
network_isolation: true
94+
env:
95+
SM_VLLM_MODEL: "/opt/ml/model"
96+
SM_VLLM_TOKENIZER_MODE: "mistral"
97+
SM_VLLM_CONFIG_FORMAT: "mistral"
98+
SM_VLLM_LOAD_FORMAT: "mistral"
99+
SM_VLLM_ENFORCE_EAGER: "true"
100+
SM_VLLM_MAX_MODEL_LEN: "8192"
101+
STANDARD_PIP_ARGS: "--no-index --find-links /opt/ml/model/requirements -r /opt/ml/model/requirements.txt"
102+
test_cases:
103+
- name: "transcription-en-isolated"
104+
route: "/v1/audio/transcriptions"
105+
content_type: "multipart/form-data"
106+
request:
107+
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
108+
file_field: "file"
109+
file_name: "test.wav"
110+
file_content_type: "audio/wav"
111+
fields:
112+
model: "/opt/ml/model"
113+
validate: "json_field:text"
114+
115+
- name: "granite-speech-4-1-2b-network-isolated"
116+
s3_model: "granite-speech-4.1-2b-isolated.tar.gz"
117+
instance_type: "ml.g6.xlarge"
118+
required_image_pattern:
119+
os_version: "amzn2023"
120+
network_isolation: true
121+
env:
122+
SM_VLLM_MODEL: "/opt/ml/model"
123+
SM_VLLM_MAX_MODEL_LEN: "2048"
124+
STANDARD_PIP_ARGS: "--no-index --find-links /opt/ml/model/requirements -r /opt/ml/model/requirements.txt"
125+
test_cases:
126+
- name: "transcription-en-isolated"
127+
route: "/v1/audio/transcriptions"
128+
content_type: "multipart/form-data"
129+
request:
130+
file_s3: "s3://dlc-cicd-models/test-fixtures/audio/asr_en.wav"
131+
file_field: "file"
132+
file_name: "test.wav"
133+
file_content_type: "audio/wav"
134+
fields:
135+
model: "/opt/ml/model"
136+
validate: "json_field:text"
137+
138+
# Qwen3-Reranker-4B — Path C: load as Qwen3ForSequenceClassification so the
139+
# Cohere-compatible /v1/rerank endpoint registers. SM_VLLM_HF_OVERRIDES is
140+
# passed verbatim to vLLM as --hf-overrides. The chat template is read from
141+
# test/vllm/scripts/amzn2023/qwen3_reranker_chat_template.jinja (shared with
142+
# the EC2 reranker scripts), flattened to single-line with {{ '\n' }} escapes
143+
# at test runtime, and injected as SM_VLLM_CHAT_TEMPLATE. vLLM's
144+
# --chat-template falls back to inline jinja for non-path values containing
145+
# '{', '}', or newline; the {{ '\n' }} expressions render to real newlines
146+
# at request time, reproducing the original template byte-for-byte.
147+
- name: "qwen3-reranker-4b"
148+
s3_model: "qwen3-reranker-4b.tar.gz"
149+
instance_type: "ml.g6.xlarge"
150+
required_image_pattern:
151+
os_version: "amzn2023"
152+
chat_template_file: "test/vllm/scripts/amzn2023/qwen3_reranker_chat_template.jinja"
153+
env:
154+
SM_VLLM_MODEL: "/opt/ml/model"
155+
SM_VLLM_RUNNER: "pooling"
156+
# Plain JSON, no literal-single-quote wrapping. standard-supervisor (>=0.1.15,
157+
# pinned in docker/vllm/Dockerfile.amzn2023) shlex.quote()s each argv element
158+
# before the supervisord round-trip, so the inner double quotes survive intact
159+
# and json.loads succeeds. Wrapping in single quotes here would pass them
160+
# through verbatim and break json.loads on the leading quote.
161+
SM_VLLM_HF_OVERRIDES: '{"architectures":["Qwen3ForSequenceClassification"],"classifier_from_token":["no","yes"],"is_original_qwen3_reranker":true}'
162+
SM_VLLM_DTYPE: "bfloat16"
163+
SM_VLLM_MAX_MODEL_LEN: "10000"
164+
SM_VLLM_GPU_MEMORY_UTILIZATION: "0.85"
165+
test_cases:
166+
- name: "rerank-basic"
167+
route: "/v1/rerank"
168+
content_type: "application/json"
169+
request:
170+
body:
171+
model: "/opt/ml/model"
172+
query: "What is the capital of France?"
173+
documents:
174+
- "Paris is the capital of France."
175+
- "Berlin is the capital of Germany."
176+
- "The Eiffel Tower is in Paris."
177+
- "Tokyo is the capital of Japan."
178+
validate: "json_field:results.0.relevance_score"

0 commit comments

Comments
 (0)