4
from http import HTTPStatus
5
from typing import cast
6
7
+import jinja2
8
import numpy as np
9
from fastapi import Request
9
-from typing_extensions import override
10
11
from vllm.engine.protocol import EngineClient
12
+from vllm.entrypoints.chat_utils import ChatTemplateContentFormatOption
13
from vllm.entrypoints.logger import RequestLogger
14
from vllm.entrypoints.openai.protocol import (
15
+ ChatCompletionRequest,
16
+ ClassificationChatRequest,
17
+ ClassificationCompletionRequest,
18
ClassificationData,
19
ClassificationRequest,
20
ClassificationResponse,
36
37
38
class ClassificationMixin(OpenAIServing):
35
- @override
39
+ chat_template: str | None
40
+ chat_template_content_format: ChatTemplateContentFormatOption
41
+ trust_request_chat_template: bool
42
+
43
async def _preprocess(
44
self,
45
ctx: ServeContext,
49
and prepare model-specific inputs.
50
"""
51
ctx = cast(ClassificationServeContext, ctx)
45
- if isinstance(ctx.request.input, str) and not ctx.request.input:
46
- return self.create_error_response(
47
- "Input cannot be empty for classification",
48
- status_code=HTTPStatus.BAD_REQUEST,
49
- )
50
-
51
- if isinstance(ctx.request.input, list) and len(ctx.request.input) == 0:
52
- return None
53
-
52
try:
53
ctx.tokenizer = await self.engine_client.get_tokenizer()
54
57
- renderer = self._get_renderer(ctx.tokenizer)
58
- ctx.engine_prompts = await renderer.render_prompt(
59
- prompt_or_prompts=ctx.request.input,
60
- config=self._build_render_config(ctx.request),
61
- )
55
+ request_obj = ctx.request
56
+
57
+ if isinstance(request_obj, ClassificationChatRequest):
58
+ chat_request = request_obj
59
+ messages = chat_request.messages
60
+ trust_request_chat_template = getattr(
61
+ self,
62
+ "trust_request_chat_template",
63
+ False,
64
+ )
65
+ ret = self._validate_chat_template(
66
+ request_chat_template=chat_request.chat_template,
67
+ chat_template_kwargs=chat_request.chat_template_kwargs,
68
+ trust_request_chat_template=trust_request_chat_template,
69
+ )
70
+ if ret:
71
+ return ret
72
+
73
+ (
74
+ _,
75
+ _,
76
+ engine_prompts,
77
+ ) = await self._preprocess_chat(
78
+ cast(ChatCompletionRequest, chat_request),
79
+ ctx.tokenizer,
80
+ messages,
81
+ chat_template=(
82
+ chat_request.chat_template
83
+ or getattr(self, "chat_template", None)
84
+ ),
85
+ chat_template_content_format=cast(
86
+ ChatTemplateContentFormatOption,
87
+ getattr(self, "chat_template_content_format", "auto"),
88
+ ),
89
+ add_generation_prompt=False,
90
+ continue_final_message=False,
91
+ add_special_tokens=chat_request.add_special_tokens,
92
+ )
93
+ ctx.engine_prompts = engine_prompts
94
+
95
+ elif isinstance(request_obj, ClassificationCompletionRequest):
96
+ completion_request = request_obj
97
+ input_data = completion_request.input
98
+ if input_data in (None, ""):
99
+ return self.create_error_response(
100
+ "Input or messages must be provided",
101
+ status_code=HTTPStatus.BAD_REQUEST,
102
+ )
103
+ if isinstance(input_data, list) and not input_data:
104
+ ctx.engine_prompts = []
105
+ return None
106
+
107
+ renderer = self._get_renderer(ctx.tokenizer)
108
+ prompt_input = cast(str | list[str], input_data)
109
+ ctx.engine_prompts = await renderer.render_prompt(
110
+ prompt_or_prompts=prompt_input,
111
+ config=self._build_render_config(completion_request),
112
+ )
113
+ else:
114
+ return self.create_error_response(
115
+ "Invalid classification request type",
116
+ status_code=HTTPStatus.BAD_REQUEST,
117
+ )
118
119
return None
120
65
- except (ValueError, TypeError) as e:
121
+ except (ValueError, TypeError, jinja2.TemplateError) as e:
122
logger.exception("Error in preprocessing prompt inputs")
123
return self.create_error_response(str(e))
124
69
- @override
125
def _build_response(
126
self,
127
ctx: ServeContext,
173
return RenderConfig(
174
max_length=self.max_model_len,
175
truncate_prompt_tokens=request.truncate_prompt_tokens,
176
+ add_special_tokens=request.add_special_tokens,
177
)
178
179
186
models: OpenAIServingModels,
187
*,
188
request_logger: RequestLogger | None,
189
+ chat_template: str | None = None,
190
+ chat_template_content_format: ChatTemplateContentFormatOption = "auto",
191
+ trust_request_chat_template: bool = False,
192
log_error_stack: bool = False,
193
) -> None:
194
super().__init__(
198
log_error_stack=log_error_stack,
199
)
200
201
+ self.chat_template = chat_template
202
+ self.chat_template_content_format = chat_template_content_format
203
+ self.trust_request_chat_template = trust_request_chat_template
204
+
205
async def create_classify(
206
self,
207
request: ClassificationRequest,
219
220
return await super().handle(ctx) # type: ignore
221
159
- @override
222
def _create_pooling_params(
223
self,
224
ctx: ClassificationServeContext,