registry.py 42.3 KB
Newer Older
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
5
6
"""
Whenever you add an architecture to this page, please also update
`tests/models/registry.py` with example HuggingFace models for it.
"""
7
import hashlib
8
import importlib
9
import json
10
import os
11
import pickle
12
13
import subprocess
import sys
14
import tempfile
15
from abc import ABC, abstractmethod
16
from collections.abc import Set
17
from dataclasses import asdict, dataclass, field
18
from functools import lru_cache
19
from pathlib import Path
20
from typing import Callable, Optional, TypeVar, Union
21
22

import torch.nn as nn
23
import transformers
24

25
from vllm import envs
26
from vllm.config import (ModelConfig, iter_architecture_defaults,
27
                         try_match_architecture_defaults)
28
from vllm.logger import init_logger
29
from vllm.logging_utils import logtime
30
31
from vllm.transformers_utils.dynamic_module import (
    try_get_class_from_dynamic_module)
32

33
34
from .interfaces import (has_inner_state, has_noops, is_attention_free,
                         is_hybrid, supports_cross_encoding,
35
36
                         supports_multimodal,
                         supports_multimodal_encoder_tp_data,
37
                         supports_multimodal_raw_input_only, supports_pp,
38
                         supports_transcription, supports_v0_only)
39
40
from .interfaces_base import (get_default_pooling_type, is_pooling_model,
                              is_text_generation_model)
41
42
43

logger = init_logger(__name__)

44
# yapf: disable
45
46
_TEXT_GENERATION_MODELS = {
    # [Decoder-only]
47
    "ApertusForCausalLM": ("apertus", "ApertusForCausalLM"),
48
49
    "AquilaModel": ("llama", "LlamaForCausalLM"),
    "AquilaForCausalLM": ("llama", "LlamaForCausalLM"),  # AquilaChat2
Raghav Ravishankar's avatar
Raghav Ravishankar committed
50
    "ArceeForCausalLM": ("arcee", "ArceeForCausalLM"),
51
    "ArcticForCausalLM": ("arctic", "ArcticForCausalLM"),
52
    "MiniMaxForCausalLM": ("minimax_text_01", "MiniMaxText01ForCausalLM"),
53
    "MiniMaxText01ForCausalLM": ("minimax_text_01", "MiniMaxText01ForCausalLM"),
54
    "MiniMaxM1ForCausalLM": ("minimax_text_01", "MiniMaxText01ForCausalLM"),
55
56
57
58
    # baichuan-7b, upper case 'C' in the class name
    "BaiChuanForCausalLM": ("baichuan", "BaiChuanForCausalLM"),
    # baichuan-13b, lower case 'c' in the class name
    "BaichuanForCausalLM": ("baichuan", "BaichuanForCausalLM"),
59
    "BailingMoeForCausalLM": ("bailing_moe", "BailingMoeForCausalLM"),
ant-yy's avatar
ant-yy committed
60
    "BailingMoeV2ForCausalLM": ("bailing_moe", "BailingMoeV2ForCausalLM"),
Yu Chin Fabian Lim's avatar
Yu Chin Fabian Lim committed
61
    "BambaForCausalLM": ("bamba", "BambaForCausalLM"),
62
    "BloomForCausalLM": ("bloom", "BloomForCausalLM"),
63
    "ChatGLMModel": ("chatglm", "ChatGLMForCausalLM"),
64
    "ChatGLMForConditionalGeneration": ("chatglm", "ChatGLMForCausalLM"),
65
    "CohereForCausalLM": ("commandr", "CohereForCausalLM"),
66
    "Cohere2ForCausalLM": ("commandr", "CohereForCausalLM"),
67
    "DbrxForCausalLM": ("dbrx", "DbrxForCausalLM"),
68
    "DeciLMForCausalLM": ("nemotron_nas", "DeciLMForCausalLM"),
69
70
    "DeepseekForCausalLM": ("deepseek", "DeepseekForCausalLM"),
    "DeepseekV2ForCausalLM": ("deepseek_v2", "DeepseekV2ForCausalLM"),
71
    "DeepseekV3ForCausalLM": ("deepseek_v2", "DeepseekV3ForCausalLM"),
72
    "Dots1ForCausalLM": ("dots1", "Dots1ForCausalLM"),
73
    "Ernie4_5ForCausalLM": ("ernie45", "Ernie4_5ForCausalLM"),
74
    "Ernie4_5_MoeForCausalLM": ("ernie45_moe", "Ernie4_5_MoeForCausalLM"),
75
    "ExaoneForCausalLM": ("exaone", "ExaoneForCausalLM"),
76
    "Exaone4ForCausalLM": ("exaone4", "Exaone4ForCausalLM"),
77
    "FalconForCausalLM": ("falcon", "FalconForCausalLM"),
78
    "Fairseq2LlamaForCausalLM": ("fairseq2_llama", "Fairseq2LlamaForCausalLM"),
79
80
    "GemmaForCausalLM": ("gemma", "GemmaForCausalLM"),
    "Gemma2ForCausalLM": ("gemma2", "Gemma2ForCausalLM"),
81
    "Gemma3ForCausalLM": ("gemma3", "Gemma3ForCausalLM"),
Nicolò Lucchesi's avatar
Nicolò Lucchesi committed
82
    "Gemma3nForCausalLM": ("gemma3n", "Gemma3nForCausalLM"),
83
    "Qwen3NextForCausalLM": ("qwen3_next", "Qwen3NextForCausalLM"),
84
    "GlmForCausalLM": ("glm", "GlmForCausalLM"),
Yuxuan Zhang's avatar
Yuxuan Zhang committed
85
    "Glm4ForCausalLM": ("glm4", "Glm4ForCausalLM"),
Yuxuan Zhang's avatar
Yuxuan Zhang committed
86
    "Glm4MoeForCausalLM": ("glm4_moe", "Glm4MoeForCausalLM"),
87
    "GptOssForCausalLM": ("gpt_oss", "GptOssForCausalLM"),
88
89
90
91
92
93
    "GPT2LMHeadModel": ("gpt2", "GPT2LMHeadModel"),
    "GPTBigCodeForCausalLM": ("gpt_bigcode", "GPTBigCodeForCausalLM"),
    "GPTJForCausalLM": ("gpt_j", "GPTJForCausalLM"),
    "GPTNeoXForCausalLM": ("gpt_neox", "GPTNeoXForCausalLM"),
    "GraniteForCausalLM": ("granite", "GraniteForCausalLM"),
    "GraniteMoeForCausalLM": ("granitemoe", "GraniteMoeForCausalLM"),
94
    "GraniteMoeHybridForCausalLM": ("granitemoehybrid", "GraniteMoeHybridForCausalLM"),   # noqa: E501
95
    "GraniteMoeSharedForCausalLM": ("granitemoeshared", "GraniteMoeSharedForCausalLM"),   # noqa: E501
96
    "GritLM": ("gritlm", "GritLM"),
Michael Goin's avatar
Michael Goin committed
97
    "Grok1ModelForCausalLM": ("grok1", "Grok1ForCausalLM"),
98
99
    "HunYuanMoEV1ForCausalLM": ("hunyuan_v1", "HunYuanMoEV1ForCausalLM"),
    "HunYuanDenseV1ForCausalLM": ("hunyuan_v1", "HunYuanDenseV1ForCausalLM"),
100
    "HCXVisionForCausalLM": ("hyperclovax_vision", "HCXVisionForCausalLM"),
101
102
    "InternLMForCausalLM": ("llama", "LlamaForCausalLM"),
    "InternLM2ForCausalLM": ("internlm2", "InternLM2ForCausalLM"),
103
    "InternLM2VEForCausalLM": ("internlm2_ve", "InternLM2VEForCausalLM"),
104
    "InternLM3ForCausalLM": ("llama", "LlamaForCausalLM"),
105
106
    "JAISLMHeadModel": ("jais", "JAISLMHeadModel"),
    "JambaForCausalLM": ("jamba", "JambaForCausalLM"),
107
    "Lfm2ForCausalLM": ("lfm2", "Lfm2ForCausalLM"),
108
    "LlamaForCausalLM": ("llama", "LlamaForCausalLM"),
109
    "Llama4ForCausalLM": ("llama4", "Llama4ForCausalLM"),  # noqa: E501
110
111
    # For decapoda-research/llama-*
    "LLaMAForCausalLM": ("llama", "LlamaForCausalLM"),
112
    "MambaForCausalLM": ("mamba", "MambaForCausalLM"),
113
    "FalconMambaForCausalLM": ("mamba", "MambaForCausalLM"),
Dhia Eddine Rhaiem's avatar
Dhia Eddine Rhaiem committed
114
    "FalconH1ForCausalLM":("falcon_h1", "FalconH1ForCausalLM"),
115
    "Mamba2ForCausalLM": ("mamba2", "Mamba2ForCausalLM"),
116
117
    "MiniCPMForCausalLM": ("minicpm", "MiniCPMForCausalLM"),
    "MiniCPM3ForCausalLM": ("minicpm3", "MiniCPM3ForCausalLM"),
118
119
    "MistralForCausalLM": ("llama", "LlamaForCausalLM"),
    "MixtralForCausalLM": ("mixtral", "MixtralForCausalLM"),
120
    "MotifForCausalLM": ("motif", "MotifForCausalLM"),
121
122
123
    # transformers's mpt class has lower case
    "MptForCausalLM": ("mpt", "MPTForCausalLM"),
    "MPTForCausalLM": ("mpt", "MPTForCausalLM"),
124
    "MiMoForCausalLM": ("mimo", "MiMoForCausalLM"),
125
    "NemotronForCausalLM": ("nemotron", "NemotronForCausalLM"),
Luis Vega's avatar
Luis Vega committed
126
    "NemotronHForCausalLM": ("nemotron_h", "NemotronHForCausalLM"),
127
    "OlmoForCausalLM": ("olmo", "OlmoForCausalLM"),
128
    "Olmo2ForCausalLM": ("olmo2", "Olmo2ForCausalLM"),
129
    "Olmo3ForCausalLM": ("olmo2", "Olmo2ForCausalLM"),
130
131
132
133
134
135
136
    "OlmoeForCausalLM": ("olmoe", "OlmoeForCausalLM"),
    "OPTForCausalLM": ("opt", "OPTForCausalLM"),
    "OrionForCausalLM": ("orion", "OrionForCausalLM"),
    "PersimmonForCausalLM": ("persimmon", "PersimmonForCausalLM"),
    "PhiForCausalLM": ("phi", "PhiForCausalLM"),
    "Phi3ForCausalLM": ("phi3", "Phi3ForCausalLM"),
    "PhiMoEForCausalLM": ("phimoe", "PhiMoEForCausalLM"),
137
    "Phi4FlashForCausalLM": ("phi4flash", "Phi4FlashForCausalLM"),
Shinichi Hemmi's avatar
Shinichi Hemmi committed
138
    "Plamo2ForCausalLM": ("plamo2", "Plamo2ForCausalLM"),
139
    "QWenLMHeadModel": ("qwen", "QWenLMHeadModel"),
140
141
    "Qwen2ForCausalLM": ("qwen2", "Qwen2ForCausalLM"),
    "Qwen2MoeForCausalLM": ("qwen2_moe", "Qwen2MoeForCausalLM"),
142
143
    "Qwen3ForCausalLM": ("qwen3", "Qwen3ForCausalLM"),
    "Qwen3MoeForCausalLM": ("qwen3_moe", "Qwen3MoeForCausalLM"),
144
    "RWForCausalLM": ("falcon", "FalconForCausalLM"),
145
    "SeedOssForCausalLM": ("seed_oss", "SeedOssForCausalLM"),
Song's avatar
Song committed
146
    "Step3TextForCausalLM": ("step3_text", "Step3TextForCausalLM"),
147
148
149
150
    "StableLMEpochForCausalLM": ("stablelm", "StablelmForCausalLM"),
    "StableLmForCausalLM": ("stablelm", "StablelmForCausalLM"),
    "Starcoder2ForCausalLM": ("starcoder2", "Starcoder2ForCausalLM"),
    "SolarForCausalLM": ("solar", "SolarForCausalLM"),
151
    "TeleChat2ForCausalLM": ("telechat2", "TeleChat2ForCausalLM"),
152
    "TeleFLMForCausalLM": ("teleflm", "TeleFLMForCausalLM"),
153
    "XverseForCausalLM": ("llama", "LlamaForCausalLM"),
154
    "Zamba2ForCausalLM": ("zamba2", "Zamba2ForCausalLM"),
155
156
157
}

_EMBEDDING_MODELS = {
158
    # [Text-only]
159
    "BertModel": ("bert", "BertEmbeddingModel"),
160
    "DeciLMForCausalLM": ("nemotron_nas", "DeciLMForCausalLM"),
161
    "Gemma2Model": ("gemma2", "Gemma2ForCausalLM"),
162
    "Gemma3TextModel": ("gemma3", "Gemma3Model"),
163
    "GlmForCausalLM": ("glm", "GlmForCausalLM"),
164
    "GPT2ForSequenceClassification": ("gpt2", "GPT2ForSequenceClassification"),
165
    "GritLM": ("gritlm", "GritLM"),
166
167
    "GteModel": ("bert_with_rope", "SnowflakeGteNewModel"),
    "GteNewModel": ("bert_with_rope", "GteNewModel"),
168
    "InternLM2ForRewardModel": ("internlm2", "InternLM2ForRewardModel"),
169
    "JambaForSequenceClassification": ("jamba", "JambaForSequenceClassification"),  # noqa: E501
170
    "LlamaModel": ("llama", "LlamaForCausalLM"),
171
172
173
174
175
    **{
        # Multiple models share the same architecture, so we include them all
        k: (mod, arch) for k, (mod, arch) in _TEXT_GENERATION_MODELS.items()
        if arch == "LlamaForCausalLM"
    },
176
    "MistralModel": ("llama", "LlamaForCausalLM"),
177
    "ModernBertModel": ("modernbert", "ModernBertModel"),
178
    "NomicBertModel": ("bert_with_rope", "NomicBertModel"),
179
    "Phi3ForCausalLM": ("phi3", "Phi3ForCausalLM"),
180
    "Qwen2Model": ("qwen2", "Qwen2ForCausalLM"),
181
    "Qwen2ForCausalLM": ("qwen2", "Qwen2ForCausalLM"),
182
    "Qwen2ForRewardModel": ("qwen2_rm", "Qwen2ForRewardModel"),
183
    "Qwen2ForProcessRewardModel": ("qwen2_rm", "Qwen2ForProcessRewardModel"),
184
185
    "RobertaForMaskedLM": ("roberta", "RobertaEmbeddingModel"),
    "RobertaModel": ("roberta", "RobertaEmbeddingModel"),
186
    "TeleChat2ForCausalLM": ("telechat2", "TeleChat2ForCausalLM"),
187
    "XLMRobertaModel": ("roberta", "RobertaEmbeddingModel"),
188
    # [Multimodal]
Cyrus Leung's avatar
Cyrus Leung committed
189
    "LlavaNextForConditionalGeneration": ("llava_next", "LlavaNextForConditionalGeneration"),  # noqa: E501
190
    "Phi3VForCausalLM": ("phi3v", "Phi3VForCausalLM"),
191
    "Qwen2VLForConditionalGeneration": ("qwen2_vl", "Qwen2VLForConditionalGeneration"),  # noqa: E501
192
193
    # Technically Terratorch models work on images, both in
    # input and output. I am adding it here because it piggy-backs on embedding
194
    # models for the time being.
195
196
    "PrithviGeoSpatialMAE": ("terratorch", "Terratorch"),
    "Terratorch": ("terratorch", "Terratorch"),
197
198
}

199
200
_CROSS_ENCODER_MODELS = {
    "BertForSequenceClassification": ("bert", "BertForSequenceClassification"),
201
    "BertForTokenClassification": ("bert", "BertForTokenClassification"),
202
203
204
205
    "GteNewForSequenceClassification": ("bert_with_rope",
                                        "GteNewForSequenceClassification"),
    "ModernBertForSequenceClassification": ("modernbert",
                                            "ModernBertForSequenceClassification"),
206
207
208
209
    "RobertaForSequenceClassification": ("roberta",
                                         "RobertaForSequenceClassification"),
    "XLMRobertaForSequenceClassification": ("roberta",
                                            "RobertaForSequenceClassification"),
210
    # [Auto-converted (see adapters.py)]
211
    "JinaVLForRanking": ("jina_vl", "JinaVLForSequenceClassification"), # noqa: E501,
212
213
}

214
_MULTIMODAL_MODELS = {
215
    # [Decoder-only]
216
    "AriaForConditionalGeneration": ("aria", "AriaForConditionalGeneration"),
Jennifer Zhao's avatar
Jennifer Zhao committed
217
    "AyaVisionForConditionalGeneration": ("aya_vision", "AyaVisionForConditionalGeneration"),  # noqa: E501
218
219
    "Blip2ForConditionalGeneration": ("blip2", "Blip2ForConditionalGeneration"),
    "ChameleonForConditionalGeneration": ("chameleon", "ChameleonForConditionalGeneration"),  # noqa: E501
220
    "Cohere2VisionForConditionalGeneration": ("cohere2_vision", "Cohere2VisionForConditionalGeneration"),  # noqa: E501
221
    "DeepseekVLV2ForCausalLM": ("deepseek_vl2", "DeepseekVLV2ForCausalLM"),
Roger Wang's avatar
Roger Wang committed
222
    "DotsOCRForCausalLM": ("dots_ocr", "DotsOCRForCausalLM"),
223
    "Ernie4_5_VLMoeForConditionalGeneration": ("ernie45_vl", "Ernie4_5_VLMoeForConditionalGeneration"),  # noqa: E501
224
    "FuyuForCausalLM": ("fuyu", "FuyuForCausalLM"),
225
    "Gemma3ForConditionalGeneration": ("gemma3_mm", "Gemma3ForConditionalGeneration"),  # noqa: E501
Nicolò Lucchesi's avatar
Nicolò Lucchesi committed
226
    "Gemma3nForConditionalGeneration": ("gemma3n_mm", "Gemma3nForConditionalGeneration"),    # noqa: E501
227
    "GLM4VForCausalLM": ("glm4v", "GLM4VForCausalLM"),
228
    "Glm4vForConditionalGeneration": ("glm4_1v", "Glm4vForConditionalGeneration"),  # noqa: E501
Jee Jee Li's avatar
Jee Jee Li committed
229
    "Glm4vMoeForConditionalGeneration": ("glm4_1v", "Glm4vMoeForConditionalGeneration"),  # noqa: E501
230
    "GraniteSpeechForConditionalGeneration": ("granite_speech", "GraniteSpeechForConditionalGeneration"),  # noqa: E501
231
    "H2OVLChatModel": ("h2ovl", "H2OVLChatModel"),
232
    "InternVLChatModel": ("internvl", "InternVLChatModel"),
233
    "NemotronH_Nano_VL": ("nano_nemotron_vl", "NemotronH_Nano_VL"),
Lyu Han's avatar
Lyu Han committed
234
    "InternS1ForConditionalGeneration": ("interns1", "InternS1ForConditionalGeneration"),  # noqa: E501
235
    "InternVLForConditionalGeneration": ("interns1", "InternS1ForConditionalGeneration"),  # noqa: E501
236
    "Idefics3ForConditionalGeneration":("idefics3","Idefics3ForConditionalGeneration"),
237
    "SmolVLMForConditionalGeneration": ("smolvlm","SmolVLMForConditionalGeneration"),  # noqa: E501
238
    "KeyeForConditionalGeneration": ("keye", "KeyeForConditionalGeneration"),
239
    "KeyeVL1_5ForConditionalGeneration": ("keye_vl1_5", "KeyeVL1_5ForConditionalGeneration"), # noqa: E501
240
    "RForConditionalGeneration": ("rvl", "RForConditionalGeneration"),
241
    "KimiVLForConditionalGeneration": ("kimi_vl", "KimiVLForConditionalGeneration"),  # noqa: E501
242
    "Llama_Nemotron_Nano_VL": ("nemotron_vl", "LlamaNemotronVLChatModel"),
243
    "Llama4ForConditionalGeneration": ("mllama4", "Llama4ForConditionalGeneration"),  # noqa: E501
244
245
246
247
    "LlavaForConditionalGeneration": ("llava", "LlavaForConditionalGeneration"),
    "LlavaNextForConditionalGeneration": ("llava_next", "LlavaNextForConditionalGeneration"),  # noqa: E501
    "LlavaNextVideoForConditionalGeneration": ("llava_next_video", "LlavaNextVideoForConditionalGeneration"),  # noqa: E501
    "LlavaOnevisionForConditionalGeneration": ("llava_onevision", "LlavaOnevisionForConditionalGeneration"),  # noqa: E501
248
    "MantisForConditionalGeneration": ("llava", "MantisForConditionalGeneration"),  # noqa: E501
249
    "MiDashengLMModel": ("midashenglm", "MiDashengLMModel"),
250
    "MiniMaxVL01ForConditionalGeneration": ("minimax_vl_01", "MiniMaxVL01ForConditionalGeneration"),  # noqa: E501
251
    "MiniCPMO": ("minicpmo", "MiniCPMO"),
252
    "MiniCPMV": ("minicpmv", "MiniCPMV"),
253
    "Mistral3ForConditionalGeneration": ("mistral3", "Mistral3ForConditionalGeneration"),  # noqa: E501
254
    "MolmoForCausalLM": ("molmo", "MolmoForCausalLM"),
255
    "NVLM_D": ("nvlm_d", "NVLM_D_Model"),
256
    "Ovis": ("ovis", "Ovis"),
257
    "Ovis2_5": ("ovis2_5", "Ovis2_5"),
258
    "PaliGemmaForConditionalGeneration": ("paligemma", "PaliGemmaForConditionalGeneration"),  # noqa: E501
259
    "Phi3VForCausalLM": ("phi3v", "Phi3VForCausalLM"),
260
261
    "Phi4MMForCausalLM": ("phi4mm", "Phi4MMForCausalLM"),
    "Phi4MultimodalForCausalLM": ("phi4_multimodal", "Phi4MultimodalForCausalLM"),  # noqa: E501
262
    "PixtralForConditionalGeneration": ("pixtral", "PixtralForConditionalGeneration"),  # noqa: E501
263
    "QwenVLForConditionalGeneration": ("qwen_vl", "QwenVLForConditionalGeneration"),  # noqa: E501
264
    "Qwen2VLForConditionalGeneration": ("qwen2_vl", "Qwen2VLForConditionalGeneration"),  # noqa: E501
Roger Wang's avatar
Roger Wang committed
265
    "Qwen2_5_VLForConditionalGeneration": ("qwen2_5_vl", "Qwen2_5_VLForConditionalGeneration"),  # noqa: E501
266
    "Qwen2AudioForConditionalGeneration": ("qwen2_audio", "Qwen2AudioForConditionalGeneration"),  # noqa: E501
267
    "Qwen2_5OmniModel": ("qwen2_5_omni_thinker", "Qwen2_5OmniThinkerForConditionalGeneration"),  # noqa: E501
268
    "Qwen2_5OmniForConditionalGeneration": ("qwen2_5_omni_thinker", "Qwen2_5OmniThinkerForConditionalGeneration"),  # noqa: E501
269
270
    "Qwen3VLForConditionalGeneration": ("qwen3_vl", "Qwen3VLForConditionalGeneration"),  # noqa: E501
    "Qwen3VLMoeForConditionalGeneration": ("qwen3_vl_moe", "Qwen3VLMoeForConditionalGeneration"),  # noqa: E501
271
    "SkyworkR1VChatModel": ("skyworkr1v", "SkyworkR1VChatModel"),
Song's avatar
Song committed
272
    "Step3VLForConditionalGeneration": ("step3_vl", "Step3VLForConditionalGeneration"),  # noqa: E501
汪志鹏's avatar
汪志鹏 committed
273
    "TarsierForConditionalGeneration": ("tarsier", "TarsierForConditionalGeneration"),  # noqa: E501
274
    "Tarsier2ForConditionalGeneration": ("qwen2_vl", "Tarsier2ForConditionalGeneration"),  # noqa: E501
275
    "UltravoxModel": ("ultravox", "UltravoxModel"),
Patrick von Platen's avatar
Patrick von Platen committed
276
    "VoxtralForConditionalGeneration": ("voxtral", "VoxtralForConditionalGeneration"),  # noqa: E501
277
    # [Encoder-decoder]
278
    "WhisperForConditionalGeneration": ("whisper", "WhisperForConditionalGeneration"),  # noqa: E501
279
}
280
281

_SPECULATIVE_DECODING_MODELS = {
282
    "MiMoMTPModel": ("mimo_mtp", "MiMoMTP"),
283
    "EagleLlamaForCausalLM": ("llama_eagle", "EagleLlamaForCausalLM"),
zhiweiz's avatar
zhiweiz committed
284
    "EagleLlama4ForCausalLM": ("llama4_eagle", "EagleLlama4ForCausalLM"),
285
    "EagleMiniCPMForCausalLM": ("minicpm_eagle", "EagleMiniCPMForCausalLM"),
286
    "Eagle3LlamaForCausalLM": ("llama_eagle3", "Eagle3LlamaForCausalLM"),
287
    "LlamaForCausalLMEagle3": ("llama_eagle3", "Eagle3LlamaForCausalLM"),
288
    "EagleDeepSeekMTPModel": ("deepseek_eagle", "EagleDeepseekV3ForCausalLM"),
289
    "DeepSeekMTPModel": ("deepseek_mtp", "DeepSeekMTP"),
290
    "ErnieMTPModel": ("ernie_mtp", "ErnieMTP"),
Yuxuan Zhang's avatar
Yuxuan Zhang committed
291
    "Glm4MoeMTPModel": ("glm4_moe_mtp", "Glm4MoeMTP"),
292
    "MedusaModel": ("medusa", "Medusa"),
293
    "Qwen3NextMTP": ("qwen3_next_mtp", "Qwen3NextMTP"),
294
295
296
    # Temporarily disabled.
    # # TODO(woosuk): Re-enable this once the MLP Speculator is supported in V1.
    # "MLPSpeculatorPreTrainedModel": ("mlp_speculator", "MLPSpeculator"),
297
}
298

299
_TRANSFORMERS_SUPPORTED_MODELS = {
300
301
302
    # Text generation models
    "SmolLM3ForCausalLM": ("transformers", "TransformersForCausalLM"),
    # Multimodal models
303
304
305
306
    "Emu3ForConditionalGeneration": ("transformers", "TransformersForMultimodalLM"),  # noqa: E501
}

_TRANSFORMERS_BACKEND_MODELS = {
307
    "TransformersModel": ("transformers", "TransformersModel"),
308
    "TransformersForCausalLM": ("transformers", "TransformersForCausalLM"),
309
    "TransformersForMultimodalLM": ("transformers", "TransformersForMultimodalLM"), # noqa: E501
310
}
311
# yapf: enable
312

313
_VLLM_MODELS = {
314
    **_TEXT_GENERATION_MODELS,
315
    **_EMBEDDING_MODELS,
316
    **_CROSS_ENCODER_MODELS,
317
    **_MULTIMODAL_MODELS,
318
    **_SPECULATIVE_DECODING_MODELS,
319
320
    **_TRANSFORMERS_SUPPORTED_MODELS,
    **_TRANSFORMERS_BACKEND_MODELS,
321
322
}

323
324
325
326
327
328
329
330
# This variable is used as the args for subprocess.run(). We
# can modify  this variable to alter the args if needed. e.g.
# when we use par format to pack things together, sys.executable
# might not be the target we want to run.
_SUBPROCESS_COMMAND = [
    sys.executable, "-m", "vllm.model_executor.models.registry"
]

331
332
333
334
335
336
337
338
339
340
341
_PREVIOUSLY_SUPPORTED_MODELS = {
    "Phi3SmallForCausalLM": "0.9.2",
    # encoder-decoder models except whisper
    # have been removed for V0 deprecation.
    "BartModel": "0.10.2",
    "BartForConditionalGeneration": "0.10.2",
    "DonutForConditionalGeneration": "0.10.2",
    "Florence2ForConditionalGeneration": "0.10.2",
    "MBartForConditionalGeneration": "0.10.2",
    "MllamaForConditionalGeneration": "0.10.2",
}
342

343

344
345
@dataclass(frozen=True)
class _ModelInfo:
346
    architecture: str
347
    is_text_generation_model: bool
348
    is_pooling_model: bool
349
    default_pooling_type: str
350
    supports_cross_encoding: bool
351
    supports_multimodal: bool
352
    supports_multimodal_raw_input_only: bool
353
    supports_multimodal_encoder_tp_data: bool
354
    supports_pp: bool
355
356
    has_inner_state: bool
    is_attention_free: bool
357
    is_hybrid: bool
358
    has_noops: bool
359
    supports_transcription: bool
360
    supports_transcription_only: bool
361
    supports_v0_only: bool
362
363

    @staticmethod
364
    def from_model_cls(model: type[nn.Module]) -> "_ModelInfo":
365
        return _ModelInfo(
366
            architecture=model.__name__,
367
            is_text_generation_model=is_text_generation_model(model),
368
            is_pooling_model=is_pooling_model(model),
369
            default_pooling_type=get_default_pooling_type(model),
370
            supports_cross_encoding=supports_cross_encoding(model),
371
            supports_multimodal=supports_multimodal(model),
372
373
            supports_multimodal_raw_input_only=
            supports_multimodal_raw_input_only(model),
374
375
            supports_multimodal_encoder_tp_data=
            supports_multimodal_encoder_tp_data(model),
376
            supports_pp=supports_pp(model),
377
378
            has_inner_state=has_inner_state(model),
            is_attention_free=is_attention_free(model),
379
            is_hybrid=is_hybrid(model),
380
            supports_transcription=supports_transcription(model),
381
382
            supports_transcription_only=(supports_transcription(model) and
                                         model.supports_transcription_only),
383
            supports_v0_only=supports_v0_only(model),
384
            has_noops=has_noops(model),
385
        )
386
387


388
class _BaseRegisteredModel(ABC):
389

390
391
392
    @abstractmethod
    def inspect_model_cls(self) -> _ModelInfo:
        raise NotImplementedError
393

394
    @abstractmethod
395
    def load_model_cls(self) -> type[nn.Module]:
396
        raise NotImplementedError
397
398


399
400
401
402
403
404
405
@dataclass(frozen=True)
class _RegisteredModel(_BaseRegisteredModel):
    """
    Represents a model that has already been imported in the main process.
    """

    interfaces: _ModelInfo
406
    model_cls: type[nn.Module]
407
408

    @staticmethod
409
    def from_model_cls(model_cls: type[nn.Module]):
410
411
412
413
414
415
416
417
        return _RegisteredModel(
            interfaces=_ModelInfo.from_model_cls(model_cls),
            model_cls=model_cls,
        )

    def inspect_model_cls(self) -> _ModelInfo:
        return self.interfaces

418
    def load_model_cls(self) -> type[nn.Module]:
419
420
421
422
423
424
425
426
427
428
429
        return self.model_cls


@dataclass(frozen=True)
class _LazyRegisteredModel(_BaseRegisteredModel):
    """
    Represents a model that has not been imported in the main process.
    """
    module_name: str
    class_name: str

430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
    @staticmethod
    def _get_cache_dir() -> Path:
        return Path(envs.VLLM_CACHE_ROOT) / "modelinfos"

    def _get_cache_filename(self) -> str:
        cls_name = f"{self.module_name}-{self.class_name}".replace(".", "-")
        return f"{cls_name}.json"

    def _load_modelinfo_from_cache(self,
                                   module_hash: str) -> _ModelInfo | None:
        try:
            try:
                modelinfo_path = self._get_cache_dir(
                ) / self._get_cache_filename()
                with open(modelinfo_path, encoding="utf-8") as file:
                    mi_dict = json.load(file)
            except FileNotFoundError:
                logger.debug(("Cached model info file "
                              "for class %s.%s not found"), self.module_name,
                             self.class_name)
                return None

            if mi_dict["hash"] != module_hash:
                logger.debug(("Cached model info file "
                              "for class %s.%s is stale"), self.module_name,
                             self.class_name)
                return None

            # file not changed, use cached _ModelInfo properties
            return _ModelInfo(**mi_dict["modelinfo"])
        except Exception:
            logger.exception(("Cached model info "
                              "for class %s.%s error. "), self.module_name,
                             self.class_name)
            return None

    def _save_modelinfo_to_cache(self, mi: _ModelInfo,
                                 module_hash: str) -> None:
        """save dictionary json file to cache"""
        from vllm.model_executor.model_loader.weight_utils import atomic_writer
        try:
            modelinfo_dict = {
                "hash": module_hash,
                "modelinfo": asdict(mi),
            }
            cache_dir = self._get_cache_dir()
            cache_dir.mkdir(parents=True, exist_ok=True)
            modelinfo_path = cache_dir / self._get_cache_filename()
            with atomic_writer(modelinfo_path, encoding='utf-8') as f:
                json.dump(modelinfo_dict, f, indent=2)
        except Exception:
            logger.exception("Error saving model info cache.")

    @logtime(logger=logger, msg="Registry inspect model class")
484
    def inspect_model_cls(self) -> _ModelInfo:
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
        model_path = Path(
            __file__).parent / f"{self.module_name.split('.')[-1]}.py"

        assert model_path.exists(), \
            f"Model {self.module_name} expected to be on path {model_path}"
        with open(model_path, "rb") as f:
            module_hash = hashlib.md5(f.read()).hexdigest()

        mi = self._load_modelinfo_from_cache(module_hash)
        if mi is not None:
            logger.debug(("Loaded model info "
                          "for class %s.%s from cache"), self.module_name,
                         self.class_name)
            return mi
        else:
            logger.debug(("Cache model info "
                          "for class %s.%s miss. "
                          "Loading model instead."), self.module_name,
                         self.class_name)

        # Performed in another process to avoid initializing CUDA
        mi = _run_in_subprocess(
507
            lambda: _ModelInfo.from_model_cls(self.load_model_cls()))
508
509
510
511
512
513
514
        logger.debug("Loaded model info for class %s.%s", self.module_name,
                     self.class_name)

        # save cache file
        self._save_modelinfo_to_cache(mi, module_hash)

        return mi
515

516
    def load_model_cls(self) -> type[nn.Module]:
517
518
519
520
521
522
523
524
        mod = importlib.import_module(self.module_name)
        return getattr(mod, self.class_name)


@lru_cache(maxsize=128)
def _try_load_model_cls(
    model_arch: str,
    model: _BaseRegisteredModel,
525
) -> Optional[type[nn.Module]]:
526
    from vllm.platforms import current_platform
527
    current_platform.verify_model_arch(model_arch)
528
529
530
531
532
533
    try:
        return model.load_model_cls()
    except Exception:
        logger.exception("Error in loading model architecture '%s'",
                         model_arch)
        return None
534
535


536
537
538
539
540
541
542
543
544
545
546
@lru_cache(maxsize=128)
def _try_inspect_model_cls(
    model_arch: str,
    model: _BaseRegisteredModel,
) -> Optional[_ModelInfo]:
    try:
        return model.inspect_model_cls()
    except Exception:
        logger.exception("Error in inspecting model architecture '%s'",
                         model_arch)
        return None
547
548


549
550
551
@dataclass
class _ModelRegistry:
    # Keyed by model_arch
552
    models: dict[str, _BaseRegisteredModel] = field(default_factory=dict)
553

554
    def get_supported_archs(self) -> Set[str]:
555
        return self.models.keys()
556

557
558
559
    def register_model(
        self,
        model_arch: str,
560
        model_cls: Union[type[nn.Module], str],
561
    ) -> None:
562
563
564
        """
        Register an external model to be used in vLLM.

565
        `model_cls` can be either:
566

567
        - A [`torch.nn.Module`][] class directly referencing the model.
568
        - A string in the format `<module>:<class>` which can be used to
569
570
          lazily import the model. This is useful to avoid initializing CUDA
          when importing the model and thus the related error
571
          `RuntimeError: Cannot re-initialize CUDA in forked subprocess`.
572
        """
573
574
575
576
        if not isinstance(model_arch, str):
            msg = f"`model_arch` should be a string, not a {type(model_arch)}"
            raise TypeError(msg)

577
        if model_arch in self.models:
578
579
580
            logger.warning(
                "Model architecture %s is already registered, and will be "
                "overwritten by the new model class %s.", model_arch,
581
582
583
584
585
586
587
                model_cls)

        if isinstance(model_cls, str):
            split_str = model_cls.split(":")
            if len(split_str) != 2:
                msg = "Expected a string in the format `<module>:<class>`"
                raise ValueError(msg)
588

589
            model = _LazyRegisteredModel(*split_str)
590
        elif isinstance(model_cls, type) and issubclass(model_cls, nn.Module):
591
            model = _RegisteredModel.from_model_cls(model_cls)
592
593
594
595
        else:
            msg = ("`model_cls` should be a string or PyTorch model class, "
                   f"not a {type(model_arch)}")
            raise TypeError(msg)
596

597
        self.models[model_arch] = model
598

599
    def _raise_for_unsupported(self, architectures: list[str]):
600
        all_supported_archs = self.get_supported_archs()
601

602
603
604
605
606
        if any(arch in all_supported_archs for arch in architectures):
            raise ValueError(
                f"Model architectures {architectures} failed "
                "to be inspected. Please check the logs for more details.")

607
608
609
610
611
612
613
614
615
616
        for arch in architectures:
            if arch in _PREVIOUSLY_SUPPORTED_MODELS:
                previous_version = _PREVIOUSLY_SUPPORTED_MODELS[arch]

                raise ValueError(
                    f"Model architecture {arch} was supported in vLLM until "
                    f"v{previous_version}, and is not supported anymore. "
                    "Please use an older version of vLLM if you want to "
                    "use this model architecture.")

617
618
619
        raise ValueError(
            f"Model architectures {architectures} are not supported for now. "
            f"Supported architectures: {all_supported_archs}")
620

621
    def _try_load_model_cls(self,
622
                            model_arch: str) -> Optional[type[nn.Module]]:
623
624
        if model_arch not in self.models:
            return None
625

626
        return _try_load_model_cls(model_arch, self.models[model_arch])
627

628
    def _try_inspect_model_cls(self, model_arch: str) -> Optional[_ModelInfo]:
629
630
        if model_arch not in self.models:
            return None
631

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
        return _try_inspect_model_cls(model_arch, self.models[model_arch])

    def _try_resolve_transformers(
        self,
        architecture: str,
        model_config: ModelConfig,
    ) -> Optional[str]:
        if architecture in _TRANSFORMERS_BACKEND_MODELS:
            return architecture

        auto_map: dict[str, str] = getattr(model_config.hf_config, "auto_map",
                                           None) or dict()

        # Make sure that config class is always initialized before model class,
        # otherwise the model class won't be able to access the config class,
        # the expected auto_map should have correct order like:
        # "auto_map": {
        #     "AutoConfig": "<your-repo-name>--<config-name>",
        #     "AutoModel": "<your-repo-name>--<config-name>",
        #     "AutoModelFor<Task>": "<your-repo-name>--<config-name>",
        # },
        for prefix in ("AutoConfig", "AutoModel"):
            for name, module in auto_map.items():
                if name.startswith(prefix):
                    try_get_class_from_dynamic_module(
                        module,
                        model_config.model,
                        revision=model_config.revision,
                        warn_on_fail=False,
                    )

        model_module = getattr(transformers, architecture, None)

        if model_module is None:
            for name, module in auto_map.items():
                if name.startswith("AutoModel"):
                    model_module = try_get_class_from_dynamic_module(
                        module,
                        model_config.model,
                        revision=model_config.revision,
                        warn_on_fail=True,
                    )
                    if model_module is not None:
                        break
            else:
677
                if model_config.model_impl != "transformers":
678
679
680
681
682
683
684
685
686
687
                    return None

                raise ValueError(
                    f"Cannot find model module. {architecture!r} is not a "
                    "registered model in the Transformers library (only "
                    "relevant if the model is meant to be in Transformers) "
                    "and 'AutoModel' is not present in the model config's "
                    "'auto_map' (relevant if the model is custom).")

        if not model_module.is_backend_compatible():
688
            if model_config.model_impl != "transformers":
689
                return None
690

691
692
693
            raise ValueError(
                f"The Transformers implementation of {architecture!r} "
                "is not compatible with vLLM.")
694

695
        return model_config._get_transformers_backend_cls()
696

697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
    def _normalize_arch(
        self,
        architecture: str,
        model_config: ModelConfig,
    ) -> str:
        if architecture in self.models:
            return architecture

        # This may be called in order to resolve runner_type and convert_type
        # in the first place, in which case we consider the default match
        match = try_match_architecture_defaults(
            architecture,
            runner_type=getattr(model_config, "runner_type", None),
            convert_type=getattr(model_config, "convert_type", None),
        )
        if match:
            suffix, _ = match

            # Get the name of the base model to convert
            for repl_suffix, _ in iter_architecture_defaults():
                base_arch = architecture.replace(suffix, repl_suffix)
                if base_arch in self.models:
                    return base_arch

        return architecture
722

723
724
    def inspect_model_cls(
        self,
725
        architectures: Union[str, list[str]],
726
        model_config: ModelConfig,
727
    ) -> tuple[_ModelInfo, str]:
728
729
        if isinstance(architectures, str):
            architectures = [architectures]
730
731
        if not architectures:
            raise ValueError("No model architectures are specified")
732
733

        # Require transformers impl
734
        if model_config.model_impl == "transformers":
735
736
737
738
739
740
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_info = self._try_inspect_model_cls(arch)
                if model_info is not None:
                    return (model_info, arch)
741
        elif model_config.model_impl == "terratorch":
742
743
            model_info = self._try_inspect_model_cls("Terratorch")
            return (model_info, "Terratorch")
744

745
746
        # Fallback to transformers impl (after resolving convert_type)
        if (all(arch not in self.models for arch in architectures)
747
                and model_config.model_impl == "auto"
748
749
750
751
752
753
754
755
756
757
                and getattr(model_config, "convert_type", "none") == "none"):
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_info = self._try_inspect_model_cls(arch)
                if model_info is not None:
                    return (model_info, arch)

        for arch in architectures:
            normalized_arch = self._normalize_arch(arch, model_config)
758
            model_info = self._try_inspect_model_cls(normalized_arch)
759
            if model_info is not None:
760
                return (model_info, arch)
761

762
763
        # Fallback to transformers impl (before resolving runner_type)
        if (all(arch not in self.models for arch in architectures)
764
                and model_config.model_impl == "auto"):
765
766
767
768
769
770
771
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_info = self._try_inspect_model_cls(arch)
                if model_info is not None:
                    return (model_info, arch)

772
        return self._raise_for_unsupported(architectures)
773

774
775
    def resolve_model_cls(
        self,
776
        architectures: Union[str, list[str]],
777
        model_config: ModelConfig,
778
    ) -> tuple[type[nn.Module], str]:
779
780
        if isinstance(architectures, str):
            architectures = [architectures]
781
782
        if not architectures:
            raise ValueError("No model architectures are specified")
783
784

        # Require transformers impl
785
        if model_config.model_impl == "transformers":
786
787
788
789
790
791
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_cls = self._try_load_model_cls(arch)
                if model_cls is not None:
                    return (model_cls, arch)
792
        elif model_config.model_impl == "terratorch":
793
794
795
796
            arch = "Terratorch"
            model_cls = self._try_load_model_cls(arch)
            if model_cls is not None:
                return (model_cls, arch)
797

798
799
        # Fallback to transformers impl (after resolving convert_type)
        if (all(arch not in self.models for arch in architectures)
800
                and model_config.model_impl == "auto"
801
802
803
804
805
806
807
808
809
810
                and getattr(model_config, "convert_type", "none") == "none"):
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_cls = self._try_load_model_cls(arch)
                if model_cls is not None:
                    return (model_cls, arch)

        for arch in architectures:
            normalized_arch = self._normalize_arch(arch, model_config)
811
            model_cls = self._try_load_model_cls(normalized_arch)
812
813
            if model_cls is not None:
                return (model_cls, arch)
814

815
816
        # Fallback to transformers impl (before resolving runner_type)
        if (all(arch not in self.models for arch in architectures)
817
                and model_config.model_impl == "auto"):
818
819
820
821
822
823
824
            arch = self._try_resolve_transformers(architectures[0],
                                                  model_config)
            if arch is not None:
                model_cls = self._try_load_model_cls(arch)
                if model_cls is not None:
                    return (model_cls, arch)

825
        return self._raise_for_unsupported(architectures)
826

827
828
    def is_text_generation_model(
        self,
829
        architectures: Union[str, list[str]],
830
        model_config: ModelConfig,
831
    ) -> bool:
832
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
833
        return model_cls.is_text_generation_model
834

835
    def is_pooling_model(
836
        self,
837
        architectures: Union[str, list[str]],
838
        model_config: ModelConfig,
839
    ) -> bool:
840
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
841
        return model_cls.is_pooling_model
842

843
844
    def is_cross_encoder_model(
        self,
845
        architectures: Union[str, list[str]],
846
        model_config: ModelConfig,
847
    ) -> bool:
848
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
849
        return model_cls.supports_cross_encoding
850

851
852
    def is_multimodal_model(
        self,
853
        architectures: Union[str, list[str]],
854
        model_config: ModelConfig,
855
    ) -> bool:
856
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
857
        return model_cls.supports_multimodal
858

859
    def is_multimodal_raw_input_only_model(
860
861
        self,
        architectures: Union[str, list[str]],
862
        model_config: ModelConfig,
863
    ) -> bool:
864
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
865
        return model_cls.supports_multimodal_raw_input_only
866

867
868
    def is_pp_supported_model(
        self,
869
        architectures: Union[str, list[str]],
870
        model_config: ModelConfig,
871
    ) -> bool:
872
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
873
        return model_cls.supports_pp
874

875
876
    def model_has_inner_state(
        self,
877
        architectures: Union[str, list[str]],
878
        model_config: ModelConfig,
879
    ) -> bool:
880
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
881
        return model_cls.has_inner_state
882

883
884
    def is_attention_free_model(
        self,
885
        architectures: Union[str, list[str]],
886
        model_config: ModelConfig,
887
    ) -> bool:
888
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
889
        return model_cls.is_attention_free
890

891
892
    def is_hybrid_model(
        self,
893
        architectures: Union[str, list[str]],
894
        model_config: ModelConfig,
895
    ) -> bool:
896
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
897
898
        return model_cls.is_hybrid

899
900
    def is_noops_model(
        self,
901
        architectures: Union[str, list[str]],
902
        model_config: ModelConfig,
903
    ) -> bool:
904
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
905
906
        return model_cls.has_noops

907
908
    def is_transcription_model(
        self,
909
        architectures: Union[str, list[str]],
910
        model_config: ModelConfig,
911
    ) -> bool:
912
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
913
914
        return model_cls.supports_transcription

915
916
917
    def is_transcription_only_model(
        self,
        architectures: Union[str, list[str]],
918
        model_config: ModelConfig,
919
    ) -> bool:
920
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
921
922
        return model_cls.supports_transcription_only

923
924
    def is_v1_compatible(
        self,
925
        architectures: Union[str, list[str]],
926
        model_config: ModelConfig,
927
    ) -> bool:
928
        model_cls, _ = self.inspect_model_cls(architectures, model_config)
929
930
        return not model_cls.supports_v0_only

931
932

ModelRegistry = _ModelRegistry({
933
934
    model_arch:
    _LazyRegisteredModel(
935
936
937
938
939
940
941
942
943
944
        module_name=f"vllm.model_executor.models.{mod_relname}",
        class_name=cls_name,
    )
    for model_arch, (mod_relname, cls_name) in _VLLM_MODELS.items()
})

_T = TypeVar("_T")


def _run_in_subprocess(fn: Callable[[], _T]) -> _T:
945
946
947
948
949
    # NOTE: We use a temporary directory instead of a temporary file to avoid
    # issues like https://stackoverflow.com/questions/23212435/permission-denied-to-write-to-my-temporary-file
    with tempfile.TemporaryDirectory() as tempdir:
        output_filepath = os.path.join(tempdir, "registry_output.tmp")

950
        # `cloudpickle` allows pickling lambda functions directly
951
        import cloudpickle
952
        input_bytes = cloudpickle.dumps((fn, output_filepath))
953
954
955

        # cannot use `sys.executable __file__` here because the script
        # contains relative imports
956
957
958
        returned = subprocess.run(_SUBPROCESS_COMMAND,
                                  input=input_bytes,
                                  capture_output=True)
959
960
961
962
963
964
965
966
967

        # check if the subprocess is successful
        try:
            returned.check_returncode()
        except Exception as e:
            # wrap raised exception to provide more information
            raise RuntimeError(f"Error raised in subprocess:\n"
                               f"{returned.stderr.decode()}") from e

968
        with open(output_filepath, "rb") as f:
969
970
971
972
973
974
975
976
977
978
979
            return pickle.load(f)


def _run() -> None:
    # Setup plugins
    from vllm.plugins import load_general_plugins
    load_general_plugins()

    fn, output_file = pickle.loads(sys.stdin.buffer.read())

    result = fn()
980
981
982

    with open(output_file, "wb") as f:
        f.write(pickle.dumps(result))
983
984
985


if __name__ == "__main__":
986
    _run()