diff --git a/components/src/dynamo/vllm/handlers.py b/components/src/dynamo/vllm/handlers.py index 36305c283051..c8a9f86725e2 100644 --- a/components/src/dynamo/vllm/handlers.py +++ b/components/src/dynamo/vllm/handlers.py @@ -4035,16 +4035,7 @@ async def generate(self, request, context): # Use context ID for request tracking and correlation with decode phase request_id = context.id() logger.debug("Prefill Request ID: %s", request_id) - try: - self._multimodal_request_processor.validate_multimodal_request(request) - except ValueError as exc: - logger.error("Request %s: %s", request_id, exc) - yield { - "status": "error", - "message": str(exc), - "disaggregated_params": None, - } - return + self._multimodal_request_processor.validate_multimodal_request(request) # Token-in-token-out mode: internal protocol format with time_and_log_code_section(f"[PREFILL] request: {request_id} generate"): diff --git a/components/src/dynamo/vllm/multimodal_utils/request_processor.py b/components/src/dynamo/vllm/multimodal_utils/request_processor.py index a12513cb2d53..2b9da813b39a 100644 --- a/components/src/dynamo/vllm/multimodal_utils/request_processor.py +++ b/components/src/dynamo/vllm/multimodal_utils/request_processor.py @@ -38,6 +38,7 @@ ) from dynamo.common.multimodal.video_loader import VideoLoader from dynamo.common.utils import nvtx_utils as _nvtx +from dynamo.llm.exceptions import InvalidArgument from .hash_utils import compute_mm_uuids_from_images from .model import ModelFamily, construct_qwen_decode_mm_data, resolve_model_family @@ -462,8 +463,8 @@ def _expand_kimi_k3_pads( return expanded @staticmethod - def _multimodal_disabled_error() -> ValueError: - return ValueError( + def _multimodal_disabled_error() -> InvalidArgument: + return InvalidArgument( "Received multimodal data but multimodal processing is not enabled. " "Use --enable-multimodal flag to enable multimodal processing." ) diff --git a/components/src/dynamo/vllm/tests/multimodal_utils/test_vllm_request_processor.py b/components/src/dynamo/vllm/tests/multimodal_utils/test_vllm_request_processor.py index 65003097c401..d8b16d214995 100644 --- a/components/src/dynamo/vllm/tests/multimodal_utils/test_vllm_request_processor.py +++ b/components/src/dynamo/vllm/tests/multimodal_utils/test_vllm_request_processor.py @@ -412,7 +412,7 @@ async def test_rejects_malformed_encoder_image_item_before_dispatch(): async def test_rejects_media_when_multimodal_is_disabled(): processor = _processor(enabled=False) - with pytest.raises(ValueError, match="--enable-multimodal"): + with pytest.raises(mod.InvalidArgument, match="--enable-multimodal"): await _prepare_prompt( processor, { @@ -424,7 +424,7 @@ async def test_rejects_media_when_multimodal_is_disabled(): DisaggregationMode.AGGREGATED, ) - with pytest.raises(ValueError, match="--enable-multimodal"): + with pytest.raises(mod.InvalidArgument, match="--enable-multimodal"): await _prepare_prompt( processor, { @@ -436,7 +436,7 @@ async def test_rejects_media_when_multimodal_is_disabled(): DisaggregationMode.AGGREGATED, ) - with pytest.raises(ValueError, match="--enable-multimodal"): + with pytest.raises(mod.InvalidArgument, match="--enable-multimodal"): await _prepare_prompt( processor, { @@ -456,7 +456,7 @@ async def test_decode_cannot_hide_disabled_media_with_expanded_tokens(): enabled=False, ) - with pytest.raises(ValueError, match="--enable-multimodal"): + with pytest.raises(mod.InvalidArgument, match="--enable-multimodal"): await _prepare_prompt( processor, { diff --git a/components/src/dynamo/vllm/tests/test_vllm_worker_handler.py b/components/src/dynamo/vllm/tests/test_vllm_worker_handler.py index fad0a4e58683..c679c2f3a5f1 100644 --- a/components/src/dynamo/vllm/tests/test_vllm_worker_handler.py +++ b/components/src/dynamo/vllm/tests/test_vllm_worker_handler.py @@ -675,7 +675,7 @@ async def test_text_mode_rejects_multimodal_input_when_disabled( handler.use_vllm_tokenizer = True handler._multimodal_request_processor.enable_multimodal = False - with pytest.raises(ValueError, match="--enable-multimodal"): + with pytest.raises(mod.InvalidArgument, match="--enable-multimodal"): async for _ in handler.generate(request_payload, MagicMock()): pass @@ -984,26 +984,21 @@ async def test_prefill_delegates_mode_policy_to_shared_processor(): @pytest.mark.asyncio -async def test_prefill_returns_structured_error_when_multimodal_is_disabled(): +async def test_prefill_raises_typed_error_when_multimodal_is_disabled(): handler = mod.PrefillWorkerHandler.__new__(mod.PrefillWorkerHandler) processor = SimpleNamespace( validate_multimodal_request=MagicMock( - side_effect=ValueError("use --enable-multimodal") + side_effect=mod.InvalidArgument("use --enable-multimodal") ) ) handler._multimodal_request_processor = processor context = MagicMock() context.id.return_value = "request-prefill-disabled" - chunks = [chunk async for chunk in handler.generate({}, context)] + with pytest.raises(mod.InvalidArgument, match="use --enable-multimodal"): + [chunk async for chunk in handler.generate({}, context)] - assert chunks == [ - { - "status": "error", - "message": "use --enable-multimodal", - "disaggregated_params": None, - } - ] + processor.validate_multimodal_request.assert_called_once_with({}) # ── Deferred abort (disagg decode KV-transfer safety) tests ────────