[bugfix] fix streaming packing with truncation_strategy=split#9738
[bugfix] fix streaming packing with truncation_strategy=split#9738Kale112 wants to merge 2 commits into
Conversation
IterablePackingDataset._fetch_data_out_queue assumed template.encode always returns a dict, but with truncation_strategy='split' an over-long sample is split into a list of chunks, causing TypeError: list indices must be integers or slices, not str. Flatten the list (mirroring RowPreprocessor.batched_preprocess) into one bin-packing item per chunk, preserving input order via res[i]. No behaviour change for delete/left/right (dict) path. Fixes modelscope#8285.
There was a problem hiding this comment.
Code Review
This pull request fixes a crash that occurs when using streaming, packing, and splitting together. It updates _fetch_data_out_queue in swift/dataset/packing.py to correctly flatten list-of-chunks outputs from template.encode and adds a corresponding regression test. The review feedback suggests adding defensive checks to ensure each chunk is not empty and contains the 'input_ids' key before accessing it to prevent potential KeyError or TypeError exceptions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| for chunk in data: | ||
| last_res.append((chunk, len(chunk['input_ids']))) |
There was a problem hiding this comment.
To ensure robust defensive programming, it is recommended to verify that each chunk is not empty and contains the 'input_ids' key before attempting to access it. This prevents potential KeyError or TypeError if an unexpected or empty chunk is returned by the template encoder.
| for chunk in data: | |
| last_res.append((chunk, len(chunk['input_ids']))) | |
| for chunk in data: | |
| if chunk and 'input_ids' in chunk: | |
| last_res.append((chunk, len(chunk['input_ids']))) |
There was a problem hiding this comment.
Thanks, applied in f9b0280. Added the defensive guard (if chunk and 'input_ids' in chunk) and a regression test (test_streaming_packing_split_skips_malformed_chunks) covering empty chunks and chunks missing 'input_ids'.
Address review on modelscope#9738: guard the inner loop with a check that each chunk is non-empty and contains the 'input_ids' key before accessing it, so an empty chunk or one missing 'input_ids' is skipped instead of raising KeyError/TypeError. Mirrors RowPreprocessor's skip-bad-data behaviour. Adds test_streaming_packing_split_skips_malformed_chunks.
PR type
PR information
Fixes #8285.
IterablePackingDataset._fetch_data_out_queueassumedtemplate.encodealways returns a single dict. Withtruncation_strategy='split', an over-long sample is split into a list of chunks (template/base.py:_encode_truncatedreturnsbatched, andencodereturns that list when more than one chunk is produced)._fetch_data_out_queuethen ranlen(data['input_ids'])on the list, raising:This only triggers for
streaming=true + packing=true + truncation_strategy=split(a common long-document streaming pretraining setup). The non-streaming path is unaffected:EncodePreprocessormaterializes split chunks into separate dataset rows beforePackingDatasetwraps them, so it only ever sees single-dict rows.Fix: flatten the list into one bin-packing item per chunk, mirroring the existing pattern in
RowPreprocessor.batched_preprocess(if isinstance(row, dict): row = [row]). Input order is preserved viares[i](required for thesequentialpacking strategy). No behaviour change for the dict path (delete/left/right): each successful sample still produces exactly one(chunk, length)item in the same order as before, and failed/empty encodes are still skipped.Experiment results
tests/train/test_packing.py::test_streaming_packing_splitcovering the dict (delete/left/right), split-list, and failed-encode (empty dict) cases; asserts correct flattening, input-order preservation, and that flattened chunks feedcalculate_matched_groupwithout error (tokens conserved).pre-commit run --files swift/dataset/packing.py tests/train/test_packing.pypasses (flake8 / isort / yapf / trailing-whitespace / double-quote-string-fixer ...).