Hugging Face checkpoints
Code version: vLLM @ 3e44c0d · 2026-10-22
In one sentence
A model folder holds a config, a generation config, tokenizer files and weight files; each part of vLLM reads its own file.
The idea#
When you run vllm serve Qwen/Qwen3-0.6B, the name points to a folder of files on the Hugging Face Hub, or to a local folder. vLLM needs four kinds of information from it. They are the shape of the model, the default sampling values, the tokenizer with its chat template, and the weights. Each kind lives in its own files.
config.json: the shape of the model#
The Hugging Face config is a JSON object. The keys that matter most for serving:
architectures: the name of the model class, for example["Qwen3ForCausalLM"]. vLLM looks this name up in its model registry to find its own implementation.max_position_embeddings: the longest context that the model was trained for. It becomes the defaultmax_model_len.torch_dtype(in newer filesdtype): the dtype of the weights, for examplebfloat16. It becomes the default--dtype.quantization_config: only in quantized checkpoints (Quantized checkpoints).rope_scalingorrope_parameters: RoPE settings for long context. Some RoPE types make the usable context longer thanmax_position_embeddings.- Sizes:
hidden_size,num_hidden_layers,num_attention_heads,num_key_value_heads,vocab_size. They give the shapes of all weights and of the KV cache.
generation_config.json: default sampling values#
The generation config holds the values that the model authors suggest for sampling, for example temperature, top_p and top_k, and the end-of-sequence token ids. By default, vLLM uses these values when a request does not set them. So two models can sample differently with the same request.
Tokenizer files and the chat template#
The tokenizer turns text into token ids (Tokens). Its files are tokenizer.json (the vocabulary and merge rules) and tokenizer_config.json (special tokens and settings). The chat template turns a list of chat messages into one prompt. It is a Jinja template, in the key chat_template of tokenizer_config.json, or in its own file chat_template.jinja in newer repositories. Transformers reads both places (general knowledge about Transformers, not vLLM code).
Weights: safetensors files#
The weights are in safetensors files. A file starts with a JSON header that lists each tensor: name, dtype, shape and byte offsets. The raw bytes follow. Loading a safetensors file runs no code, unlike a pickled .bin file. A small model has one model.safetensors. A large model has shards such as model-00001-of-00004.safetensors, and model.safetensors.index.json maps each tensor name to its shard.
Where the files come from#
- A Hub name such as
Qwen/Qwen3-0.6B. vLLM downloads the files with thehuggingface_hublibrary into the Hugging Face cache. By default, this library uses~/.cache/huggingface/hub.HF_HOMEmoves it to$HF_HOME/hub, andHF_HUB_CACHEsets it directly (general knowledge abouthuggingface_hub). A second start finds the files there. - A local path such as
/models/qwen3. vLLM reads the folder directly and downloads nothing. --revisionselects a branch, a tag or a commit of the Hub repository. Pin a commit in production, so a new upload does not change your server.
An everyday comparison#
Think of a flat-pack furniture box. config.json is the label with the model name and the sizes. generation_config.json is the "recommended use" sheet. The tokenizer files are the dictionary for the instruction language. The safetensors files are the parts, in numbered bags, and the index is the parts list. vLLM is the worker who reads the label first, picks the right assembly plan (the model class), and then puts each part in its place.
The comparison stops here: vLLM does not use the assembly plan in the box (the Python code of the repository) by default. It uses its own plan for the architecture name. Only with --trust-remote-code can code from the repository run.
The picture#
Read panel 1 row by row, from the file on the left to what vLLM gets on the right. Panel 2 follows only the first row further: from the name on the command line to the class that the workers build.
In vLLM#
This is a basics page, so this part gives only pointers. The pages How a vLLM model file is written (llama.py), "Weight loading: from checkpoint files to GPU parameters" and Renderers and chat templates: from messages to token ids explain the code in full.
- The default model.
ModelConfig.modeldefaults to"Qwen/Qwen3-0.6B"(vllm/config/model.py:155), andEngineArgscopies it (vllm/engine/arg_utils.py:486).vllm serve <name>puts the positional name intoargs.model(vllm/entrypoints/cli/serve.py:56-57). - Name → config → class.
ModelConfig.__post_init__()callsget_config()(vllm/config/model.py:680-690). It then asks the registry for the architecture:registry.inspect_model_cls(architectures, self)(vllm/config/model.py:739-744). On the workers,resolve_model_cls()imports the class (vllm/model_executor/model_loader/utils.py:310-313). - The registry entry.
"Qwen3ForCausalLM": ("qwen3", "Qwen3ForCausalLM")maps the name to a module and a class (vllm/model_executor/models/registry.py:199).
hf_config = get_config(
self.hf_config_path or self.model,
self.trust_remote_code,
self._hf_config_revision or self.revision,
self.code_revision,
self.config_format,
hf_overrides_kw=hf_overrides_kw,
hf_overrides_fn=hf_overrides_fn,
token=self.hf_token,
)
self.hf_config = hf_configModelConfig loads config.json with get_config(): from --hf-config-path or the model, with --trust-remote-code and the revision.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 diveconfig.json: how vLLM reads it
- Find the format. With
--config-format auto,get_config()checks for a Mistral repository withparams.jsonfirst, then forconfig.json. With neither, it raisesValueErrorwith a list of what to check (vllm/transformers_utils/config.py:883-917). - Parse. A config parser builds a Transformers
PretrainedConfig. It passestrust_remote_code,revision,code_revisionand--hf-overrides(vllm/transformers_utils/config.py:919-931). - No
architectures? vLLM fills it frommodel_typeif Transformers knows the type. Otherwise it warns and expectshf_overrides={'architectures': [...]}(vllm/transformers_utils/config.py:933-943). - Quantization config. It takes
quantization_configfrom the file, or fromhf_quant_config.jsonfor old ModelOpt checkpoints (vllm/transformers_utils/config.py:945-958).
dtype. With --dtype auto (the default, vllm/config/model.py:200), vLLM reads the config dtype (vllm/transformers_utils/model_arch_config_convertor.py:208-219). If the config has none, it reads the dtypes in the safetensors headers (vllm/transformers_utils/model_arch_config_convertor.py:221-231). FP32 models get the first dtype that the platform supports, or FP16 for pooling models when the platform supports it (vllm/config/model.py:2429-2445).
max_model_len. vLLM takes the smallest of several keys: max_position_embeddings, n_positions, max_seq_len, seq_length and others (vllm/transformers_utils/model_arch_config_convertor.py:372-399). It also caps the value with model_max_length from tokenizer_config.json (vllm/config/model.py:2570-2575). Some RoPE scaling types multiply it by their factor; for llama3, yarn and others the config value already includes the scaling (vllm/config/model.py:2603-2634). With no key at all, vLLM assumes 2,048 and warns (vllm/config/model.py:2589-2596). Example: Qwen3-0.6B has max_position_embeddings 40,960 and no RoPE scaling, so its default max_model_len is 40,960. These two values come from its config.json on the Hugging Face Hub, not from the vLLM repository.
Deep divegeneration_config.json and --generation-config
--generation-config has the default "auto" (vllm/config/model.py:358-364). The three values:
| Value | Default sampling values come from |
|---|---|
auto (default) | generation_config.json of the model folder |
vllm | vLLM's neutral defaults; the file is ignored for sampling |
| a folder path | generation_config.json in that folder |
try_get_generation_config() loads the file with Transformers. If the folder has no such file, it makes a generation config from config.json (vllm/transformers_utils/config.py:1269-1295). get_diff_sampling_param() then keeps only these keys: repetition_penalty, presence_penalty, frequency_penalty, temperature, top_k, top_p, min_p, max_new_tokens and skip_special_tokens. It renames max_new_tokens to max_tokens (vllm/config/model.py:1877-1902). --override-generation-config is applied on top (vllm/config/model.py:1880).
When the file changes the defaults, vLLM logs: "Default vLLM sampling parameters have been overridden by the model's generation_config.json" (vllm/config/model.py:1906-1914). The EOS ids of the file become extra stop token ids (vllm/sampling_params.py:789-803). The page The OpenAI-compatible API shows how a request value, this default and the OpenAI default combine.
Deep diveTokenizer and chat template
The tokenizer path defaults to the model path (vllm/config/model.py:607-610). --tokenizer and --tokenizer-revision change it. cached_tokenizer_from_config() loads it with the tokenizer mode, revision and trust_remote_code of the model config (vllm/tokenizers/registry.py:298-315).
The chat template comes from the first source that has one (vllm/renderers/hf.py:264-319):
- the template that the server or the request gives (
--chat-template); - the template of the Transformers processor (
AutoProcessor), but only when the request has no tools; - the template of the tokenizer: Transformers reads it from
tokenizer_config.jsonorchat_template.jinja(Transformers behaviour); - a template that vLLM ships for some model types.
The page Renderers and chat templates: from messages to token ids explains how the renderer uses the template.
Deep diveWeights: download, cache and the index file
DefaultModelLoader._prepare_weights() finds the weight files (vllm/model_executor/model_loader/default_loader.py:147-263):
- Format. With
--load-format auto, a folder withconsolidated*.safetensorsuses the Mistral format. Otherwise vLLM tries*.safetensors, then*.bin(vllm/model_executor/model_loader/default_loader.py:170-186). - Download. For a Hub name,
download_weights_from_hf()callssnapshot_download()with the allowed patterns and--download-diras the cache directory (vllm/model_executor/model_loader/default_loader.py:213-221). A file lock stops two processes from downloading the same model at once (vllm/model_executor/model_loader/weight_utils.py:516-527). WithHF_HUB_OFFLINE=1, it uses only local files (vllm/model_executor/model_loader/weight_utils.py:466). - Filter. It keeps only files that
model.safetensors.index.jsonnames. A file that the index names but that is missing raisesFileNotFoundError(vllm/model_executor/model_loader/weight_utils.py:590-616). This matters for repositories that contain both shards and one consolidated file. - Nothing found.
RuntimeError: Cannot find any model weights(vllm/model_executor/model_loader/default_loader.py:258-261).
The cache folder. --download-dir defaults to the Hugging Face default cache (vllm/config/load.py:62-64). The cache stores each repository as models--<org>--<name>/snapshots/<commit>/ under HF_HUB_CACHE (vllm/model_executor/model_loader/weight_utils.py:903-906). With VLLM_USE_MODELSCOPE, vLLM downloads from ModelScope instead (vllm/model_executor/model_loader/weight_utils.py:161-175).
After the files are found, each worker reads the tensors and gives them to model.load_weights(). With TP, each worker keeps only its slice ("Weight loading: from checkpoint files to GPU parameters").
Deep diveNames, revisions and remote code
| Flag | Default | Effect |
|---|---|---|
--revision | none (the default branch) | Branch, tag or commit of the model (vllm/config/model.py:238-240). The tokenizer revision follows it unless --tokenizer-revision is set (vllm/config/model.py:609-610). |
--trust-remote-code | off | Allows Python code from the repository, for config and tokenizer classes (vllm/config/model.py:197-199). |
--tokenizer | the model | Another tokenizer name or path (vllm/config/model.py:170-172). |
--served-model-name | the model | The names that clients put in model. The first name is in responses and metrics (vllm/config/model.py:329-336). The API server uses all names, or [args.model] (vllm/entrypoints/launchers/api_server/app_state.py:49-52). |
--hf-overrides | {} | Changes keys of config.json at load time (vllm/config/model.py:348-350). |
Architecture lookup. inspect_model_cls() tries each name in architectures. If vLLM has no implementation for any of them and --model-impl is auto, it falls back to the Transformers implementation (vllm/model_executor/models/registry.py:1373-1400). The model loader then logs that some features may not work (vllm/model_executor/model_loader/utils.py:315-323). With no match at all, vLLM raises an error for an unsupported architecture (vllm/model_executor/models/registry.py:1402).
What can go wrong#
- "Invalid repository ID or local directory". The name is wrong, the repository is private (log in, or set
HF_TOKEN), or the folder has noconfig.json(vllm/transformers_utils/config.py:904-917). - Unexpected sampling. The model's
generation_config.jsonchanges the defaults, for example temperature 0.6 (Qwen3-0.6B on the Hub) instead of 1.0. Check the log line, or start with--generation-config vllm(vllm/config/model.py:1906-1914). - A slow model on the Transformers fallback. If vLLM has no class for the architecture, it uses the Transformers implementation and warns (
vllm/model_executor/model_loader/utils.py:315-323). Check the "Resolved architecture" log line (vllm/config/model.py:744). - A model that changes under you. Without
--revision, a new upload to the repository can change the weights or the chat template at the next start. Pin a commit. - Missing shards. An index file that names a shard that is not in the folder raises
FileNotFoundError(vllm/model_executor/model_loader/weight_utils.py:609-613). - Remote code. Some repositories need
--trust-remote-code. It runs Python code from the repository in every vLLM process. Read the code first, and pin the revision.
Why it matters. Most surprises at start-up come from the model folder: the wrong class, the wrong context length, other default sampling values or a missing file. When you know which file feeds which setting, you can fix them with the right flag.
Check yourself#
- 1Which key ofeasy
config.jsonselects the Python class that vLLM builds?Show answer
architectures.ModelConfigpasses it toregistry.inspect_model_cls()(vllm/config/model.py:741), and the registry maps the name to a module and a class (vllm/model_executor/models/registry.py:199). - 2What model doeseasy
vllm serveuse when you give no model name?Show answer
Qwen/Qwen3-0.6B, the default ofModelConfig.model(vllm/config/model.py:155). - 3A request sets nomedium
temperature. The model folder hasgeneration_config.jsonwith"temperature": 0.6. Which temperature does the request get by default, and how can the operator change that?Show answer
0.6. With the default
--generation-config auto,get_diff_sampling_param()takestemperaturefrom the file (vllm/config/model.py:1877-1902).--generation-config vllmignores the file, and--override-generation-config '{"temperature": 0.7}'replaces the value (vllm/config/model.py:1880). - 4A config hashard
max_position_embeddings131072, andtokenizer_config.jsonhasmodel_max_length32768. What is the defaultmax_model_len, before RoPE scaling?Show answer
32,768. vLLM takes the derived length from the config and then the minimum with
model_max_lengthof the tokenizer config (vllm/config/model.py:2570-2575). - 5A repository has bothhard
model-00001-of-00002.safetensors,model-00002-of-00002.safetensorsand aconsolidated.safetensorswith the same weights. Why does vLLM not load the weights twice?Show answer
With
--load-format auto, a folder withconsolidated*.safetensorsuses the Mistral format and loads only those files (vllm/model_executor/model_loader/default_loader.py:170-182). In thehfformat, vLLM keeps only the files thatmodel.safetensors.index.jsonnames (vllm/model_executor/model_loader/default_loader.py:236-254). Both rules stop it from loading both copies.