feat(vllm_model): optional session-affinity header for upstream routers - #2570
Open
aoshen02 wants to merge 1 commit into
Open
feat(vllm_model): optional session-affinity header for upstream routers#2570aoshen02 wants to merge 1 commit into
aoshen02 wants to merge 1 commit into
Conversation
New session_affinity_header config: when set, the per-session OpenAI client sends the Gym session id in that header (e.g. X-Session-ID), so an upstream router can keep session->replica affinity that survives vllm_model running with num_workers > 1, where the in-process session->client map is per uvicorn worker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: aoshen02 <aoshen@inferact.ai>
ananthsub
force-pushed
the
feat/router-session-affinity
branch
from
August 18, 2026 16:46
c5ad41f to
5ae024d
Compare
ananthsub
reviewed
Aug 18, 2026
ananthsub
requested changes
Aug 18, 2026
waple0820
added a commit
to waple0820/Gym
that referenced
this pull request
Sep 2, 2026
…rify NVIDIA-NeMo#2114 landed the rollout correlation contract, but it is one-directional. The training side learns nothing about the handle the environment actually allocated — the container, the browser context, the provider session that consumed quota — so a rollout record and a provider-side log can only be joined on a timestamp. `env_session_id` is optional on both `BaseSeedSessionResponse` and `BaseVerifyResponse`, opaque to Gym, and absent unless an environment reports one, so nothing changes for an environment that does not. This originally also added a `rollout_correlation_enabled` key so the rollout prefix could reach resources servers without turning on model-call capture. NVIDIA-NeMo#2783 removes that need by making correlation independent of the observability gate rather than adding a second flag, which is the better shape, so that half is dropped here. No training framework needs a transport change: verl and NeMo-RL already carry the whole verify response as `full_result`. Terminology: this is the environment session created by `/seed_session`, not the vLLM router KV-cache affinity of NVIDIA-NeMo#2570 / NVIDIA-NeMo#2347 / NVIDIA-NeMo#2369. Signed-off-by: waple0820 <232305951+waple0820@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
session_affinity_headerconfig onvllm_model: when set, the per-session OpenAI client sends the Gym session id in that header (e.g.X-Session-ID), so an upstream router can key session→replica affinity on it. Default is unset (no behavior change).Why
vllm_modelalready keeps a session on one replica for its whole lifetime, andsha256(session_id) % len(clients)does that consistently across uvicorn workers. What it cannot do is take anything else into account: the assignment is uniform and blind to which replica already holds a prefix, how loaded a replica is, or replicas joining and leaving.Exporting the session id lets an upstream router make that decision with information Gym does not have, without Gym taking on any routing policy of its own.
Measurements
From NVIDIA-NeMo/RL#3663's replay campaign: 610 recorded coding-agent sessions replayed turn by turn (byte-identical request sequence per arm), Nemotron-3-Super-120B-A12B-BF16, TP2 engines,
max_num_batched_tokens8480, greedy decoding withignore_eos, 0 preemptions everywhere.8 nodes / 12 engines,
max_concurrent_rollouts: 0(uncapped)16 nodes / 28 engines,
max_concurrent_rollouts: 112Code under test: this PR's commits on Gym upstream
473f446f(2026-07-24), with NVIDIA-NeMo/RL#3663's commits on NeMo-RL upstreamdaf46ff3(2026-08-06). One asymmetry, since it is what separates the twono routerrows: the 28-engineno routerarm ran on473f446f's per-uvicorn-process counter assignment, while the 12-engine one ran withsha256(session_id) % len(clients)as on currentmain. The 85.6% is a property of the counter, not of going routerless.Read honestly: with the stable assignment underneath, the three arms are level on hit rate, so this header buys nothing on cache locality at 12 engines. The remaining signal is
cache_aware's 12% wall-clock lead, and it is not a caching effect — 91.6% is the lowest hit rate of the three. The likely mechanism is first-turn placement: with no prefix to match yet,cache_awarefalls back to least-loaded, while a hash places blind; the 28-engine per-engine load spread is consistent with that (1.9x vs 2.5x). Single unreplicated run, so: a hypothesis with support, not a result. Rerunning the routed arms is listed as an open cell in the report; we would rather land the config knob and let the data decide than argue from one run.Full report: https://github.com/aoshen02/RL/blob/feat/gym-router-url/experiments/routing/README.md
Same shape as #2347; opened alongside the NeMo-RL side so the two halves of the router path can be reviewed together.
Testing
AI assistance was used for this work; the submitting human has reviewed every line.
🤖 Generated with Claude Code