feat: parse vLLM's native sampling_mask field - #144
Merged
Conversation
vLLM 0.28 returns kept-set sampling masks natively on /inference/v1/generate choices as sampling_mask (one list of surviving vocab ids per completion token, plain JSON). Surface that field on the generate result and drop the custom base64 kept_tokens splice, which no server emits anymore.
This was referenced Aug 29, 2026
mikasenghaas
added a commit
to PrimeIntellect-ai/prime-rl
that referenced
this pull request
Aug 29, 2026
Truncated train sampling (top_p < 1, top_k) renormalizes the rollout distribution over the surviving kept set; rollout logprobs reflect that (processed_logprobs) while the trainer normalizes over the full vocab, biasing every importance ratio. Record the kept set at sampling time and renormalize trainer logprobs over the same set (DeepSeek V3.2's Keep Sampling Mask, arXiv:2512.02556 3.1). Same user API as #3235: [orchestrator.train.sampling] top_p/top_k, no replay flags. Truncating policy sampling auto-enables inference.enable_return_sampling_mask, bounds top_k to 512 (trainer mask tensors pad to the largest kept set), and rejects opd/opsd and temperature 0. Unlike #3235 the capture is vLLM's native --return-sampling-mask (>= 0.28, V2 model runner) instead of custom engine patches: the /generate response carries sampling_mask natively, renderers parse it (PrimeIntellect-ai/renderers#144) and verifiers carry it as KeptTokens arrays (PrimeIntellect-ai/verifiers#2460). Capture is engine-wide: vLLM rejects requests with temperature <= 0 or top_k <= 0 while it is on, and it is incompatible with router replay (V1-only).
hallerite
approved these changes
Sep 1, 2026
hallerite
marked this pull request as ready for review
September 1, 2026 21:30
mikasenghaas
added a commit
to PrimeIntellect-ai/verifiers
that referenced
this pull request
Sep 1, 2026
## Summary - Build `SamplingMask` from a `sampling_mask` payload. - Keep sampling-mask trace types independent of the inference producer. - Require the published `renderers==0.1.12.dev2` feature floor. - Store masks as flat int32 `ids` and per-token `counts` arrays. - Use `sampling_mask` across response tokens, message nodes, branch views, wire serialization, and public exports. - Keep `Branch.sampling_mask` aligned to the branch token ids for prime-rl. Pairs with [PrimeIntellect-ai/renderers#144](PrimeIntellect-ai/renderers#144) and prime-rl's native sampling-replay PR. ## Breaking - Rename `vf.KeptTokens` to `vf.SamplingMask`. - Rename `TurnTokens.kept_tokens`, `MessageNode.kept_tokens`, and `Branch.kept_tokens` to `sampling_mask`. - Update serialized trace consumers to read the `sampling_mask` field. ## Verification - Merged current `main` without conflicts. - `uv sync --upgrade-package renderers`: lockfile regenerated with `renderers==0.1.12.dev2`. - `uv run pytest tests/v1/test_graph.py tests/v1/test_trace.py -q`: 21 passed. - Ruff, format, type, lock, and diff checks pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Replace `KeptTokens` with `SamplingMask` across v1 verifier pipeline > - Adds the `SamplingMask` dataclass in [types.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2460/files#diff-88a4e4ee0c58ac5dc7344161a011374082a3767eda2ad32af97c942dca982112) with `ids` and `counts` fields plus `from_sampling_mask`, and swaps `kept_tokens` for `sampling_mask` on `TurnTokens` and `MessageNode` > - [train.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2460/files#diff-c9bcd6c691890b1de6942fe012d2e74678460b22bf335721a8bba5e939d139c3) now reads `sampling_mask` from the generate result and builds it via `SamplingMask.from_sampling_mask`; `response_from_generate` populates `TurnTokens.sampling_mask` > - [graph.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2460/files#diff-64c9d635eb215902a512c9e8ab316fd9b9967c36db03d4049aacc812cdfbd740) renames `_attribute_kept_tokens` to `_attribute_sampling_mask`, which validates `counts`/`ids` lengths and assigns the `SamplingMask` directly (no base64 decode or buffer copy) > - [trace.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2460/files#diff-834820c3aa80d87ee1c1c57fb54c74f09c66ac727036d964e6005c8613c00b5e) excludes `sampling_mask` from disk serialization and exposes `Branch.sampling_mask`, which scatters per-node `counts` onto sampled positions and concatenates `ids` > - Bumps `renderers[multimodal]` dependency from `>=0.1.11` to `>=0.1.12.dev2` in [pyproject.toml](https://github.com/PrimeIntellect-ai/verifiers/pull/2460/files#diff-50c86b7ed8ac2cf95bd48334961bf0530cdc77b5a56f852c5c61b89d735fd711) > - Behavioral Change: `KeptTokens` is removed from the `verifiers.v1` public API (`__all__`) and `SamplingMask` replaces it; any downstream code importing `KeptTokens` or reading `kept_tokens` on `TurnTokens`, `MessageNode`, or `Branch` must switch to `sampling_mask`. `_attribute_sampling_mask` no longer base64-decodes or copies buffers — callers must provide a valid `SamplingMask` payload directly > > <!-- Macroscope's changelog starts here --> > #### Changes since #2460 opened > > - Removed producer-specific references from sampling mask documentation [3ff2553] > <!-- Macroscope's changelog ends here --> > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 67af753.</sup> > <!-- Macroscope's review summary ends here --> > <!-- Macroscope's pull request summary ends here --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Breaking rename across the v1 training/trace wire contract; incorrect mask alignment would silently drop masks at attribution, and consumers must upgrade renderers in lockstep. > > **Overview** > **Replaces the `KeptTokens` / `kept_tokens` surface with `SamplingMask` / `sampling_mask`** so training traces align with the new `renderers` generate payload (`sampling_mask` as nested token-id rows). Public exports, `TurnTokens`, `MessageNode`, wire serialization, and `Branch.sampling_mask` all use the new names; disk dumps still exclude the tensor field via `EXCLUDE_FIELDS`. > > **`SamplingMask`** holds flat int32 `ids` and per-completion-token `counts`, with **`from_sampling_mask`** to build from the engine’s `list[list[int]]`. The train client maps `result["sampling_mask"]` into `TurnTokens`; graph attribution validates row counts against sampled positions and stores the mask on the assistant node (no base64 decode/copy path like the old kept-set flow). > > **Dependency:** `renderers[multimodal]` is pinned to **`>=0.1.12.dev2`** (lockfile updated). Downstream code that imported `KeptTokens` or read `kept_tokens` on traces must switch to `sampling_mask`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3ff2553. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
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
sampling_maskfield on thegenerateresult.kept_tokensresponse splice.Consumed by PrimeIntellect-ai/verifiers#2460 and prime-rl's native sampling-replay PR.
Breaking
kept_tokenswithsampling_mask.Verification
uv run pytest tests/test_client.py -q: 21 passed, 5 skipped.🤖 Generated with Claude Code
Note
Replace
kept_tokenswith vLLM's nativesampling_maskfieldbuild_trajectory_stepin renderers/base.py to exposesampling_mask(set to None) instead ofkept_tokensin the returned trajectory step dict.KEPT_TOKENS_IDS_PREFIXand the stripping/decoding ofkept_tokens.idsinparse_generate_responsein renderers/client.py.generateasync function in renderers/client.py to readsampling_maskfrom the engine response instead ofkept_tokens, matching vLLM's kept-set sampling mask.kept_tokensfrom trajectory steps or generation results must switch tosampling_mask; the old key is no longer present.Macroscope summarized a9dccfb.
Note
Medium Risk
Breaking change to the generate/trajectory result schema; downstream verifiers and prime-rl must switch field names and payload shape, but logic is limited to optional metadata parsing.
Overview
Breaking: Renderer
generateresults and trajectory steps now exposesampling_maskinstead ofkept_tokens. Consumers must read vLLM’s native list-of-lists (one surviving vocab-id list per completion token when--return-sampling-maskis enabled).In
renderers/client.py, the customkept_tokens.idsbase64 splice (KEPT_TOKENS_IDS_PREFIXinparse_generate_response) is removed.generatetakeschoice.get("sampling_mask")and returns it on the result dict.In
renderers/base.py,build_trajectory_stepinitializes the step payload withsampling_mask: Noneinstead ofkept_tokens.Reviewed by Cursor Bugbot for commit a9dccfb. Bugbot is set up for automated code reviews on this repo. Configure here.