188 lines
5.2 KiB
Python
188 lines
5.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
import time
|
|
from typing import Any, TypeAlias
|
|
|
|
from pydantic import BaseModel, Field, model_validator
|
|
|
|
from vllm import PoolingParams
|
|
from vllm.config import ModelConfig
|
|
from vllm.entrypoints.openai.engine.protocol import OpenAIBaseModel, UsageInfo
|
|
from vllm.renderers import TokenizeParams
|
|
from vllm.tasks import PoolingTask
|
|
from vllm.utils import random_uuid
|
|
|
|
from ..base.protocol import ClassifyRequestMixin, PoolingBasicRequestMixin
|
|
from .typing import ScoreContentPartParam, ScoreInput
|
|
|
|
|
|
class ScoringRequestMixin(PoolingBasicRequestMixin, ClassifyRequestMixin):
|
|
# --8<-- [start:scoring-common-params]
|
|
max_tokens_per_query: int = Field(
|
|
default=0,
|
|
description=(
|
|
"Maximum number of tokens per query. Queries longer than "
|
|
"this will be truncated to this length. 0 means no "
|
|
"query-level truncation is applied."
|
|
),
|
|
)
|
|
max_tokens_per_doc: int = Field(
|
|
default=0,
|
|
description=(
|
|
"Maximum number of tokens per document. Documents longer than "
|
|
"this will be truncated to this length. 0 means no "
|
|
"document-level truncation is applied (only truncate_prompt_tokens "
|
|
"applies to the combined query+document)."
|
|
),
|
|
)
|
|
instruction: str | None = Field(
|
|
default=None,
|
|
description=(
|
|
"Task instruction prepended to each scored pair via the chat "
|
|
"template. Equivalent to passing "
|
|
"chat_template_kwargs={'instruction': ...}."
|
|
),
|
|
)
|
|
chat_template_kwargs: dict[str, Any] | None = Field(
|
|
default=None,
|
|
description=(
|
|
"Additional keyword args to pass to the chat template renderer. "
|
|
"Will be accessible by the score/rerank chat template."
|
|
),
|
|
)
|
|
# --8<-- [end:scoring-common-params]
|
|
|
|
@model_validator(mode="after")
|
|
def _merge_instruction_into_kwargs(self) -> "ScoringRequestMixin":
|
|
"""Fold the top-level `instruction` field into `chat_template_kwargs`.
|
|
|
|
This allows callers to use either the convenience field or the generic
|
|
dict. Explicit keys inside `chat_template_kwargs` take precedence over
|
|
the top-level `instruction` field.
|
|
"""
|
|
if self.instruction is not None:
|
|
merged = dict(self.chat_template_kwargs or {})
|
|
merged.setdefault("instruction", self.instruction)
|
|
self.chat_template_kwargs = merged
|
|
return self
|
|
|
|
def build_tok_params(self, model_config: ModelConfig) -> TokenizeParams:
|
|
return self._build_pooling_tok_params(
|
|
model_config,
|
|
add_special_tokens=True,
|
|
max_total_tokens=model_config.max_model_len,
|
|
max_output_tokens=0,
|
|
)
|
|
|
|
def to_pooling_params(self, task: PoolingTask = "classify"):
|
|
return PoolingParams(
|
|
task=task,
|
|
use_activation=self.use_activation,
|
|
)
|
|
|
|
|
|
class ScoreDataRequest(ScoringRequestMixin):
|
|
data_1: ScoreInput | list[ScoreInput]
|
|
data_2: ScoreInput | list[ScoreInput]
|
|
|
|
|
|
class ScoreQueriesDocumentsRequest(ScoringRequestMixin):
|
|
# --8<-- [start:score-request-params]
|
|
queries: ScoreInput | list[ScoreInput]
|
|
documents: ScoreInput | list[ScoreInput]
|
|
# --8<-- [end:score-request-params]
|
|
|
|
@property
|
|
def data_1(self):
|
|
return self.queries
|
|
|
|
@property
|
|
def data_2(self):
|
|
return self.documents
|
|
|
|
|
|
class ScoreQueriesItemsRequest(ScoringRequestMixin):
|
|
queries: ScoreInput | list[ScoreInput]
|
|
items: ScoreInput | list[ScoreInput]
|
|
|
|
@property
|
|
def data_1(self):
|
|
return self.queries
|
|
|
|
@property
|
|
def data_2(self):
|
|
return self.items
|
|
|
|
|
|
class ScoreTextRequest(ScoringRequestMixin):
|
|
text_1: ScoreInput | list[ScoreInput]
|
|
text_2: ScoreInput | list[ScoreInput]
|
|
|
|
@property
|
|
def data_1(self):
|
|
return self.text_1
|
|
|
|
@property
|
|
def data_2(self):
|
|
return self.text_2
|
|
|
|
|
|
ScoreRequest: TypeAlias = (
|
|
ScoreQueriesDocumentsRequest
|
|
| ScoreQueriesItemsRequest
|
|
| ScoreDataRequest
|
|
| ScoreTextRequest
|
|
)
|
|
|
|
|
|
class RerankRequest(ScoringRequestMixin):
|
|
# --8<-- [start:rerank-request-params]
|
|
query: ScoreInput
|
|
documents: ScoreInput | list[ScoreInput]
|
|
top_n: int = Field(default=0, ge=0)
|
|
# --8<-- [end:rerank-request-params]
|
|
|
|
|
|
ScoringRequest: TypeAlias = ScoreRequest | RerankRequest
|
|
|
|
|
|
class RerankDocument(BaseModel):
|
|
text: str | None = None
|
|
multi_modal: list[ScoreContentPartParam] | None = None
|
|
|
|
|
|
class RerankResult(BaseModel):
|
|
index: int
|
|
document: RerankDocument
|
|
relevance_score: float
|
|
|
|
|
|
class RerankUsage(BaseModel):
|
|
prompt_tokens: int
|
|
total_tokens: int
|
|
|
|
|
|
class RerankResponse(OpenAIBaseModel):
|
|
id: str
|
|
model: str
|
|
usage: RerankUsage
|
|
results: list[RerankResult]
|
|
|
|
|
|
class ScoreResponseData(OpenAIBaseModel):
|
|
index: int
|
|
object: str = "score"
|
|
score: float
|
|
|
|
|
|
class ScoreResponse(OpenAIBaseModel):
|
|
id: str = Field(default_factory=lambda: f"embd-{random_uuid()}")
|
|
object: str = "list"
|
|
created: int = Field(default_factory=lambda: int(time.time()))
|
|
model: str
|
|
data: list[ScoreResponseData]
|
|
usage: UsageInfo
|
|
|
|
|
|
ScoringResponse: TypeAlias = RerankResponse | ScoreResponse
|