main
py 116 lines 3.76 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from vllm.config import VllmConfig
5 from vllm.entrypoints.chat_utils import (
6 ChatCompletionMessageParam,
7 ConversationMessage,
8 parse_chat_messages,
9 parse_chat_messages_async,
10 )
11 from vllm.logger import init_logger
12 from vllm.tokenizers.mistral import MistralTokenizer
13 from vllm.utils.async_utils import make_async
14
15 from .base import BaseRenderer
16 from .inputs import DictPrompt
17 from .inputs.preprocess import parse_dec_only_prompt
18 from .params import ChatParams
19
20 logger = init_logger(__name__)
21
22
23 def safe_apply_chat_template(
24 tokenizer: MistralTokenizer,
25 messages: list[ChatCompletionMessageParam],
26 **kwargs,
27 ) -> str | list[int]:
28 from mistral_common.exceptions import MistralCommonException
29
30 try:
31 return tokenizer.apply_chat_template(messages, **kwargs)
32 # mistral-common uses assert statements to stop processing of input
33 # if input does not comply with the expected format.
34 # We convert those assertion errors to ValueErrors so they can be
35 # properly caught in the preprocessing_input step
36 except (AssertionError, MistralCommonException) as e:
37 raise ValueError(str(e)) from e
38
39 # External library exceptions can sometimes occur despite the framework's
40 # internal exception management capabilities.
41 except Exception as e:
42 # Log and report any library-related exceptions for further
43 # investigation.
44 logger.exception(
45 "An error occurred in `mistral_common` while applying chat template"
46 )
47 raise ValueError(str(e)) from e
48
49
50 class MistralRenderer(BaseRenderer[MistralTokenizer]):
51 def __init__(
52 self,
53 config: VllmConfig,
54 tokenizer: MistralTokenizer | None,
55 ) -> None:
56 super().__init__(config, tokenizer)
57
58 self._apply_chat_template_async = make_async(
59 safe_apply_chat_template, executor=self._executor
60 )
61
62 def render_messages(
63 self,
64 messages: list[ChatCompletionMessageParam],
65 params: ChatParams,
66 ) -> tuple[list[ConversationMessage], DictPrompt]:
67 tokenizer = self.get_tokenizer()
68 conversation, mm_data, mm_uuids = parse_chat_messages(
69 messages,
70 self.model_config,
71 content_format="string",
72 media_io_kwargs=params.media_io_kwargs,
73 mm_processor_kwargs=params.mm_processor_kwargs,
74 )
75
76 prompt_raw = safe_apply_chat_template(
77 tokenizer,
78 messages,
79 **params.get_apply_chat_template_kwargs(),
80 )
81
82 prompt = parse_dec_only_prompt(prompt_raw)
83 if mm_data is not None:
84 prompt["multi_modal_data"] = mm_data
85 if mm_uuids is not None:
86 prompt["multi_modal_uuids"] = mm_uuids
87
88 return conversation, prompt
89
90 async def render_messages_async(
91 self,
92 messages: list[ChatCompletionMessageParam],
93 params: ChatParams,
94 ) -> tuple[list[ConversationMessage], DictPrompt]:
95 tokenizer = self.get_tokenizer()
96 conversation, mm_data, mm_uuids = await parse_chat_messages_async(
97 messages,
98 self.model_config,
99 content_format="string",
100 media_io_kwargs=params.media_io_kwargs,
101 mm_processor_kwargs=params.mm_processor_kwargs,
102 )
103
104 prompt_raw = await self._apply_chat_template_async(
105 tokenizer,
106 messages,
107 **params.get_apply_chat_template_kwargs(),
108 )
109
110 prompt = parse_dec_only_prompt(prompt_raw)
111 if mm_data is not None:
112 prompt["multi_modal_data"] = mm_data
113 if mm_uuids is not None:
114 prompt["multi_modal_uuids"] = mm_uuids
115
116 return conversation, prompt