[Misc] Use VLLMValidationError in Cohere request validation (#52467)
Signed-off-by: frank-suwen <suwenw2@outlook.com> Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -36,6 +36,10 @@ from vllm.entrypoints.serve.exception_handling.handlers.http import (
|
||||
from vllm.entrypoints.serve.exception_handling.handlers.validation import (
|
||||
validation_exception_handler,
|
||||
)
|
||||
from vllm.entrypoints.serve.exception_handling.handlers.vllm_error import (
|
||||
vllm_error_handler,
|
||||
)
|
||||
from vllm.exceptions import VLLMError
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -83,7 +87,7 @@ def _build_app(handler: _Handler | None) -> FastAPI:
|
||||
|
||||
def _build_app_with_vllm_handlers(handler: _Handler | None) -> FastAPI:
|
||||
"""Build a FastAPI app that mirrors the real vLLM setup by installing
|
||||
``validation_exception_handler`` and ``http_exception_handler``. The
|
||||
validation, HTTP, and vLLM exception handlers. The
|
||||
:class:`CohereErrorEnvelopeMiddleware` registered by ``attach_router``
|
||||
is expected to translate any resulting vLLM ``ErrorResponse`` body
|
||||
into the ``CohereError`` wire shape.
|
||||
@@ -96,6 +100,7 @@ def _build_app_with_vllm_handlers(handler: _Handler | None) -> FastAPI:
|
||||
app.state.args = Namespace(log_error_stack=False)
|
||||
app.exception_handler(RequestValidationError)(validation_exception_handler)
|
||||
app.exception_handler(HTTPException)(http_exception_handler)
|
||||
app.exception_handler(VLLMError)(vllm_error_handler)
|
||||
return app
|
||||
|
||||
|
||||
@@ -333,8 +338,8 @@ class TestCohereErrorEnvelope:
|
||||
|
||||
def test_validation_error_body_is_cohere_shaped(self):
|
||||
# ``model=""`` and ``messages=[]`` trip our custom field
|
||||
# validators, which raise pydantic ValueErrors and are routed
|
||||
# through ``validation_exception_handler`` in the real vLLM
|
||||
# validators, which raise VLLMValidationError and are routed
|
||||
# through ``vllm_error_handler`` in the real vLLM
|
||||
# server (producing the ``{"error": {...}}`` shape).
|
||||
app = _build_app_with_vllm_handlers(handler=None)
|
||||
with TestClient(app) as client:
|
||||
|
||||
@@ -6,7 +6,8 @@ The module is mostly a thin wrapper around the official ``cohere`` SDK
|
||||
types plus a few local additions:
|
||||
|
||||
* :class:`CohereError` envelope.
|
||||
* :class:`CohereChatV2Request` (model required, ``max_tokens`` non-negative).
|
||||
* :class:`CohereChatV2Request` (model and messages required,
|
||||
``max_tokens`` non-negative).
|
||||
* :class:`CohereChatV2Response` plus the usage / logprob helpers.
|
||||
* The streaming event subclasses that bake a wire-format ``type``
|
||||
discriminator into ``model_dump()`` so SSE consumers can demux on it.
|
||||
@@ -33,6 +34,7 @@ from vllm.entrypoints.cohere.protocol import (
|
||||
ToolCallStartEvent,
|
||||
ToolPlanDeltaEvent,
|
||||
)
|
||||
from vllm.exceptions import VLLMValidationError
|
||||
|
||||
# ======================================================================
|
||||
# CohereError
|
||||
@@ -52,16 +54,28 @@ class TestCohereError:
|
||||
|
||||
class TestCohereChatV2Request:
|
||||
def test_empty_model_rejected(self):
|
||||
with pytest.raises(ValidationError, match="model is required"):
|
||||
with pytest.raises(VLLMValidationError, match="model is required") as exc_info:
|
||||
CohereChatV2Request(model="", messages=[{"role": "user", "content": "hi"}])
|
||||
assert exc_info.value.parameter == "model"
|
||||
assert exc_info.value.value is None
|
||||
|
||||
def test_negative_max_tokens_rejected(self):
|
||||
with pytest.raises(ValidationError, match="non-negative"):
|
||||
with pytest.raises(VLLMValidationError, match="non-negative") as exc_info:
|
||||
CohereChatV2Request(
|
||||
model="m",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
max_tokens=-1,
|
||||
)
|
||||
assert exc_info.value.parameter == "max_tokens"
|
||||
assert exc_info.value.value == -1
|
||||
|
||||
def test_empty_messages_rejected(self):
|
||||
with pytest.raises(
|
||||
VLLMValidationError, match="messages must contain at least one message"
|
||||
) as exc_info:
|
||||
CohereChatV2Request(model="m", messages=[])
|
||||
assert exc_info.value.parameter == "messages"
|
||||
assert exc_info.value.value is None
|
||||
|
||||
def test_zero_max_tokens_allowed(self):
|
||||
# Zero is allowed (the docs allow 0 -> return prompt only).
|
||||
|
||||
@@ -46,6 +46,8 @@ from cohere.types import (
|
||||
)
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from vllm.exceptions import VLLMValidationError
|
||||
|
||||
# Re-export the SDK wire-format types alongside our local extensions so
|
||||
# ``vllm.entrypoints.cohere.serving`` and friends can import everything
|
||||
# they need from this module.
|
||||
@@ -186,14 +188,18 @@ class CohereChatV2Request(BaseModel):
|
||||
@classmethod
|
||||
def _validate_model(cls, v: str) -> str:
|
||||
if not v:
|
||||
raise ValueError("model is required")
|
||||
raise VLLMValidationError("model is required", parameter="model")
|
||||
return v
|
||||
|
||||
@field_validator("max_tokens")
|
||||
@classmethod
|
||||
def _validate_max_tokens(cls, v: int | None) -> int | None:
|
||||
if v is not None and v < 0:
|
||||
raise ValueError("max_tokens must be non-negative")
|
||||
raise VLLMValidationError(
|
||||
"max_tokens must be non-negative",
|
||||
parameter="max_tokens",
|
||||
value=v,
|
||||
)
|
||||
return v
|
||||
|
||||
@field_validator("messages", mode="before")
|
||||
@@ -233,7 +239,9 @@ class CohereChatV2Request(BaseModel):
|
||||
@classmethod
|
||||
def _validate_messages(cls, v: list[ChatMessageV2]) -> list[ChatMessageV2]:
|
||||
if not v:
|
||||
raise ValueError("messages must contain at least one message")
|
||||
raise VLLMValidationError(
|
||||
"messages must contain at least one message", parameter="messages"
|
||||
)
|
||||
return v
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user