[Frontend] Make `model` optional on all `/derender` request classes (#51463)
Signed-off-by: Vinay Damodaran <vrdn@hey.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vinay R Damodaran committed
Aug 11, 2026 at 21:48 UTC
3ee2df30337a301164c46ae444b76ee67e71c106
3 files changed
+37
-14
tests/entrypoints/scale_out/derender/test_derender.py
+19
@@ -308,6 +308,25 @@ async def test_derender_chat_unknown_model(client):
308
assert response.status_code == 404
309
310
311
+@pytest.mark.asyncio
312
+async def test_derender_chat_model_omitted_resolves_served_name(client):
313
+ """Omitting `model` resolves the served name rather than rejecting.
314
+
315
+ Mirrors test_serving_chat.py's "full name is returned when no model is
316
+ specified" assertion for the derender path. Asserts the resolved value,
317
+ not just the status, so the fallback is proven to have fired.
318
+ """
319
+ gen_req = await _render_chat(client)
320
+ synthetic_ids = gen_req["token_ids"][:3]
321
+
322
+ response = await client.post(
323
+ "/v1/chat/completions/derender",
324
+ json={"generate_response": _make_generate_response(synthetic_ids)},
325
+ )
326
+ assert response.status_code == 200
327
+ assert response.json()["model"] == MODEL_NAME
328
+
329
+
330
# ---------------------------------------------------------------------------
331
# Completion derender tests
332
# ---------------------------------------------------------------------------
vllm/entrypoints/scale_out/derender/serving.py
+12
-8
@@ -166,16 +166,17 @@ class ServingDerender(BaseServing):
166
total_tokens=prompt_tokens + completion_tokens,
167
)
168
169
+ model_name = request.model or self.models.model_name()
170
logger.debug(
171
"derender_chat request_id=%s model=%s choices=%d completion_tokens=%d",
172
gen.request_id,
172
- request.model,
173
+ model_name,
174
len(choices),
175
completion_tokens,
176
)
177
return ChatCompletionResponse(
178
id=gen.request_id,
178
- model=request.model,
179
+ model=model_name,
180
created=int(time.time()),
181
choices=choices,
182
usage=usage,
@@ -231,17 +232,18 @@ class ServingDerender(BaseServing):
232
total_tokens=total_prompt_tokens + total_completion_tokens,
233
)
234
235
+ model_name = request.model or self.models.model_name()
236
logger.debug(
237
"derender_completion request_id=%s model=%s choices=%d"
238
" completion_tokens=%d",
239
first.request_id,
238
- request.model,
240
+ model_name,
241
len(choices),
242
total_completion_tokens,
243
)
244
return CompletionResponse(
245
id=first.request_id,
244
- model=request.model,
246
+ model=model_name,
247
created=int(time.time()),
248
choices=choices,
249
usage=usage,
@@ -264,9 +266,10 @@ class ServingDerender(BaseServing):
266
if error_check_ret is not None:
267
return error_check_ret
268
269
+ model_name = request.model or self.models.model_name()
270
try:
271
chunk, updated_state = await self.online_derenderer.derender_chat_stream(
269
- model=request.model,
272
+ model=model_name,
273
generate_chunk=request.generate_chunk,
274
state=request.stream_state,
275
chat_request=request.chat_request,
@@ -284,7 +287,7 @@ class ServingDerender(BaseServing):
287
logger.debug(
288
"derender_chat_stream request_id=%s model=%s delta_tokens=%d",
289
request.generate_chunk.request_id,
287
- request.model,
290
+ model_name,
291
sum(
292
len(c.token_ids) for c in request.generate_chunk.choices if c.token_ids
293
),
@@ -304,12 +307,13 @@ class ServingDerender(BaseServing):
307
if error_check_ret is not None:
308
return error_check_ret
309
310
+ model_name = request.model or self.models.model_name()
311
try:
312
(
313
chunk,
314
updated_state,
315
) = await self.online_derenderer.derender_completion_stream(
312
- model=request.model,
316
+ model=model_name,
317
generate_chunk=request.generate_chunk,
318
state=request.stream_state,
319
prompt_tokens=request.prompt_tokens,
@@ -325,7 +329,7 @@ class ServingDerender(BaseServing):
329
logger.debug(
330
"derender_completion_stream request_id=%s model=%s delta_tokens=%d",
331
request.generate_chunk.request_id,
328
- request.model,
332
+ model_name,
333
sum(
334
len(c.token_ids) for c in request.generate_chunk.choices if c.token_ids
335
),
vllm/entrypoints/scale_out/token_in_token_out/protocol.py
+6
-6
@@ -277,8 +277,8 @@ class DerenderChatRequest(BaseModel):
277
# --8<-- [start:derender-chat-request]
278
stream: Literal[False] = False
279
280
- model: str
281
- """Served model name."""
280
+ model: str | None = None
281
+ """Served model name. Defaults to the server's served model name."""
282
283
generate_response: GenerateResponse
284
"""The complete token-in / token-out engine response to derender."""
@@ -311,8 +311,8 @@ class DerenderCompletionRequest(BaseModel):
311
# --8<-- [start:derender-completion-request]
312
stream: Literal[False] = False
313
314
- model: str
315
- """Served model name."""
314
+ model: str | None = None
315
+ """Served model name. Defaults to the server's served model name."""
316
317
generate_responses: list[GenerateResponse]
318
"""One response per prompt, parallel to the list[GenerateRequest]
@@ -428,7 +428,7 @@ class DerenderChatStreamRequest(BaseModel):
428
429
stream: Literal[True]
430
431
- model: str
431
+ model: str | None = None
432
generate_chunk: GenerateStreamResponse
433
"""One SSE chunk from ``/inference/v1/generate`` (``stream=True``)."""
434
@@ -452,7 +452,7 @@ class DerenderCompletionStreamRequest(BaseModel):
452
453
stream: Literal[True]
454
455
- model: str
455
+ model: str | None = None
456
generate_chunk: GenerateStreamResponse
457
"""One SSE chunk from ``/inference/v1/generate``."""
458