forked from Karylab-cklius/vllm
161 lines
5.0 KiB
Python
161 lines
5.0 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from vllm.config import ModelConfig
|
|
from vllm.entrypoints.chat_utils import ChatTemplateConfig
|
|
from vllm.entrypoints.openai.engine.protocol import UsageInfo
|
|
from vllm.entrypoints.pooling.base.io_processor import PoolingIOProcessor
|
|
from vllm.entrypoints.pooling.base.serving import PoolingServing
|
|
from vllm.logger import init_logger
|
|
from vllm.outputs import PoolingRequestOutput, ScoringRequestOutput
|
|
from vllm.renderers import BaseRenderer
|
|
|
|
from .io_processor import ScoringIOProcessors, ScoringServeContext
|
|
from .protocol import (
|
|
RerankDocument,
|
|
RerankRequest,
|
|
RerankResponse,
|
|
RerankResult,
|
|
RerankUsage,
|
|
ScoreRequest,
|
|
ScoreResponse,
|
|
ScoreResponseData,
|
|
)
|
|
from .typing import ScoreInput
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
|
|
class ServingScores(PoolingServing):
|
|
request_id_prefix = "score"
|
|
|
|
def init_io_processor(
|
|
self,
|
|
model_config: ModelConfig,
|
|
renderer: BaseRenderer,
|
|
chat_template_config: ChatTemplateConfig,
|
|
) -> PoolingIOProcessor:
|
|
score_type = model_config.score_type
|
|
assert score_type in ScoringIOProcessors
|
|
processor_cls = ScoringIOProcessors[score_type]
|
|
return processor_cls(
|
|
model_config=model_config,
|
|
renderer=renderer,
|
|
chat_template_config=chat_template_config,
|
|
)
|
|
|
|
async def _build_response(
|
|
self,
|
|
ctx: ScoringServeContext,
|
|
) -> JSONResponse:
|
|
final_res_batch = ctx.final_res_batch
|
|
request_id = ctx.request_id
|
|
created_time = ctx.created_time
|
|
model_name = self.models.model_name()
|
|
|
|
if isinstance(ctx.request, ScoreRequest):
|
|
return self._request_output_to_score_response(
|
|
final_res_batch,
|
|
request_id,
|
|
created_time,
|
|
model_name,
|
|
)
|
|
elif isinstance(ctx.request, RerankRequest):
|
|
return self._request_output_to_rerank_response(
|
|
final_res_batch,
|
|
request_id,
|
|
model_name,
|
|
ctx.request.documents,
|
|
ctx.request.top_n if ctx.request.top_n > 0 else len(final_res_batch),
|
|
)
|
|
else:
|
|
raise NotImplementedError("")
|
|
|
|
def _request_output_to_score_response(
|
|
self,
|
|
final_res_batch: list[PoolingRequestOutput],
|
|
request_id: str,
|
|
created_time: int,
|
|
model_name: str,
|
|
) -> JSONResponse:
|
|
items: list[ScoreResponseData] = []
|
|
num_prompt_tokens = 0
|
|
|
|
for idx, final_res in enumerate(final_res_batch):
|
|
classify_res = ScoringRequestOutput.from_base(final_res)
|
|
|
|
item = ScoreResponseData(
|
|
index=idx,
|
|
score=classify_res.outputs.score,
|
|
)
|
|
prompt_token_ids = final_res.prompt_token_ids
|
|
|
|
items.append(item)
|
|
num_prompt_tokens += len(prompt_token_ids)
|
|
|
|
usage = UsageInfo(
|
|
prompt_tokens=num_prompt_tokens,
|
|
total_tokens=num_prompt_tokens,
|
|
)
|
|
|
|
response = ScoreResponse(
|
|
id=request_id,
|
|
created=created_time,
|
|
model=model_name,
|
|
data=items,
|
|
usage=usage,
|
|
)
|
|
|
|
return JSONResponse(content=response.model_dump())
|
|
|
|
def _request_output_to_rerank_response(
|
|
self,
|
|
final_res_batch: list[PoolingRequestOutput],
|
|
request_id: str,
|
|
model_name: str,
|
|
documents: ScoreInput | list[ScoreInput],
|
|
top_n: int,
|
|
) -> JSONResponse:
|
|
if not isinstance(documents, list):
|
|
documents = [documents]
|
|
|
|
results: list[RerankResult] = []
|
|
num_prompt_tokens = 0
|
|
for idx, final_res in enumerate(final_res_batch):
|
|
classify_res = ScoringRequestOutput.from_base(final_res)
|
|
|
|
document = documents[idx]
|
|
if isinstance(document, str):
|
|
rerank_document = RerankDocument(text=document)
|
|
else:
|
|
rerank_document = RerankDocument(
|
|
multi_modal=document.get("content", [])
|
|
)
|
|
|
|
result = RerankResult(
|
|
index=idx,
|
|
document=rerank_document,
|
|
relevance_score=classify_res.outputs.score,
|
|
)
|
|
results.append(result)
|
|
prompt_token_ids = final_res.prompt_token_ids
|
|
num_prompt_tokens += len(prompt_token_ids)
|
|
|
|
# sort by relevance, then return the top n if set
|
|
results.sort(key=lambda x: x.relevance_score, reverse=True)
|
|
if top_n < len(documents):
|
|
results = results[:top_n]
|
|
|
|
response = RerankResponse(
|
|
id=request_id,
|
|
model=model_name,
|
|
results=results,
|
|
usage=RerankUsage(
|
|
total_tokens=num_prompt_tokens, prompt_tokens=num_prompt_tokens
|
|
),
|
|
)
|
|
|
|
return JSONResponse(content=response.model_dump())
|