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, 07:15 UTC
[Core] Rework dtype resolution (#18751)
Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>
Cyrus Leung committed
Jun 1, 2025 at 11:04 UTC
6aa8f9a4e7ed7af459476afe4f293f383e35f3e8
13 files changed
+314
-119
tests/basic_correctness/test_basic_correctness.py
+1
-4
@@ -60,7 +60,6 @@ def _fix_prompt_embed_outputs(
60
61
@pytest.mark.parametrize("model", MODELS)
62
@pytest.mark.parametrize("backend", ["FLASH_ATTN"])
63
-@pytest.mark.parametrize("dtype", ["half"])
63
@pytest.mark.parametrize("max_tokens", [5])
64
@pytest.mark.parametrize("enforce_eager", [False])
65
@pytest.mark.parametrize("enable_prompt_embeds", [True, False])
@@ -69,7 +68,6 @@ def test_models(
68
hf_runner,
69
model: str,
70
backend: str,
72
- dtype: str,
71
max_tokens: int,
72
enforce_eager: bool,
73
enable_prompt_embeds: bool,
@@ -97,7 +95,7 @@ def test_models(
95
str(i) for i in range(1024)) + " are:"
96
example_prompts = [prompt]
97
100
- with hf_runner(model, dtype=dtype) as hf_model:
98
+ with hf_runner(model) as hf_model:
99
hf_outputs = hf_model.generate_greedy(example_prompts, max_tokens)
100
if enable_prompt_embeds:
101
with torch.no_grad():
@@ -106,7 +104,6 @@ def test_models(
104
105
with VllmRunner(model,
106
max_model_len=8192,
109
- dtype=dtype,
107
enforce_eager=enforce_eager,
108
enable_prompt_embeds=enable_prompt_embeds,
109
gpu_memory_utilization=0.7) as vllm_model:
tests/conftest.py
+6
-1
@@ -324,7 +324,12 @@ class HfRunner:
324
trust_remote_code=trust_remote_code,
325
)
326
self.device = self.get_default_device()
327
- self.dtype = torch_dtype = _get_and_verify_dtype(self.config, dtype)
327
+ self.dtype = torch_dtype = _get_and_verify_dtype(
328
+ self.model_name,
329
+ self.config,
330
+ dtype=dtype,
331
+ is_pooling_model=is_sentence_transformer or is_cross_encoder,
332
+ )
333
334
model_kwargs = model_kwargs if model_kwargs is not None else {}
335
model_kwargs.setdefault("torch_dtype", torch_dtype)
tests/models/language/pooling/mteb_utils.py
+4
-7
@@ -102,21 +102,18 @@ def mteb_test_embed_models(hf_runner,
102
vllm_main_score = run_mteb_embed_task(VllmMtebEncoder(vllm_model),
103
MTEB_EMBED_TASKS)
104
vllm_dtype = vllm_model.model.llm_engine.model_config.dtype
105
- model_dtype = getattr(
106
- vllm_model.model.llm_engine.model_config.hf_config, "torch_dtype",
107
- vllm_dtype)
105
109
- with set_default_torch_dtype(model_dtype) and hf_runner(
106
+ with set_default_torch_dtype(vllm_dtype) and hf_runner(
107
model_info.name, is_sentence_transformer=True,
111
- dtype=model_dtype) as hf_model:
108
+ dtype=vllm_dtype) as hf_model:
109
110
if hf_model_callback is not None:
111
hf_model_callback(hf_model)
112
113
st_main_score = run_mteb_embed_task(hf_model, MTEB_EMBED_TASKS)
114
118
- print("VLLM:", vllm_dtype, vllm_main_score)
119
- print("SentenceTransformer:", model_dtype, st_main_score)
115
+ print("VLLM:", vllm_main_score)
116
+ print("SentenceTransformers:", st_main_score)
117
print("Difference:", st_main_score - vllm_main_score)
118
119
assert st_main_score == pytest.approx(vllm_main_score, abs=MTEB_EMBED_TOL)
tests/models/language/pooling/test_classification.py
+1
-1
@@ -43,6 +43,6 @@ def test_models(
43
44
# the tolerance value of 1e-2 is selected based on the
45
# half datatype tests in
46
- # tests/models/embedding/language/test_embedding.py
46
+ # tests/models/language/pooling/test_embedding.py
47
assert torch.allclose(hf_output, vllm_output,
48
1e-3 if dtype == "float" else 1e-2)
tests/models/language/pooling/test_embedding.py
+1
-5
@@ -30,13 +30,11 @@ from ...utils import check_embeddings_close
30
pytest.param("sentence-transformers/stsb-roberta-base-v2"),
31
],
32
)
33
-@pytest.mark.parametrize("dtype", ["half"])
33
def test_models(
34
hf_runner,
35
vllm_runner,
36
example_prompts,
37
model,
39
- dtype: str,
38
monkeypatch,
39
) -> None:
40
@@ -58,13 +56,11 @@ def test_models(
56
# So we need to strip the input texts to avoid test failing.
57
example_prompts = [str(s).strip() for s in example_prompts]
58
61
- with hf_runner(model, dtype=dtype,
62
- is_sentence_transformer=True) as hf_model:
59
+ with hf_runner(model, is_sentence_transformer=True) as hf_model:
60
hf_outputs = hf_model.encode(example_prompts)
61
62
with vllm_runner(model,
63
task="embed",
67
- dtype=dtype,
64
max_model_len=None,
65
**vllm_extra_kwargs) as vllm_model:
66
vllm_outputs = vllm_model.encode(example_prompts)
tests/models/multimodal/generation/test_whisper.py
+1
@@ -100,6 +100,7 @@ def run_test(
100
101
with vllm_runner(
102
model,
103
+ dtype="half",
104
max_model_len=448,
105
tensor_parallel_size=tensor_parallel_size,
106
distributed_executor_backend=distributed_executor_backend,
tests/models/multimodal/processing/test_common.py
+1
-1
@@ -40,7 +40,7 @@ def _test_processing_correctness(
40
tokenizer_mode=model_info.tokenizer_mode,
41
trust_remote_code=model_info.trust_remote_code,
42
seed=0,
43
- dtype="float16",
43
+ dtype="auto",
44
revision=None,
45
hf_overrides=model_info.hf_overrides,
46
)
tests/samplers/test_no_bad_words.py
+1
-1
@@ -103,7 +103,7 @@ class TestTwoTokenBadWord:
103
add_special_tokens=False)[0]
104
105
def test_two_token_bad_word(self, vllm_runner):
106
- with vllm_runner(self.MODEL) as llm:
106
+ with vllm_runner(self.MODEL, dtype="half") as llm:
107
output_token_ids = self._generate(llm)
108
assert output_token_ids[:2] == [
109
self.target_token_id1, self.target_token_id2
tests/test_utils.py
+80
-22
@@ -17,7 +17,8 @@ from vllm_test_utils.monitor import monitor
17
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
18
from vllm.utils import (CacheInfo, FlexibleArgumentParser, LRUCache,
19
MemorySnapshot, PlaceholderModule, StoreBoolean,
20
- bind_kv_cache, deprecate_kwargs, get_open_port,
20
+ bind_kv_cache, common_broadcastable_dtype,
21
+ deprecate_kwargs, get_open_port, is_lossless_cast,
22
make_zmq_path, make_zmq_socket, memory_profiling,
23
merge_async_iterators, sha256, split_zmq_path,
24
supports_kw, swap_dict_values)
@@ -567,12 +568,65 @@ def test_lru_cache():
568
assert 6 in cache
569
570
571
+# yapf: disable
572
+@pytest.mark.parametrize(
573
+ ("src_dtype", "tgt_dtype", "expected_result"),
574
+ [
575
+ # Different precision_levels
576
+ (torch.bool, torch.int8, True),
577
+ (torch.bool, torch.float16, True),
578
+ (torch.bool, torch.complex32, True),
579
+ (torch.int64, torch.bool, False),
580
+ (torch.int64, torch.float16, True),
581
+ (torch.int64, torch.complex32, True),
582
+ (torch.float64, torch.bool, False),
583
+ (torch.float64, torch.int8, False),
584
+ (torch.float64, torch.complex32, True),
585
+ (torch.complex128, torch.bool, False),
586
+ (torch.complex128, torch.int8, False),
587
+ (torch.complex128, torch.float16, False),
588
+ # precision_level=0
589
+ (torch.bool, torch.bool, True),
590
+ # precision_level=1
591
+ (torch.int8, torch.int16, True),
592
+ (torch.int16, torch.int8, False),
593
+ (torch.uint8, torch.int8, False),
594
+ (torch.int8, torch.uint8, False),
595
+ # precision_level=2
596
+ (torch.float16, torch.float32, True),
597
+ (torch.float32, torch.float16, False),
598
+ (torch.bfloat16, torch.float32, True),
599
+ (torch.float32, torch.bfloat16, False),
600
+ # precision_level=3
601
+ (torch.complex32, torch.complex64, True),
602
+ (torch.complex64, torch.complex32, False),
603
+ ],
604
+)
605
+# yapf: enable
606
+def test_is_lossless_cast(src_dtype, tgt_dtype, expected_result):
607
+ assert is_lossless_cast(src_dtype, tgt_dtype) == expected_result
608
+
609
+
610
+# yapf: disable
611
+@pytest.mark.parametrize(
612
+ ("dtypes", "expected_result"),
613
+ [
614
+ ([torch.bool], torch.bool),
615
+ ([torch.bool, torch.int8], torch.int8),
616
+ ([torch.bool, torch.int8, torch.float16], torch.float16),
617
+ ([torch.bool, torch.int8, torch.float16, torch.complex32], torch.complex32), # noqa: E501
618
+ ],
619
+)
620
+# yapf: enable
621
+def test_common_broadcastable_dtype(dtypes, expected_result):
622
+ assert common_broadcastable_dtype(dtypes) == expected_result
623
+
624
+
625
def test_placeholder_module_error_handling():
626
placeholder = PlaceholderModule("placeholder_1234")
627
628
def build_ctx():
574
- return pytest.raises(ModuleNotFoundError,
575
- match="No module named")
629
+ return pytest.raises(ModuleNotFoundError, match="No module named")
630
631
with build_ctx():
632
int(placeholder)
@@ -608,6 +662,7 @@ def test_placeholder_module_error_handling():
662
_ = placeholder_attr.module
663
664
665
+# yapf: disable
666
@pytest.mark.parametrize(
667
"obj,key1,key2",
668
[
@@ -618,6 +673,7 @@ def test_placeholder_module_error_handling():
673
# Tests for both keys do not exist
674
({1: "a", 2: "b"}, 3, 4),
675
])
676
+# yapf: enable
677
def test_swap_dict_values(obj, key1, key2):
678
original_obj = obj.copy()
679
swap_dict_values(obj, key1, key2)
@@ -631,19 +687,19 @@ def test_swap_dict_values(obj, key1, key2):
687
assert key1 not in obj
688
689
634
-def test_model_specification(parser_with_config,
635
- cli_config_file,
690
+def test_model_specification(parser_with_config, cli_config_file,
691
cli_config_file_with_model):
692
# Test model in CLI takes precedence over config
638
- args = parser_with_config.parse_args([
639
- 'serve', 'cli-model', '--config', cli_config_file_with_model
640
- ])
693
+ args = parser_with_config.parse_args(
694
+ ['serve', 'cli-model', '--config', cli_config_file_with_model])
695
assert args.model_tag == 'cli-model'
696
assert args.served_model_name == 'mymodel'
697
698
# Test model from config file works
699
args = parser_with_config.parse_args([
646
- 'serve', '--config', cli_config_file_with_model,
700
+ 'serve',
701
+ '--config',
702
+ cli_config_file_with_model,
703
])
704
assert args.model == 'config-model'
705
assert args.served_model_name == 'mymodel'
@@ -654,17 +710,19 @@ def test_model_specification(parser_with_config,
710
711
# Test using --model option raises error
712
with pytest.raises(
657
- ValueError,
658
- match=(
659
- "With `vllm serve`, you should provide the model as a positional "
660
- "argument or in a config file instead of via the `--model` option."
661
- ),
713
+ ValueError,
714
+ match=
715
+ ("With `vllm serve`, you should provide the model as a positional "
716
+ "argument or in a config file instead of via the `--model` option."),
717
):
718
parser_with_config.parse_args(['serve', '--model', 'my-model'])
719
720
# Test other config values are preserved
721
args = parser_with_config.parse_args([
667
- 'serve', 'cli-model', '--config', cli_config_file_with_model,
722
+ 'serve',
723
+ 'cli-model',
724
+ '--config',
725
+ cli_config_file_with_model,
726
])
727
assert args.tensor_parallel_size == 2
728
assert args.trust_remote_code is True
@@ -673,7 +731,7 @@ def test_model_specification(parser_with_config,
731
732
733
@pytest.mark.parametrize("input", [(), ("abc", ), (None, ),
676
- (None, bool, [1, 2, 3])])
734
+ (None, bool, [1, 2, 3])])
735
@pytest.mark.parametrize("output", [0, 1, 2])
736
def test_sha256(input: tuple, output: int):
737
hash = sha256(input)
@@ -682,7 +740,8 @@ def test_sha256(input: tuple, output: int):
740
assert hash != 0
741
742
bytes = pickle.dumps(input, protocol=pickle.HIGHEST_PROTOCOL)
685
- assert hash == int.from_bytes(hashlib.sha256(bytes).digest(), byteorder="big")
743
+ assert hash == int.from_bytes(hashlib.sha256(bytes).digest(),
744
+ byteorder="big")
745
746
# hashing again, returns the same value
747
assert hash == sha256(input)
@@ -698,8 +757,7 @@ def test_sha256(input: tuple, output: int):
757
("tcp://127.0.0.1:5555", ("tcp", "127.0.0.1", "5555")),
758
("tcp://[::1]:5555", ("tcp", "::1", "5555")), # IPv6 address
759
("inproc://some_identifier", ("inproc", "some_identifier", "")),
701
- ]
702
-)
760
+ ])
761
def test_split_zmq_path(path, expected):
762
assert split_zmq_path(path) == expected
763
@@ -711,8 +769,7 @@ def test_split_zmq_path(path, expected):
769
"tcp://127.0.0.1", # Missing port
770
"tcp://[::1]", # Missing port for IPv6
771
"tcp://:5555", # Missing host
714
- ]
715
-)
772
+ ])
773
def test_split_zmq_path_invalid(invalid_path):
774
with pytest.raises(ValueError):
775
split_zmq_path(invalid_path)
@@ -734,7 +791,8 @@ def test_make_zmq_socket_ipv6():
791
zsock: zmq.Socket = make_zmq_socket(ctx, ipv6_path, socket_type)
792
793
# Verify that the IPV6 option is set
737
- assert zsock.getsockopt(zmq.IPV6) == 1, "IPV6 option should be enabled for IPv6 addresses"
794
+ assert zsock.getsockopt(
795
+ zmq.IPV6) == 1, "IPV6 option should be enabled for IPv6 addresses"
796
797
# Clean up
798
zsock.close()
vllm/config.py
+135
-67
@@ -24,6 +24,7 @@ import torch
24
from pydantic import (ConfigDict, SkipValidation, TypeAdapter, field_validator,
25
model_validator)
26
from pydantic.dataclasses import dataclass
27
+from safetensors.torch import _TYPES as _SAFETENSORS_TO_TORCH_DTYPE
28
from torch.distributed import ProcessGroup, ReduceOp
29
from transformers import PretrainedConfig
30
from typing_extensions import deprecated, runtime_checkable
@@ -42,15 +43,16 @@ from vllm.transformers_utils.config import (
43
ConfigFormat, get_config, get_hf_image_processor_config,
44
get_hf_text_config, get_pooling_config,
45
get_sentence_transformer_tokenizer_config, is_encoder_decoder,
45
- try_get_generation_config, uses_mrope)
46
+ try_get_generation_config, try_get_safetensors_metadata, uses_mrope)
47
from vllm.transformers_utils.s3_utils import S3Model
48
from vllm.transformers_utils.utils import is_s3, maybe_model_redirect
49
from vllm.utils import (DEFAULT_MAX_NUM_BATCHED_TOKENS,
50
MULTIMODAL_MODEL_MAX_NUM_BATCHED_TOKENS,
51
POOLING_MODEL_MAX_NUM_BATCHED_TOKENS, GiB_bytes,
51
- LayerBlockType, cuda_device_count_stateless,
52
- get_cpu_memory, get_open_port, is_torch_equal_or_newer,
53
- random_uuid, resolve_obj_by_qualname)
52
+ LayerBlockType, common_broadcastable_dtype,
53
+ cuda_device_count_stateless, get_cpu_memory,
54
+ get_open_port, is_torch_equal_or_newer, random_uuid,
55
+ resolve_obj_by_qualname)
56
57
if TYPE_CHECKING:
58
from _typeshed import DataclassInstance
@@ -540,7 +542,24 @@ class ModelConfig:
542
self.encoder_config = self._get_encoder_config()
543
self.hf_image_processor_config = get_hf_image_processor_config(
544
self.model, hf_token=self.hf_token, revision=self.revision)
543
- self.dtype = _get_and_verify_dtype(self.hf_config, self.dtype)
545
+
546
+ supported_tasks, task = self._resolve_task(self.task)
547
+ self.supported_tasks = supported_tasks
548
+ self.task = task
549
+ if self.task in ("draft", "generate"):
550
+ self.truncation_side = "left"
551
+ else:
552
+ self.truncation_side = "right"
553
+
554
+ self.pooler_config = self._init_pooler_config()
555
+
556
+ self.dtype = _get_and_verify_dtype(
557
+ self.model,
558
+ self.hf_config,
559
+ self.dtype,
560
+ is_pooling_model=self.runner_type == "pooling",
561
+ revision=self.revision,
562
+ )
563
564
# Workaround for Gemma 2 which uses interleaved sliding window
565
# attention, but it's not specified in its config. TODO: remove this
@@ -597,16 +616,6 @@ class ModelConfig:
616
raise ValueError(
617
"`override_neuron_config` is only supported on Neuron.")
618
600
- supported_tasks, task = self._resolve_task(self.task)
601
- self.supported_tasks = supported_tasks
602
- self.task = task
603
- if self.task in ("draft", "generate"):
604
- self.truncation_side = "left"
605
- else:
606
- self.truncation_side = "right"
607
-
608
- self.pooler_config = self._init_pooler_config()
609
-
619
self._verify_quantization()
620
self._verify_cuda_graph()
621
self._verify_bnb_config()
@@ -692,7 +701,6 @@ class ModelConfig:
701
self.model, self.revision)
702
703
def _init_pooler_config(self) -> Optional["PoolerConfig"]:
695
-
704
if self.runner_type == "pooling":
705
if isinstance(self.override_pooler_config, dict):
706
self.override_pooler_config = PoolerConfig(
@@ -3074,13 +3082,37 @@ _STR_DTYPE_TO_TORCH_DTYPE = {
3082
"bfloat16": torch.bfloat16,
3083
}
3084
3077
-_ROCM_NOT_SUPPORTED_DTYPE: list[str] = [] #
3085
+# model_type -> reason
3086
+_FLOAT16_NOT_SUPPORTED_MODELS = {
3087
+ "gemma2": "Numerical instability. Please use bfloat16 or float32 instead.",
3088
+ "gemma3": "Numerical instability. Please use bfloat16 or float32 instead.",
3089
+ "plamo2": "Numerical instability. Please use bfloat16 or float32 instead.",
3090
+ "glm4": "Numerical instability. Please use bfloat16 or float32 instead.",
3091
+}
3092
3093
3080
-def _get_and_verify_dtype(
3094
+def _is_valid_dtype(model_type: str, dtype: torch.dtype):
3095
+ if model_type in _FLOAT16_NOT_SUPPORTED_MODELS and dtype == torch.float16: # noqa: E501, SIM103
3096
+ return False
3097
+
3098
+ return True
3099
+
3100
+
3101
+def _check_valid_dtype(model_type: str, dtype: torch.dtype):
3102
+ if model_type in _FLOAT16_NOT_SUPPORTED_MODELS and dtype == torch.float16:
3103
+ reason = _FLOAT16_NOT_SUPPORTED_MODELS[model_type]
3104
+ raise ValueError(f"The model type {model_type!r} "
3105
+ f"does not support float16. Reason: {reason}")
3106
+
3107
+ return True
3108
+
3109
+
3110
+def _find_dtype(
3111
+ model_id: str,
3112
config: PretrainedConfig,
3082
- dtype: Union[str, torch.dtype],
3083
-) -> torch.dtype:
3113
+ *,
3114
+ revision: Optional[str],
3115
+):
3116
# NOTE: getattr(config, "torch_dtype", torch.float32) is not correct
3117
# because config.torch_dtype can be None.
3118
config_dtype = getattr(config, "torch_dtype", None)
@@ -3092,75 +3124,111 @@ def _get_and_verify_dtype(
3124
if config_dtype is None and hasattr(config, "vision_config"):
3125
config_dtype = getattr(config.vision_config, "torch_dtype", None)
3126
3127
+ # Try to read the dtype of the weights if they are in safetensors format
3128
+ if config_dtype is None:
3129
+ repo_mt = try_get_safetensors_metadata(model_id, revision=revision)
3130
+
3131
+ if repo_mt and (files_mt := repo_mt.files_metadata):
3132
+ param_dtypes: set[torch.dtype] = {
3133
+ _SAFETENSORS_TO_TORCH_DTYPE[dtype_str]
3134
+ for file_mt in files_mt.values()
3135
+ for dtype_str in file_mt.parameter_count
3136
+ if dtype_str in _SAFETENSORS_TO_TORCH_DTYPE
3137
+ }
3138
+
3139
+ if param_dtypes:
3140
+ return common_broadcastable_dtype(param_dtypes)
3141
+
3142
if config_dtype is None:
3143
config_dtype = torch.float32
3144
3098
- if isinstance(dtype, str):
3099
- dtype = dtype.lower()
3100
- if dtype == "auto":
3101
- # Set default dtype from model config
3102
- if config_dtype == torch.float32:
3103
- # Following common practice, we use float16 for float32 models
3104
- torch_dtype = torch.float16
3105
- else:
3106
- torch_dtype = config_dtype
3145
+ return config_dtype
3146
3108
- if config.model_type == "plamo2":
3109
- logger.warning(
3110
- "For PLaMo2, we cast models to bfloat16 instead of using "
3111
- "float16 by default. This is because float16 does not work."
3112
- )
3113
- torch_dtype = torch.bfloat16
3147
3115
- # Deal with torch dtype fallback for device compatibility.
3116
- from vllm.platforms import current_platform
3117
- if torch_dtype not in current_platform.supported_dtypes:
3118
- device_name = current_platform.get_device_name()
3148
+def _resolve_auto_dtype(
3149
+ model_type: str,
3150
+ config_dtype: torch.dtype,
3151
+ *,
3152
+ is_pooling_model: bool,
3153
+):
3154
+ from vllm.platforms import current_platform
3155
3120
- if ((capability := current_platform.get_device_capability())
3121
- is None):
3122
- compute_str = ""
3123
- else:
3124
- version_str = capability.as_version_str()
3125
- compute_str = f" (with compute capability {version_str})"
3126
- fallback_dtype = current_platform.supported_dtypes[0]
3127
- logger.warning(
3128
- "Your %s device%s doesn't support %s. " \
3129
- "Falling back to %s for compatibility.",
3130
- device_name, compute_str, torch_dtype, fallback_dtype
3131
- )
3132
- torch_dtype = fallback_dtype
3156
+ supported_dtypes = [
3157
+ dtype for dtype in current_platform.supported_dtypes
3158
+ if _is_valid_dtype(model_type, dtype)
3159
+ ]
3160
3134
- if current_platform.is_hpu() and torch_dtype == torch.float16:
3135
- logger.warning(
3136
- "For HPU, we cast models to bfloat16 instead of "
3137
- "using float16 by default. Please specify `dtype` if you "
3138
- "want to use float16.")
3139
- torch_dtype = torch.bfloat16
3140
- elif dtype == "float16" and config.model_type == "plamo2":
3141
- logger.warning(
3142
- "For PLaMo2, using float16 is unstable and might cause "
3143
- "unexpected behavior. Please use bfloat16 or float32 instead.")
3144
- torch_dtype = torch.float16
3161
+ if is_pooling_model and torch.float16 in supported_dtypes:
3162
+ preferred_dtype = torch.float16
3163
+ else:
3164
+ preferred_dtype = supported_dtypes[0]
3165
+
3166
+ # Downcast for float32 models
3167
+ if config_dtype == torch.float32:
3168
+ config_dtype = preferred_dtype
3169
+
3170
+ if config_dtype in supported_dtypes:
3171
+ return config_dtype
3172
+
3173
+ # Ensure device compatibility
3174
+ device_name = current_platform.get_device_name()
3175
+ device_capability = current_platform.get_device_capability()
3176
+
3177
+ if device_capability is None:
3178
+ device_str = f"{device_name!r}"
3179
+ else:
3180
+ version_str = device_capability.as_version_str()
3181
+ device_str = f"{device_name!r} (with compute capability {version_str})"
3182
+
3183
+ logger.warning(
3184
+ "Your device %s doesn't support %s. "
3185
+ "Falling back to %s for compatibility.",
3186
+ device_str,
3187
+ config_dtype,
3188
+ preferred_dtype,
3189
+ )
3190
+
3191
+ return preferred_dtype
3192
+
3193
+
3194
+def _get_and_verify_dtype(
3195
+ model_id: str,
3196
+ config: PretrainedConfig,
3197
+ dtype: Union[str, torch.dtype],
3198
+ *,
3199
+ is_pooling_model: bool,
3200
+ revision: Optional[str] = None,
3201
+) -> torch.dtype:
3202
+ config_dtype = _find_dtype(model_id, config, revision=revision)
3203
+ model_type = config.model_type
3204
+
3205
+ if isinstance(dtype, str):
3206
+ dtype = dtype.lower()
3207
+ if dtype == "auto":
3208
+ # Set default dtype from model config
3209
+ torch_dtype = _resolve_auto_dtype(
3210
+ model_type,
3211
+ config_dtype,
3212
+ is_pooling_model=is_pooling_model,
3213
+ )
3214
else:
3215
if dtype not in _STR_DTYPE_TO_TORCH_DTYPE:
3147
- raise ValueError(f"Unknown dtype: {dtype}")
3216
+ raise ValueError(f"Unknown dtype: {dtype!r}")
3217
torch_dtype = _STR_DTYPE_TO_TORCH_DTYPE[dtype]
3218
elif isinstance(dtype, torch.dtype):
3219
torch_dtype = dtype
3220
else:
3221
raise ValueError(f"Unknown dtype: {dtype}")
3222
3154
- # Verify the dtype.
3223
+ _check_valid_dtype(model_type, torch_dtype)
3224
+
3225
if torch_dtype != config_dtype:
3226
if torch_dtype == torch.float32:
3227
# Upcasting to float32 is allowed.
3228
logger.info("Upcasting %s to %s.", config_dtype, torch_dtype)
3159
- pass
3229
elif config_dtype == torch.float32:
3230
# Downcasting from float32 to float16 or bfloat16 is allowed.
3231
logger.info("Downcasting %s to %s.", config_dtype, torch_dtype)
3163
- pass
3232
else:
3233
# Casting between float16 and bfloat16 is allowed with a warning.
3234
logger.warning("Casting %s to %s.", config_dtype, torch_dtype)
vllm/platforms/cpu.py
+1
-1
@@ -28,7 +28,7 @@ class CpuPlatform(Platform):
28
dispatch_key: str = "CPU"
29
30
@property
31
- def supported_dtypes(self) -> list:
31
+ def supported_dtypes(self) -> list[torch.dtype]:
32
if self.get_cpu_architecture() == CpuArchEnum.POWERPC:
33
return [torch.bfloat16, torch.float32]
34
elif sys.platform.startswith(
vllm/transformers_utils/config.py
+33
-7
@@ -4,12 +4,12 @@ import enum
4
import json
5
import os
6
import time
7
-from functools import cache
7
+from functools import cache, partial
8
from pathlib import Path
9
-from typing import Any, Callable, Literal, Optional, Union
9
+from typing import Any, Callable, Literal, Optional, TypeVar, Union
10
11
import huggingface_hub
12
-from huggingface_hub import hf_hub_download
12
+from huggingface_hub import get_safetensors_metadata, hf_hub_download
13
from huggingface_hub import list_repo_files as hf_list_repo_files
14
from huggingface_hub import try_to_load_from_cache
15
from huggingface_hub.utils import (EntryNotFoundError, HfHubHTTPError,
@@ -93,10 +93,15 @@ class ConfigFormat(str, enum.Enum):
93
MISTRAL = "mistral"
94
95
96
-def with_retry(func: Callable[[], Any],
97
- log_msg: str,
98
- max_retries: int = 2,
99
- retry_delay: int = 2):
96
+_R = TypeVar("_R")
97
+
98
+
99
+def with_retry(
100
+ func: Callable[[], _R],
101
+ log_msg: str,
102
+ max_retries: int = 2,
103
+ retry_delay: int = 2,
104
+) -> _R:
105
for attempt in range(max_retries):
106
try:
107
return func()
@@ -109,6 +114,8 @@ def with_retry(func: Callable[[], Any],
114
time.sleep(retry_delay)
115
retry_delay *= 2
116
117
+ raise AssertionError("Should not be reached")
118
+
119
120
# @cache doesn't cache exceptions
121
@cache
@@ -840,3 +847,22 @@ def get_cross_encoder_activation_function(config: PretrainedConfig):
847
return resolve_obj_by_qualname(function_name)()
848
else:
849
return nn.Sigmoid() if config.num_labels == 1 else nn.Identity()
850
+
851
+
852
+def try_get_safetensors_metadata(
853
+ model: str,
854
+ *,
855
+ revision: Optional[str] = None,
856
+):
857
+ get_safetensors_metadata_partial = partial(
858
+ get_safetensors_metadata,
859
+ model,
860
+ revision=revision,
861
+ token=os.getenv('HF_TOKEN', None),
862
+ )
863
+
864
+ try:
865
+ return with_retry(get_safetensors_metadata_partial,
866
+ "Error retrieving safetensors")
867
+ except Exception:
868
+ return None
vllm/utils.py
+49
-2
@@ -37,8 +37,8 @@ from argparse import (Action, ArgumentDefaultsHelpFormatter, ArgumentParser,
37
_ArgumentGroup)
38
from asyncio import FIRST_COMPLETED, AbstractEventLoop, Task
39
from collections import UserDict, defaultdict
40
-from collections.abc import (AsyncGenerator, Awaitable, Generator, Hashable,
41
- Iterable, Iterator, KeysView, Mapping)
40
+from collections.abc import (AsyncGenerator, Awaitable, Collection, Generator,
41
+ Hashable, Iterable, Iterator, KeysView, Mapping)
42
from concurrent.futures.process import ProcessPoolExecutor
43
from dataclasses import dataclass, field
44
from functools import cache, lru_cache, partial, wraps
@@ -979,6 +979,53 @@ def get_dtype_size(dtype: torch.dtype) -> int:
979
return torch.tensor([], dtype=dtype).element_size()
980
981
982
+# bool = 0, int = 1, float = 2, complex = 3
983
+def _get_precision_level(dtype: torch.dtype) -> int:
984
+ # NOTE: Complex dtypes return `is_floating_point=False`
985
+ return ((dtype != torch.bool) + dtype.is_floating_point +
986
+ dtype.is_complex * 2)
987
+
988
+
989
+def is_lossless_cast(src_dtype: torch.dtype, tgt_dtype: torch.dtype):
990
+ """
991
+ Test whether it is lossless to cast a tensor from
992
+ `src_dtype` to `tgt_dtype`.
993
+ """
994
+ if src_dtype == tgt_dtype:
995
+ return True
996
+
997
+ src_level = _get_precision_level(src_dtype)
998
+ tgt_level = _get_precision_level(tgt_dtype)
999
+
1000
+ if src_level < tgt_level:
1001
+ return True
1002
+ if src_level > tgt_level:
1003
+ return False
1004
+
1005
+ # Compare integral types
1006
+ if not src_dtype.is_floating_point and not src_dtype.is_complex:
1007
+ src_info = torch.iinfo(src_dtype)
1008
+ tgt_info = torch.iinfo(tgt_dtype)
1009
+ return src_info.min >= tgt_info.min and src_info.max <= tgt_info.max
1010
+
1011
+ # Compare floating-point types
1012
+ src_info = torch.finfo(src_dtype)
1013
+ tgt_info = torch.finfo(tgt_dtype)
1014
+ return (src_info.min >= tgt_info.min and src_info.max <= tgt_info.max
1015
+ and src_info.resolution >= tgt_info.resolution)
1016
+
1017
+
1018
+def common_broadcastable_dtype(dtypes: Collection[torch.dtype]):
1019
+ """
1020
+ Get the common `dtype` where all of the other `dtypes` can be
1021
+ cast to it without losing any information.
1022
+ """
1023
+ return max(
1024
+ dtypes,
1025
+ key=lambda dtype: sum(is_lossless_cast(dt, dtype) for dt in dtypes),
1026
+ )
1027
+
1028
+
1029
# `collections` helpers
1030
def is_list_of(
1031
value: object,