Skip to content

fix(io_struct): index extra_key per sub-request in batched GenerateReqInput#26971

Merged
hnyls2002 merged 1 commit into
sgl-project:mainfrom
HumphreySun98:fix/generate-req-input-extra-key-batched
Jun 14, 2026
Merged

fix(io_struct): index extra_key per sub-request in batched GenerateReqInput#26971
hnyls2002 merged 1 commit into
sgl-project:mainfrom
HumphreySun98:fix/generate-req-input-extra-key-batched

Conversation

@HumphreySun98

@HumphreySun98 HumphreySun98 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Motivation

GenerateReqInput.extra_key is typed Optional[Union[List[str], str]], but batch normalization did not normalize/expand it and __getitem__ passed extra_key=self.extra_key to every sub-request instead of indexing it (issue #26755).

A batched request with a per-request list crashes downstream prefix-cache matching:

{"text": ["P", "P"], "extra_key": ["tenant-A", "tenant-B"], "sampling_params": [{"max_new_tokens": 1}, {"max_new_tokens": 1}]}

both sub-requests inherit extra_key=["tenant-A", "tenant-B"], and RadixKey.child_key() produces (['tenant-A', 'tenant-B'], 50) which fails with TypeError: unhashable type: 'list'.

Modifications

python/sglang/srt/managers/io_struct.py:

  1. New _normalize_extra_key(num) wired into _normalize_batch_inputs, mirroring the existing _normalize_custom_logit_processor style:
    • None → unchanged
    • str[str] * num (broadcast to every sub-request)
    • list of length batch_size → expanded by parallel_sample_num
    • list of mismatched length → ValueError with a clear message
    • Anything else → ValueError
  2. __getitem__ now reads self.extra_key[i] when non-None and None otherwise — matches the custom_logit_processor / lora_path pattern already used a few lines above in the same method.

After normalization the invariant becomes: extra_key is None or len(extra_key) == batch_size * parallel_sample_num, identical to the convention used by lora_path / custom_logit_processor / logprob_params.

Tests

test/registered/unit/managers/test_io_struct.py::TestGenerateReqInputNormalization::test_extra_key_normalization covers:

  • Per-request list → normalized list intact, __getitem__ returns scalar per index (the bug fix).
  • Scalar string → broadcast to a list, __getitem__ returns the same string for every index.
  • None → stays None, __getitem__ broadcasts None.
  • parallel_sample_num=2 → list doubled by parallel_sample_num.
  • Wrong-length list → ValueError with batch size in the message.
  • Single (non-batched) request with scalar extra_key → unchanged.

Registered to base-b-test-cpu / stage-b-test-1-gpu-small-amd / cuda base-b 1-gpu-large via the file's existing register_*_ci(...) block.

Accuracy

Not applicable. No change to sampling, model forward, or scheduling. Only the routing of extra_key from the batched input to its sub-requests changes; downstream consumers (RadixKey, prefix cache) already expect a scalar per request.

Speed Tests and Profiling

Not applicable. Normalization adds one Python list operation per batched request at admission time; negligible vs. tokenization and prefill.

Checklist

  • Format your code according to pre-commit.
  • Add unit tests.
  • Update documentation — N/A, no API change.
  • Provide accuracy and speed benchmark results — N/A, see sections above.

Closes #26755.


CI States

Latest PR Test (Base): ⏳ Run #26981186663
Latest PR Test (Extra): ❌ Run #26981186627

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

…qInput

Closes sgl-project#26755.

`GenerateReqInput.extra_key` is typed `Optional[Union[List[str], str]]`,
but batch normalization did not normalize/expand it and `__getitem__`
passed `extra_key=self.extra_key` to every sub-request instead of
indexing it. A batched request with `extra_key=["tenant-A", "tenant-B"]`
crashed downstream prefix-cache matching with
`TypeError: unhashable type: 'list'` in `RadixKey.child_key()` because
every sub-request inherited the full list.

Fix:
- `_normalize_extra_key`: broadcast a scalar string to a list of length
  `num`, validate list length against `batch_size`, expand by
  `parallel_sample_num`. Mirrors the existing
  `_normalize_custom_logit_processor` style.
- `__getitem__`: index `self.extra_key[i]` when non-None, broadcast None
  otherwise (matches `custom_logit_processor` / `lora_path` pattern in
  the same method).

Tests: extended `TestGenerateReqInputNormalization` in
`test/registered/unit/managers/test_io_struct.py` with a
`test_extra_key_normalization` case covering per-request list, scalar
broadcast, None, parallel-sample expansion, and the wrong-length error
path. Single (non-batched) requests with a scalar `extra_key` are
unchanged.
@HumphreySun98 HumphreySun98 force-pushed the fix/generate-req-input-extra-key-batched branch from 5ce8ab7 to 2e0dd54 Compare June 4, 2026 21:38
@hnyls2002

Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@hnyls2002 hnyls2002 merged commit 8c334e2 into sgl-project:main Jun 14, 2026
133 of 183 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Batched GenerateReqInput passes extra_key list to every sub-request instead of indexing it

2 participants