[Feature] Abort diffusion request (#1156)
Signed-off-by: dengyunyang <584797741@qq.com>
This commit is contained in:
1 parent
2ec1fc3c9c
commit
dc45f43a6a
6 files changed
+90
-16
No files matched your search
@@ -2,8 +2,8 @@
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
import os
|
||||
import signal
|
||||
import time
|
||||
from collections.abc import Iterable
|
||||
from typing import Any
|
||||
|
||||
import PIL.Image
|
||||
@@ -371,7 +371,13 @@ class DiffusionEngine:
|
||||
if hasattr(self, "executor"):
|
||||
self.executor.shutdown()
|
||||
|
||||
def abort(self, request_id: str | Iterable[str]) -> None:
|
||||
# TODO implement it
|
||||
logger.warning("DiffusionEngine abort is not implemented yet")
|
||||
pass
|
||||
def abort(self) -> None:
|
||||
"""
|
||||
Abort diffusion generation requests.
|
||||
"""
|
||||
try:
|
||||
for worker_pid in self.executor.get_worker_pids():
|
||||
os.kill(worker_pid, signal.SIGUSR1)
|
||||
logger.info("Interrupt signal sent to worker.")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to interrupt current diffusion forward: {e}")
|
||||
@@ -84,3 +84,8 @@ class DiffusionExecutor(ABC):
|
||||
def shutdown(self) -> None:
|
||||
"""Shutdown the executor and release resources."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_worker_pids(self) -> None:
|
||||
"""Get worker pid."""
|
||||
pass
|
||||
@@ -130,6 +130,9 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
|
||||
def add_req(self, request: OmniDiffusionRequest) -> DiffusionOutput:
|
||||
return self.scheduler.add_req(request)
|
||||
|
||||
def get_worker_pids(self):
|
||||
return [process.pid for process in self._processes]
|
||||
|
||||
def collective_rpc(
|
||||
self,
|
||||
method: str,
|
||||
|
||||
@@ -9,6 +9,7 @@ to DiffusionModelRunner.
|
||||
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import signal
|
||||
from contextlib import AbstractContextManager, nullcontext
|
||||
|
||||
import torch
|
||||
@@ -66,6 +67,21 @@ class DiffusionWorker:
|
||||
self._sleep_saved_buffers: dict[str, torch.Tensor] = {}
|
||||
self.lora_manager: DiffusionLoRAManager | None = None
|
||||
self.init_device()
|
||||
# Registry signal
|
||||
signal.signal(signal.SIGUSR1, self._on_interrupt)
|
||||
|
||||
def _on_interrupt(self, signum, frame):
|
||||
# Set interrupt flag on the pipeline
|
||||
# The pipeline checks this flag in the diffuse loop
|
||||
if (
|
||||
self.model_runner is not None
|
||||
and self.model_runner.pipeline is not None
|
||||
and hasattr(self.model_runner.pipeline, "_interrupt")
|
||||
):
|
||||
self.model_runner.pipeline._interrupt = True
|
||||
logger.info("Worker: Interrupt flag set for current Running Request")
|
||||
else:
|
||||
logger.warning("Worker: Cannot set interrupt flag - model_runner or pipeline is None")
|
||||
|
||||
def init_device(self) -> None:
|
||||
"""Initialize the device and distributed environment."""
|
||||
|
||||
@@ -9,8 +9,9 @@ enabling concurrent request handling and streaming generation.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
import uuid
|
||||
from collections.abc import AsyncGenerator, Iterable
|
||||
from collections.abc import AsyncGenerator
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Any
|
||||
|
||||
@@ -106,8 +107,24 @@ class AsyncOmniDiffusion:
|
||||
self._executor = ThreadPoolExecutor(max_workers=1)
|
||||
self._closed = False
|
||||
|
||||
# Track active requests for abort functionality
|
||||
self._active_generate_task: dict[str, asyncio.Task] = {}
|
||||
self._active_engine_step_task: dict[str, asyncio.Task] = {}
|
||||
self._running_lock = threading.Lock()
|
||||
self._running_requests: set[str] = set()
|
||||
|
||||
logger.info("AsyncOmniDiffusion initialized with model: %s", model)
|
||||
|
||||
def _engine_step_wrapper(self, request, request_id):
|
||||
with self._running_lock:
|
||||
self._running_requests.add(request_id)
|
||||
|
||||
try:
|
||||
return self.engine.step(request)
|
||||
finally:
|
||||
with self._running_lock:
|
||||
self._running_requests.discard(request_id)
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
prompt: OmniPromptType,
|
||||
@@ -137,6 +154,10 @@ class AsyncOmniDiffusion:
|
||||
if lora_request is not None:
|
||||
sampling_params.lora_request = lora_request
|
||||
|
||||
# Register this task for tracking
|
||||
current_task = asyncio.current_task()
|
||||
self._active_generate_task[request_id] = current_task
|
||||
|
||||
request = OmniDiffusionRequest(
|
||||
prompts=[prompt],
|
||||
sampling_params=sampling_params,
|
||||
@@ -148,12 +169,17 @@ class AsyncOmniDiffusion:
|
||||
# Run engine in thread pool
|
||||
loop = asyncio.get_event_loop()
|
||||
# In async mode, only a single request is submitted at a time
|
||||
result = await loop.run_in_executor(
|
||||
self._executor,
|
||||
self.engine.step,
|
||||
request,
|
||||
)
|
||||
result = result[0]
|
||||
try:
|
||||
# In async mode, only a single request is submitted at a time
|
||||
step_task = loop.run_in_executor(self._executor, self._engine_step_wrapper, request, request_id)
|
||||
self._active_engine_step_task[request_id] = step_task
|
||||
result = await step_task
|
||||
result = result[0]
|
||||
finally:
|
||||
# Unregister the request
|
||||
self._active_generate_task.pop(request_id, None)
|
||||
self._active_engine_step_task.pop(request_id, None)
|
||||
logger.debug(f"{request_id} finished")
|
||||
|
||||
# Update request_id if needed
|
||||
if not result.request_id:
|
||||
@@ -215,9 +241,26 @@ class AsyncOmniDiffusion:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def abort(self, request_id: str | Iterable[str]) -> None:
|
||||
"""Abort a request."""
|
||||
self.engine.abort(request_id)
|
||||
async def abort(self, request_id: str):
|
||||
logger.info(f"Aborting {request_id}.")
|
||||
with self._running_lock:
|
||||
is_running = request_id in self._running_requests
|
||||
if is_running:
|
||||
self.engine.abort()
|
||||
else:
|
||||
step_task = self._active_engine_step_task.get(request_id)
|
||||
if step_task and not step_task.done():
|
||||
step_task.cancel()
|
||||
if is_running:
|
||||
logger.info(f"{request_id} engine step is running, sending interrupt")
|
||||
else:
|
||||
logger.info(f"{request_id} engine step not running, cancel in queue")
|
||||
|
||||
gen_task = self._active_generate_task.get(request_id)
|
||||
if gen_task and not gen_task.done():
|
||||
gen_task.cancel()
|
||||
self._active_engine_step_task.pop(request_id, None)
|
||||
self._active_generate_task.pop(request_id, None)
|
||||
|
||||
@property
|
||||
def is_running(self) -> bool:
|
||||
|
||||
@@ -991,6 +991,7 @@ async def generate_images(request: ImageGenerationRequest, raw_request: Request)
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR.value: {"model": ErrorResponse},
|
||||
},
|
||||
)
|
||||
@with_cancellation
|
||||
async def edit_images(
|
||||
raw_request: Request,
|
||||
image: list[UploadFile] | None = File(None),
|
||||
@@ -1014,7 +1015,7 @@ async def edit_images(
|
||||
seed: int | None = Form(None),
|
||||
# vllm-omni extension for per-request LoRA.
|
||||
lora: str | None = Form(None), # Json string
|
||||
) -> ImageGenerationResponse:
|
||||
) -> ImageGenerationResponse | None:
|
||||
"""
|
||||
OpenAI-compatible image edit endpoint.
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user