From 768943b8791abf30a1cc7b1cf82cbbad5d5ee247 Mon Sep 17 00:00:00 2001 From: Reid <61492567+reidliu41@users.noreply.github.com> Date: Sun, 17 May 2026 22:10:26 +0800 Subject: [PATCH] [Frontend]Handle audio generate engine errors consistently (#3316) Signed-off-by: reidliu41 Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com> --- .../openai_api/test_serving_audio_generate.py | 61 ++++++++++++++++++- vllm_omni/entrypoints/openai/api_server.py | 2 + 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/tests/entrypoints/openai_api/test_serving_audio_generate.py b/tests/entrypoints/openai_api/test_serving_audio_generate.py index ffb23bf4f..e1150c05b 100644 --- a/tests/entrypoints/openai_api/test_serving_audio_generate.py +++ b/tests/entrypoints/openai_api/test_serving_audio_generate.py @@ -1,13 +1,16 @@ -# tests/entrypoints/openai_api/test_serving_audio_generate.py import logging from inspect import Signature, signature -from unittest.mock import MagicMock, patch +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock, patch import pytest import torch -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.testclient import TestClient +from vllm.v1.engine.exceptions import EngineGenerateError +from vllm_omni.entrypoints.omni_base import OmniEngineDeadError +from vllm_omni.entrypoints.openai import api_server as api_server_module from vllm_omni.entrypoints.openai.protocol.audio import ( CreateAudio, OpenAICreateAudioGenerateRequest, @@ -106,6 +109,29 @@ def client(test_app): return TestClient(test_app) +def _make_api_server_test_app(handler, *, request_id: str = "audio-gen-req-1"): + app = FastAPI() + app.state.openai_serving_audio_generate = handler + app.state.engine_client = SimpleNamespace( + engine=SimpleNamespace(is_alive=lambda: False), + errored=True, + ) + app.state.server = SimpleNamespace() + + @app.middleware("http") + async def add_request_metadata(request: Request, call_next): + request.state.request_metadata = SimpleNamespace(request_id=request_id) + return await call_next(request) + + app.add_api_route( + "/v1/audio/generate", + api_server_module.create_audio_generate, + methods=["POST"], + response_model=None, + ) + return app + + # Request Validation (Pydantic model) class TestRequestValidation: """Validate OpenAICreateAudioGenerateRequest pydantic constraints.""" @@ -472,6 +498,35 @@ class TestErrorHandling: assert "Audio generation failed" in resp.error.message + @pytest.mark.parametrize( + ("exc", "expected_message", "expected_stage_id"), + [ + (OmniEngineDeadError("engine dead", error_stage_id=2), "engine dead", 2), + (EngineGenerateError("engine generate failed"), "engine generate failed", None), + ], + ) + def test_api_server_engine_error_response_includes_request_and_stage_id( + self, + exc, + expected_message, + expected_stage_id, + ): + handler = MagicMock() + handler.create_audio_generate = AsyncMock(side_effect=exc) + app = _make_api_server_test_app(handler) + + with patch.object(api_server_module, "terminate_if_errored") as terminate_mock: + with TestClient(app) as client: + response = client.post("/v1/audio/generate", json={"input": "Hello"}) + + assert response.status_code == 500 + payload = response.json() + assert payload["error"]["message"] == expected_message + assert payload["error"]["code"] == 500 + assert payload["error"]["request_id"] == "audio-gen-req-1" + assert payload["error"]["error_stage_id"] == expected_stage_id + terminate_mock.assert_called_once() + # End-to-End via TestClient class TestAudioGenerateAPI: diff --git a/vllm_omni/entrypoints/openai/api_server.py b/vllm_omni/entrypoints/openai/api_server.py index c1467f719..f00e6448a 100644 --- a/vllm_omni/entrypoints/openai/api_server.py +++ b/vllm_omni/entrypoints/openai/api_server.py @@ -1164,6 +1164,8 @@ async def create_audio_generate(request: OpenAICreateAudioGenerateRequest, raw_r status_code=result.error.code if result.error else 400, ) return result + except (EngineGenerateError, EngineDeadError) as exc: + return _create_engine_error_json_response(raw_request, exc) except Exception as e: raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value, detail=str(e)) from e