"model_executor/models/llama.py: the Llama model"
Code version: vLLM @ 3e44c0d · 2026-10-22
Purpose#
vllm/model_executor/models/llama.py implements the Llama architecture with vLLM layers, so that the worker can build it, load a Hugging Face checkpoint into it, and run it in the forward pass of each step.
| File | vllm/model_executor/models/llama.py, 585 lines of Python. The header says it is adapted from the Transformers Llama code, v4.28.0 (vllm/model_executor/models/llama.py:4-5). |
|---|---|
| Main class | LlamaForCausalLM (vllm/model_executor/models/llama.py:464-561). |
| Runs in | Each worker (one per GPU), or the engine core process with the uni executor. Built once in load_model; forward runs on the GPU in every step, usually inside a compiled graph or a CUDA graph. |
| Called by | The model loader through initialize_model() after the registry resolves the name (vllm/model_executor/model_loader/utils.py:96-103); Model Runner V2 for forward and compute_logits (vllm/v1/worker/gpu/model_runner.py:2070, vllm/v1/worker/gpu/model_runner.py:1638); 21 other files that import it. |
| Calls | The TP layers of linear.py and vocab_parallel_embedding.py, Attention, RMSNorm, get_rope, SiluAndMul, LogitsProcessor, and the helpers make_layers, AutoWeightsLoader and WeightsMapper (vllm/model_executor/models/llama.py:35-79). |
| Not in this file | How the layers split and communicate (The tensor-parallel layers (linear.py and vocab_parallel_embedding.py)), the attention kernels (Attention backends: backend, metadata builder and implementation), compilation (torch.compile and CUDA graphs in vLLM), the registry lookup (model_executor/models/registry.py: the model registry). |
The page How a vLLM model file is written (llama.py) explains the patterns of this file. This page reads the file class by class and follows the load_weights path.
Where it sits#
Who calls it#
- The registry. Nine names map to this file, seven of them to
LlamaForCausalLM:LlamaForCausalLM,LLaMAForCausalLM,InternLM3ForCausalLM,IQuestCoderForCausalLM,TeleChat3ForCausalLM, and the embedding namesLlamaModelandMistralModel(vllm/model_executor/models/registry.py:140-155,vllm/model_executor/models/registry.py:212,vllm/model_executor/models/registry.py:231-238). Two more map to the bidirectional wrappers (vllm/model_executor/models/registry.py:230,vllm/model_executor/models/registry.py:326-329). (Counts from an evaluation of the tables.) - The loader.
initialize_model()builds the class; the default loader callsload_weights()(vllm/model_executor/model_loader/default_loader.py:503). - The model runner. Each step calls the model with
input_ids,positions,inputs_embedsandintermediate_tensors(vllm/v1/worker/gpu/model_runner.py:1984-2009), thencompute_logits()on the sampled rows. - Other model files. For example
MistralForCausalLM,Phi3ForCausalLMandTeleChat2ForCausalLMsubclassLlamaForCausalLM(vllm/model_executor/models/mistral.py:236,vllm/model_executor/models/phi3.py:10,vllm/model_executor/models/telechat2.py:107).
What it calls#
QKVParallelLinear,MergedColumnParallelLinear,RowParallelLinear(vllm/model_executor/models/llama.py:45-49),VocabParallelEmbeddingandParallelLMHead(vllm/model_executor/models/llama.py:53-56).AttentionorEncoderOnlyAttention(vllm/model_executor/models/llama.py:39-42).- CustomOps:
RMSNorm,SiluAndMul, and the rotary embedding fromget_rope(vllm/model_executor/models/llama.py:38,vllm/model_executor/models/llama.py:44,vllm/model_executor/models/llama.py:52). maybe_fused_act_quant, which can fuse the activation with the quantization of the next layer's input (vllm/model_executor/models/llama.py:43,vllm/model_executor/models/llama.py:120).- Helpers from
models/utils.pyand mixins frommodels/interfaces.py(vllm/model_executor/models/llama.py:61-79).
Key classes and functions#
| Name | Lines | Role |
|---|---|---|
LlamaMLP | vllm/model_executor/models/llama.py:82-122 | gate_up (column), SiLU × up, down (row). |
LlamaAttention | vllm/model_executor/models/llama.py:125-248 | Per-rank heads, QKV, RoPE, Attention, o_proj. |
LlamaDecoderLayer | vllm/model_executor/models/llama.py:251-334 | Reads config knobs; norm, attention, norm, MLP with a separate residual. |
LlamaModel | vllm/model_executor/models/llama.py:337-461 | Compiled inner model: embedding, layers of this PP stage, final norm, the mapper and load_weights. |
LlamaForCausalLM | vllm/model_executor/models/llama.py:464-561 | Outer class: mixins, LoRA attributes, LM head, compute_logits, load_weights. |
| bidirectional bases | vllm/model_executor/models/llama.py:564-571 | as_seq_cls_model and as_embedding_model of LlamaForCausalLM. |
LlamaBidirectionalForSequenceClassification, LlamaBidirectionalModel | vllm/model_executor/models/llama.py:574-585 | Empty subclasses; the config sets the attention and pooling types. |
Walk through the main methods#
LlamaAttention.__init__: sliding windows and the attention class#
The head arithmetic and the two linear layers are on How a vLLM model file is written (llama.py) (lines 143-181). The second half of the constructor:
self._init_rotary_emb(config, quant_config=quant_config)
sliding_window = None
if layer_types := getattr(config, "layer_types", None):
# Fix for Eagle3 compatibility:
# for draft models, subtract target layer count
# to get draft-relative layer index starting from 0
if hasattr(config, "target_layer_count"):
# This is a draft model,
# adjust layer_idx to be relative to draft layers
effective_layer_idx = layer_idx - config.target_layer_count
else:
# This is a target model, use layer_idx directly
effective_layer_idx = layer_idx
assert effective_layer_idx < len(layer_types), (
f"effective_layer_idx: {effective_layer_idx} "
f"is out of bounds for layer_types: {layer_types}"
)
is_sliding = layer_types[effective_layer_idx] == "sliding_attention"
if is_sliding:
sliding_window = config.sliding_window
attn_cls = (
EncoderOnlyAttention
if attn_type == AttentionType.ENCODER_ONLY
else Attention
)
self.attn = attn_cls(
self.num_heads,
self.head_dim,
self.scaling,
num_kv_heads=self.num_kv_heads,
cache_config=cache_config,
quant_config=quant_config,
per_layer_sliding_window=sliding_window,
attn_type=attn_type,
prefix=f"{prefix}.attn",
)- Line 183.
_init_rotary_embcallsget_ropewith the head size,max_position_embeddingsand the config'srope_parameters, NeoX style (lines 236-248). - Lines 186-204. If the config has
layer_types, a layer markedsliding_attentiongetsconfig.sliding_window. The layer index comes from the prefix (line 141). For an EAGLE-3 draft, the index is shifted bytarget_layer_count. - Lines 206-210.
ENCODER_ONLYselectsEncoderOnlyAttention; everything else usesAttention. - Lines 212-222. The attention layer gets per-rank heads (
self.num_heads,self.num_kv_heads), the scalehead_dim ** -0.5(line 162) and its prefix.
LlamaDecoderLayer: config knobs and the residual#
config = config or vllm_config.model_config.hf_config
cache_config = vllm_config.cache_config
quant_config = self.get_quant_config(vllm_config)
self.hidden_size = config.hidden_size
max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
# Support abacusai/Smaug-72B-v0.1 with attention_bias
attention_bias = getattr(config, "attention_bias", False) or getattr(
config, "bias", False
)
bias_o_proj = attention_bias
# support internlm/internlm3-8b with qkv_bias
if hasattr(config, "qkv_bias"):
attention_bias = config.qkv_bias
# By default, Llama uses causal attention as it is a decoder-only model.
# You can override the HF config with `is_causal=False` to enable
# bidirectional attention, which is used in some embedding models
# (e.g. nvidia/llama-nemotron-embed-1b-v2)
if getattr(config, "is_causal", True):
attn_type = AttentionType.DECODER
else:
attn_type = AttentionType.ENCODER_ONLY- Line 263. The quant config comes from
get_quant_config(), which subclasses can override (lines 332-334). - Lines 268-274.
attention_biasorbiasturns on biases in QKV and o_proj;qkv_biassets the QKV bias alone. The comments name the models that need this. - Lines 280-283.
is_causal=Falsein the config makes the layer bidirectional, for embedding models.
def forward(
self,
positions: torch.Tensor,
hidden_states: torch.Tensor,
residual: torch.Tensor | None,
) -> tuple[torch.Tensor, torch.Tensor]:
# Self Attention
if residual is None:
residual = hidden_states
hidden_states = self.input_layernorm(hidden_states)
else:
hidden_states, residual = self.input_layernorm(hidden_states, residual)
hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
# Fully Connected
hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
hidden_states = self.mlp(hidden_states)
return hidden_states, residual- Lines 320-322. The first layer has no residual yet: it keeps the input as the residual and normalizes.
- Line 324. Later layers call the norm with the residual.
RMSNormadds it and normalizes in one op, and returns the new residual too. - Line 328. The same fused add-and-norm before the MLP. The layer returns
(hidden_states, residual); the final norm ofLlamaModeladds the last residual (line 452).
LlamaModel.load_weights and LlamaForCausalLM.load_weights: the load path#
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
loader = AutoWeightsLoader(self)
return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper) def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
loader = AutoWeightsLoader(self)
return loader.load_weights(weights)Follow one tensor, model.layers.0.mlp.up_proj.weight, in a TP = 2 worker:
- The default loader calls
LlamaForCausalLM.load_weights(iterator)(vllm/model_executor/model_loader/default_loader.py:503). - The outer
AutoWeightsLoaderhas no model mapper. It adds the quant config mappers, if any, and the rule that drops old rotary tables (vllm/model_executor/models/utils.py:460-473). - The walk groups by
modeland findsself.model, aLlamaModelwith its ownload_weights. It passes the rest of the name:layers.0.mlp.up_proj.weight(vllm/model_executor/models/utils.py:387-400). LlamaModel.load_weightsmakes a secondAutoWeightsLoaderwithhf_to_vllm_mapper..up_projbecomes.gate_up_projwith shard id 1 (vllm/model_executor/models/llama.py:354-355).- The walk goes
layers→0→mlp→gate_up_proj.MergedColumnParallelLinearhas aload_weights; it readsshard_id = 1from the tensor and calls the parameter's loader (vllm/model_executor/layers/linear.py:985-1000). - The v2 loader computes offset 14336 / 2 = 7168 and size 7168 in the parameter. It copies checkpoint rows
rank × 7168torank × 7168 + 7167(vllm/model_executor/layers/linear.py:965-983,vllm/model_executor/parameter.py:153-173). - The loaded name goes back up the tree with its prefixes:
model.layers.0.mlp.gate_up_proj.weight. The default loader uses the set of these names for its completeness check (vllm/model_executor/model_loader/default_loader.py:510-523).
The tensor lm_head.weight takes another path. In the outer walk, lm_head is a ParallelLMHead without load_weights, so its weight parameter uses VocabParallelEmbedding.weight_loader (vllm/model_executor/models/utils.py:416-424). If tie_word_embeddings is set, lm_head.weight is the same object as model.embed_tokens.weight (lines 509-510), and the loader skips the second name (vllm/model_executor/models/utils.py:255-259). One exception: if the config says tied but the checkpoint has its own lm_head, VllmConfig turns the flag off, so the head loads (vllm/config/vllm.py:1594-1599). After loading, vLLM ties the two again if they are equal (vllm/config/model.py:1255-1281, vllm/model_executor/model_loader/utils.py:155).
LlamaForCausalLM: class attributes#
class LlamaForCausalLM(
LocalArgmaxMixin,
nn.Module,
SupportsLoRA,
SupportsPP,
SupportsEagle,
SupportsEagle3,
SupportsQuant,
):
hf_to_vllm_mapper = LlamaModel.hf_to_vllm_mapper
# LoRA specific attributes
packed_modules_mapping = {
"qkv_proj": ["q_proj", "k_proj", "v_proj"],
"gate_up_proj": ["gate_proj", "up_proj"],
}
embedding_modules = {
"embed_tokens": "input_embeddings",
"lm_head": "output_embeddings",
}- Lines 464-472. The mixins; How a vLLM model file is written (llama.py) has a table of what each one adds.
- Line 473. The outer class shares the mapper of
LlamaModel.SupportsQuant.__new__gives its rename-only part to the quant config, so names inignored_layerscan match vLLM names (vllm/model_executor/models/interfaces.py:1292-1299). For Llama this part is empty:get_rename_mapper()drops the stacked rules, which are the only rules of this mapper (vllm/model_executor/models/utils.py:180-199). - Lines 475-478.
packed_modules_mappingtells LoRA and the quant config which checkpoint layers form each stacked layer. - Lines 479-482.
embedding_moduleslists the layers that get LoRA embedding buffers; Mistral clears it (vllm/model_executor/models/mistral.py:237-238).
Deep dive#
The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.
Deep diveData flow: types and shapes
| Value | Type, shape (per rank) | Where |
|---|---|---|
input_ids | int tensor [T], or None when inputs_embeds is given or on a later PP stage | vllm/v1/worker/gpu/model_runner.py:1981-1997 |
positions | [T] | vllm/v1/worker/gpu/model_runner.py:1986 |
dynamic dimensions for torch.compile | dimension 0 of all four inputs | vllm/model_executor/models/llama.py:340-345 |
qkv | [T, q_size + 2 × kv_size] | vllm/model_executor/models/llama.py:229-230 |
| decoder layer output | (hidden_states, residual), both [T, hidden_size] | vllm/model_executor/models/llama.py:313-330 |
LlamaModel.forward result | [T, hidden_size]; IntermediateTensors on non-last PP stages; (hidden, aux list) with EAGLE-3 | vllm/model_executor/models/llama.py:443-457 |
compute_logits result | [R, vocab_size] float logits after the TP gather | vllm/model_executor/models/llama.py:546-551 |
load_weights result | set[str] of loaded parameter names | vllm/model_executor/models/llama.py:559-561 |
T is the number of scheduled tokens of the step after padding; R is the number of rows that sample.
Deep diveInteraction with other subsystems
- Forward context. Each
Attentionregisters under its prefix, for examplemodel.layers.0.self_attn.attn(vllm/model_executor/layers/attention/attention.py:443-445). See The forward context: how layers find per-step data. - torch.compile. Only
LlamaModel.forwardis compiled (vllm/model_executor/models/llama.py:337-346); the attention call is an opaque custom op on CUDA-like platforms (vllm/model_executor/layers/attention/attention.py:436-440). - LoRA. The LoRA manager wraps the linear layers that it finds with
named_modules();packed_modules_mappingmapsqkv_projto the three LoRA targets (vllm/model_executor/models/llama.py:475-478). The names inembedding_modules(lines 479-482) also count as supported LoRA modules (vllm/lora/utils.py:251-254). - Speculative decoding. EAGLE-3 sets the layers whose hidden states
EagleModelMixincollects (lines 427-441); with PP they travel inIntermediateTensorsunderaux_hidden_states_<i>(vllm/model_executor/models/interfaces.py:1652-1661).spec_decode_needs_target_embedkeeps a real embedding on the last PP stage for some drafters (vllm/model_executor/models/utils.py:863-883). - Pooling. The bidirectional classes reuse the whole file through the adapters
as_seq_cls_modelandas_embedding_model(lines 564-571).
Deep diveWorked example: which config keys change the module tree
| Config key (Hugging Face) | Effect | Line |
|---|---|---|
num_key_value_heads (missing) | defaults to num_attention_heads (no GQA) | vllm/model_executor/models/llama.py:289-291 |
head_dim | overrides hidden_size / num_attention_heads | vllm/model_executor/models/llama.py:158-159 |
attention_bias / bias / qkv_bias | biases in QKV and o_proj | vllm/model_executor/models/llama.py:268-274 |
mlp_bias | biases in gate_up and down | vllm/model_executor/models/llama.py:305 |
hidden_act | must be silu | vllm/model_executor/models/llama.py:112-115 |
is_causal | false → EncoderOnlyAttention | vllm/model_executor/models/llama.py:280-283 |
layer_types, sliding_window | per-layer sliding window | vllm/model_executor/models/llama.py:186-204 |
tie_word_embeddings | LM head shares the embedding weight; embedding also on the last PP stage | vllm/model_executor/models/llama.py:379, vllm/model_executor/models/llama.py:509-510 |
logit_scale | multiplies the logits (default 1.0) | vllm/model_executor/models/llama.py:512-515 |
Example: internlm/internlm3-8b maps to LlamaForCausalLM (vllm/model_executor/models/registry.py:140), and the comment at line 272 names it as the reason for qkv_bias.
Deep diveError paths
| Situation | Result | Where |
|---|---|---|
hidden_act is not silu | ValueError | vllm/model_executor/models/llama.py:112-115 |
| query heads do not divide by TP | AssertionError (start-up normally stops earlier with a ValueError) | vllm/model_executor/models/llama.py:145, vllm/config/model.py:1559-1564 |
| KV heads and TP do not divide either way | AssertionError | vllm/model_executor/models/llama.py:148-155 |
layer index outside layer_types | AssertionError with both values | vllm/model_executor/models/llama.py:197-200 |
| a non-first PP stage gets no intermediate tensors | AssertionError | vllm/model_executor/models/llama.py:421 |
| a checkpoint name with no module or parameter | ValueError from the loader | vllm/model_executor/models/utils.py:442-448 |
Deep diveEnvironment variables
The file reads no environment variable. Two variables help when you study it:
| Variable | Default | Read at | Effect |
|---|---|---|---|
VLLM_LOG_MODEL_INSPECTION | 0 | vllm/envs.py:2153-2155 | Logs the built module tree after create_model (vllm/model_executor/model_loader/base_loader.py:118-125). |
VLLM_LOGGING_LEVEL=DEBUG | INFO | vllm/envs.py:860 | Shows one "Loaded weight ... with shape ..." line per parameter (vllm/model_executor/models/utils.py:343). |
Related concepts#
- How a vLLM model file is written (llama.py): the patterns of this file, with the prefix rules and PP.
- "Weight loading: from checkpoint files to GPU parameters": the loader, the mapper and the weight loaders.
- The tensor-parallel layers (linear.py and vocab_parallel_embedding.py): the layer classes that this file builds.
- Model capability protocols (SupportsPP, SupportsLoRA, ...): what the mixins mean to the registry.
- CustomOp, out-of-tree ops and IR op providers:
RMSNorm,SiluAndMuland the rotary embedding.
Check yourself#
- 1Which class of llama.py carrieseasy
@support_torch_compile?Show answer
LlamaModel(vllm/model_executor/models/llama.py:337-347).LlamaForCausalLM.forwardonly calls it. - 2Why doesmedium
LlamaForCausalLM.load_weightspass no mapper, although the class hashf_to_vllm_mapper?Show answer
The outer loader hands every
model.*tensor toLlamaModel.load_weights, which applies the mapper itself (vllm/model_executor/models/utils.py:387-400,vllm/model_executor/models/llama.py:459-461). The class attribute on the outer class is for others:SupportsQuantgives its rename part to the quant config (vllm/model_executor/models/interfaces.py:1296-1297). For Llama that part is empty, because the mapper has only stacked rules (vllm/model_executor/models/utils.py:186-196). - 3A config hasmedium
"qkv_bias": trueand noattention_bias. Which layers get a bias?Show answer
Only
qkv_proj.bias_o_projtakes the value ofattention_biasbeforeqkv_biasoverridesattention_bias(vllm/model_executor/models/llama.py:268-274). - 4Followhard
model.layers.0.mlp.gate_proj.weighton TP rank 1 of a Llama-3-8B with TP = 2. Which rows of the checkpoint tensor end up where?Show answer
The mapper renames it to
layers.0.mlp.gate_up_proj.weightwith shard id 0 (vllm/model_executor/models/llama.py:354). The v2 loader uses offset 0 and size 14336 / 2 = 7168 in the parameter (vllm/model_executor/layers/linear.py:967-970). The parameter copies checkpoint rows 7168-14335 (rank 1 × 7168) into its rows 0-7167 (vllm/model_executor/parameter.py:168-173).