Bump Transformers version to 5.10.4 (#41359)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Harry Mellor committed Jul 7, 2026 at 13:13 UTC dd944845777b303000a2e153707382cf03383e26
51 files changed +271 -234
benchmarks/backend_request_func.py
+2 -2
@@ -12,7 +12,7 @@ from dataclasses import dataclass, field
12 import aiohttp
13 import huggingface_hub.constants
14 from tqdm.asyncio import tqdm
15 -from transformers import AutoTokenizer, PreTrainedTokenizer, PreTrainedTokenizerFast
15 +from transformers import AutoTokenizer, PythonBackend, TokenizersBackend
16
17 # NOTE(simon): do not import vLLM here so the benchmark script
18 # can run without vLLM installed.
@@ -609,7 +609,7 @@ def get_tokenizer(
609 tokenizer_mode: str = "auto",
610 trust_remote_code: bool = False,
611 **kwargs,
612 -) -> PreTrainedTokenizer | PreTrainedTokenizerFast:
612 +) -> PythonBackend | TokenizersBackend:
613 if pretrained_model_name_or_path is not None and not os.path.exists(
614 pretrained_model_name_or_path
615 ):
docs/features/reasoning_outputs.md
+1 -1
@@ -439,7 +439,7 @@ Additionally, to enable structured output, you'll need to create a new `Reasoner
439 end_token: str = "</think>"
440
441 @classmethod
442 - def from_tokenizer(cls, tokenizer: PreTrainedTokenizer) -> Reasoner:
442 + def from_tokenizer(cls, tokenizer: PythonBackend) -> Reasoner:
443 return cls(
444 start_token_id=tokenizer.encode("<think>", add_special_tokens=False)[0],
445 end_token_id=tokenizer.encode("</think>", add_special_tokens=False)[0],
examples/features/prompt_embed/prompt_embed_offline.py
+4 -4
@@ -19,7 +19,7 @@ Run:
19 """
20
21 import torch
22 -from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedTokenizer
22 +from transformers import AutoModelForCausalLM, AutoTokenizer, PythonBackend
23
24 from vllm import LLM
25
@@ -34,7 +34,7 @@ def init_tokenizer_and_llm(model_name: str):
34
35 def get_prompt_embeds(
36 chat: list[dict[str, str]],
37 - tokenizer: PreTrainedTokenizer,
37 + tokenizer: PythonBackend,
38 embedding_layer: torch.nn.Module,
39 ):
40 token_ids = tokenizer.apply_chat_template(
@@ -45,7 +45,7 @@ def get_prompt_embeds(
45
46
47 def single_prompt_inference(
48 - llm: LLM, tokenizer: PreTrainedTokenizer, embedding_layer: torch.nn.Module
48 + llm: LLM, tokenizer: PythonBackend, embedding_layer: torch.nn.Module
49 ):
50 chat = [{"role": "user", "content": "Please tell me about the capital of France."}]
51 prompt_embeds = get_prompt_embeds(chat, tokenizer, embedding_layer)
@@ -64,7 +64,7 @@ def single_prompt_inference(
64
65
66 def batch_prompt_inference(
67 - llm: LLM, tokenizer: PreTrainedTokenizer, embedding_layer: torch.nn.Module
67 + llm: LLM, tokenizer: PythonBackend, embedding_layer: torch.nn.Module
68 ):
69 chats = [
70 [{"role": "user", "content": "Please tell me about the capital of France."}],
requirements/test/cpu.txt
+2 -2
@@ -672,7 +672,7 @@ pathvalidate==3.2.1
672 # via pytablewriter
673 patsy==1.0.1
674 # via statsmodels
675 -peft==0.18.1
675 +peft==0.19.1
676 # via -r requirements/test/cuda.in
677 perceptron==0.1.4
678 # via -r requirements/test/cuda.in
@@ -1159,7 +1159,7 @@ tqdm==4.67.3
1159 # segmentation-models-pytorch
1160 # sentence-transformers
1161 # transformers
1162 -transformers==5.5.3
1162 +transformers==5.10.4
1163 # via
1164 # -r requirements/test/../common.txt
1165 # -r requirements/test/cuda.in
requirements/test/cuda.in
+2 -2
@@ -20,7 +20,7 @@ httpx
20 librosa # required for audio tests
21 vector_quantize_pytorch # required for minicpmo_26 test
22 vocos # required for minicpmo_26 test
23 -peft>=0.18.1 # required for phi-4-mm test
23 +peft>=0.19.1 # required for phi-4-mm test
24 pqdm
25 ray[cgraph,default]>=2.48.0 # Ray Compiled Graph, required by pipeline parallelism tests
26 sentence-transformers>=5.2.0 # required for embedding tests
@@ -39,7 +39,7 @@ open_clip_torch==2.32.0 # Required for nemotron_vl test, Nemotron Parse in test_
39 datamodel_code_generator # required for minicpm3 test
40 lm-eval[api]>=0.4.12 # required for model evaluation test
41 mteb[bm25s]>=2, <3 # required for mteb test
42 -transformers==5.5.3
42 +transformers==5.10.4
43 tokenizers==0.22.2
44 schemathesis>=4.0.0 # Required for openai schema test.
45 # quantization
requirements/test/cuda.txt
+2 -2
@@ -755,7 +755,7 @@ pathvalidate==3.2.1
755 # via pytablewriter
756 patsy==1.0.1
757 # via statsmodels
758 -peft==0.18.1
758 +peft==0.19.1
759 # via -r requirements/test/cuda.in
760 perceptron==0.1.4
761 # via -r requirements/test/cuda.in
@@ -1261,7 +1261,7 @@ tqdm==4.67.3
1261 # segmentation-models-pytorch
1262 # sentence-transformers
1263 # transformers
1264 -transformers==5.5.3
1264 +transformers==5.10.4
1265 # via
1266 # -c requirements/common.txt
1267 # -r requirements/test/../common.txt
requirements/test/nightly-torch.txt
+1 -1
@@ -29,7 +29,7 @@ opencv-python-headless >= 4.13.0 # required for video test
29 datamodel_code_generator # required for minicpm3 test
30 lm-eval[api]>=0.4.12 # required for model evaluation test
31 mteb[bm25s]>=2, <3 # required for mteb test
32 -transformers==5.5.3
32 +transformers==5.10.4
33 tokenizers==0.22.2
34 schemathesis>=4.0.0 # Required for openai schema test.
35 # quantization
requirements/test/rocm.in
+2 -2
@@ -19,7 +19,7 @@ httpx
19 librosa # required for audio tests
20 vector_quantize_pytorch # required for minicpmo_26 test
21 vocos # required for minicpmo_26 test
22 -peft>=0.15.0 # required for phi-4-mm test
22 +peft>=0.19.1 # required for phi-4-mm test
23 pqdm
24 ray[cgraph,default]>=2.48.0 # Ray Compiled Graph, required by pipeline parallelism tests
25 sentence-transformers>=5.2.0 # required for embedding tests
@@ -35,7 +35,7 @@ open_clip_torch==2.32.0 # Required for nemotron_vl test, Nemotron Parse in test_
35 datamodel_code_generator # required for minicpm3 test
36 lm-eval[api]>=0.4.12 # required for model evaluation test
37 mteb[bm25s]>=2, <3 # required for mteb test
38 -transformers==5.5.3
38 +transformers==5.10.4
39 tokenizers==0.22.2
40 schemathesis>=4.0.0 # Required for openai schema test
41 # quantization
requirements/test/rocm.txt
+2 -2
@@ -731,7 +731,7 @@ pathvalidate==3.3.1
731 # via pytablewriter
732 patsy==1.0.2
733 # via statsmodels
734 -peft==0.18.1
734 +peft==0.19.1
735 # via -r requirements/test/rocm.in
736 perceptron==0.1.4
737 # via -r requirements/test/rocm.in
@@ -1218,7 +1218,7 @@ tqdm==4.67.3
1218 # sentence-transformers
1219 # tilelang
1220 # transformers
1221 -transformers==5.5.3
1221 +transformers==5.10.4
1222 # via
1223 # -c requirements/common.txt
1224 # -r requirements/test/../common.txt
requirements/test/xpu.in
+1
@@ -17,6 +17,7 @@ accelerate
17 arctic-inference
18 lm_eval[api]>=0.4.12
19 modelscope<1.38
20 +transformers==5.10.4
21
22 # --- Audio Processing ---
23 librosa
requirements/test/xpu.txt
+2 -1
@@ -940,10 +940,11 @@ tqdm==4.67.3
940 # pqdm
941 # sentence-transformers
942 # transformers
943 -transformers==5.5.3
943 +transformers==5.10.4
944 # via
945 # -c requirements/common.txt
946 # -r requirements/test/../common.txt
947 + # -r requirements/test/xpu.in
948 # compressed-tensors
949 # sentence-transformers
950 # xgrammar
rust/src/text/src/backend/hf/model_files.rs
+1 -1
@@ -378,7 +378,7 @@ mod tests {
378 fs::write(dir.path().join("tokenizer.json"), "{}").expect("write tokenizer");
379 fs::write(
380 dir.path().join("tokenizer_config.json"),
381 - r#"{"tokenizer_class":"PreTrainedTokenizerFast"}"#,
381 + r#"{"tokenizer_class":"TokenizersBackend"}"#,
382 )
383 .expect("write tokenizer config");
384 fs::write(dir.path().join("config.json"), "{}").expect("write config");
tests/conftest.py
+2 -2
@@ -74,7 +74,7 @@ from torch._inductor.utils import fresh_cache
74
75
76 if TYPE_CHECKING:
77 - from transformers import PreTrainedTokenizer, PreTrainedTokenizerFast
77 + from transformers import PythonBackend, TokenizersBackend
78 from transformers.generation.utils import GenerateOutput
79
80
@@ -499,7 +499,7 @@ class HfRunner:
499 self.model = model
500
501 if not skip_tokenizer_init:
502 - self.tokenizer: "PreTrainedTokenizer | PreTrainedTokenizerFast" = (
502 + self.tokenizer: "PythonBackend | TokenizersBackend" = (
503 AutoTokenizer.from_pretrained(
504 tokenizer_name or model_name,
505 trust_remote_code=trust_remote_code,
tests/entrypoints/pooling/embed/test_online.py
+2 -2
@@ -369,7 +369,7 @@ async def test_chat_request(
369 assert output.object == "list"
370 assert len(output.data) == 1
371 assert output.model == MODEL_NAME
372 - assert output.usage.prompt_tokens == 34
372 + assert output.usage.prompt_tokens == 33
373
374 # test continue_final_message
375 response = requests.post(
@@ -401,7 +401,7 @@ async def test_chat_request(
401 assert output.object == "list"
402 assert len(output.data) == 1
403 assert output.model == MODEL_NAME
404 - assert output.usage.prompt_tokens == 36
404 + assert output.usage.prompt_tokens == 35
405
406 # test continue_final_message with add_generation_prompt
407 response = requests.post(
tests/models/multimodal/generation/test_common.py
-1
@@ -906,7 +906,6 @@ VLM_TEST_SETTINGS = {
906 max_model_len=4096,
907 use_tokenizer_eos=True,
908 auto_cls=AutoModelForImageTextToText,
909 - hf_model_kwargs=model_utils.qianfan_ocr_hf_model_kwargs("baidu/Qianfan-OCR"),
909 ),
910 "qwen2_vl": VLMTestInfo(
911 models=["Qwen/Qwen2-VL-2B-Instruct"],
tests/models/multimodal/generation/vlm_utils/model_utils.py
-91
@@ -1492,94 +1492,3 @@ def moondream3_patch_hf_runner(hf_model: HfRunner) -> HfRunner:
1492
1493 hf_model.model.generate = types.MethodType(_generate, hf_model.model)
1494 return hf_model
1495 -
1496 -
1497 -def qianfan_ocr_hf_model_kwargs(model_name: str) -> dict:
1498 - """Return hf_model_kwargs with a patched config for QianfanOCR."""
1499 - from vllm.transformers_utils.configs.qianfan_ocr import QianfanOCRConfig
1500 -
1501 - config = QianfanOCRConfig.from_pretrained(model_name)
1502 - vc = config.vision_config
1503 - if isinstance(vc.image_size, int):
1504 - vc.image_size = (vc.image_size, vc.image_size)
1505 - if isinstance(vc.patch_size, int):
1506 - vc.patch_size = (vc.patch_size, vc.patch_size)
1507 - return {"config": config}
1508 -
1509 -
1510 -def qianfan_ocr_patch_hf_runner(hf_model: HfRunner) -> HfRunner:
1511 - """Patches an HfRunner instance to run QianfanOCR model inference.
1512 -
1513 - QianfanOCR shares the same architecture as InternVLChatModel, so the
1514 - patching logic mirrors ``internvl_patch_hf_runner``. The only difference
1515 - is that we load the config via vllm's registered ``QianfanOCRConfig``
1516 - instead of relying on ``trust_remote_code``.
1517 - """
1518 -
1519 - class QianfanOCRProcessor:
1520 - def __init__(self, hf_runner: HfRunner):
1521 - self.tokenizer = hf_runner.tokenizer
1522 -
1523 - from vllm.transformers_utils.configs.qianfan_ocr import QianfanOCRConfig
1524 -
1525 - self.config = QianfanOCRConfig.from_pretrained(hf_runner.model_name)
1526 - self.vision_config = self.config.vision_config
1527 - self.use_thumbnail = self.config.use_thumbnail
1528 - self.min_num = self.config.min_dynamic_patch
1529 - self.max_num = self.config.max_dynamic_patch
1530 - self.image_size = self.vision_config.image_size
1531 -
1532 - # Compute num_image_token from config instead of model attribute,
1533 - # since the transformers-native model doesn't expose it.
1534 - image_size = self.config.force_image_size or self.vision_config.image_size
1535 - patch_size = self.vision_config.patch_size
1536 - downsample_ratio = self.config.downsample_ratio
1537 - self.num_image_token = int(
1538 - (image_size // patch_size) ** 2 * (downsample_ratio**2)
1539 - )
1540 -
1541 - def __call__(
1542 - self,
1543 - text: str,
1544 - images: PIL.Image.Image | list[PIL.Image.Image] = None,
1545 - **kwargs,
1546 - ):
1547 - from vllm.transformers_utils.processors.internvl import (
1548 - image_to_pixel_values_internvl,
1549 - )
1550 -
1551 - IMG_START = "<img>"
1552 - IMG_END = "</img>"
1553 - IMG_CONTEXT = "<IMG_CONTEXT>"
1554 -
1555 - images = [images] if isinstance(images, PIL.Image.Image) else images
1556 - pixel_values_list = [
1557 - image_to_pixel_values_internvl(
1558 - image,
1559 - input_size=self.image_size,
1560 - min_num=self.min_num,
1561 - max_num=self.max_num,
1562 - use_thumbnail=self.use_thumbnail,
1563 - )
1564 - for image in images
1565 - ]
1566 - num_patches_list = [pv.shape[0] for pv in pixel_values_list]
1567 - pixel_values = torch.cat(pixel_values_list, dim=0)
1568 -
1569 - for num_patches in num_patches_list:
1570 - context_tokens = IMG_CONTEXT * self.num_image_token * num_patches
1571 - image_tokens = IMG_START + context_tokens + IMG_END
1572 - text = text.replace("<image>", image_tokens, 1)
1573 -
1574 - prompt = self.tokenizer(text, return_tensors="pt")
1575 - prompt.update({"pixel_values": pixel_values})
1576 - return prompt
1577 -
1578 - img_context_token_id = hf_model.tokenizer.convert_tokens_to_ids("<IMG_CONTEXT>")
1579 - hf_model.model.img_context_token_id = img_context_token_id
1580 - hf_model.processor = QianfanOCRProcessor(hf_model)
1581 - hf_model.model.get_output_embeddings = (
1582 - lambda: hf_model.model.language_model.get_output_embeddings()
1583 - )
1584 - hf_model.model.generate = types.MethodType(_internvl_generate, hf_model.model)
1585 - return hf_model
tests/models/registry.py
+1 -3
@@ -202,9 +202,7 @@ _TEXT_GENERATION_EXAMPLE_MODELS = {
202 "AfmoeForCausalLM": _HfExamplesInfo("arcee-ai/Trinity-Nano-Preview"),
203 "ApertusForCausalLM": _HfExamplesInfo("swiss-ai/Apertus-8B-Instruct-2509"),
204 "ArceeForCausalLM": _HfExamplesInfo("arcee-ai/AFM-4.5B-Base"),
205 - "ArcticForCausalLM": _HfExamplesInfo(
206 - "Snowflake/snowflake-arctic-instruct", trust_remote_code=True
207 - ),
205 + "ArcticForCausalLM": _HfExamplesInfo("Snowflake/snowflake-arctic-instruct"),
206 "AXK1ForCausalLM": _HfExamplesInfo("skt/A.X-K1", trust_remote_code=True),
207 "BailingMoeForCausalLM": _HfExamplesInfo(
208 "inclusionAI/Ling-lite-1.5", trust_remote_code=True
tests/models/utils.py
+1
@@ -486,6 +486,7 @@ def dummy_hf_overrides(
486 "Gemma3nForConditionalGeneration",
487 "Gemma4ForCausalLM",
488 "Gemma4ForConditionalGeneration",
489 + "Gemma4MTPModel",
490 "DiffusionGemmaForBlockDiffusion",
491 )
492 else 1
tests/tokenizers_/test_basic.py
+3 -3
@@ -5,7 +5,7 @@ from typing import _get_protocol_attrs # type: ignore
5 import pytest
6 from transformers import (
7 PreTrainedTokenizerBase,
8 - PreTrainedTokenizerFast,
8 + TokenizersBackend,
9 )
10
11 from vllm.tokenizers import TokenizerLike, get_tokenizer
@@ -24,7 +24,7 @@ def _assert_tokenizer_like(tokenizer: object):
24
25 def test_tokenizer_like_protocol():
26 tokenizer = get_tokenizer("openai-community/gpt2", use_fast=True)
27 - assert isinstance(tokenizer, PreTrainedTokenizerFast)
27 + assert isinstance(tokenizer, TokenizersBackend)
28 _assert_tokenizer_like(tokenizer)
29
30 tokenizer = get_tokenizer(
@@ -38,7 +38,7 @@ def test_tokenizer_like_protocol():
38 assert isinstance(tokenizer, HfTokenizer)
39
40 # Verify it's a fast tokenizer (required for FastIncrementalDetokenizer)
41 - assert isinstance(tokenizer, PreTrainedTokenizerFast)
41 + assert isinstance(tokenizer, TokenizersBackend)
42 assert "DSV32" in tokenizer.__class__.__name__
43 _assert_tokenizer_like(tokenizer)
44
tests/tokenizers_/test_detokenize.py
+5 -5
@@ -5,7 +5,7 @@ from collections.abc import Generator
5 from typing import Any
6
7 import pytest
8 -from transformers import AutoTokenizer, PreTrainedTokenizer, PreTrainedTokenizerFast
8 +from transformers import AutoTokenizer, PythonBackend, TokenizersBackend
9
10 from vllm.sampling_params import SamplingParams
11 from vllm.tokenizers.mistral import MistralTokenizer
@@ -153,13 +153,13 @@ def test_decode_streaming(
153 spaces_between_special_tokens,
154 fast,
155 ):
156 - if fast and not isinstance(tokenizer, PreTrainedTokenizerFast):
156 + if fast and not isinstance(tokenizer, TokenizersBackend):
157 pytest.skip()
158
159 if skip_special_tokens and not spaces_between_special_tokens:
160 pytest.skip()
161
162 - if not fast and isinstance(tokenizer, PreTrainedTokenizerFast):
162 + if not fast and isinstance(tokenizer, TokenizersBackend):
163 # Fix up inconsistency in fast/slow tokenizer behaviour.
164 tokenizer.add_special_tokens(
165 {
@@ -173,7 +173,7 @@ def test_decode_streaming(
173
174 extra_decode_args = (
175 {}
176 - if not isinstance(tokenizer, PreTrainedTokenizer)
176 + if not isinstance(tokenizer, PythonBackend)
177 else {"spaces_between_special_tokens": spaces_between_special_tokens}
178 )
179
@@ -225,7 +225,7 @@ def test_decode_streaming(
225 @pytest.mark.parametrize("tokenizer_name", TOKENIZERS)
226 @pytest.mark.parametrize("fast", (True, False))
227 def test_oov_decode(tokenizer, fast):
228 - if fast and not isinstance(tokenizer, PreTrainedTokenizerFast):
228 + if fast and not isinstance(tokenizer, TokenizersBackend):
229 pytest.skip()
230
231 decoded_text, out_ids = _run_incremental_decode(
tests/v1/engine/utils.py
+5 -5
@@ -7,14 +7,14 @@ from typing import TypeAlias
7
8 import numpy as np
9 import torch
10 -from transformers import PreTrainedTokenizer, PreTrainedTokenizerFast
10 +from transformers import PythonBackend, TokenizersBackend
11
12 from vllm.engine.arg_utils import EngineArgs
13 from vllm.v1.engine import EngineCoreOutput, FinishReason
14 from vllm.v1.metrics.stats import PrefillStats
15 from vllm.v1.outputs import LogprobsLists, LogprobsTensors
16
17 -GeneralTokenizerType: TypeAlias = PreTrainedTokenizer | PreTrainedTokenizerFast
17 +GeneralTokenizerType: TypeAlias = PythonBackend | TokenizersBackend
18
19 # Number of sample logprobs to request when testing sample logprobs
20 NUM_SAMPLE_LOGPROBS_UNDER_TEST = 5
@@ -193,7 +193,7 @@ def _create_random_top_token_test_matrix(
193
194 def decode_token(
195 tok_id: int,
196 - tokenizer: PreTrainedTokenizer,
196 + tokenizer: PythonBackend,
197 ) -> str:
198 """Reproduce the process of detokenizing a token for testing purposes.
199
@@ -210,7 +210,7 @@ def decode_token(
210 def generate_dummy_sample_logprobs(
211 sampled_tokens_list: list,
212 num_logprobs: int,
213 - tokenizer: PreTrainedTokenizer,
213 + tokenizer: PythonBackend,
214 ) -> list[tuple[list[int], list[float], int]]:
215 """Generate dummy sample logprobs
216
@@ -259,7 +259,7 @@ def generate_dummy_sample_logprobs(
259 def generate_dummy_prompt_logprobs_tensors(
260 prompt_tokens_list: list,
261 num_logprobs: int,
262 - tokenizer: PreTrainedTokenizer,
262 + tokenizer: PythonBackend,
263 ) -> LogprobsTensors:
264 """Generate dummy prompt logprobs tensors
265
vllm/model_executor/models/funaudiochat.py
+4 -4
@@ -20,7 +20,7 @@ from typing import Any
20 import numpy as np
21 import torch
22 import torch.nn as nn
23 -from transformers import PreTrainedTokenizerFast, WhisperFeatureExtractor
23 +from transformers import TokenizersBackend, WhisperFeatureExtractor
24 from transformers.activations import get_activation
25 from transformers.feature_extraction_utils import BatchFeature
26 from transformers.modeling_outputs import BaseModelOutput
@@ -556,15 +556,15 @@ class FunAudioChatProcessingInfo(BaseProcessingInfo):
556 return WhisperFeatureExtractor.from_pretrained(self.model_id)
557
558 @cached_property
559 - def speech_tokenizer(self) -> PreTrainedTokenizerFast:
560 - return PreTrainedTokenizerFast.from_pretrained(
559 + def speech_tokenizer(self) -> TokenizersBackend:
560 + return TokenizersBackend.from_pretrained(
561 self.model_id, subfolder="speech_tokenizer"
562 )
563
564 def get_feature_extractor(self) -> WhisperFeatureExtractor:
565 return self.feature_extractor
566
567 - def get_speech_tokenizer(self) -> PreTrainedTokenizerFast:
567 + def get_speech_tokenizer(self) -> TokenizersBackend:
568 return self.speech_tokenizer
569
570 def get_data_parser(self):
vllm/model_executor/models/minicpmv.py
+1
@@ -552,6 +552,7 @@ class MiniCPMVProcessingInfo(BaseProcessingInfo):
552 vendored_processor = MiniCPMVProcessor(
553 image_processor=hf_processor.image_processor,
554 tokenizer=hf_processor.tokenizer,
555 + version=self.get_model_version(),
556 )
557 hf_processor = vendored_processor
558
vllm/model_executor/models/minicpmv4_6.py
+31
@@ -35,6 +35,8 @@ from vllm.multimodal.parse import ImageProcessorItems, ImageSize, VideoProcessor
35 from vllm.multimodal.processing.processor import (
36 PromptReplacement,
37 PromptUpdateDetails,
38 + ResolvedPromptUpdate,
39 + _seq2text,
40 )
41 from vllm.sequence import IntermediateTensors
42
@@ -408,6 +410,35 @@ class MiniCPMV4_6MultiModalProcessor(MiniCPMVMultiModalProcessor):
410 for modality, pattern in placeholders
411 ]
412
413 + def _recompute_cached_prompt_update(
414 + self, cached_update: ResolvedPromptUpdate, new_item_idx: int
415 + ) -> ResolvedPromptUpdate:
416 + new_update = super()._recompute_cached_prompt_update(
417 + cached_update, new_item_idx
418 + )
419 + # MiniCPM-V 4.6 prefixes video placeholders with `<image_id>{idx}</image_id>`
420 + # (the base class only rewrites the image modality).
421 + if cached_update.modality == "video":
422 + tokenizer = self.info.get_tokenizer()
423 + id_start = getattr(tokenizer, "image_id_start_token", "<image_id>")
424 + id_end = getattr(tokenizer, "image_id_end_token", "</image_id>")
425 + video_token = getattr(tokenizer, "video_token", "<|video_pad|>")
426 +
427 + text = _seq2text(tokenizer, cached_update.content.full)
428 + prev_item_idx = cached_update.item_idx
429 +
430 + new_update = new_update.with_content(
431 + PromptUpdateDetails.select_text(
432 + text.replace(
433 + f"{id_start}{prev_item_idx}{id_end}",
434 + f"{id_start}{new_item_idx}{id_end}",
435 + 1,
436 + ),
437 + video_token,
438 + )
439 + )
440 + return new_update
441 +
442 def _get_mm_fields_config(
443 self,
444 hf_inputs,
vllm/model_executor/models/qwen3_vl.py
+38 -22
@@ -33,7 +33,7 @@ import numpy as np
33 import torch
34 import torch.nn as nn
35 import torch.nn.functional as F
36 -from transformers import BatchFeature
36 +from transformers import BatchFeature, ProcessorMixin
37 from transformers.models.qwen2_vl import Qwen2VLImageProcessor
38 from transformers.models.qwen2_vl.image_processing_qwen2_vl import (
39 smart_resize as image_smart_resize,
@@ -1191,34 +1191,31 @@ def _replace_video_token_placeholders(
1191 target: list[int],
1192 replacements: list[list[int]],
1193 ) -> list[int]:
1194 - """Replace each 3-token video placeholder with its expanded sequence.
1194 + """Replace each video placeholder with its expanded sequence.
1195
1196 Args:
1197 prompt_ids: Token IDs of the original (unexpanded) prompt.
1198 - target: 3-element list ``[vision_start_id, video_pad_id,
1199 - vision_end_id]`` to search for.
1198 + target: Token-ID sequence to search for. New-style
1199 + (transformers>=5.10) processors expand only the bare
1200 + `[video_pad_id]`; older ones expand the full
1201 + `[vision_start_id, video_pad_id, vision_end_id]` triplet.
1202 replacements: Per-video expanded token sequences, in prompt order.
1203
1204 Returns:
1203 - Token IDs with every placeholder triplet replaced.
1205 + Token IDs with every placeholder replaced.
1206 """
1207 result: list[int] = []
1208 repl_idx = 0
1209 i = 0
1210 n = len(prompt_ids)
1209 - t0, t1, t2 = target
1211 + k = len(target)
1212 num_repl = len(replacements)
1213
1214 while i < n:
1213 - if (
1214 - i + 2 < n
1215 - and prompt_ids[i] == t0
1216 - and prompt_ids[i + 1] == t1
1217 - and prompt_ids[i + 2] == t2
1218 - ):
1215 + if prompt_ids[i : i + k] == target:
1216 result.extend(replacements[repl_idx])
1217 repl_idx += 1
1221 - i += 3
1218 + i += k
1219 else:
1220 result.append(prompt_ids[i])
1221 i += 1
@@ -1230,6 +1227,15 @@ def _replace_video_token_placeholders(
1227
1228
1229 class Qwen3VLMultiModalProcessor(BaseMultiModalProcessor[Qwen3VLProcessingInfo]):
1230 + @staticmethod
1231 + def _expands_only_video_token(hf_processor: ProcessorMixin) -> bool:
1232 + """Transformers>=5.10 processors override `replace_video_token`
1233 + to expand only the bare video token, keeping the prompt's outer
1234 + `<|vision_start|>`/`<|vision_end|>` markers."""
1235 + mixin_impl = getattr(ProcessorMixin, "replace_video_token", None)
1236 + proc_impl = getattr(type(hf_processor), "replace_video_token", None)
1237 + return proc_impl is not None and proc_impl is not mixin_impl
1238 +
1239 def _call_hf_processor(
1240 self,
1241 prompt: str,
@@ -1365,14 +1371,17 @@ class Qwen3VLMultiModalProcessor(BaseMultiModalProcessor[Qwen3VLProcessingInfo])
1371 tok_kwargs=tok_kwargs,
1372 )
1373
1368 - # Replace each placeholder triplet with pre-computed video tokens.
1374 + # Replace each placeholder with pre-computed video tokens.
1375 if video_input_ids_lst:
1376 hf_config = self.info.get_hf_config()
1371 - video_target = [
1372 - hf_config.vision_start_token_id,
1373 - hf_config.video_token_id,
1374 - hf_config.vision_end_token_id,
1375 - ]
1377 + if self._expands_only_video_token(self.info.get_hf_processor()):
1378 + video_target = [hf_config.video_token_id]
1379 + else:
1380 + video_target = [
1381 + hf_config.vision_start_token_id,
1382 + hf_config.video_token_id,
1383 + hf_config.vision_end_token_id,
1384 + ]
1385 input_ids = processed_outputs.pop("input_ids")
1386 if not isinstance(input_ids, list):
1387 input_ids = input_ids.tolist()
@@ -1464,17 +1473,24 @@ class Qwen3VLMultiModalProcessor(BaseMultiModalProcessor[Qwen3VLProcessingInfo])
1473 select_token_id=select_token_id,
1474 )
1475
1476 + if self._expands_only_video_token(hf_processor):
1477 + # transformers>=5.10 expands only the bare video_token
1478 + video_target = hf_processor.video_token
1479 + else:
1480 + # Old-style processors expand the full placeholder
1481 + # NOTE: We match string on purpose since searching sequence of
1482 + # token ids takes more time.
1483 + video_target = "<|vision_start|><|video_pad|><|vision_end|>"
1484 +
1485 return [
1486 PromptReplacement(
1487 modality="image",
1488 target=hf_processor.image_token,
1489 replacement=get_image_replacement_qwen3vl,
1490 ),
1473 - # NOTE: We match string on purpose since searching sequence of
1474 - # token ids takes more time.
1491 PromptReplacement(
1492 modality="video",
1477 - target="<|vision_start|><|video_pad|><|vision_end|>",
1493 + target=video_target,
1494 replacement=get_video_replacement_qwen3vl,
1495 ),
1496 ]
vllm/model_executor/models/transformers/base.py
+2 -2
@@ -276,13 +276,13 @@ class Base(
276 - Any quantization config specific mappings
277 """
278 self.hf_to_vllm_mapper = WeightsMapper()
279 - orig_to_new_renamings = self.hf_to_vllm_mapper.orig_to_new_renamings
279 + orig_to_new_renaming = self.hf_to_vllm_mapper.orig_to_new_renaming
280 orig_to_new_regex = self.hf_to_vllm_mapper.orig_to_new_regex
281
282 for mapping in get_model_conversion_mapping(self.model):
283 # Handle weights which have been renamed in Transformers
284 if isinstance(mapping, WeightRenaming):
285 - orig_to_new_renamings.append(mapping)
285 + orig_to_new_renaming.append(mapping)
286 # TODO: Handle WeightConverter to enable layer merging
287
288 # Handle unexpected weights which should be ignored
vllm/model_executor/models/utils.py
+9 -7
@@ -11,7 +11,6 @@ import regex as re
11 import torch
12 import torch.nn as nn
13 from torch.nn.modules.module import register_module_module_registration_hook
14 -from transformers import PretrainedConfig
14
15 from vllm.config import VllmConfig
16 from vllm.distributed import (
@@ -33,6 +32,9 @@ from vllm.utils.torch_utils import (
32 )
33
34 if TYPE_CHECKING:
35 + from transformers import PretrainedConfig
36 + from transformers.conversion_mapping import WeightRenaming
37 +
38 from vllm.model_executor.layers.quantization import QuantizationConfig
39
40 logger = init_logger(__name__)
@@ -46,7 +48,7 @@ class WeightsMapper:
48
49 If a key maps to a value of `None`, the corresponding weight is ignored."""
50
49 - orig_to_new_renamings: list[Any] = field(default_factory=list)
51 + orig_to_new_renaming: list["WeightRenaming"] = field(default_factory=list)
52 orig_to_new_regex: Mapping[re.Pattern, str | None] = field(default_factory=dict)
53 orig_to_new_substr: Mapping[str, str | None] = field(default_factory=dict)
54 orig_to_new_stacked: Mapping[str, tuple[str, ShardId]] = field(default_factory=dict)
@@ -56,9 +58,9 @@ class WeightsMapper:
58 def __or__(self, other: "WeightsMapper") -> "WeightsMapper":
59 """Combine two `WeightsMapper`s by merging their mappings."""
60 return WeightsMapper(
59 - orig_to_new_renamings=[
60 - *self.orig_to_new_renamings,
61 - *other.orig_to_new_renamings,
61 + orig_to_new_renaming=[
62 + *self.orig_to_new_renaming,
63 + *other.orig_to_new_renaming,
64 ],
65 orig_to_new_regex={**self.orig_to_new_regex, **other.orig_to_new_regex},
66 orig_to_new_substr={**self.orig_to_new_substr, **other.orig_to_new_substr},
@@ -92,7 +94,7 @@ class WeightsMapper:
94 "k_scale to v_scale"
95 )
96
95 - for renaming in self.orig_to_new_renamings:
97 + for renaming in self.orig_to_new_renaming:
98 key, _ = renaming.rename_source_key(key)
99
100 for pattern, new_key in self.orig_to_new_regex.items():
@@ -426,7 +428,7 @@ def init_vllm_registered_model(
428 vllm_config: VllmConfig,
429 *,
430 prefix: str = "",
429 - hf_config: PretrainedConfig | None = None,
431 + hf_config: "PretrainedConfig | None" = None,
432 architectures: list[str] | None = None,
433 ) -> nn.Module:
434 """
vllm/reasoning/abs_reasoning_parsers.py
+1 -1
@@ -41,7 +41,7 @@ class ReasoningParser:
41
42 @cached_property
43 def vocab(self) -> dict[str, int]:
44 - # NOTE: Only PreTrainedTokenizerFast is guaranteed to have .vocab
44 + # NOTE: Only TokenizersBackend is guaranteed to have .vocab
45 # whereas all tokenizers have .get_vocab()
46 return self.model_tokenizer.get_vocab()
47
vllm/renderers/hf.py
+5 -13
@@ -228,20 +228,12 @@ def _try_get_processor_chat_template(
228 if cache_key in _PROCESSOR_CHAT_TEMPLATES:
229 return _PROCESSOR_CHAT_TEMPLATES[cache_key]
230
231 - from transformers import (
232 - PreTrainedTokenizer,
233 - PreTrainedTokenizerFast,
234 - ProcessorMixin,
235 - )
231 + from transformers import ProcessorMixin, PythonBackend, TokenizersBackend
232
233 try:
234 processor = cached_get_processor(
235 tokenizer.name_or_path,
240 - processor_cls=(
241 - PreTrainedTokenizer,
242 - PreTrainedTokenizerFast,
243 - ProcessorMixin,
244 - ),
236 + processor_cls=(PythonBackend, TokenizersBackend, ProcessorMixin),
237 trust_remote_code=trust_remote_code,
238 )
239 if (
@@ -619,15 +611,15 @@ _cached_resolve_chat_template_kwargs = lru_cache(_resolve_chat_template_kwargs)
611
612 @lru_cache
613 def _get_hf_base_chat_template_params() -> frozenset[str]:
622 - from transformers import PreTrainedTokenizer
614 + from transformers import PythonBackend
615
616 # Get standard parameters from HuggingFace's base tokenizer class.
625 - # This dynamically extracts parameters from PreTrainedTokenizer's
617 + # This dynamically extracts parameters from PythonBackend's
618 # apply_chat_template method, ensuring compatibility with tokenizers
619 # that use **kwargs to receive standard parameters.
620
621 # Read signature from HF's base class - the single source of truth
630 - base_sig = inspect.signature(PreTrainedTokenizer.apply_chat_template)
622 + base_sig = inspect.signature(PythonBackend.apply_chat_template)
623
624 # Exclude VAR_KEYWORD (**kwargs) and VAR_POSITIONAL (*args) placeholders
625 return frozenset(
vllm/tokenizers/deepseek_v32.py
+2 -2
@@ -3,7 +3,7 @@
3 import copy
4 from typing import Any
5
6 -from transformers import PreTrainedTokenizerFast
6 +from transformers import TokenizersBackend
7
8 from vllm.entrypoints.chat_utils import ChatCompletionMessageParam
9
@@ -85,5 +85,5 @@ def get_deepseek_v32_tokenizer(tokenizer: HfTokenizer) -> HfTokenizer:
85 class DeepseekV32Tokenizer(TokenizerLike):
86 @classmethod
87 def from_pretrained(cls, *args, **kwargs) -> HfTokenizer:
88 - tokenizer = PreTrainedTokenizerFast.from_pretrained(*args, **kwargs)
88 + tokenizer = TokenizersBackend.from_pretrained(*args, **kwargs)
89 return get_cached_tokenizer(get_deepseek_v32_tokenizer(tokenizer))
vllm/tokenizers/deepseek_v4.py
+2 -2
@@ -3,7 +3,7 @@
3 import copy
4 from typing import Any
5
6 -from transformers import PreTrainedTokenizerFast
6 +from transformers import TokenizersBackend
7
8 from vllm.entrypoints.chat_utils import ChatCompletionMessageParam
9
@@ -92,5 +92,5 @@ def get_deepseek_v4_tokenizer(tokenizer: HfTokenizer) -> HfTokenizer:
92 class DeepseekV4Tokenizer(TokenizerLike):
93 @classmethod
94 def from_pretrained(cls, *args, **kwargs) -> HfTokenizer:
95 - tokenizer = PreTrainedTokenizerFast.from_pretrained(*args, **kwargs)
95 + tokenizer = TokenizersBackend.from_pretrained(*args, **kwargs)
96 return get_cached_tokenizer(get_deepseek_v4_tokenizer(tokenizer))
vllm/tokenizers/hf.py
+40 -5
@@ -6,13 +6,13 @@ import queue
6 from pathlib import Path
7 from typing import TypeAlias, TypeVar
8
9 -from transformers import AutoTokenizer, PreTrainedTokenizer, PreTrainedTokenizerFast
9 +from transformers import AutoTokenizer, PythonBackend, TokenizersBackend
10
11 from vllm.transformers_utils.config import get_sentence_transformer_tokenizer_config
12
13 from .protocol import TokenizerLike
14
15 -HfTokenizer: TypeAlias = PreTrainedTokenizer | PreTrainedTokenizerFast
15 +HfTokenizer: TypeAlias = PythonBackend | TokenizersBackend
16 _T = TypeVar("_T", bound=TokenizerLike)
17
18
@@ -24,7 +24,7 @@ class ThreadSafeHFTokenizerMixin:
24
25 def maybe_make_thread_pool(tokenizer: _T, copies: int = 1):
26 """
27 - If `tokenizer` is a `PreTrainedTokenizerFast`, modify the tokenizer
27 + If `tokenizer` is a `TokenizersBackend`, modify the tokenizer
28 in-place to make the public interface thread-safe by routing calls
29 through a deep-copied tokenizer pool.
30
@@ -34,14 +34,14 @@ def maybe_make_thread_pool(tokenizer: _T, copies: int = 1):
34 methods like ``add_special_tokens`` or ``add_tokens``.
35 - Adjacent method calls could happen on different deep copies.
36 """
37 - if not isinstance(tokenizer, PreTrainedTokenizerFast) or isinstance(
37 + if not isinstance(tokenizer, TokenizersBackend) or isinstance(
38 tokenizer, ThreadSafeHFTokenizerMixin
39 ):
40 return tokenizer
41
42 og_tokenizer = copy.copy(tokenizer)
43
44 - tokenizer_pool: queue.Queue[PreTrainedTokenizerFast] = queue.Queue()
44 + tokenizer_pool: queue.Queue[TokenizersBackend] = queue.Queue()
45 for _ in range(copies):
46 tokenizer_pool.put(copy.deepcopy(og_tokenizer))
47
@@ -116,6 +116,16 @@ def get_cached_tokenizer(tokenizer: HfTokenizer) -> HfTokenizer:
116 tokenizer_all_special_tokens = tokenizer.all_special_tokens
117 tokenizer_vocab = tokenizer.get_vocab()
118 tokenizer_len = len(tokenizer)
119 + # The underlying tokenizer class could be MistralCommonBackend,
120 + # which does not implement is_fast in Transformers
121 + tokenizer_is_fast = getattr(tokenizer, "is_fast", True)
122 +
123 + # MistralCommonBackend is tekken-backed and needs byte-fallback-aware tokenization.
124 + mistral_tekkenizer = None
125 + if getattr(getattr(tokenizer, "tokenizer", None), "instruct_tokenizer", None):
126 + from vllm.tokenizers.mistral import mistral_common_tekkenizer
127 +
128 + mistral_tekkenizer = mistral_common_tekkenizer(tokenizer)
129
130 max_token_id = max(tokenizer_vocab.values())
131 max_chars_per_token = max(len(tok) for tok in tokenizer_vocab)
@@ -145,6 +155,31 @@ def get_cached_tokenizer(tokenizer: HfTokenizer) -> HfTokenizer:
155 def max_chars_per_token(self) -> int:
156 return max_chars_per_token
157
158 + @property
159 + def is_fast(self) -> bool:
160 + return tokenizer_is_fast
161 +
162 + def convert_ids_to_tokens(self, ids, skip_special_tokens: bool = False):
163 + if mistral_tekkenizer is not None:
164 + from vllm.tokenizers.mistral import tekken_convert_ids_to_tokens
165 +
166 + return tekken_convert_ids_to_tokens(mistral_tekkenizer, ids)
167 + return super().convert_ids_to_tokens(
168 + ids, skip_special_tokens=skip_special_tokens
169 + )
170 +
171 + def convert_tokens_to_string(self, tokens: list[str]) -> str:
172 + if mistral_tekkenizer is not None:
173 + from vllm.tokenizers.mistral import tekken_convert_tokens_to_string
174 +
175 + return tekken_convert_tokens_to_string(mistral_tekkenizer, tokens)
176 + try:
177 + return super().convert_tokens_to_string(tokens)
178 + except NotImplementedError:
179 + # The underlying tokenizer class could be MistralCommonBackend,
180 + # which does not implement convert_tokens_to_string in Transformers
181 + return "".join(tokens)
182 +
183 def get_vocab(self) -> dict[str, int]:
184 return tokenizer_vocab
185
vllm/tokenizers/mistral.py
+35 -8
@@ -188,6 +188,39 @@ def _tekken_token_to_id(tokenizer: "Tekkenizer", t: str | bytes) -> int:
188 return tokenizer.unk_id
189
190
191 +def mistral_common_tekkenizer(tokenizer: object) -> "Tekkenizer | None":
192 + """Return the underlying `Tekkenizer` for a `MistralCommonBackend`."""
193 + mistral = getattr(tokenizer, "tokenizer", None)
194 + instruct = getattr(mistral, "instruct_tokenizer", None)
195 + tekken = getattr(instruct, "tokenizer", None)
196 + return tekken if isinstance(tekken, Tekkenizer) else None
197 +
198 +
199 +def tekken_convert_ids_to_tokens(
200 + tokenizer: "Tekkenizer", ids: Sequence[int]
201 +) -> list[str | bytes]:
202 + """Convert ids to pieces, using raw `bytes` for byte-fallback tokens."""
203 + tokens: list[str | bytes] = [tokenizer.id_to_piece(i) for i in ids]
204 + if any("�" in t for t in tokens):
205 + tokens = [
206 + tokenizer.id_to_byte_piece(i, SpecialTokenPolicy.KEEP)
207 + if i >= tokenizer.num_special_tokens
208 + else tokenizer.decode([i], SpecialTokenPolicy.KEEP)
209 + for i in ids
210 + ]
211 + return tokens
212 +
213 +
214 +def tekken_convert_tokens_to_string(
215 + tokenizer: "Tekkenizer", tokens: Sequence[str | bytes]
216 +) -> str:
217 + """Reassemble pieces from `tekken_convert_ids_to_tokens` into text."""
218 + if any(isinstance(t, bytes) for t in tokens):
219 + ids = [_tekken_token_to_id(tokenizer, t) for t in tokens]
220 + return tokenizer.decode(ids, SpecialTokenPolicy.KEEP)
221 + return "".join(cast(Sequence[str], tokens))
222 +
223 +
224 class MistralTokenizer(TokenizerLike):
225 IS_MISTRAL_TOKENIZER = True # used by vllm.utils.mistral
226
@@ -453,14 +486,8 @@ class MistralTokenizer(TokenizerLike):
486 if (t in to_decode_special_tokens or t not in self._special_tokens_set)
487 ]
488
456 - if any(isinstance(t, bytes) for t in tokens):
457 - # we need to encode and decode all tokens again
458 - ids = [_tekken_token_to_id(self.tokenizer, t) for t in tokens]
459 - # We filtered unwanted special tokens before
460 - # so we can decode the rest.
461 - decoded = self.tokenizer.decode(ids, SpecialTokenPolicy.KEEP)
462 - else:
463 - decoded = "".join(tokens)
489 + # We filtered unwanted special tokens before so we can decode the rest.
490 + decoded = tekken_convert_tokens_to_string(self.tokenizer, tokens)
491 else:
492 # make sure certain special tokens like Tool calls are
493 # not decoded
vllm/tokenizers/registry.py
+1
@@ -32,6 +32,7 @@ logger = init_logger(__name__)
32 # - Add model type to MODELS_WITH_INCORRECT_HUB_TOKENIZER_CLASS in transformers (better)
33 # - Fix tokenizer_class on the hub for the affected models (best)
34 _MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS: set[str] = {
35 + "internlm2",
36 "step3_vl",
37 "step3p7",
38 "unlimited-ocr",
vllm/tool_parsers/abstract_tool_parser.py
+1 -1
@@ -112,7 +112,7 @@ class ToolParser:
112
113 @cached_property
114 def vocab(self) -> dict[str, int]:
115 - # NOTE: Only PreTrainedTokenizerFast is guaranteed to have .vocab
115 + # NOTE: Only TokenizersBackend is guaranteed to have .vocab
116 # whereas all tokenizers have .get_vocab()
117 return self.model_tokenizer.get_vocab()
118
vllm/transformers_utils/config.py
+1
@@ -71,6 +71,7 @@ class LazyConfigDict(dict):
71
72 _CONFIG_REGISTRY: dict[str, type[PretrainedConfig]] = LazyConfigDict(
73 afmoe="AfmoeConfig",
74 + arctic="ArcticConfig",
75 bagel="BagelConfig",
76 umm="CheersConfig",
77 chatglm="ChatGLMConfig",
vllm/transformers_utils/configs/__init__.py
+2
@@ -16,6 +16,7 @@ import importlib
16
17 _CLASS_TO_MODULE: dict[str, str] = {
18 "AfmoeConfig": "vllm.transformers_utils.configs.afmoe",
19 + "ArcticConfig": "vllm.transformers_utils.configs.arctic",
20 "AXK1Config": "vllm.transformers_utils.configs.AXK1",
21 "BagelConfig": "vllm.transformers_utils.configs.bagel",
22 "CheersConfig": "vllm.transformers_utils.configs.cheers",
@@ -92,6 +93,7 @@ _CLASS_TO_MODULE: dict[str, str] = {
93
94 __all__ = [
95 "AfmoeConfig",
96 + "ArcticConfig",
97 "AXK1Config",
98 "BagelConfig",
99 "CheersConfig",
vllm/transformers_utils/processors/fireredlid.py
+1 -1
@@ -232,7 +232,7 @@ class FireRedLIDProcessor(ProcessorMixin):
232 """
233
234 feature_extractor_class = "FireRedLIDFeatureExtractor"
235 - tokenizer_class = ("PreTrainedTokenizer", "PreTrainedTokenizerFast")
235 + tokenizer_class = ("PythonBackend", "TokenizersBackend")
236
237 def __init__(self, feature_extractor, tokenizer):
238 super().__init__(feature_extractor, tokenizer)
vllm/transformers_utils/processors/glm4v.py
+2 -2
@@ -3,7 +3,7 @@
3
4 # Adapted from
5 # https://github.com/zai-org/CogAgent
6 -from transformers import PreTrainedTokenizer
6 +from transformers import PythonBackend
7 from transformers.image_processing_utils_fast import BaseImageProcessorFast
8 from transformers.image_utils import PILImageResampling
9 from transformers.processing_utils import ProcessorMixin
@@ -30,7 +30,7 @@ class GLM4VProcessor(ProcessorMixin):
30 def __init__(
31 self,
32 image_processor: GLM4VImageProcessorFast,
33 - tokenizer: PreTrainedTokenizer,
33 + tokenizer: PythonBackend,
34 ) -> None:
35 self.image_processor = image_processor
36 self.tokenizer = tokenizer
vllm/transformers_utils/processors/internvl.py
+8 -3
@@ -12,7 +12,12 @@ import numpy.typing as npt
12 import torch
13 import torchvision.transforms as T
14 from PIL import Image
15 -from transformers import BatchFeature, TensorType
15 +from transformers import (
16 + BaseVideoProcessor,
17 + BatchFeature,
18 + ImageProcessingMixin,
19 + TensorType,
20 +)
21 from transformers.processing_utils import ProcessorMixin
22
23 from vllm.multimodal.image import convert_image_mode
@@ -215,7 +220,7 @@ def video_to_pixel_values_internvl(
220 return pixel_values
221
222
218 -class InternVLImageProcessor:
223 +class InternVLImageProcessor(ImageProcessingMixin):
224 def __init__(
225 self,
226 image_size: int,
@@ -312,7 +317,7 @@ class InternVLImageProcessor:
317 return BatchFeature(image_inputs, tensor_type=return_tensors)
318
319
315 -class InternVLVideoProcessor:
320 +class InternVLVideoProcessor(BaseVideoProcessor):
321 def __init__(
322 self,
323 image_size: int,
vllm/transformers_utils/processors/isaac.py
+7 -2
@@ -7,7 +7,12 @@ import numpy as np
7 import torch
8 import torch.nn.functional as F
9 from PIL import Image
10 -from transformers import BatchFeature, ProcessorMixin, TensorType
10 +from transformers import (
11 + BatchFeature,
12 + ImageProcessingMixin,
13 + ProcessorMixin,
14 + TensorType,
15 +)
16 from transformers.processing_utils import ProcessingKwargs
17 from typing_extensions import Unpack
18
@@ -322,7 +327,7 @@ class IsaacProcessorKwargs(ProcessingKwargs, total=False): # type: ignore[call-
327 }
328
329
325 -class IsaacImageProcessor:
330 +class IsaacImageProcessor(ImageProcessingMixin):
331 model_input_names = ["pixel_values", "image_grid_thw"]
332
333 def __init__(
vllm/transformers_utils/processors/minicpmo.py
+2 -2
@@ -268,7 +268,7 @@ class MiniCPMOProcessor(ProcessorMixin):
268 def batch_decode(self, *args, **kwargs):
269 """
270 This method forwards all its arguments to LlamaTokenizerFast's
271 - [`~PreTrainedTokenizer.batch_decode`]. Please refer to the
271 + [`~PythonBackend.batch_decode`]. Please refer to the
272 docstring of this method for more information.
273 """
274 output_ids = args[0]
@@ -289,7 +289,7 @@ class MiniCPMOProcessor(ProcessorMixin):
289 def decode(self, *args, **kwargs):
290 """
291 This method forwards all its arguments to LlamaTokenizerFast's
292 - [`~PreTrainedTokenizer.decode`]. Please refer to the docstring
292 + [`~PythonBackend.decode`]. Please refer to the docstring
293 of this method for more information.
294 """
295 result = args[0]
vllm/transformers_utils/processors/minicpmv.py
+5 -10
@@ -56,14 +56,9 @@ class MiniCPMVProcessor(ProcessorMixin):
56 image_processor_class = "AutoImageProcessor"
57 tokenizer_class = "AutoTokenizer"
58
59 - def __init__(self, image_processor=None, tokenizer=None):
59 + def __init__(self, image_processor=None, tokenizer=None, version=None):
60 super().__init__(image_processor, tokenizer)
61 - # Newer (transformers v5.7+) MiniCPM-V image processors, e.g.
62 - # MiniCPMV4_6ImageProcessor, no longer carry a `version` attribute.
63 - # Fall back to None instead of hard-crashing: `version` is only used
64 - # to special-case the 2.5 tokenization path in `_convert`, and any
65 - # value other than 2.5 takes the default branch anyway.
66 - self.version = getattr(image_processor, "version", None)
61 + self.version = version
62
63 def __call__(
64 self,
@@ -100,7 +95,7 @@ class MiniCPMVProcessor(ProcessorMixin):
95 def batch_decode(self, *args, **kwargs):
96 """
97 This method forwards all its arguments to LlamaTokenizerFast's
103 - [`~PreTrainedTokenizer.batch_decode`]. Please refer to the
98 + [`~PythonBackend.batch_decode`]. Please refer to the
99 docstring of this method for more information.
100 """
101 output_ids = args[0]
@@ -133,7 +128,7 @@ class MiniCPMVProcessor(ProcessorMixin):
128 def decode(self, *args, **kwargs):
129 """
130 This method forwards all its arguments to LlamaTokenizerFast's
136 - [`~PreTrainedTokenizer.decode`]. Please refer to the docstring
131 + [`~PythonBackend.decode`]. Please refer to the docstring
132 of this method for more information.
133 """
134 result = args[0]
@@ -161,7 +156,7 @@ class MiniCPMVProcessor(ProcessorMixin):
156
157 def _convert(self, input_str, max_inp_length: int | None = None):
158 add_bos = getattr(self.tokenizer, "add_bos_token", False)
164 - if self.version == 2.5 or add_bos:
159 + if self.version == (2, 5) or add_bos:
160 input_ids = self.tokenizer.encode(input_str)
161 else:
162 bos_id = getattr(
vllm/transformers_utils/processors/moondream3.py
+2 -2
@@ -195,7 +195,7 @@ class Moondream3Processor(ProcessorMixin):
195 The moondream3 model uses a custom tokenizer from 'moondream/starmie-v1'
196 instead of having tokenizer files in the model repo.
197 """
198 - from transformers import AutoTokenizer, PreTrainedTokenizerFast
198 + from transformers import AutoTokenizer, TokenizersBackend
199 from transformers.utils import cached_file
200
201 tokenizer = kwargs.pop("tokenizer", None)
@@ -237,7 +237,7 @@ class Moondream3Processor(ProcessorMixin):
237 "tokenizer.json",
238 **cached_file_kwargs,
239 )
240 - return PreTrainedTokenizerFast(
240 + return TokenizersBackend(
241 tokenizer_file=tokenizer_file,
242 clean_up_tokenization_spaces=False,
243 )
vllm/transformers_utils/processors/openvla.py
+2 -2
@@ -7,7 +7,7 @@ from typing import Any
7 import numpy as np
8 import torch
9 from PIL import Image
10 -from transformers.processing_utils import ProcessorMixin
10 +from transformers import ImageProcessingMixin, ProcessorMixin
11
12 IMAGENET_MEAN = np.array([0.484375, 0.455078125, 0.40625], dtype=np.float32)
13 IMAGENET_STD = np.array([0.228515625, 0.2236328125, 0.224609375], dtype=np.float32)
@@ -65,7 +65,7 @@ def preprocess_openvla_image(image: Any, image_size: int) -> torch.Tensor:
65 return torch.from_numpy(pixel_values)
66
67
68 -class OpenVLAImageProcessor:
68 +class OpenVLAImageProcessor(ImageProcessingMixin):
69 def __init__(self, *, image_size: int) -> None:
70 self.image_size = image_size
71
vllm/transformers_utils/processors/ovis.py
+2 -2
@@ -417,14 +417,14 @@ class OvisProcessor(ProcessorMixin):
417
418 def batch_decode(self, *args, **kwargs):
419 """
420 - This method forwards all its arguments to Qwen2TokenizerFast's [`~PreTrainedTokenizer.batch_decode`]. Please
420 + This method forwards all its arguments to Qwen2TokenizerFast's [`~PythonBackend.batch_decode`]. Please
421 refer to the docstring of this method for more information.
422 """
423 return self.tokenizer.batch_decode(*args, **kwargs)
424
425 def decode(self, *args, **kwargs):
426 """
427 - This method forwards all its arguments to Qwen2TokenizerFast's [`~PreTrainedTokenizer.decode`]. Please refer to
427 + This method forwards all its arguments to Qwen2TokenizerFast's [`~PythonBackend.decode`]. Please refer to
428 the docstring of this method for more information.
429 """
430 return self.tokenizer.decode(*args, **kwargs)
vllm/transformers_utils/processors/pixtral.py
+2 -2
@@ -4,13 +4,13 @@ import torch
4 from mistral_common.protocol.instruct.chunk import ImageChunk
5 from mistral_common.tokens.tokenizers.multimodal import ImageEncoder
6 from PIL import Image
7 -from transformers import BatchFeature, ProcessorMixin, TensorType
7 +from transformers import BatchFeature, ImageProcessingMixin, ProcessorMixin, TensorType
8 from transformers.image_utils import ImageInput
9
10 from vllm.tokenizers.mistral import MistralTokenizer
11
12
13 -class MistralCommonImageProcessor:
13 +class MistralCommonImageProcessor(ImageProcessingMixin):
14 """
15 Provide a HF-compatible interface for
16 `mistral_common.tokens.tokenizers.multimodal.ImageEncoder`.
vllm/transformers_utils/processors/step3_vl.py
+7 -2
@@ -8,7 +8,12 @@ import torch
8 from PIL import Image
9 from torchvision import transforms
10 from torchvision.transforms.functional import InterpolationMode
11 -from transformers import BatchFeature, ProcessorMixin, TensorType
11 +from transformers import (
12 + BatchFeature,
13 + ImageProcessingMixin,
14 + ProcessorMixin,
15 + TensorType,
16 +)
17
18 from vllm.tokenizers import TokenizerLike
19
@@ -240,7 +245,7 @@ class ImagePatcher:
245 )
246
247
243 -class Step3VLImageProcessor:
248 +class Step3VLImageProcessor(ImageProcessingMixin):
249 def __init__(
250 self,
251 image_size: int = 728,
vllm/transformers_utils/processors/voxtral.py
+7 -2
@@ -6,13 +6,18 @@ from math import ceil
6 import numpy as np
7 import torch
8 from mistral_common.tokens.tokenizers.audio import AudioEncoder
9 -from transformers import BatchFeature, ProcessorMixin, TensorType
9 +from transformers import (
10 + BatchFeature,
11 + ProcessorMixin,
12 + SequenceFeatureExtractor,
13 + TensorType,
14 +)
15 from transformers.audio_utils import AudioInput
16
17 from vllm.tokenizers.mistral import MistralTokenizer
18
19
15 -class MistralCommonFeatureExtractor:
20 +class MistralCommonFeatureExtractor(SequenceFeatureExtractor):
21 """
22 Provide a HF-compatible interface for
23 `mistral_common.tokens.tokenizers.multimodal.AudioEncoder`.
vllm/v1/engine/detokenizer.py
+3 -3
@@ -6,7 +6,7 @@ import tokenizers
6 import tokenizers.decoders
7 from packaging import version
8 from tokenizers import Tokenizer
9 -from transformers import PreTrainedTokenizerFast
9 +from transformers import TokenizersBackend
10
11 from vllm.logger import init_logger
12 from vllm.tokenizers import TokenizerLike
@@ -57,7 +57,7 @@ class IncrementalDetokenizer:
57 # No tokenizer => skipping detokenization.
58 return IncrementalDetokenizer()
59
60 - if USE_FAST_DETOKENIZER and isinstance(tokenizer, PreTrainedTokenizerFast):
60 + if USE_FAST_DETOKENIZER and isinstance(tokenizer, TokenizersBackend):
61 # Fast tokenizer => use tokenizers library DecodeStream.
62 return FastIncrementalDetokenizer(tokenizer, request)
63
@@ -165,7 +165,7 @@ class BaseIncrementalDetokenizer(IncrementalDetokenizer, ABC):
165
166
167 class FastIncrementalDetokenizer(BaseIncrementalDetokenizer):
168 - def __init__(self, tokenizer: PreTrainedTokenizerFast, request: EngineCoreRequest):
168 + def __init__(self, tokenizer: TokenizersBackend, request: EngineCoreRequest):
169 super().__init__(request)
170
171 sampling_params = request.sampling_params
vllm/v1/structured_output/backend_guidance.py
+5
@@ -8,6 +8,7 @@ from dataclasses import dataclass
8 from typing import TYPE_CHECKING, Any
9
10 import torch
11 +from transformers import MistralCommonBackend
12
13 from vllm.logger import init_logger
14 from vllm.sampling_params import SamplingParams
@@ -95,6 +96,10 @@ class GuidanceBackend(StructuredOutputBackend):
96
97 if is_mistral_tokenizer(self.tokenizer):
98 self.ll_tokenizer = self.tokenizer.llg_tokenizer
99 + elif isinstance(self.tokenizer, MistralCommonBackend):
100 + from mistral_common.guidance.tokenizer import from_mistral_tokenizer
101 +
102 + self.ll_tokenizer = from_mistral_tokenizer(self.tokenizer.tokenizer)
103 else:
104 self.ll_tokenizer = llguidance_hf.from_tokenizer(
105 self.tokenizer, max(self.vocab_size, len(self.tokenizer))