[Bugfix][Frontend] Constrain Anthropic cache_salt to non-empty (#50764)
Signed-off-by: Omkar Shewale <omkarshewale2001@gmail.com> Signed-off-by: Andreas Karatzas <Andreas.Karatzas@amd.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com> Co-authored-by: Andreas Karatzas <Andreas.Karatzas@amd.com>
Omkar Shewale committed
Aug 3, 2026 at 00:33 UTC
f5bb701fa270f5c801f1572e1478b56f292d8dfc
2 files changed
+49
tests/entrypoints/anthropic/test_anthropic_messages_conversion.py
+48
@@ -13,10 +13,16 @@ Also covers cache usage computation in ``_build_anthropic_usage``.
13
"""
14
15
import json
16
+from argparse import Namespace
17
+from http import HTTPStatus
18
from unittest.mock import MagicMock
19
20
import pytest
21
+from fastapi import FastAPI
22
+from fastapi.exceptions import RequestValidationError
23
+from fastapi.testclient import TestClient
24
25
+from vllm.entrypoints.anthropic.api_router import attach_router
26
from vllm.entrypoints.anthropic.protocol import (
27
AnthropicMessagesRequest,
28
)
@@ -38,6 +44,7 @@ from vllm.entrypoints.openai.engine.protocol import (
44
PromptTokenUsageInfo,
45
UsageInfo,
46
)
47
+from vllm.entrypoints.serve.utils.server_utils import validation_exception_handler
48
49
_convert = AnthropicServingMessages._convert_anthropic_to_openai_request
50
_img_url = AnthropicServingMessages._convert_image_source_to_url
@@ -1408,3 +1415,44 @@ class TestCacheSalt:
1415
request = _make_request([{"role": "user", "content": "Hello"}])
1416
result = _convert(request)
1417
assert result.cache_salt is None
1418
+
1419
+ @staticmethod
1420
+ def _make_api_app():
1421
+ app = FastAPI()
1422
+ attach_router(app)
1423
+ app.state.args = Namespace(log_error_stack=False)
1424
+ app.exception_handler(RequestValidationError)(validation_exception_handler)
1425
+
1426
+ handler = MagicMock(spec=AnthropicServingMessages)
1427
+ handler.create_messages.side_effect = AssertionError(
1428
+ "invalid requests must not reach the serving handler"
1429
+ )
1430
+ app.state.anthropic_serving_messages = handler
1431
+ return app, handler
1432
+
1433
+ def test_cache_salt_openapi_requires_non_empty_string(self):
1434
+ app, _ = self._make_api_app()
1435
+ field_schema = app.openapi()["components"]["schemas"][
1436
+ "AnthropicMessagesRequest"
1437
+ ]["properties"]["cache_salt"]
1438
+ string_schema = next(
1439
+ option for option in field_schema["anyOf"] if option.get("type") == "string"
1440
+ )
1441
+
1442
+ assert string_schema["minLength"] == 1
1443
+
1444
+ def test_empty_cache_salt_returns_bad_request(self):
1445
+ app, handler = self._make_api_app()
1446
+ with TestClient(app, raise_server_exceptions=False) as client:
1447
+ response = client.post(
1448
+ "/v1/messages",
1449
+ json={
1450
+ "model": "test-model",
1451
+ "max_tokens": 1,
1452
+ "messages": [{"role": "user", "content": "Hello"}],
1453
+ "cache_salt": "",
1454
+ },
1455
+ )
1456
+
1457
+ assert response.status_code == HTTPStatus.BAD_REQUEST
1458
+ handler.create_messages.assert_not_awaited()
vllm/entrypoints/anthropic/protocol.py
+1
@@ -135,6 +135,7 @@ class AnthropicMessagesRequest(BaseModel):
135
# vLLM-specific fields that are not in Anthropic spec
136
cache_salt: str | None = Field(
137
default=None,
138
+ min_length=1,
139
description=(
140
"If specified, the prefix cache will be salted with the provided "
141
"string to prevent an attacker to guess prompts in multi-user "