@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 6ece16c4fe
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 01:00 UTC

[Misc] Add dummy maverick test (#21199)

Signed-off-by: Ming Yang <minos.future@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

Ming Yang committed Jul 21, 2025 at 09:08 UTC 6ece16c4fe8c6f8f49b66c95cd3dd06b1c75de35
1 file changed +649
tests/models/multimodal/generation/test_maverick.py new
+649
@@ -0,0 +1,649 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""
4 +Create a reduced-layer version of the Maverick model for testing purposes.
5 +
6 +This script creates a new model with fewer layers by:
7 +1. Loading the original Maverick model configuration
8 +2. Creating a reduced configuration
9 +3. Generating compatible safetensors files with appropriate weights
10 +4. Creating the necessary index files for vLLM compatibility
11 +"""
12 +
13 +import json
14 +import shutil
15 +from pathlib import Path
16 +from typing import Any
17 +
18 +import pytest
19 +import torch
20 +from safetensors.torch import save_file
21 +from transformers import (AutoConfig, AutoProcessor, AutoTokenizer,
22 + GenerationConfig)
23 +
24 +from vllm import LLM, SamplingParams
25 +
26 +# Sample prompts for testing
27 +PROMPTS: list[str] = [
28 + "Hello, my name is",
29 + "The president of the United States is",
30 + "The capital of France is",
31 + "The future of AI is",
32 +]
33 +
34 +
35 +def run_maverick_serving(model: str):
36 + """Test Llama-4-Maverick model with vLLM LLM class using CLI equivalent
37 + options with reduced layers.
38 + """
39 +
40 + try:
41 + sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
42 +
43 + llm = LLM(
44 + model=model,
45 + max_model_len=2048,
46 + enforce_eager=True,
47 + tensor_parallel_size=8,
48 + enable_expert_parallel=True,
49 + trust_remote_code=True,
50 + gpu_memory_utilization=0.4,
51 + kv_cache_dtype="fp8",
52 + )
53 +
54 + outputs = llm.generate(PROMPTS, sampling_params)
55 +
56 + # Print the outputs
57 + print("\nGenerated Outputs:\n" + "-" * 60)
58 + for output in outputs:
59 + prompt = output.prompt
60 + generated_text = output.outputs[0].text
61 + print(f"Prompt: {prompt!r}")
62 + print(f"Output: {generated_text!r}")
63 + print("-" * 60)
64 +
65 + except Exception as e:
66 + print(f"Error initializing or running model: {e}")
67 + raise
68 +
69 +
70 +def create_reduced_maverick_model(
71 + original_model_name:
72 + str = "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
73 + output_dir: str = "/tmp/reduced_maverick",
74 + text_layers: int = 4,
75 + num_experts: int = 4,
76 + vision_layers: int = 2,
77 + force_recreate: bool = False,
78 +) -> str:
79 + """
80 + Create a reduced-layer version of the Maverick model.
81 +
82 + Args:
83 + original_model_name: Name of the original Maverick model
84 + output_dir: Directory to save the reduced model
85 + text_layers: Number of text transformer layers
86 + num_experts: Number of experts per layer
87 + vision_layers: Number of vision transformer layers
88 + force_recreate: Whether to recreate if output_dir already exists
89 +
90 + Returns:
91 + Path to the created reduced model directory
92 + """
93 +
94 + print(
95 + f"Creating reduced Maverick model with {text_layers} text layers and "
96 + f"{vision_layers} vision layers...")
97 +
98 + # Create output directory
99 + output_path = Path(output_dir)
100 + if output_path.exists():
101 + if force_recreate:
102 + shutil.rmtree(output_path)
103 + else:
104 + print(f"Output directory {output_dir} already exists. "
105 + "Use --force-recreate to overwrite.")
106 + return str(output_path)
107 +
108 + output_path.mkdir(parents=True, exist_ok=True)
109 +
110 + try:
111 + print("Loading original model configuration...")
112 + original_config = AutoConfig.from_pretrained(original_model_name,
113 + trust_remote_code=True)
114 +
115 + print("Creating reduced configuration...")
116 + reduced_config = create_reduced_config(original_config, text_layers,
117 + num_experts, vision_layers)
118 +
119 + config_path = output_path / "config.json"
120 + with open(config_path, "w") as f:
121 + json.dump(reduced_config, f, indent=2)
122 + print(f"Saved reduced config to {config_path}")
123 +
124 + print("Copying tokenizer files...")
125 + copy_tokenizer_files(original_model_name, output_path)
126 +
127 + print("Creating reduced safetensors files...")
128 + create_reduced_safetensors(original_config, reduced_config,
129 + output_path)
130 +
131 + print("Creating preprocessor config...")
132 + create_preprocessor_config(original_config, output_path)
133 +
134 + try:
135 + gen_config = GenerationConfig.from_pretrained(original_model_name)
136 + gen_config.save_pretrained(output_path)
137 + print("Copied generation config")
138 + except Exception as e:
139 + print(f"Could not copy generation config: {e}")
140 +
141 + print(f"Successfully created reduced Maverick model at {output_path}")
142 + return str(output_path)
143 +
144 + except Exception as e:
145 + print(f"Error creating reduced model: {e}")
146 + # Clean up on failure
147 + if output_path.exists():
148 + shutil.rmtree(output_path)
149 + raise
150 +
151 +
152 +def create_reduced_config(original_config: Any, text_layers: int,
153 + num_experts: int,
154 + vision_layers: int) -> dict[str, Any]:
155 + """Create a reduced configuration based on the original."""
156 +
157 + # Convert config to dictionary
158 + config_dict = original_config.to_dict()
159 +
160 + # Reduce text layers
161 + if "text_config" in config_dict:
162 + original_text_layers = config_dict["text_config"]["num_hidden_layers"]
163 + config_dict["text_config"]["num_hidden_layers"] = text_layers
164 + print(
165 + f"Reduced text layers from {original_text_layers} to {text_layers}"
166 + )
167 +
168 + original_num_experts = config_dict["text_config"]["num_local_experts"]
169 + config_dict["text_config"]["num_local_experts"] = num_experts
170 + print(
171 + f"Reduced num experts from {original_num_experts} to {num_experts}"
172 + )
173 +
174 + hidden_dim_divisor = 4
175 +
176 + original_hidden_size = config_dict["text_config"]["hidden_size"]
177 + new_hidden_size = original_hidden_size // hidden_dim_divisor
178 + config_dict["text_config"]["hidden_size"] = new_hidden_size
179 + print(f"Reduced hidden size from {original_hidden_size} to "
180 + f"{new_hidden_size}")
181 +
182 + original_head_dim = config_dict["text_config"]["head_dim"]
183 + new_head_dim = original_head_dim // hidden_dim_divisor
184 + config_dict["text_config"]["head_dim"] = new_head_dim
185 + print(f"Reduced head dim from {original_head_dim} to {new_head_dim}")
186 +
187 + # Reduce vision layers
188 + if "vision_config" in config_dict:
189 + original_vision_layers = config_dict["vision_config"][
190 + "num_hidden_layers"]
191 + config_dict["vision_config"]["num_hidden_layers"] = vision_layers
192 + print(f"Reduced vision layers from {original_vision_layers} "
193 + f"to {vision_layers}")
194 +
195 + # Update model name to indicate it's a reduced version
196 + config_dict["_name_or_path"] = (
197 + f"reduced_maverick_{text_layers}t_{vision_layers}v")
198 +
199 + return config_dict
200 +
201 +
202 +def copy_tokenizer_files(original_model_name: str, output_path: Path) -> None:
203 + """Copy tokenizer files from the original model."""
204 +
205 + try:
206 + tokenizer = AutoTokenizer.from_pretrained(original_model_name,
207 + trust_remote_code=True)
208 + tokenizer.save_pretrained(output_path)
209 + print("Tokenizer files copied successfully")
210 + except Exception as e:
211 + print(f"Warning: Could not copy tokenizer files: {e}")
212 +
213 +
214 +def create_preprocessor_config(original_config: Any,
215 + output_path: Path) -> None:
216 + """Create preprocessor_config.json for multimodal model."""
217 +
218 + # Try to load the original preprocessor config
219 + try:
220 + processor = AutoProcessor.from_pretrained(
221 + original_config._name_or_path
222 + or "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
223 + trust_remote_code=True,
224 + )
225 + processor.save_pretrained(output_path)
226 + print("Copied original preprocessor config")
227 + return
228 + except Exception as e:
229 + print(f"Could not copy original preprocessor config: {e}")
230 + raise
231 +
232 +
233 +def create_reduced_safetensors(original_config: Any, reduced_config: dict[str,
234 + Any],
235 + output_path: Path) -> None:
236 + """Create safetensors files with weights for the reduced model."""
237 +
238 + print("Generating synthetic weights for reduced model...")
239 +
240 + text_config = reduced_config["text_config"]
241 + vision_config = reduced_config["vision_config"]
242 +
243 + weights = {}
244 +
245 + print("Creating text model weights...")
246 + weights.update(create_text_model_weights(text_config))
247 +
248 + print("Creating vision model weights...")
249 + weights.update(create_vision_model_weights(vision_config))
250 +
251 + print("Creating shared model weights...")
252 + weights.update(create_shared_weights(text_config, vision_config))
253 +
254 + print("Saving weights to safetensors files...")
255 + save_weights_to_safetensors(weights, output_path)
256 +
257 +
258 +def create_text_model_weights(
259 + text_config: dict[str, Any]) -> dict[str, torch.Tensor]:
260 + """Create synthetic weights for the text model with MoE structure."""
261 +
262 + weights = {}
263 +
264 + vocab_size = text_config["vocab_size"]
265 + hidden_size = text_config["hidden_size"]
266 + intermediate_size = text_config["intermediate_size"]
267 + intermediate_size_mlp = text_config["intermediate_size_mlp"]
268 + num_layers = text_config["num_hidden_layers"]
269 + num_attention_heads = text_config["num_attention_heads"]
270 + num_key_value_heads = text_config.get("num_key_value_heads",
271 + num_attention_heads)
272 +
273 + # MoE specific parameters
274 + num_experts = text_config.get("num_local_experts")
275 + assert (num_experts
276 + is not None), "num_local_experts must be specified for MoE"
277 +
278 + head_dim = hidden_size // num_attention_heads
279 +
280 + # Embedding layers
281 + weights["language_model.model.embed_tokens.weight"] = torch.randn(
282 + vocab_size, hidden_size, dtype=torch.float16)
283 +
284 + # Transformer layers
285 + for layer_idx in range(num_layers):
286 + layer_prefix = f"language_model.model.layers.{layer_idx}"
287 + print(f"Creating weights for layer {layer_prefix}...")
288 +
289 + # Self-attention weights (separate q, k, v projections)
290 + weights[f"{layer_prefix}.self_attn.q_proj.weight"] = torch.randn(
291 + hidden_size, num_attention_heads * head_dim, dtype=torch.bfloat16)
292 + weights[f"{layer_prefix}.self_attn.k_proj.weight"] = torch.randn(
293 + hidden_size, num_key_value_heads * head_dim, dtype=torch.bfloat16)
294 + weights[f"{layer_prefix}.self_attn.v_proj.weight"] = torch.randn(
295 + num_key_value_heads * head_dim, hidden_size, dtype=torch.bfloat16)
296 + weights[f"{layer_prefix}.self_attn.o_proj.weight"] = torch.randn(
297 + hidden_size, num_attention_heads * head_dim, dtype=torch.bfloat16)
298 + print("Self-attention weights created.")
299 +
300 + # Feed-forward weights - MoE pattern based on interleave_moe_layer_step
301 + # For interleave_moe_layer_step=2: layers 1,3,5,... are MoE, layers
302 + # 0,2,4,... are dense
303 + interleave_step = text_config.get("interleave_moe_layer_step", 1)
304 + is_moe_layer = (interleave_step > 0
305 + and (layer_idx + 1) % interleave_step == 0)
306 +
307 + if is_moe_layer:
308 + # MoE layer structure
309 + # 1. Router weights
310 + weights[
311 + f"{layer_prefix}.feed_forward.router.weight"] = torch.randn(
312 + num_experts, hidden_size, dtype=torch.float16)
313 +
314 + # 2. Individual expert weights (not fused)
315 + for expert_idx in range(num_experts):
316 + expert_prefix = (
317 + f"{layer_prefix}.feed_forward.experts.{expert_idx}")
318 +
319 + weights[f"{expert_prefix}.gate_proj.weight"] = torch.randn(
320 + intermediate_size, hidden_size, dtype=torch.bfloat16)
321 + weights[f"{expert_prefix}.up_proj.weight"] = torch.randn(
322 + intermediate_size, hidden_size, dtype=torch.bfloat16)
323 + weights[f"{expert_prefix}.down_proj.weight"] = torch.randn(
324 + hidden_size, intermediate_size, dtype=torch.bfloat16)
325 +
326 + # Expert weight scales (FP8 quantization)
327 + weights[
328 + f"{expert_prefix}.gate_proj.weight_scale"] = torch.ones(
329 + intermediate_size, 1, dtype=torch.bfloat16)
330 + weights[f"{expert_prefix}.up_proj.weight_scale"] = torch.ones(
331 + intermediate_size, 1, dtype=torch.bfloat16)
332 + weights[
333 + f"{expert_prefix}.down_proj.weight_scale"] = torch.ones(
334 + hidden_size, 1, dtype=torch.bfloat16)
335 +
336 + # 3. Shared expert weights
337 + shared_expert_prefix = f"{layer_prefix}.feed_forward.shared_expert"
338 + weights[f"{shared_expert_prefix}.gate_proj.weight"] = torch.randn(
339 + intermediate_size, hidden_size, dtype=torch.bfloat16)
340 + weights[f"{shared_expert_prefix}.up_proj.weight"] = torch.randn(
341 + intermediate_size, hidden_size, dtype=torch.bfloat16)
342 + weights[f"{shared_expert_prefix}.down_proj.weight"] = torch.randn(
343 + hidden_size, intermediate_size, dtype=torch.bfloat16)
344 + print(f"MoE feed-forward weights created for layer {layer_idx}.")
345 + else:
346 + # Dense layer structure
347 + weights[f"{layer_prefix}.feed_forward.gate_proj.weight"] = (
348 + torch.randn(intermediate_size_mlp,
349 + hidden_size,
350 + dtype=torch.bfloat16))
351 + weights[f"{layer_prefix}.feed_forward.up_proj.weight"] = (
352 + torch.randn(intermediate_size_mlp,
353 + hidden_size,
354 + dtype=torch.bfloat16))
355 + weights[f"{layer_prefix}.feed_forward.down_proj.weight"] = (
356 + torch.randn(hidden_size,
357 + intermediate_size_mlp,
358 + dtype=torch.bfloat16))
359 + print(f"Dense feed-forward weights created for layer {layer_idx}.")
360 +
361 + # Layer norms
362 + weights[f"{layer_prefix}.input_layernorm.weight"] = torch.ones(
363 + hidden_size, dtype=torch.bfloat16)
364 + weights[
365 + f"{layer_prefix}.post_attention_layernorm.weight"] = torch.ones(
366 + hidden_size, dtype=torch.bfloat16)
367 + print("Layer norms created.")
368 +
369 + # Final layer norm and output projection
370 + weights["language_model.model.norm.weight"] = torch.ones(
371 + hidden_size, dtype=torch.bfloat16)
372 + weights["language_model.lm_head.weight"] = torch.randn(
373 + vocab_size, hidden_size, dtype=torch.bfloat16)
374 +
375 + return weights
376 +
377 +
378 +def create_vision_model_weights(
379 + vision_config: dict[str, Any]) -> dict[str, torch.Tensor]:
380 + """Create synthetic weights for the vision model."""
381 +
382 + weights = {}
383 +
384 + hidden_size = vision_config["hidden_size"]
385 + intermediate_size = vision_config["intermediate_size"]
386 + num_layers = vision_config["num_hidden_layers"]
387 +
388 + # Vision transformer layers
389 + for layer_idx in range(num_layers):
390 + layer_prefix = f"vision_model.model.layers.{layer_idx}"
391 +
392 + weights[f"{layer_prefix}.self_attn.q_proj.weight"] = torch.randn(
393 + hidden_size, hidden_size, dtype=torch.bfloat16)
394 + weights[f"{layer_prefix}.self_attn.q_proj.bias"] = torch.zeros(
395 + hidden_size, dtype=torch.bfloat16)
396 + weights[f"{layer_prefix}.self_attn.k_proj.weight"] = torch.randn(
397 + hidden_size, hidden_size, dtype=torch.bfloat16)
398 + weights[f"{layer_prefix}.self_attn.k_proj.bias"] = torch.zeros(
399 + hidden_size, dtype=torch.bfloat16)
400 + weights[f"{layer_prefix}.self_attn.v_proj.weight"] = torch.randn(
401 + hidden_size, hidden_size, dtype=torch.bfloat16)
402 + weights[f"{layer_prefix}.self_attn.v_proj.bias"] = torch.zeros(
403 + hidden_size, dtype=torch.bfloat16)
404 + weights[f"{layer_prefix}.self_attn.o_proj.weight"] = torch.randn(
405 + hidden_size, hidden_size, dtype=torch.bfloat16)
406 + weights[f"{layer_prefix}.self_attn.o_proj.bias"] = torch.zeros(
407 + hidden_size, dtype=torch.bfloat16)
408 +
409 + weights[f"{layer_prefix}.mlp.fc1.weight"] = torch.randn(
410 + intermediate_size, hidden_size, dtype=torch.bfloat16)
411 + weights[f"{layer_prefix}.mlp.fc1.bias"] = torch.zeros(
412 + intermediate_size, dtype=torch.bfloat16)
413 + weights[f"{layer_prefix}.mlp.fc2.weight"] = torch.randn(
414 + hidden_size, intermediate_size, dtype=torch.bfloat16)
415 + weights[f"{layer_prefix}.mlp.fc2.bias"] = torch.zeros(
416 + hidden_size, dtype=torch.bfloat16)
417 +
418 + weights[f"{layer_prefix}.input_layernorm.weight"] = torch.ones(
419 + hidden_size, dtype=torch.bfloat16)
420 + weights[f"{layer_prefix}.input_layernorm.bias"] = torch.zeros(
421 + hidden_size, dtype=torch.bfloat16)
422 + weights[
423 + f"{layer_prefix}.post_attention_layernorm.weight"] = torch.ones(
424 + hidden_size, dtype=torch.bfloat16)
425 + weights[f"{layer_prefix}.post_attention_layernorm.bias"] = torch.zeros(
426 + hidden_size, dtype=torch.bfloat16)
427 +
428 + return weights
429 +
430 +
431 +def create_shared_weights(
432 + text_config: dict[str, Any],
433 + vision_config: dict[str, Any]) -> dict[str, torch.Tensor]:
434 + """Create weights for shared components (vision-language connector)"""
435 +
436 + weights = {}
437 +
438 + text_hidden_size = text_config["hidden_size"]
439 + projector_input_dim = vision_config["projector_input_dim"]
440 +
441 + # Vision-language connector (projects vision features to text space)
442 + weights["multi_modal_projector.linear_1.weight"] = torch.randn(
443 + text_hidden_size, projector_input_dim, dtype=torch.bfloat16)
444 +
445 + return weights
446 +
447 +
448 +def save_weights_to_safetensors(weights: dict[str, torch.Tensor],
449 + output_path: Path) -> None:
450 + """Save weights to safetensors files and create index."""
451 +
452 + # Determine how to shard the weights
453 + max_shard_size = 5 * 1024 * 1024 * 1024 # 5GB per shard
454 +
455 + # Calculate sizes and create shards
456 + shards = []
457 + current_shard: dict[str, torch.Tensor] = {}
458 + current_size = 0
459 +
460 + for name, tensor in weights.items():
461 + tensor_size = tensor.numel() * tensor.element_size()
462 +
463 + if current_size + tensor_size > max_shard_size and current_shard:
464 + shards.append(current_shard)
465 + current_shard = {}
466 + current_size = 0
467 +
468 + current_shard[name] = tensor
469 + current_size += tensor_size
470 +
471 + if current_shard:
472 + shards.append(current_shard)
473 +
474 + # Save shards and create index
475 + weight_map = {}
476 +
477 + if len(shards) == 1:
478 + # Single file
479 + filename = "model.safetensors"
480 + save_file(shards[0], output_path / filename)
481 + weight_map = {name: filename for name in shards[0]}
482 + print(f"Saved weights to single file: {filename}")
483 + else:
484 + # Multiple shards
485 + for i, shard in enumerate(shards):
486 + filename = f"model-{i+1:05d}-of-{len(shards):05d}.safetensors"
487 + save_file(shard, output_path / filename)
488 + for name in shard:
489 + weight_map[name] = filename
490 + print(f"Saved shard {i+1}/{len(shards)}: {filename}")
491 +
492 + # Create index file
493 + index_data = {
494 + "metadata": {
495 + "total_size":
496 + sum(tensor.numel() * tensor.element_size()
497 + for tensor in weights.values())
498 + },
499 + "weight_map": weight_map,
500 + }
501 +
502 + index_path = output_path / "model.safetensors.index.json"
503 + with open(index_path, "w") as f:
504 + json.dump(index_data, f, indent=2)
505 +
506 + print(f"Created index file: {index_path}")
507 + print(f"Total model size: "
508 + f"{index_data['metadata']['total_size'] / (1024**3):.2f} GB")
509 +
510 +
511 +def run_reduced_model(model_path: str,
512 + should_profile: bool = False,
513 + **kwargs) -> None:
514 + """Test the created reduced model with vLLM."""
515 +
516 + print(f"\nTesting reduced model at {model_path}...")
517 +
518 + llm = LLM(
519 + model=model_path,
520 + trust_remote_code=True,
521 + max_model_len=512, # Small context for testing
522 + gpu_memory_utilization=0.3, # Conservative memory usage
523 + **kwargs,
524 + )
525 +
526 + sampling_params = SamplingParams(temperature=0.8,
527 + top_p=0.95,
528 + max_tokens=50)
529 +
530 + if should_profile:
531 + llm.start_profile()
532 + outputs = llm.generate(PROMPTS, sampling_params)
533 + if should_profile:
534 + llm.stop_profile()
535 +
536 + print("Test generation successful!")
537 + for output in outputs:
538 + print(f"Prompt: {output.prompt}")
539 + print(f"Output: "
540 + f"{output.outputs[0].text}")
541 + print("-" * 40)
542 +
543 +
544 +@pytest.mark.parametrize(
545 + "original_model_name,text_layers,num_experts,vision_layers,",
546 + [("meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", 4, 4, 2)])
547 +@pytest.mark.parametrize("enforce_eager", [True, False])
548 +@pytest.mark.parametrize("tp,ep", [(2, True)])
549 +@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
550 +def test_dummy_maverick(
551 + original_model_name: str,
552 + text_layers: int,
553 + num_experts: int,
554 + vision_layers: int,
555 + enforce_eager: bool,
556 + tp: int,
557 + ep: bool,
558 + output_dir: str = "/tmp/reduced_maverick",
559 + force_recreate: bool = True,
560 + profile: bool = False,
561 +) -> None:
562 + model_path = create_reduced_maverick_model(
563 + original_model_name=original_model_name,
564 + output_dir=output_dir,
565 + text_layers=text_layers,
566 + num_experts=num_experts,
567 + vision_layers=vision_layers,
568 + force_recreate=force_recreate,
569 + )
570 +
571 + print(f"\nReduced model created successfully at: {model_path}")
572 +
573 + run_reduced_model(model_path=model_path,
574 + should_profile=profile,
575 + enforce_eager=enforce_eager,
576 + tensor_parallel_size=tp,
577 + enable_expert_parallel=ep)
578 +
579 +
580 +def main():
581 + """Main function to create and test the reduced model."""
582 +
583 + import argparse
584 +
585 + parser = argparse.ArgumentParser(
586 + description="Create a reduced-layer Maverick model")
587 + parser.add_argument(
588 + "--output-dir",
589 + default="/tmp/reduced_maverick",
590 + help="Output directory for the reduced model",
591 + )
592 + parser.add_argument(
593 + "--text-layers",
594 + type=int,
595 + default=4,
596 + help="Number of text transformer layers",
597 + )
598 + parser.add_argument("--num-experts",
599 + type=int,
600 + default=4,
601 + help="Number of experts")
602 + parser.add_argument(
603 + "--vision-layers",
604 + type=int,
605 + default=2,
606 + help="Number of vision transformer layers",
607 + )
608 + parser.add_argument(
609 + "--force-recreate",
610 + action="store_true",
611 + help="Force recreation if output directory exists",
612 + )
613 + parser.add_argument("--test",
614 + action="store_true",
615 + help="Test the created model with vLLM")
616 + parser.add_argument("--profile",
617 + action="store_true",
618 + help="Profile the created model with vLLM")
619 + parser.add_argument(
620 + "--test-original",
621 + action="store_true",
622 + help="Test the original model with vLLM",
623 + )
624 + parser.add_argument(
625 + "--original-model",
626 + default="meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
627 + help="Original model name to base the reduction on",
628 + )
629 +
630 + args = parser.parse_args()
631 +
632 + if args.test:
633 + test_dummy_maverick(original_model_name=args.original_model,
634 + output_dir=args.output_dir,
635 + text_layers=args.text_layers,
636 + num_experts=args.num_experts,
637 + vision_layers=args.vision_layers,
638 + force_recreate=args.force_recreate,
639 + tp=2,
640 + ep=True,
641 + enforce_eager=True,
642 + profile=args.profile)
643 +
644 + if args.test_original:
645 + run_maverick_serving(args.original_model)
646 +
647 +
648 +if __name__ == "__main__":
649 + exit(main())