From 8c334e22243989ebf6cc9ace41fb598d18624ad4 Mon Sep 17 00:00:00 2001 From: Humphrey Date: Sun, 14 Jun 2026 02:50:38 -0500 Subject: [PATCH] fix(io_struct): index extra_key per sub-request in batched GenerateReqInput (#26971) --- python/sglang/srt/managers/io_struct.py | 18 ++++++- .../unit/managers/test_io_struct.py | 51 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/python/sglang/srt/managers/io_struct.py b/python/sglang/srt/managers/io_struct.py index 987ec5121..f24bc2408 100644 --- a/python/sglang/srt/managers/io_struct.py +++ b/python/sglang/srt/managers/io_struct.py @@ -428,6 +428,7 @@ class GenerateReqInput(BaseReq): self._normalize_sampling_params(num) self._normalize_logprob_params(num) self._normalize_custom_logit_processor(num) + self._normalize_extra_key(num) self._normalize_bootstrap_params(num) def _expand_inputs(self, num): @@ -597,6 +598,21 @@ class GenerateReqInput(BaseReq): "Cannot use list custom_logit_processor with parallel_sample_num > 1" ) + def _normalize_extra_key(self, num): + """Normalize extra_key for batch processing.""" + if self.extra_key is None: + return + if isinstance(self.extra_key, str): + self.extra_key = [self.extra_key] * num + elif isinstance(self.extra_key, list): + if len(self.extra_key) != self.batch_size: + raise ValueError( + "The length of extra_key should be equal to the batch size." + ) + self.extra_key = self.extra_key * self.parallel_sample_num + else: + raise ValueError("extra_key should be a list or a string.") + def _normalize_bootstrap_params(self, num): """Normalize bootstrap parameters for batch processing.""" # Normalize bootstrap_host @@ -713,7 +729,7 @@ class GenerateReqInput(BaseReq): disagg_prefill_dp_rank=self.disagg_prefill_dp_rank, conversation_id=self.conversation_id, priority=self.priority, - extra_key=self.extra_key, + extra_key=self.extra_key[i] if self.extra_key is not None else None, no_logs=self.no_logs, custom_labels=self.custom_labels, return_bytes=self.return_bytes, diff --git a/test/registered/unit/managers/test_io_struct.py b/test/registered/unit/managers/test_io_struct.py index 42ed241a1..85496ce8e 100644 --- a/test/registered/unit/managers/test_io_struct.py +++ b/test/registered/unit/managers/test_io_struct.py @@ -419,6 +419,57 @@ class TestGenerateReqInputNormalization(CustomTestCase): req.normalize_batch_and_arguments() self.assertEqual(req.lora_path, expected_lora_paths) + def test_extra_key_normalization(self): + """Test normalization of extra_key.""" + # Per-request list + req = GenerateReqInput( + text=["Hello", "World"], + extra_key=["tenant-A", "tenant-B"], + sampling_params=[{}, {}], + ) + req.normalize_batch_and_arguments() + self.assertEqual(req.extra_key, ["tenant-A", "tenant-B"]) + self.assertEqual(req[0].extra_key, "tenant-A") + self.assertEqual(req[1].extra_key, "tenant-B") + + # Scalar broadcast + req = GenerateReqInput( + text=["Hello", "World"], + extra_key="shared", + sampling_params=[{}, {}], + ) + req.normalize_batch_and_arguments() + self.assertEqual(req.extra_key, ["shared", "shared"]) + + # None stays None + req = GenerateReqInput(text=["Hello", "World"], sampling_params=[{}, {}]) + req.normalize_batch_and_arguments() + self.assertIsNone(req.extra_key) + self.assertIsNone(req[0].extra_key) + + # Parallel sampling expansion + req = GenerateReqInput( + text=["Hello", "World"], + extra_key=["tenant-A", "tenant-B"], + sampling_params={"n": 2}, + ) + req.normalize_batch_and_arguments() + self.assertEqual(req.extra_key, ["tenant-A", "tenant-B"] * 2) + + # Wrong-length list + req = GenerateReqInput( + text=["Hello", "World"], + extra_key=["only-one"], + sampling_params=[{}, {}], + ) + with self.assertRaisesRegex(ValueError, "batch size"): + req.normalize_batch_and_arguments() + + # Non-batched scalar unchanged + req = GenerateReqInput(text="Hello", extra_key="solo") + req.normalize_batch_and_arguments() + self.assertEqual(req.extra_key, "solo") + def test_logprob_parameters_normalization(self): """Test normalization of logprob-related parameters.""" # Test single example