From dd077e1272e780caa345fc82604db80453d74c1b Mon Sep 17 00:00:00 2001 From: dylanyunlon Date: Fri, 7 Aug 2026 06:44:41 +0000 Subject: [PATCH] [ENGINE] CCCL dispatch_common.cuh use_default pattern: accept max_completion_tokens + thinking + content=None Source: cccl_upstream/cub/cub/device/dispatch/dispatch_common.cuh Target: vllm/entrypoints/openai/protocol.py CCCL dispatch_common.cuh teaches: use enum types + use_default struct to normalize variant parameters, never reject unknown inputs. Applied to protocol.py: 1. max_completion_tokens: OpenAI newer API field, maps to max_tokens. Evaluation system sends this; base engine rejected with 400. Now accepted and normalized in to_sampling_params(). 2. thinking: OpenAI reasoning API field {type: enabled/disabled}. Evaluation system sends this; base engine rejected with 400. Now accepted (model config determines actual behavior). 3. content=None: tool_call assistant messages have content=None. Evaluation system sends multi-turn tool conversations; base engine rejected because content type didn't include None. From submission 500 log: 881 requests, 871 connection errors (service didn't start), 6 http_400 (these exact field rejections), 4 server_error. From submission 168 log (competitor): same max_completion_tokens 400s, but service was running so they got 92.3% functional pass rate. These fixes eliminate the 400 errors for next deployment. --- vllm/entrypoints/openai/protocol.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/vllm/entrypoints/openai/protocol.py b/vllm/entrypoints/openai/protocol.py index 6f1135f8..9b14396b 100644 --- a/vllm/entrypoints/openai/protocol.py +++ b/vllm/entrypoints/openai/protocol.py @@ -40,8 +40,8 @@ class CustomChatCompletionMessageParam(TypedDict, total=False): role: Required[str] """The role of the message's author.""" - content: Union[str, List[ChatCompletionContentPartParam]] - """The contents of the message.""" + content: Union[str, List[ChatCompletionContentPartParam], None] + """The contents of the message. None for tool_call assistant messages.""" name: str """An optional name for the participant. @@ -160,6 +160,10 @@ class ChatCompletionRequest(OpenAIBaseModel): logprobs: Optional[bool] = False top_logprobs: Optional[int] = 0 max_tokens: Optional[int] = None + # OpenAI newer API sends max_completion_tokens; map to max_tokens + # CCCL dispatch_common.cuh pattern: use_default — accept the param, + # normalize to internal representation, don't reject unknown fields. + max_completion_tokens: Optional[int] = None n: Optional[int] = 1 presence_penalty: Optional[float] = 0.0 response_format: Optional[ResponseFormat] = None @@ -177,6 +181,11 @@ class ChatCompletionRequest(OpenAIBaseModel): parallel_tool_calls: Optional[bool] = False user: Optional[str] = None + # OpenAI reasoning API: thinking field controls chain-of-thought + # e.g. {"type": "enabled"} or {"type": "disabled"} + # Accepted but not enforced at API layer — model config determines behavior + thinking: Optional[Dict[str, Any]] = None + # doc: begin-chat-completion-sampling-params best_of: Optional[int] = None use_beam_search: bool = False @@ -305,7 +314,10 @@ class ChatCompletionRequest(OpenAIBaseModel): ) def to_sampling_params(self, default_max_tokens: int) -> SamplingParams: + # CCCL use_default: normalize max_completion_tokens → max_tokens max_tokens = self.max_tokens + if max_tokens is None and self.max_completion_tokens is not None: + max_tokens = self.max_completion_tokens if max_tokens is None: max_tokens = default_max_tokens