Skip to content

vllm.renderers.chat_utils

Modules:

Classes:

Functions:

Attributes:

PROMPT_EMBEDS_PLACEHOLDER_TOKEN = '<prompt_embeds>' module-attribute

The special token used as a placeholder for each embedding position during chat template rendering.

Registered as an additional special token when --enable-prompt-embeds is set. See _ensure_prompt_embeds_placeholder_token in vllm/renderers/hf.py.

BaseMultiModalItemTracker

Bases: ABC, Generic[_T]

Tracks multi-modal items in a given request and ensures that the number of multi-modal items in a given request does not exceed the configured maximum per prompt.

Methods:

  • add –

    Add a multi-modal item to the current prompt and returns the

Attributes:

Source code in vllm/renderers/chat_utils/item_tracker.py
class BaseMultiModalItemTracker(ABC, Generic[_T]):
    """Tracks multi-modal items in a given request and ensures that the number
    of multi-modal items in a given request does not exceed the configured
    maximum per prompt.
    """

    def __init__(
        self,
        model_config: ModelConfig,
        media_io_kwargs: dict[str, dict[str, Any]] | None = None,
    ):
        super().__init__()

        self._model_config = model_config
        self._media_io_kwargs = media_io_kwargs

        self._items_by_modality = defaultdict[str, list[_T]](list)
        # Track original modality for each vision_chunk item (image or video)
        self._modality_order = defaultdict[str, list[str]](list)
        self.decoded_budget = MediaDecodedBudget()
        self.download_budget = MediaRequestDownloadBudget()

    @cached_property
    def use_unified_vision_chunk_modality(self) -> bool:
        """Check if model uses unified vision_chunk modality for images/videos."""
        return getattr(self._model_config.hf_config, "use_unified_vision_chunk", False)

    @property
    def model_config(self) -> ModelConfig:
        return self._model_config

    @cached_property
    def model_cls(self) -> type[SupportsMultiModal]:
        from vllm.model_executor.model_loader import get_model_cls

        model_cls = get_model_cls(self.model_config)
        return cast(type[SupportsMultiModal], model_cls)

    @property
    def media_io_kwargs(self) -> dict[str, dict[str, Any]] | None:
        return self._media_io_kwargs or (
            self._model_config.multimodal_config.media_io_kwargs
            if self._model_config.multimodal_config
            else None
        )

    @property
    def allowed_local_media_path(self):
        return self._model_config.allowed_local_media_path

    @property
    def allowed_media_domains(self):
        return self._model_config.allowed_media_domains

    @property
    def mm_registry(self):
        return MULTIMODAL_REGISTRY

    @cached_property
    def mm_processor(self):
        return self.mm_registry.create_processor(self.model_config)

    @property
    def video_processor_name(self) -> str | None:
        return get_video_processor_cls_name(self.model_config)

    def add(self, modality: ModalityStr, item: _T) -> str | None:
        """Add a multi-modal item to the current prompt and returns the
        placeholder string to use, if any.

        An optional uuid can be added which serves as a unique identifier of the
        media.

        Note:
            `prompt_embeds` bypass MM-processor validation because they are
            pre-computed embeddings that do not go through any HF processor, encoder,
            or model-specific placeholder logic. The corresponding placeholder string is
            managed by the parser via `_add_placeholder`, so we return None here.

        """
        add_info = self._validate_add(modality)
        if add_info is None:
            self._items_by_modality["prompt_embeds"].append(item)
            return None

        input_modality, original_modality, use_vision_chunk, num_items = add_info

        # Track original modality for vision_chunk items
        if use_vision_chunk:
            self._items_by_modality[input_modality].append(item)  # type: ignore
            self._modality_order["vision_chunk"].append(original_modality)
        else:
            self._items_by_modality[original_modality].append(item)

        return self.model_cls.get_placeholder_str(modality, num_items)

    def _validate_add(self, modality: ModalityStr) -> tuple[str, str, bool, int] | None:
        """Validate that one more item of the modality can be tracked."""
        if modality == "prompt_embeds":
            return None

        input_modality = modality.replace("_embeds", "")
        original_modality = modality
        use_vision_chunk = (
            self.use_unified_vision_chunk_modality
            and original_modality in ["video", "image"]
        )

        # If use_unified_vision_chunk_modality is enabled,
        # map image/video to vision_chunk
        if use_vision_chunk:
            # To avoid validation fail
            # because models with use_unified_vision_chunk_modality=True
            # will only accept vision_chunk modality.
            input_modality = "vision_chunk"
            num_items = len(self._items_by_modality[input_modality]) + 1
        else:
            num_items = len(self._items_by_modality[original_modality]) + 1

        mm_config = self.model_config.multimodal_config
        if (
            mm_config is not None
            and mm_config.enable_mm_embeds
            and mm_config.get_limit_per_prompt(input_modality) == 0
            and original_modality.endswith("_embeds")
        ):
            # Skip validation: embeddings bypass limit when enable_mm_embeds=True
            pass
        else:
            self.mm_processor.info.validate_num_items(input_modality, num_items)

        return input_modality, original_modality, use_vision_chunk, num_items

    @abstractmethod
    def create_parser(
        self, mm_processor_kwargs: dict[str, Any] | None = None
    ) -> "BaseMultiModalContentParser":
        raise NotImplementedError

use_unified_vision_chunk_modality cached property

Check if model uses unified vision_chunk modality for images/videos.

_validate_add(modality)

Validate that one more item of the modality can be tracked.

Source code in vllm/renderers/chat_utils/item_tracker.py
def _validate_add(self, modality: ModalityStr) -> tuple[str, str, bool, int] | None:
    """Validate that one more item of the modality can be tracked."""
    if modality == "prompt_embeds":
        return None

    input_modality = modality.replace("_embeds", "")
    original_modality = modality
    use_vision_chunk = (
        self.use_unified_vision_chunk_modality
        and original_modality in ["video", "image"]
    )

    # If use_unified_vision_chunk_modality is enabled,
    # map image/video to vision_chunk
    if use_vision_chunk:
        # To avoid validation fail
        # because models with use_unified_vision_chunk_modality=True
        # will only accept vision_chunk modality.
        input_modality = "vision_chunk"
        num_items = len(self._items_by_modality[input_modality]) + 1
    else:
        num_items = len(self._items_by_modality[original_modality]) + 1

    mm_config = self.model_config.multimodal_config
    if (
        mm_config is not None
        and mm_config.enable_mm_embeds
        and mm_config.get_limit_per_prompt(input_modality) == 0
        and original_modality.endswith("_embeds")
    ):
        # Skip validation: embeddings bypass limit when enable_mm_embeds=True
        pass
    else:
        self.mm_processor.info.validate_num_items(input_modality, num_items)

    return input_modality, original_modality, use_vision_chunk, num_items

add(modality, item)

Add a multi-modal item to the current prompt and returns the placeholder string to use, if any.

An optional uuid can be added which serves as a unique identifier of the media.

Note

prompt_embeds bypass MM-processor validation because they are pre-computed embeddings that do not go through any HF processor, encoder, or model-specific placeholder logic. The corresponding placeholder string is managed by the parser via _add_placeholder, so we return None here.

Source code in vllm/renderers/chat_utils/item_tracker.py
def add(self, modality: ModalityStr, item: _T) -> str | None:
    """Add a multi-modal item to the current prompt and returns the
    placeholder string to use, if any.

    An optional uuid can be added which serves as a unique identifier of the
    media.

    Note:
        `prompt_embeds` bypass MM-processor validation because they are
        pre-computed embeddings that do not go through any HF processor, encoder,
        or model-specific placeholder logic. The corresponding placeholder string is
        managed by the parser via `_add_placeholder`, so we return None here.

    """
    add_info = self._validate_add(modality)
    if add_info is None:
        self._items_by_modality["prompt_embeds"].append(item)
        return None

    input_modality, original_modality, use_vision_chunk, num_items = add_info

    # Track original modality for vision_chunk items
    if use_vision_chunk:
        self._items_by_modality[input_modality].append(item)  # type: ignore
        self._modality_order["vision_chunk"].append(original_modality)
    else:
        self._items_by_modality[original_modality].append(item)

    return self.model_cls.get_placeholder_str(modality, num_items)

ChatCompletionContentPartAudioEmbedsParam

Bases: TypedDict

Attributes:

  • audio_embeds (MultiModalEmbedsPayload | None) –

    The audio embeddings. It can be either:

  • type (Required[Literal['audio_embeds']]) –

    The type of the content part.

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartAudioEmbedsParam(TypedDict, total=False):
    audio_embeds: MultiModalEmbedsPayload | None
    """
    The audio embeddings. It can be either:
    - A single base64 string representing a serialized torch tensor.
    - A dictionary of base64 tensors or numeric JSON metadata arrays.
    """
    type: Required[Literal["audio_embeds"]]
    """The type of the content part."""
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

audio_embeds instance-attribute

The audio embeddings. It can be either: - A single base64 string representing a serialized torch tensor. - A dictionary of base64 tensors or numeric JSON metadata arrays.

type instance-attribute

The type of the content part.

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

ChatCompletionContentPartAudioParam

Bases: TypedDict

Attributes:

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartAudioParam(TypedDict, total=False):
    audio_url: Required[AudioURL]

    type: Required[Literal["audio_url"]]
    """The type of the content part."""

type instance-attribute

The type of the content part.

ChatCompletionContentPartImageEmbedsParam

Bases: TypedDict

Attributes:

  • image_embeds (MultiModalEmbedsPayload | None) –

    The image embeddings. It can be either:

  • type (Required[Literal['image_embeds']]) –

    The type of the content part.

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartImageEmbedsParam(TypedDict, total=False):
    image_embeds: MultiModalEmbedsPayload | None
    """
    The image embeddings. It can be either:
    - A single base64 string.
    - A dictionary of base64 tensors or numeric JSON metadata arrays.
    """
    type: Required[Literal["image_embeds"]]
    """The type of the content part."""
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

image_embeds instance-attribute

The image embeddings. It can be either: - A single base64 string. - A dictionary of base64 tensors or numeric JSON metadata arrays.

type instance-attribute

The type of the content part.

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

ChatCompletionContentPartPromptEmbedsParam

Bases: TypedDict

Attributes:

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartPromptEmbedsParam(TypedDict, total=False):
    data: Required[str]
    """
    Base64-encoded bytes of a serialized `torch.Tensor` of shape
    `(num_tokens, hidden_size)`. The tensor's `dtype` and `hidden_size` must
    match the model's input embedding layer.
    """
    type: Required[Literal["prompt_embeds"]]
    """The type of the content part."""

data instance-attribute

Base64-encoded bytes of a serialized torch.Tensor of shape (num_tokens, hidden_size). The tensor's dtype and hidden_size must match the model's input embedding layer.

type instance-attribute

The type of the content part.

ChatCompletionContentPartVideoEmbedsParam

Bases: TypedDict

Attributes:

  • type (Required[Literal['video_embeds']]) –

    The type of the content part.

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

  • video_embeds (MultiModalEmbedsPayload | None) –

    The video embeddings. It can be either:

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartVideoEmbedsParam(TypedDict, total=False):
    video_embeds: MultiModalEmbedsPayload | None
    """
    The video embeddings. It can be either:
    - A single base64 string representing a serialized torch tensor.
    - A dictionary of base64 tensors or numeric JSON metadata arrays.
    """
    type: Required[Literal["video_embeds"]]
    """The type of the content part."""
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

type instance-attribute

The type of the content part.

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

video_embeds instance-attribute

The video embeddings. It can be either: - A single base64 string representing a serialized torch tensor. - A dictionary of base64 tensors or numeric JSON metadata arrays.

ChatCompletionContentPartVideoParam

Bases: TypedDict

Attributes:

Source code in vllm/renderers/chat_utils/types.py
class ChatCompletionContentPartVideoParam(TypedDict, total=False):
    video_url: Required[VideoURL]

    type: Required[Literal["video_url"]]
    """The type of the content part."""

type instance-attribute

The type of the content part.

ConversationMessage

Bases: TypedDict

Attributes:

  • content (str | None | list[dict[str, str]]) –

    The contents of the message

  • name (str | None) –

    The name of the function to call

  • reasoning (str | None) –

    The reasoning content for interleaved thinking.

  • reasoning_content (str | None) –

    Deprecated: The reasoning content for interleaved thinking.

  • role (Required[str]) –

    The role of the message's author.

  • task (str | None) –

    Model-specific task marker. Currently passed through for DeepSeek V4.

  • tool_call_id (str | None) –

    Tool call that this message is responding to.

  • tool_calls (list[ChatCompletionMessageToolCallParam] | None) –

    The tool calls generated by the model, such as function calls.

  • tools (list[ChatCompletionFunctionToolParam] | None) –

    The tools for developer role.

Source code in vllm/renderers/chat_utils/types.py
class ConversationMessage(TypedDict, total=False):
    role: Required[str]
    """The role of the message's author."""

    content: str | None | list[dict[str, str]]
    """The contents of the message"""

    tool_call_id: str | None
    """Tool call that this message is responding to."""

    name: str | None
    """The name of the function to call"""

    tool_calls: list[ChatCompletionMessageToolCallParam] | None
    """The tool calls generated by the model, such as function calls."""

    reasoning: str | None
    """The reasoning content for interleaved thinking."""

    reasoning_content: str | None
    """Deprecated: The reasoning content for interleaved thinking."""

    tools: list[ChatCompletionFunctionToolParam] | None
    """The tools for developer role."""

    task: str | None
    """Model-specific task marker. Currently passed through for DeepSeek V4."""

content instance-attribute

The contents of the message

name instance-attribute

The name of the function to call

reasoning instance-attribute

The reasoning content for interleaved thinking.

reasoning_content instance-attribute

Deprecated: The reasoning content for interleaved thinking.

role instance-attribute

The role of the message's author.

task instance-attribute

Model-specific task marker. Currently passed through for DeepSeek V4.

tool_call_id instance-attribute

Tool call that this message is responding to.

tool_calls instance-attribute

The tool calls generated by the model, such as function calls.

tools instance-attribute

The tools for developer role.

CustomChatCompletionContentPILImageParam

Bases: TypedDict

A simpler version of the param that only accepts a PIL image.

Example: { "image_pil": ImageAsset('cherry_blossom').pil_image }

Attributes:

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionContentPILImageParam(TypedDict, total=False):
    """A simpler version of the param that only accepts a PIL image.

    Example:
    {
        "image_pil": ImageAsset('cherry_blossom').pil_image
    }

    """

    image_pil: PILImage | None
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

CustomChatCompletionContentSimpleAudioParam

Bases: TypedDict

A simpler version of the param that only accepts a plain audio_url.

Example: { "audio_url": "https://example.com/audio.mp3" }

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionContentSimpleAudioParam(TypedDict, total=False):
    """A simpler version of the param that only accepts a plain audio_url.

    Example:
    {
        "audio_url": "https://example.com/audio.mp3"
    }

    """

    audio_url: str | None

CustomChatCompletionContentSimpleImageParam

Bases: TypedDict

A simpler version of the param that only accepts a plain image_url. This is supported by OpenAI API, although it is not documented.

Example: { "image_url": "https://example.com/image.jpg" }

Attributes:

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionContentSimpleImageParam(TypedDict, total=False):
    """A simpler version of the param that only accepts a plain image_url.
    This is supported by OpenAI API, although it is not documented.

    Example:
    {
        "image_url": "https://example.com/image.jpg"
    }

    """

    image_url: str | None
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

CustomChatCompletionContentSimpleVideoParam

Bases: TypedDict

A simpler version of the param that only accepts a plain audio_url.

Example: { "video_url": "https://example.com/video.mp4" }

Attributes:

  • uuid (str | None) –

    User-provided UUID of a media. User must guarantee that it is properly

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionContentSimpleVideoParam(TypedDict, total=False):
    """A simpler version of the param that only accepts a plain audio_url.

    Example:
    {
        "video_url": "https://example.com/video.mp4"
    }

    """

    video_url: str | None
    uuid: str | None
    """
    User-provided UUID of a media. User must guarantee that it is properly
    generated and unique for different medias.
    """

uuid instance-attribute

User-provided UUID of a media. User must guarantee that it is properly generated and unique for different medias.

CustomChatCompletionContentToolReferenceParam

Bases: TypedDict

A tool reference content param that only accepts a plain tool name.

Example: { "name": "get_weather", "type": "tool_reference" }

Attributes:

  • name (str) –

    The name of the tool being referenced.

  • type (Literal['tool_reference']) –

    The content type.

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionContentToolReferenceParam(TypedDict, total=False):
    """A tool reference content param that only accepts a plain tool name.

    Example:
    {
        "name": "get_weather",
        "type": "tool_reference"
    }

    """

    name: str
    """The name of the tool being referenced."""

    type: Literal["tool_reference"]
    """The content type."""

name instance-attribute

The name of the tool being referenced.

type instance-attribute

The content type.

CustomChatCompletionMessageParam

Bases: TypedDict

Enables custom roles in the Chat Completion API.

Attributes:

  • content (str | list[ChatCompletionContentPartParam]) –

    The contents of the message.

  • name (str) –

    An optional name for the participant.

  • reasoning (str | None) –

    The reasoning content for interleaved thinking.

  • role (Required[str]) –

    The role of the message's author.

  • task (str | None) –

    Model-specific task marker. Currently passed through for DeepSeek V4.

  • tool_call_id (str | None) –

    Tool call that this message is responding to.

  • tool_calls (list[ChatCompletionMessageToolCallParam] | None) –

    The tool calls generated by the model, such as function calls.

  • tools (list[ChatCompletionFunctionToolParam] | None) –

    The tools for developer role.

Source code in vllm/renderers/chat_utils/types.py
class CustomChatCompletionMessageParam(TypedDict, total=False):
    """Enables custom roles in the Chat Completion API."""

    role: Required[str]
    """The role of the message's author."""

    content: str | list[ChatCompletionContentPartParam]
    """The contents of the message."""

    name: str
    """An optional name for the participant.

    Provides the model information to differentiate between participants of the
    same role.
    """

    tool_call_id: str | None
    """Tool call that this message is responding to."""

    tool_calls: list[ChatCompletionMessageToolCallParam] | None
    """The tool calls generated by the model, such as function calls."""

    reasoning: str | None
    """The reasoning content for interleaved thinking."""

    tools: list[ChatCompletionFunctionToolParam] | None
    """The tools for developer role."""

    task: str | None
    """Model-specific task marker. Currently passed through for DeepSeek V4."""

content instance-attribute

The contents of the message.

name instance-attribute

An optional name for the participant.

Provides the model information to differentiate between participants of the same role.

reasoning instance-attribute

The reasoning content for interleaved thinking.

role instance-attribute

The role of the message's author.

task instance-attribute

Model-specific task marker. Currently passed through for DeepSeek V4.

tool_call_id instance-attribute

Tool call that this message is responding to.

tool_calls instance-attribute

The tool calls generated by the model, such as function calls.

tools instance-attribute

The tools for developer role.

CustomThinkCompletionContentParam

Bases: TypedDict

A Think Completion Content Param that accepts a plain text and a boolean.

Example: { "thinking": "I am thinking about the answer", "closed": True, "type": "thinking" }

Attributes:

Source code in vllm/renderers/chat_utils/types.py
class CustomThinkCompletionContentParam(TypedDict, total=False):
    """A Think Completion Content Param that accepts a plain text and a boolean.

    Example:
    {
        "thinking": "I am thinking about the answer",
        "closed": True,
        "type": "thinking"
    }

    """

    thinking: Required[str]
    """The thinking content."""

    closed: bool
    """Whether the thinking is closed."""

    type: Required[Literal["thinking"]]
    """The thinking type."""

closed instance-attribute

Whether the thinking is closed.

thinking instance-attribute

The thinking content.

type instance-attribute

The thinking type.

PILImage

Bases: BaseModel

A PIL.Image.Image object.

Source code in vllm/renderers/chat_utils/types.py
class PILImage(BaseModel):
    """A PIL.Image.Image object."""

    image_pil: Image.Image
    model_config = ConfigDict(arbitrary_types_allowed=True)

get_tool_call_id_type(model_config)

Return the tool-call ID type for a given model configuration.

Source code in vllm/renderers/chat_utils/tool_call.py
def get_tool_call_id_type(model_config: ModelConfig) -> str:
    """Return the tool-call ID type for a given model configuration."""
    hf_overrides = getattr(model_config, "hf_overrides", None)
    hf_config = getattr(model_config, "hf_config", None)
    hf_text_config = getattr(model_config, "hf_text_config", None)
    model_types = (
        getattr(hf_config, "model_type", None),
        getattr(hf_text_config, "model_type", None),
    )
    if any(model_type in _KIMI_MODEL_TYPES for model_type in model_types) or (
        isinstance(hf_overrides, dict)
        and hf_overrides.get("model_type") in _KIMI_MODEL_TYPES
    ):
        return "kimi_k2"
    return "random"

has_non_text_content(messages)

Whether any message in messages has a non-text content part.

Only list content is inspected, so validated chat content that is a one-shot iterator is never consumed.

Source code in vllm/renderers/chat_utils/chat_message.py
def has_non_text_content(messages: Any) -> bool:
    """Whether any message in ``messages`` has a non-text content part.

    Only list content is inspected, so validated chat content that is a
    one-shot iterator is never consumed.
    """
    if not isinstance(messages, list):
        return False
    for msg in messages:
        content = msg.get("content") if isinstance(msg, dict) else None
        if not isinstance(content, list):
            continue
        for part in content:
            if isinstance(part, dict) and (
                any(key in part for key in _MEDIA_CONTENT_PART_KEYS)
                or not isinstance(part_type := part.get("type", "text"), str)
                or part_type not in _TEXT_CONTENT_PART_TYPES
            ):
                return True
    return False

validate_chat_template(chat_template)

Raises if the provided chat template appears invalid.

Source code in vllm/renderers/chat_utils/chat_template.py
def validate_chat_template(chat_template: Path | str | None):
    """Raises if the provided chat template appears invalid."""
    if chat_template is None:
        return

    elif isinstance(chat_template, Path) and not chat_template.exists():
        raise FileNotFoundError("the supplied chat template path doesn't exist")

    elif isinstance(chat_template, str):
        JINJA_CHARS = "{}\n"
        if (
            not any(c in chat_template for c in JINJA_CHARS)
            and not Path(chat_template).exists()
        ):
            # Try to find the template in the built-in templates directory
            from vllm.transformers_utils.chat_templates.registry import (
                CHAT_TEMPLATES_DIR,
            )

            builtin_template_path = CHAT_TEMPLATES_DIR / chat_template
            if not builtin_template_path.exists():
                raise VLLMValidationError(
                    f"The supplied chat template string ({chat_template}) "
                    f"appears path-like, but doesn't exist! "
                    f"Tried: {chat_template} and {builtin_template_path}",
                    parameter="chat_template",
                )

    else:
        raise VLLMValidationError(
            f"{type(chat_template)} is not a valid chat template type",
            parameter="chat_template",
        )