Skip to content

fix(trtllm): Align HTTP server sampling params, use generate_async - #3537

Merged
yuki-97 merged 5 commits into
NVIDIA-NeMo:mainfrom
shuyixiong:shuyix/trtllm_server_fix
Aug 11, 2026
Merged

fix(trtllm): Align HTTP server sampling params, use generate_async#3537
yuki-97 merged 5 commits into
NVIDIA-NeMo:mainfrom
shuyixiong:shuyix/trtllm_server_fix

Conversation

@shuyixiong

@shuyixiong shuyixiong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do ?

Make the TRT-LLM HTTP rollout path sample the same way the directgenerate() path already does, and serve it without a worker thread.
TrtllmAsyncGenerationWorker reaches the engine two ways: generate() / generate_async() call _build_sampling_params() directly, while NeMo-Gym rollouts go through the HTTP server, which built its own SamplingParams. The two had drifted:

direct path HTTP path (before)
top_k applied not passed — silently ignored
logprob format logprobs_simple_format=True dict per generated token
engine call native async asyncio.to_thread(llm.generate, ...)

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you run the unit tests and functional tests locally? Visit our Testing Guide for how to run tests
  • Did you add or update any necessary documentation? Visit our Document Development Guide for how to write, build and test the docs.

Additional Information

  • ...

@shuyixiong
shuyixiong requested a review from a team as a code owner August 7, 2026 13:19
@copy-pr-bot

copy-pr-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@shuyixiong shuyixiong added the CI:Lfast Runs a fast test suite and re-use nightly `main` container (but sync dependencies to PRs version) label Aug 7, 2026
@shuyixiong
shuyixiong force-pushed the shuyix/trtllm_server_fix branch from 0aba997 to 612f958 Compare August 7, 2026 13:38
@shuyixiong

Copy link
Copy Markdown
Contributor Author

/ok to test 612f958

@shuyixiong
shuyixiong requested a review from hchings August 7, 2026 13:42
shuyixiong and others added 2 commits August 7, 2026 07:41
The HTTP server left logprobs in the default per-token dict format while
the direct generate() path (_build_sampling_params) already asked for the
flat one. TRT-LLM normalizes logprobs=True to 0 before validating
logprobs_simple_format, so the combination is accepted, and the response
handler already accepts both shapes -- it just stops allocating a dict per
generated token.

The same divergence hid a second one: the HTTP server built its
SamplingParams without top_k, so a configured generation.top_k was
silently ignored for every NeMo-Gym rollout while the direct path applied
it, leaving the two paths sampling from different distributions on the
same config. Pass it through, mapping an unset value to 0 the way the
direct path does, since that is how TRT-LLM spells "no top-k
restriction".

top_k now also participates in the request/config equality check that
already guarded temperature and top_p, so a request cannot ask for a
sampling profile the generation config did not specify. The lookups there
move to .get() so a server whose sampling_config predates this key does
not raise KeyError.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: shuyixiong <219646547+shuyixiong@users.noreply.github.com>
The handler ran the blocking llm.generate() on a worker thread via
asyncio.to_thread, so every concurrent rollout request consumed a thread
from the default executor while it waited on the engine. Multi-turn SWE
rollouts keep hundreds of requests in flight, which is far more than that
pool is sized for, so requests queued on threads rather than on the
engine's own scheduler.

generate_async is the engine's native awaitable and needs no thread: the
request goes straight onto the executor's queue and the coroutine parks on
its future. It takes a single prompt instead of a batch, so the one-element
list and the outputs[0] unwrap go away, and asyncio is no longer used
anywhere in this module. RequestError still surfaces the same way, leaving
the context-length 400 path unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: shuyixiong <219646547+shuyixiong@users.noreply.github.com>
@shuyixiong
shuyixiong force-pushed the shuyix/trtllm_server_fix branch from 612f958 to bf2602e Compare August 7, 2026 14:42
@shuyixiong

Copy link
Copy Markdown
Contributor Author

/ok to test bf2602e

@yuki-97 yuki-97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shuyixiong thanks for the fix! left some minor comments.

Comment thread nemo_rl/models/generation/trtllm/trtllm_http_server.py Outdated
Comment thread nemo_rl/models/generation/trtllm/trtllm_worker_async.py
Comment thread nemo_rl/models/generation/trtllm/trtllm_http_server.py Outdated
@shuyixiong
shuyixiong requested a review from a team as a code owner August 10, 2026 11:50
@shuyixiong

Copy link
Copy Markdown
Contributor Author

/ok to test 889aaed

@shuyixiong
shuyixiong requested a review from yuki-97 August 11, 2026 01:17
shuyixiong and others added 3 commits August 10, 2026 18:54
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: shuyixiong <219646547+shuyixiong@users.noreply.github.com>
LLM._prepare_sampling_params already calls sampling_params._setup(self.tokenizer, ...)
which sets end_id = tokenizer.eos_token_id when None, making the explicit
AutoConfig lookup redundant.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: shuyixiong <219646547+shuyixiong@users.noreply.github.com>
The HTTP server built its SamplingParams without stop_token_ids while the
direct generate() path passed them, so a configured generation.stop_token_ids
only reached the engine on one of the two paths. TRT-LLM's SamplingParams._setup
appends generation_config.eos_token_id to stop_token_ids rather than replacing
it, so passing them is additive and leaves the model's own EOS handling intact.

Extract the construction into a module-level _build_sampling_params that takes
the SamplingParams class as an argument, mirroring the direct path's method of
the same name. It was previously inline inside create_app's request closure,
which made the sampling params untestable without standing up the whole app.

The sampling_config lookups also drop .get() for subscripts: top_k is a required
GenerationConfig key and the sole construction site fills all three, so .get()
would turn a missing key into a silent "no top-k restriction" instead of failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: shuyixiong <219646547+shuyixiong@users.noreply.github.com>
@shuyixiong
shuyixiong force-pushed the shuyix/trtllm_server_fix branch from 889aaed to 4c56846 Compare August 11, 2026 01:56
@yuki-97

yuki-97 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

/ok to test 4c56846

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI:Lfast Runs a fast test suite and re-use nightly `main` container (but sync dependencies to PRs version)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants