| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | from collections.abc import Callable |
| 4 | from pathlib import Path |
| 5 | from typing import TypeAlias |
| 6 | |
| 7 | from vllm.logger import init_logger |
| 8 | |
| 9 | logger = init_logger(__file__) |
| 10 | |
| 11 | CHAT_TEMPLATES_DIR = Path(__file__).parent |
| 12 | |
| 13 | ChatTemplatePath: TypeAlias = Path | Callable[[str], Path | None] |
| 14 | |
| 15 | |
| 16 | def _get_minicpmv_chat_template_fallback(tokenizer_name_or_path: str) -> Path | None: |
| 17 | # MiniCPM-V-4.5 version uses a dedicated template |
| 18 | if "4.5" in tokenizer_name_or_path or "4_5" in tokenizer_name_or_path: |
| 19 | return CHAT_TEMPLATES_DIR / "template_minicpmv45.jinja" |
| 20 | |
| 21 | # Other versions use chatml template |
| 22 | return CHAT_TEMPLATES_DIR / "template_chatml.jinja" |
| 23 | |
| 24 | |
| 25 | _MODEL_TYPE_TO_CHAT_TEMPLATE_FALLBACK: dict[str, ChatTemplatePath] = { |
| 26 | "blip-2": CHAT_TEMPLATES_DIR / "template_blip2.jinja", |
| 27 | "clip": CHAT_TEMPLATES_DIR / "template_basic.jinja", |
| 28 | "colpali": CHAT_TEMPLATES_DIR / "template_basic.jinja", |
| 29 | "deepseek_ocr": CHAT_TEMPLATES_DIR / "template_deepseek_ocr.jinja", |
| 30 | "deepseek_ocr2": CHAT_TEMPLATES_DIR / "template_deepseek_ocr.jinja", |
| 31 | "unlimited-ocr": CHAT_TEMPLATES_DIR / "template_deepseek_ocr.jinja", |
| 32 | "deepseek_vl_v2": CHAT_TEMPLATES_DIR / "template_deepseek_vl2.jinja", |
| 33 | "minicpmv": _get_minicpmv_chat_template_fallback, |
| 34 | "minicpmv4_6": _get_minicpmv_chat_template_fallback, |
| 35 | "paligemma": CHAT_TEMPLATES_DIR / "template_basic.jinja", |
| 36 | "siglip": CHAT_TEMPLATES_DIR / "template_basic.jinja", |
| 37 | "siglip2": CHAT_TEMPLATES_DIR / "template_basic.jinja", |
| 38 | } |
| 39 | |
| 40 | |
| 41 | def register_chat_template_fallback_path( |
| 42 | model_type: str, |
| 43 | chat_template: ChatTemplatePath, |
| 44 | ) -> None: |
| 45 | if model_type in _MODEL_TYPE_TO_CHAT_TEMPLATE_FALLBACK: |
| 46 | logger.warning( |
| 47 | "Model type %s already has a chat template registered. " |
| 48 | "It will be overwritten by the new chat template %s.", |
| 49 | model_type, |
| 50 | chat_template, |
| 51 | ) |
| 52 | |
| 53 | _MODEL_TYPE_TO_CHAT_TEMPLATE_FALLBACK[model_type] = chat_template |
| 54 | |
| 55 | |
| 56 | def get_chat_template_fallback_path( |
| 57 | model_type: str, |
| 58 | tokenizer_name_or_path: str, |
| 59 | ) -> Path | None: |
| 60 | chat_template = _MODEL_TYPE_TO_CHAT_TEMPLATE_FALLBACK.get(model_type) |
| 61 | if callable(chat_template): |
| 62 | chat_template = chat_template(tokenizer_name_or_path) |
| 63 | |
| 64 | if chat_template is None: |
| 65 | return None |
| 66 | |
| 67 | return chat_template |