Repository navigation
fix(frontend): apply --default-chat-template-kwargs for vllm/sglang - #16570
Conversation
⏹️ Dynamo PR CI cancelled — run 41728830927 (attempt 1) on
|
| Framework | Build |
|---|---|
| vLLM | ⏹️ 1 ✅ 5 |
Failure details
No job failed outright. The only non-passing job, vllm-runtime-efa / Build multi-arch cuda13.0, was cancelled because it hit the job's 1h timeout. It was not a fail-fast or manual cancel. backend-status-check fails only because it mirrors that job.
⏰ vllm-runtime-efa / Build multi-arch cuda13.0: job exceeded maximum execution time of 1h0m0s
Job: vllm-runtime-efa / Build multi-arch cuda13.0 · Cancelled step: Build and Push Test Image · Logs: gh api repos/ai-dynamo/dynamo/actions/jobs/126282944377/logs
#24 exporting manifest list sha256:b51cf01284417b0ca7870c806523e7ba2397947fc4ccb340f1d642976eedab89 done
#24 DONE 13.2s
#1 [internal] load build definition from Dockerfile.test
#28 [linux/arm64 internal] load metadata for ***.dkr.ecr.us-west-2.amazonaws.com/ai-dynamo/dynamo:02c7e3853b19bae78df6b9d8d4a43c42daaf2d85-vllm-runtime-efa
#29 [linux/arm64 internal] load metadata for docker.io/library/debian:bookworm-slim
#30 [internal] load .dockerignore
##[error]The operation was canceled.
Annotation: The job has exceeded the maximum execution time of 1h0m0s
The multi-arch runtime image built and was pushed (job started 23:42 UTC). The build of the follow-on test image started at 00:40 UTC, and GitHub cancelled the job at the 60-minute limit (00:42 UTC). The log has no build or compile error. The build ran out of time, so rerunning the failed jobs is likely enough, especially with the build cache now warm.
For agents
{"pr": 16570, "run_id": 41728830927, "run_attempt": 1, "head_sha": "02c7e3853b19bae78df6b9d8d4a43c42daaf2d85",
"failures": [{"job": "vllm-runtime-efa / Build multi-arch cuda13.0", "job_id": 126282944377, "failed_step": "Build and Push Test Image", "conclusion": "cancelled", "signature": "job exceeded maximum execution time of 1h0m0s", "tests": [], "log_cmd": "gh api repos/ai-dynamo/dynamo/actions/jobs/126282944377/logs"}]}Posted automatically by Devin for run 41728830927. Updated on every full-CI run of this PR.
There was a problem hiding this comment.
Reviewed and independently reproduced #16530 against head f64a4ddc45e89f781750fc9dee933edbb6f98416.
The scoped source review found no concrete correctness defect in this fix: the worker publishes defaults in engine-specific runtime metadata; the Rust and SGLang frontend paths merge them per key below explicit request values; the process-pool initializer carries them into workers; and model-card identity includes the defaults. Explicit false survives the merge.
Reproduction used the actual cached GLM-5 tokenizer/chat template (revision c183ef8c61faee82855eca1ed9bb3a9a7ce3b0b2) and a multi-turn conversation containing earlier assistant reasoning:
- Python pre-fix production module: deployment
clear_thinking=falseis ignored; earlier reasoning disappears (24 prompt tokens). Exact-head production module: reasoning is preserved (33 tokens). Five cases passed, including explicit true/false override, no defaults, and per-key merging. - Rust full
OpenAIPreprocessor::generatewith captured backend-bound token IDs: actual parent preprocessing/model-card modules reproduce the missing reasoning; restored exact-head modules pass all four cases (33/24/33/24 tokens). Other supporting source/dependencies stayed at head for these parent-module comparisons. - Real spawned
ProcessPoolExecutorusing production initializer/preprocessing worker: three cases passed. Existing focused argument guards (2), frontend merge (1), and Rust merge/checksum-isolation tests (2) passed.
These are real template/tokenization and operator-path checks, not GPU generation or a model-quality benchmark. Python used SGLang 0.5.19 and Transformers 5.12.1; the repository's supported SGLang version integration and live HTTP/discovery deployment were not exercised.
The remaining metadata test also passes with a freshly built, unmodified exact-head native extension loaded through a temporary overlay: get_runtime_config publishes {"clear_thinking": false} into real ModelRuntimeConfig runtime data (1 passed); an independent native engine-specific JSON roundtrip passes. The complete runtime-metadata test file also passes (62 tests) with those fresh bindings. The fixture mocks topology/bootstrap/capacity helpers, so this verifies publication through PyO3, not live worker discovery. The isolated build used the supported NIXL SDK-absent stub/dlopen mode; no transport validation is implied. The shared virtualenv was not changed.
Current GitHub checks still contain failures; these scoped local results do not establish CI health or merge readiness.
Additional supported-version validation (2026-10-16), unchanged head f64a4dd: reran with isolated repository-pinned SGLang 0.5.21 and exact-head native bindings. Original GLM-5 CPU preprocessing baseline remains RED; all five fixed cases, three spawned-process cases, runtime-metadata 62/62, CLI guards 2/2, and frontend merge 1/1 pass. Shared virtualenv unchanged. This closes the earlier installed-SGLang-version limitation for these paths; GPU inference, live HTTP serving, and CI health remain unverified.
rmccorm4
left a comment
There was a problem hiding this comment.
Two reproduced P2 findings in the frontend defaults implementation.
biswapanda
left a comment
There was a problem hiding this comment.
A design consideration - Currently the frontend flag sets the same defaults for all models. Models that need different defaults require request overrides or separate frontends.
Can each deployment publish its defaults through its Model Deployment Card (MDC), as it does for default_thinking_mode? With this design, one frontend can serve models with different settings. Where an engine cannot expose its native defaults, we could accept an explicit Dynamo-owned worker/sidecar option and publish it through MDC.
@KrishnanPrash what do you think?
biswapanda
left a comment
There was a problem hiding this comment.
Inline location for the unset-value issue described in #16570 (comment).
@biswapanda agree with the high-level feedback. For the upcoming v1.7 release, will unblock by merging this PR as-is to provide a frontend lever. And then draft a more comprehensive MDC proposal for future release. |
087f8bf to
148325d
Compare
Sounds good @KrishnanPrash. |
jthomson04
left a comment
There was a problem hiding this comment.
One must-fix (the vLLM CPU-only test) and a few nits.
Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
02c7e38 to
0c5a76c
Compare
Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
- Engine loss (#17972): api.v1 has no state stream, so poll GetModelInfo every 1 s, each poll bounded by min(connect_attempt_timeout, 5 s), and report "SGLang engine lost: ..." through main's engine_lost hook after 3 failed polls in a row. HealthCheck is not polled: it runs a probe generation and fails while the scheduler is paused. - Media (#16203): not ported. Requests with multi_modal_data keep the stack's "multimodal payloads are not supported" refusal; MediaSupport, media_support, require_media_support and the runtime.v1 media wire-tag test are dropped. Main's mm_processor_kwargs, media-uuid and encoder_result refusals stay. - Native HTTP (#16983): keep the per-dispatch rid and inspect(). Abort goes through the api.v1 Abort RPC with the dispatch rid, falling back to ctx.id(); main's HTTP /abort_request call is dropped, and its malformed-output abort cases now check the Abort RPC. - #16570: drop the default-chat-template-kwargs warning and its test; api.v1 GetServerInfo has no such field. - Keep #17710's migration mapping and #16303's logit_bias refusal. - Remove the runtime.v1 sglang.proto and headless.rs. proto/README.md now names sglang#45859 and #46637 as service.proto's sources. - .pre-commit-config.yaml: union of both exclude patterns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: jain-ria <riajain@NVIDIA.com>
Overview
Dynamo renders the chat template in the frontend, so an engine's
--default-chat-template-kwargsnever reaches Dynamo traffic. SGLang workers ignore it (#16530), and the vLLM and SGLang sidecars cannot see it. The frontend already took the flag, but only for--dyn-chat-processor vllm. This PR applies it on every backend and every chat processor. The chat, Responses and Anthropic handlers merge the defaults into each request before they read its thinking controls, so a default such as{"enable_thinking": false}acts like the same key sent by the client.Repro (Qwen3-0.6B). Set the flag on the frontend; either backend gets it:
chat_template_kwargs2, same as native SGLang{"enable_thinking": true}Key Changes
nulland"auto", which count as unset, as in vLLM. A request thinking control replaces the defaults' thinking keys. The defaults rank above--dyn-default-thinking-mode.Validation
Live with SGLang 0.5.22 and vLLM 0.32.0 on SGLang and vLLM workers and sidecars, and on both Python chat processors: the default reached the prompt, and a request key won. Unit tests cover the merge,
nulland"auto", and the reasoning decision after the merge.Breaking Changes
python -m dynamo.sglangandpython -m dynamo.vllmrefuse--default-chat-template-kwargsand name the frontend flag. The frontend flag cannot setchat_template.Issues and Ongoing Work
Pull Request Checklist
/pr-checklistskill.Summary by CodeRabbit