From c79360858d7b81330fe6231e2b0e2adcab7b310f Mon Sep 17 00:00:00 2001 From: Jiajun Li Date: Tue, 22 Sep 2026 03:46:48 -0700 Subject: [PATCH] test(session): keep training-session sampling tests unbounded Sampling-support replay (PR #3354 and its follow-up) makes the session server reject a training request that samples from a bounded support (top_p < 1 or top_k > 0) on a run whose launch flags do not capture rollout sampling support, because the trainer would score the full vocabulary while the engine truncated the distribution. Several session tests create training sessions with incidentally bounded values and would start failing once that rule lands, even though boundedness is not what they test. Move the training-session test data to unbounded values (top_p 1.0, top_k -1) while keeping each test's purpose: default filling of omitted fields, temperature-mismatch rejection before forwarding, the null temperature fallback, and the integral-float top_k coercion (now created as an evaluation session, matching the eval dataset YAML scenario in its docstring). Evaluation sessions keep bounded values through a separate EVAL_SAMPLING constant. The prepare_chat_request unit test gets the same split so bounded defaults never reach a training session there either. No production code changes. Co-Authored-By: Claude Fable 5.1 --- .../fast/rollout/session/test_request_args.py | 6 ++++-- tests/fast/router/test_session_evaluation.py | 20 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/fast/rollout/session/test_request_args.py b/tests/fast/rollout/session/test_request_args.py index da80bed65fa..e8140b1cf54 100644 --- a/tests/fast/rollout/session/test_request_args.py +++ b/tests/fast/rollout/session/test_request_args.py @@ -249,6 +249,8 @@ def resolve_request_args(self, request_args, *, turn_args): @pytest.mark.parametrize("evaluation", [False, True]) def test_session_sampling_defaults_fill_only_omitted_fields(evaluation): + # Training defaults stay unbounded; see SAMPLING in tests/fast/router/test_session_evaluation.py. + defaults = {"temperature": 0.6, "top_p": 0.9 if evaluation else 1.0, "top_k": 20 if evaluation else -1} temperature = 0.1 if evaluation else 0.6 client_args = {"temperature": temperature, "top_p": None, "top_k": -1} original = deepcopy(client_args) @@ -258,11 +260,11 @@ def test_session_sampling_defaults_fill_only_omitted_fields(evaluation): config=make_session_server_config(), turn_args=None, evaluation=evaluation, - sampling_defaults={"temperature": 0.6, "top_p": 0.9, "top_k": 20}, + sampling_defaults=defaults, ) assert {key: prepared.body[key] for key in ("temperature", "top_p", "top_k")} == { "temperature": temperature, - "top_p": 0.9, + "top_p": defaults["top_p"], "top_k": -1, } assert client_args == original diff --git a/tests/fast/router/test_session_evaluation.py b/tests/fast/router/test_session_evaluation.py index 211b0256dc7..b5f9e06b942 100644 --- a/tests/fast/router/test_session_evaluation.py +++ b/tests/fast/router/test_session_evaluation.py @@ -183,18 +183,22 @@ async def test_concurrent_train_and_eval_do_not_share_policy(env): assert sorted(request["return_indexer_topk"] for request in env.backend.requests) == [False, True] -SAMPLING = {"temperature": 0.6, "top_p": 0.9, "top_k": 20} +# Training sessions stay unbounded: sampling-support replay rejects bounded training sampling +# (top_p < 1 or top_k > 0) on a run that does not capture rollout sampling support. +SAMPLING = {"temperature": 0.6, "top_p": 1.0, "top_k": -1} +EVAL_SAMPLING = {"temperature": 0.6, "top_p": 0.9, "top_k": 20} @pytest.mark.parametrize("evaluation", [False, True]) async def test_creation_sampling_defaults_fill_only_omitted_fields(env, evaluation): - sid = await _create(env, json.dumps({**SAMPLING, "evaluation": evaluation}).encode()) + defaults = EVAL_SAMPLING if evaluation else SAMPLING + sid = await _create(env, json.dumps({**defaults, "evaluation": evaluation}).encode()) await _chat(env, sid, [USER]) - temperature = 0.1 if evaluation else SAMPLING["temperature"] + temperature = 0.1 if evaluation else defaults["temperature"] await _chat(env, sid, [USER, ASSISTANT, TOOL], temperature=temperature, top_p=None, top_k=-1) omitted, explicit = env.backend.requests[-2:] - assert {key: omitted[key] for key in SAMPLING} == SAMPLING - assert {key: explicit[key] for key in SAMPLING} == {**SAMPLING, "temperature": temperature, "top_k": -1} + assert {key: omitted[key] for key in defaults} == defaults + assert {key: explicit[key] for key in defaults} == {**defaults, "temperature": temperature, "top_k": -1} @pytest.mark.parametrize("saved,requested", [(0.6, 0.1), (0.6, 0.0), (0.0, 0.6)]) @@ -212,9 +216,9 @@ async def test_training_temperature_mismatch_is_rejected_before_forwarding(env, f"temperature={requested!r} does not match the training session temperature={saved!r}" ) assert len(env.backend.requests) == request_count - await _chat(env, sid, messages, temperature=saved, top_p=0.5, top_k=-1) + await _chat(env, sid, messages, temperature=saved, top_p=1.0, top_k=-1) wire = env.backend.requests[-1] - assert (wire["temperature"], wire["top_p"], wire["top_k"]) == (saved, 0.5, -1) + assert (wire["temperature"], wire["top_p"], wire["top_k"]) == (saved, 1.0, -1) async def test_training_null_temperature_uses_registered_value(env): @@ -240,7 +244,7 @@ async def test_an_integer_temperature_is_accepted_as_a_float_default(env): async def test_an_integral_float_top_k_is_stored_as_an_int(env): """An eval dataset YAML can spell top_k as 40.0; the engine must still receive an integer.""" - sid = await _create(env, b'{"top_k": 40.0}') + sid = await _create(env, b'{"evaluation": true, "top_k": 40.0}') await _chat(env, sid, [USER]) top_k = env.backend.requests[-1]["top_k"] assert top_k == 40 and isinstance(top_k, int)