[Test][Determinism] Cover VLM batch invariance in default execution mode (#56528)

Signed-off-by: Shenglei Fu <sfu@confluent.io> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

Shenglei Fu committed Sep 13, 2026 at 07:22 UTC 8cf9de9080ea5cded1c93e3d9a5ac531da0c93b6
1 file changed +41 -7
tests/v1/determinism/test_batch_invariance_vlm.py
+41 -7
@@ -59,15 +59,22 @@ def _assert_batch_invariant(llm: LLM, inputs: list[dict], sampling) -> None:
59 _extract_step_logprobs(llm.generate([inp], sampling, use_tqdm=False)[0])
60 for inp in inputs
61 ]
62 - bsN = [_extract_step_logprobs(out) for out in llm.generate(inputs, sampling)]
62 + bsN_outputs = llm.generate(inputs, sampling, use_tqdm=False)
63 + assert len(bs1) == len(inputs)
64 + assert len(bsN_outputs) == len(inputs)
65 + bsN = [_extract_step_logprobs(out) for out in bsN_outputs]
66
67 failures = []
68 for i, ((lp1, t1), (lpN, tN)) in enumerate(zip(bs1, bsN)):
69 if lp1 is None or lpN is None:
67 - pytest.skip(
68 - "Logits are not available on RequestOutput; "
69 - "enable logprobs return to run this test."
70 + failures.append(f"req {i}: selected-token logprobs are unavailable")
71 + continue
72 + if len(t1) != sampling.max_tokens or len(tN) != sampling.max_tokens:
73 + failures.append(
74 + f"req {i}: expected {sampling.max_tokens} tokens, "
75 + f"got bs1={len(t1)} bsN={len(tN)}"
76 )
77 + continue
78 if t1 != tN:
79 failures.append(f"req {i}: token mismatch bs1={t1} bsN={tN}")
80 elif not torch.equal(lp1, lpN):
@@ -84,16 +91,43 @@ def _assert_batch_invariant(llm: LLM, inputs: list[dict], sampling) -> None:
91 @skip_if_not_cuda
92 @pytest.mark.parametrize("input_type", ["image", "video"])
93 @pytest.mark.parametrize("mm_encoder_attn_backend", ["FLASH_ATTN", "TORCH_SDPA"])
87 -def test_vlm_batch_invariance_bs1_vs_bsN(input_type: str, mm_encoder_attn_backend: str):
94 +def test_vlm_batch_invariance_bs1_vs_bsN(
95 + input_type: str,
96 + mm_encoder_attn_backend: str,
97 + monkeypatch: pytest.MonkeyPatch,
98 +):
99 + monkeypatch.setenv("VLLM_USE_FLASHINFER_SAMPLER", "0")
100 + _run_vlm_batch_invariance(input_type, mm_encoder_attn_backend, enforce_eager=True)
101 +
102 +
103 +@skip_if_not_cuda
104 +def test_vlm_batch_invariance_default_execution(monkeypatch: pytest.MonkeyPatch):
105 + monkeypatch.setenv("VLLM_USE_FLASHINFER_SAMPLER", "0")
106 + _run_vlm_batch_invariance("image", "FLASH_ATTN", enforce_eager=False)
107 +
108 +
109 +def _run_vlm_batch_invariance(
110 + input_type: str, mm_encoder_attn_backend: str, enforce_eager: bool
111 +) -> None:
112 inputs = _make_inputs(input_type, num_reqs=4)
89 - sampling = SamplingParams(temperature=0.0, max_tokens=8, seed=1234, logprobs=5)
113 + sampling = SamplingParams(
114 + temperature=0.0,
115 + max_tokens=8,
116 + seed=1234,
117 + ignore_eos=True,
118 + logprobs=5,
119 + )
120
121 llm = LLM(
122 model=VLM_TEST_MODEL,
123 dtype="bfloat16",
94 - enforce_eager=True,
124 + enforce_eager=enforce_eager,
125 max_num_seqs=16,
126 + max_model_len=4096,
127 + max_num_batched_tokens=2048,
128 gpu_memory_utilization=0.85,
129 + enable_prefix_caching=False,
130 + mm_processor_cache_gb=0,
131 mm_encoder_attn_backend=mm_encoder_attn_backend,
132 )
133 try: