[Frontend] Support bad_words in the /v1/completions endpoint (#46793)
Signed-off-by: sungbin1015 <sbin@solbox.com> Co-authored-by: Claude <noreply@anthropic.com>
sungbin1015 committed
Jul 8, 2026 at 18:51 UTC
04a703e397a310d397bb15cc4d15365312782ff3
2 files changed
+39
tests/entrypoints/openai/completion/test_completion.py
+37
@@ -9,6 +9,8 @@ import regex as re
9
from openai import BadRequestError
10
11
from tests.utils import RemoteOpenAIServer
12
+from vllm.entrypoints.openai.completion.protocol import CompletionRequest
13
+from vllm.sampling_params import SamplingParams
14
from vllm.tokenizers import get_tokenizer
15
16
# any model with a chat template should work here
@@ -730,3 +732,38 @@ async def test_invalid_grammar(client: openai.AsyncOpenAI, model_name: str):
732
"structured_outputs": {"grammar": invalid_simplified_sql_grammar}
733
},
734
)
735
+
736
+
737
+# Unit tests for bad_words in CompletionRequest.to_sampling_params()
738
+def test_completion_request_bad_words_to_sampling_params():
739
+ """bad_words should be forwarded to SamplingParams (parity with chat)."""
740
+ request = CompletionRequest(
741
+ model="test-model",
742
+ prompt="Hello",
743
+ bad_words=["foo", "bar"],
744
+ max_tokens=10,
745
+ )
746
+
747
+ sampling_params = request.to_sampling_params(
748
+ max_tokens=10,
749
+ default_sampling_params={},
750
+ )
751
+
752
+ assert isinstance(sampling_params, SamplingParams)
753
+ assert sampling_params.bad_words == ["foo", "bar"]
754
+
755
+
756
+def test_completion_request_bad_words_default_empty():
757
+ """bad_words defaults to an empty list, matching the chat endpoint."""
758
+ request = CompletionRequest(
759
+ model="test-model",
760
+ prompt="Hello",
761
+ max_tokens=10,
762
+ )
763
+
764
+ assert request.bad_words == []
765
+ sampling_params = request.to_sampling_params(
766
+ max_tokens=10,
767
+ default_sampling_params={},
768
+ )
769
+ assert sampling_params.bad_words == []
vllm/entrypoints/openai/completion/protocol.py
+2
@@ -95,6 +95,7 @@ class CompletionRequest(OpenAIBaseModel):
95
)
96
allowed_token_ids: list[int] | None = None
97
prompt_logprobs: int | None = None
98
+ bad_words: list[str] = Field(default_factory=list)
99
# --8<-- [end:completion-sampling-params]
100
101
# --8<-- [start:completion-extra-params]
@@ -371,6 +372,7 @@ class CompletionRequest(OpenAIBaseModel):
372
structured_outputs=self.structured_outputs,
373
logit_bias=self.logit_bias,
374
allowed_token_ids=self.allowed_token_ids,
375
+ bad_words=self.bad_words,
376
extra_args=extra_args or None,
377
skip_clone=True, # Created fresh per request, safe to skip clone
378
repetition_detection=self.repetition_detection,