Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

148 lines
6.2 KiB
Python

# Copyright (c) ModelScope Contributors. All rights reserved.
import os
import torch
from PIL import Image
from tqdm.asyncio import tqdm_asyncio
from typing import Any, Dict, List, Optional, Union
from swift.metrics import Metric
from swift.rlhf_trainers.utils import VLLM_LORA_INT_ID, VLLM_LORA_NAME, VLLM_LORA_PATH
from swift.template import Template
from .protocol import (ChatCompletionResponse, ChatCompletionResponseChoice, ChatMessage, InferRequest, RequestConfig,
RolloutOutput)
from .utils import AdapterRequest
from .vllm_engine import VllmEngine
try:
os.environ['VLLM_WORKER_MULTIPROC_METHOD'] = 'spawn'
os.environ['VLLM_ENGINE_ITERATION_TIMEOUT_S'] = '86400'
from vllm.lora.request import LoRARequest
except Exception:
raise
class GRPOVllmEngine(VllmEngine):
def infer(
self,
infer_requests: List[Union[InferRequest, Dict[str, Any]]],
request_config: Optional[RequestConfig] = None,
metrics: Optional[List[Metric]] = None,
*,
use_tqdm: Optional[bool] = None,
adapter_request: Optional[AdapterRequest] = None,
) -> List[RolloutOutput]:
if not adapter_request and self.enable_lora:
lora_loaded = VLLM_LORA_INT_ID in self.engine.list_loras()
if lora_loaded:
adapter_request = LoRARequest(
lora_name=VLLM_LORA_NAME, lora_int_id=VLLM_LORA_INT_ID, lora_path=VLLM_LORA_PATH)
res = super().infer(
infer_requests,
request_config,
metrics,
use_tqdm=use_tqdm,
adapter_request=adapter_request,
)
if not isinstance(res, list):
res = [res]
for i, result in enumerate(res):
if not isinstance(result, RolloutOutput):
if not isinstance(result, ChatCompletionResponse):
raise TypeError('Result must be a ChatCompletionResponse or RolloutOutput instance.')
res[i] = RolloutOutput(response=result)
return res
async def async_infer(self,
infer_requests: List[InferRequest],
request_config: Optional[RequestConfig] = None,
metrics: Optional[List[Metric]] = None,
*,
use_tqdm: Optional[bool] = None,
**kwargs) -> List[RolloutOutput]:
if request_config is None:
request_config = RequestConfig()
assert request_config.n == 1
tasks = [self.infer_async(infer_request, request_config, **kwargs) for infer_request in infer_requests]
if use_tqdm is None:
use_tqdm = len(infer_requests) > 1
res = await self._batch_infer_stream(tasks, request_config.stream, use_tqdm, metrics)
for i, result in enumerate(res):
if not isinstance(result, RolloutOutput):
if not isinstance(result, ChatCompletionResponse):
raise TypeError('Result must be a ChatCompletionResponse or RolloutOutput instance.')
res[i] = RolloutOutput(response=result)
return res
async def _batch_infer_stream(self,
tasks,
stream: bool = True,
use_tqdm: bool = True,
metrics: Optional[List[Metric]] = None):
assert not stream
prog_bar = tqdm_asyncio(total=len(tasks), dynamic_ncols=True, disable=not use_tqdm)
async def _new_run(task):
try:
res = await task
except Exception as e:
if getattr(self, 'strict', True):
raise
res = e
prog_bar.update()
self._update_metrics(res, metrics)
return res
new_tasks = [_new_run(task) for task in tasks]
return await self.batch_run(new_tasks)
def _create_chat_completion_response(self, result, inputs, request_config, request_id) -> ChatCompletionResponse:
assert result is not None
num_generated_tokens = sum(len(output.token_ids) for output in result.outputs)
usage_info = self._get_usage_info(len(result.prompt_token_ids), num_generated_tokens)
choices = []
for output in result.outputs:
output.token_ids = list(output.token_ids)
response = self.template.decode_generate_ids(output.token_ids, template_inputs=inputs['template_inputs'])
logprobs = self._get_logprobs(output.logprobs, output.token_ids, request_config.top_logprobs)
toolcall = self._get_toolcall(response)
token_ids = output.token_ids if request_config.return_details else None
choice = ChatCompletionResponseChoice(
index=output.index,
message=ChatMessage(role='assistant', content=response, tool_calls=toolcall),
finish_reason=output.finish_reason,
logprobs=logprobs,
token_ids=token_ids,
routed_experts=getattr(output, 'routed_experts', None))
choices.append(choice)
prompt_token_ids = None
images_size = None
if request_config.return_details:
prompt_token_ids = result.prompt_token_ids
images = inputs['template_inputs'].images
if all(isinstance(image, Image.Image) for image in images):
images_size = [image.size for image in images]
return ChatCompletionResponse(
model=self.model_name,
choices=choices,
usage=usage_info,
id=request_id,
prompt_token_ids=prompt_token_ids,
images_size=images_size)
def _add_adapter(self, adapter_request: Optional[Union[AdapterRequest, LoRARequest]] = None):
assert self.enable_lora, f'adapter_request: {adapter_request}, self.enable_lora: {self.enable_lora}'
from vllm.lora.request import LoRARequest
if isinstance(adapter_request, AdapterRequest):
return super()._add_adapter(adapter_request)
elif isinstance(adapter_request, LoRARequest):
return adapter_request
else:
raise ValueError(f'Invalid adapter request: {adapter_request}')