Repository navigation
fix(trtllm-sidecar): enable guided decoding for required tool_choice - #15649
Conversation
Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (4)
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review. WalkthroughThe TensorRT-LLM deployment configurations and launch scripts now set ChangesTensorRT-LLM guided decoding
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The configurations enable xgrammar for the guided-decoding path, and aggregate profile-memory overrides remain forwarded. No actionable merge risk is evident in the supplied changes. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
✅ Dynamo PR CI passed — run 37416041191 (attempt 1) on
|
…d drop guided decoding Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
|
Disagg verification request: does the guide reach the decode leg? In TensorRT-LLM's OpenEngine disagg path, the grammar matcher state is not part of the KV handoff. KvSessionRef carries the KV state and the first/draft tokens, but no matcher state or schema. On its first generation-only iteration, the decode worker builds a new matcher from that request's own guided_decoding_params. If the decode-leg request has no guided field, decode output is unconstrained, even with guided_decoding_backend: xgrammar set on both engines. Sidecar side is fine. Prefill and decode use the same request builder, which always sets guided: guided_decoding(request)? (lib/sidecar/trtllm/src/convert.rs:128). Only kv differs between the phases. Suggested check: run the PR's repro against lib/sidecar/trtllm/launch/disagg.sh with a schema an unconstrained model is unlikely to satisfy by chance. Prefill samples only the first token, so the result shows whether decode enforced the schema. For example: What to expect:
It would be worth adding the disagg result to the PR description next to the agg before/after. |
…gine start and the disagg refusal Signed-off-by: Krishnan Prashanth <kprashanth@nvidia.com>
Yes. I ran
The I added this result to the PR description. f203a3c also adds the backend to the README's manual engine command and names it in the |
Overview:
The TensorRT-LLM sidecar launch scripts and deploy examples start the OpenEngine server without a guided-decoding backend. Dynamo sends a required or named
tool_choiceto the engine as a JSON schema, so those requests now fail with a 500. The launch scripts before #14349 set no backend either, so the schema was never enforced: with no backend the engine drops the guide, and the OpenEngine servicer from #14349 rejects the request instead. This PR setsguided_decoding_backend: xgrammarin the engine configuration of both launch scripts, both deploy examples and the README's manual engine command, so TensorRT-LLM enforces the schema and the frontend parses the tool call.Repro (TensorRT-LLM 1.3.0rc27 with the
openengineextra, Qwen3-0.6B, one TensorRT-LLM sidecar worker):Before:
The worker log shows the cause:
After:
{"content": "", "tool_calls": [{"name": "get_weather", "arguments": "{\"city\": \"Paris\"}"}], "finish_reason": "tool_calls"}Disaggregated (
launch/disagg.sh, TensorRT-LLM 1.3.0rc29, prefill and decode engines on one GPU, NIXL transceiver), with a stricter schema that unconstrained output is unlikely to match:unitmust bekelvinorrankine, anddaysmust be 3. Each request went through the prefill worker and then the decode worker. Prefill samples only the first token, so the arguments show that the decode engine enforced the schema. Theautorow has no guide and is the control.tool_choicerequired{"city": "Paris", "unit": "kelvin", "days": 3}required, streamed{"city": "Paris", "unit": "kelvin", "days": 3}get_weather{"city": "Paris", "unit": "kelvin", "days": 3}auto(no guide){"city": "Paris", "unit": "kelvin", "days": 1}daysis 1Details:
TensorRT-LLM applies a JSON schema, regex, grammar or structural tag only when the LLM API option
guided_decoding_backendis set. The OpenEngine launch path never set it, so every guided request reached an engine that could not enforce it.launch/agg.shnow always writes the engine configuration file and adds the backend to it. Before, it wrote the file only when a memory override was set.launch/disagg.shadds the backend next to the cache transceiver setting, so the prefill and the decode engine both have it. The deploy ConfigMaps get the same key.agg.shnow refuses a forwarded--extra_llm_api_optionsor--config, asdisagg.shalready does, because either one would replace the generated file and drop the backend. The refusal message ofdisagg.shnow names both keys that its file carries.The README's manual engine command now passes an options file with the backend, and its disaggregation section says that both engines need it.
Out of scope: a guided request that reaches an engine without a backend still returns a 500 instead of a 400 with the engine's reason.
Related Issues
None
🤖 Generated with Claude Code
Summary by CodeRabbit