Skip to content

feat(session): fill omitted sampling fields from defaults set at session creation - #3585

Merged
guapisolo merged 7 commits into
jiajun/session-eval-policyfrom
jiajun/session-sampling-defaults
Sep 22, 2026
Merged

guapisolo merged 7 commits into
jiajun/session-eval-policyfrom
jiajun/session-sampling-defaults

Conversation

@guapisolo

@guapisolo guapisolo commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Depends on: #3323
Stacked on the parent PR's branch. Review / merge the parent first.

Summary

Register session sampling defaults and enforce a fixed training temperature.

Motivation

Agent frameworks can drop the sampling values Miles supplies in request_kwargs. A request then reaches the engine without temperature, top_p, or top_k, while training still scales logits by --rollout-temperature, making the rollout silently off-policy. The value belongs to the session because rollout and dataset-driven eval may share an engine but require different sampling settings.

Usage

POST /sessions
{"evaluation": false, "temperature": 0.8, "top_p": 0.95, "top_k": 32}

Omitted or null fields use these defaults. A training request with temperature: 0.1 returns HTTP 400; evaluation temperature and top_p/top_k remain overridable.

Design Notes

  • Mental model: GenerateFnInput.sampling_params passes through OpenAIEndpointTracer.create into validated v1/v2 session state; each chat fills omitted fields and checks training temperature before model rules, rendering, or forwarding, without changing engine launch arguments.
  • Training temperature must match its registered value to align logprob computation; top_p/top_k can vary because replay uses the realized sampling mask.
  • Creation remains strict; the tracer excludes engine-only fields such as max_new_tokens before validation.
  • SGLang's global preferred_sampling_params merge provides fallback semantics, but session-owned defaults support different rollout/eval settings on a shared engine.

Verification

  • /opt/sglang/bin/python -m pytest tests/fast/router/test_session_evaluation.py tests/fast/rollout/session/test_request_args.py tests/fast/rollout/generate_utils/test_openai_endpoint_utils.py tests/fast/rollout/generate_hub/test_agentic_v2.py tests/fast/rollout/generate_hub/test_multi_turn.py -q --tb=short --durations=5: 168 passed, 7 skipped against b65b0b4adfd60cef18bfad99d4e29f393137c4c7, covering v1/v2 gates, retries, defaults, tracer filtering, and agentic multi-turn requests.
  • ruff, autoflake, isort, black, and git diff --check passed on the gate changes.
  • Verification used CPU/mock-backend tests; GPU E2E and third-party harnesses were not run.

Review Focus

  • apply_session_sampling_defaults: reject temperature mismatches before backend forwarding or session-state mutation.
  • OpenAIEndpointTracer.create / CreateSessionRequest: filter engine-only fields while retaining strict public creation validation.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@guapisolo

Copy link
Copy Markdown
Collaborator Author

@claude review always

@guapisolo
guapisolo force-pushed the jiajun/session-sampling-defaults branch from e65c7ce to d59aaec Compare September 22, 2026 04:07
Comment thread miles/rollout/generate_utils/openai_endpoint_utils.py Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

guapisolo and others added 5 commits September 21, 2026 22:24
…ion creation

Agent frameworks may drop the sampling values Miles hands them in
request_kwargs, so a chat request can reach the engine without
temperature/top_p/top_k and sample at the model's generation_config or
SGLang's defaults while training scales logits by --rollout-temperature.
An engine-level default cannot express this either: rollout and eval
share engines and eval datasets differ per sample, so the value is a
property of the session, not the engine.

POST /sessions now accepts temperature, top_p and top_k next to
evaluation. The agentic generator passes the sample's resolved
sampling_params (rollout flags for training, the dataset's values for
eval), the session keeps them for its lifetime, and prepare_chat_request
fills the fields a request leaves unset, after the server rules and
before the model rules so turn_args records the effective request. An
explicit request value still wins; null counts as unset, matching the
engine's own chat semantics. request_kwargs to the agent and the
built-in /generate path are unchanged, and nothing is added to engine
launch arguments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CreateSessionRequest.sampling_defaults() only re-expressed pydantic's
model_dump(include=..., exclude_none=True). The route now calls that
directly and the request model keeps only its fields.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The tracer validates sampling_params through CreateSessionRequest and
sends its dump; the route dumps the same model minus evaluation. The
field list therefore lives only on the model, and SESSION_SAMPLING_FIELDS
goes away.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The eval table documents --eval-temperature and --eval-top-p but not
--eval-top-k, although the parser registers all three with the same
rollout fallback. A reader checking which eval sampling flags exist
could not find top-k.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An eval dataset YAML can spell top_k as 40.0. EvalDatasetConfig keeps
the float, and since the tracer now validates sampling_params through
CreateSessionRequest, StrictInt rejected it: every group of that
dataset failed at session creation and was dropped with one error log,
where the same config generated before this branch.

The request model now folds an integral float into an int before
validation; 20.5, booleans and strings are still rejected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@guapisolo
guapisolo force-pushed the jiajun/session-sampling-defaults branch from fbd6d8b to 8c897ff Compare September 22, 2026 05:24

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The prior finding is fixed: OpenAIEndpointTracer.create now builds its POST /sessions body via CreateSessionRequest.model_validate(...) (openai_endpoint_utils.py:60-61), so an eval dataset's top_k: 40.0 is coerced to int by the same validator used at session creation, with a new regression test (test_an_integral_float_top_k_is_stored_as_an_int). I didn't find anything else to raise.

Extended reasoning...

Since my prior review, the author pushed 5 commits, the last of which (8c897ff) fixes exactly the float-top_k ValidationError I flagged: openai_endpoint_utils.py now routes the outgoing session-creation body through CreateSessionRequest.model_validate, reusing types.py's integral-float coercion, and a new test in test_session_evaluation.py covers it directly. The other new commits are small refactors (deriving the creation body from the request model, inlining sampling fields, a docs update) that don't introduce new surface area. This is the third+ pushed version I've reviewed; given the fix is concrete and tested and no new concerns surfaced, this note is informational rather than a fresh objection, but a human should still confirm the fix before merge given the session/sampling code is security/behavior sensitive for training correctness.

@guapisolo
guapisolo added this pull request to stack #3591 September 22, 2026 05:53
A field the client sent with any value but ``None`` is kept; ``None`` counts as unset,
as it does for the engine's own chat defaults.
"""
for key, value in sampling_defaults.items():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

qq: curious why don't we add an assertion/warning between the saved value and the request_args[key], if it is not none? Is the agent changing the sampling params expected?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

codex cmt

Added a hard gate in b65b0b4ad: training requests return HTTP 400 when an explicit temperature differs from the registered value; omitted/null values use the registered value. Evaluation can still override temperature.

top_p/top_k remain overridable because the realized sampling mask is the source of truth for replayed support.

Verified v1/v2 continuations, retries, and recovery after rejection: 168 passed, 7 skipped in the targeted CPU/mock-backend tests.

@guapisolo
guapisolo force-pushed the jiajun/session-sampling-defaults branch from 8c897ff to 3f565e1 Compare September 22, 2026 08:51
Keep creation defaults, explicit request precedence, and evaluation capture limits concise in the agentic rollout guide.
Reject explicit training temperature mismatches with HTTP 400 before forwarding or mutating session state. Preserve omitted/null defaults, evaluation overrides, and independent top-p/top-k settings.

Filter engine-only sampling fields before strict session-creation validation. Cover v1/v2 first turns, continuations, retries, and recovery after rejection.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

I reviewed this PR again on the latest push. Beyond the docs inline nit below, a human should specifically re-check one item that this round's pipeline marked as "ruled out": the concern that OpenAIEndpointTracer.create (miles/rollout/generate_utils/openai_endpoint_utils.py:61) spreads the caller's entire sampling_params dict into CreateSessionRequest.model_validate. Tracing the actual call path (compute_sampling_params / GenerateState.sampling_params -> agentic_tool_call.generate -> OpenAIEndpointTracer.create), that dict always carries non-sampling keys such as max_new_tokens, stop, stop_token_ids, skip_special_tokens, etc. CreateSessionRequest is a StrictBaseModel (extra="forbid") that only declares evaluation/temperature/top_p/top_k, and I don't see any filtering step added between the sampling_params dict and model_validate. From the code alone this still looks like it would raise a pydantic ValidationError on essentially every agentic-tool-call session creation, not just the misconfigured-eval-dataset case the earlier objection was framed around. Worth confirming with an actual run of tests/fast/rollout/generate_utils/test_openai_endpoint_utils.py before merging.

Extended reasoning...

The diff plumbs session-level sampling defaults (temperature/top_p/top_k) through CreateSessionRequest, OpenAIEndpointTracer.create, the /sessions route, LinearTrajectory/SessionStateV2, and prepare_chat_request, with matching tests and docs; it touches no auth/crypto surface but does sit on the core rollout request path used by every agentic sample. An earlier round's objection that OpenAIEndpointTracer.create spreads the full sampling_params dict into the strict, extra-forbidding CreateSessionRequest was marked resolved by the author and then classified as ruled-out this run, but my own trace of compute_sampling_params -> GenerateState.sampling_params -> agentic_tool_call.generate -> OpenAIEndpointTracer.create shows the non-sampling keys (max_new_tokens, stop, etc.) are still spread unfiltered into model_validate, with no filtering step added anywhere in the diff. I was not able to execute the test suite in this environment to confirm the ValidationError actually fires, which is why I'm flagging it for a human to verify rather than asserting it outright.

Comment thread docs/user-guide/agentic-rollout.md Outdated
Use `{"evaluation": true}` in `POST /sessions` for evaluation; omitting it defaults to training. The agentic generator sets this automatically, and the purpose stays fixed across turns, retries, and branches.

Evaluation sessions force `return_sampling_mask`, `return_routed_experts`, and `return_indexer_topk` to `false`, including when a request or model rule asks to enable them. They ignore `routed_experts_start_len`. Sampling defaults and override order stay unchanged, and temperature can vary between turns. TITO rendering, token IDs, logprobs, and template compatibility checks still apply; agentic evaluation still collects samples.
`temperature`, `top_p`, and `top_k` in `POST /sessions` provide defaults for omitted or `null` chat fields; explicit request values win. The agentic generator registers the sample's resolved values automatically.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 nit (optional): New line 138 says session-level temperature/top_p/top_k now fill omitted chat fields, but line 131 in the same doc ('Choose template options per session') still states 'Sampling parameters remain per-request', unchanged by this PR. A reader hits contradictory claims about the same behavior a few lines apart. Fix: update line 131 to note that sampling parameters are per-request except for fields a session registers as defaults, so the two sections agree.

Why this was flagged

docs/user-guide/agentic-rollout.md:131 (unchanged by this diff) asserts 'Sampling parameters remain per-request' under the 'Choose template options per session' heading. This PR adds docs/user-guide/agentic-rollout.md:138 stating temperature/top_p/top_k in POST /sessions now provide session-level defaults for omitted chat fields, which is exactly a per-session (not per-request) behavior. A maintainer or integrator reading the file top to bottom gets two directly conflicting statements about the same feature within one page, with no cross-reference resolving them. Nothing in the diff updates line 131 to reflect the new session-level default path introduced alongside line 138.

Verification: nit. docs/user-guide/agentic-rollout.md:131 (unchanged by this PR) states "Sampling parameters remain per-request." while the PR adds line 138: "temperature, top_p, and top_k in POST /sessions provide defaults for omitted or null chat fields; explicit request values win." Both statements are present and describe the same fields (temperature/top_p/top_k). Line 138 introduces… | nit.…

@guapisolo
guapisolo merged commit 366f614 into main Sep 22, 2026
17 of 18 checks passed
@guapisolo
guapisolo deleted the jiajun/session-sampling-defaults branch September 22, 2026 09:29

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review found no issues

No high-confidence issues detected in this change.

Still open from earlier reviews (1):

  • Unresolved: 1 minor or pre-existing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants