@@ -507,6 +507,41 @@ def test_preprocess_packed_seqs(self):
507507
508508 self .destroy_parallel_state ()
509509
510+ @pytest .mark .skipif (
511+ not torch .cuda .is_available () or int (os .environ .get ("WORLD_SIZE" , "1" )) < 2 ,
512+ reason = "Requires at least 2 GPUs" ,
513+ )
514+ def test_preprocess_packed_seqs_row_shorter_than_alignment (self ):
515+ """A row whose valid length is below the alignment padding must not raise.
516+
517+ Chunk indices come from the alignment-padded length while ``d`` holds only the
518+ valid tokens, so on the higher CP ranks the first-chunk slice can start past the
519+ end of ``d``. Assigning that empty slice used to raise::
520+
521+ RuntimeError: The expanded size of the tensor (1) must match the existing
522+ size (0) at non-singleton dimension 0
523+
524+ With tp=1/cp=2 the alignment is 4, so a single-token row is the smallest case
525+ that reaches the out-of-range slice on cp_rank 1.
526+ """
527+ tp_size = 1
528+ cp_size = 2
529+ self ._setup_parallel_state (tp_size = tp_size , cp_size = cp_size )
530+
531+ batch_size , seq_len = 2 , 8
532+ input_ids = torch .randint (0 , 1000 , (batch_size , seq_len ))
533+ attention_mask = torch .zeros (batch_size , seq_len , dtype = torch .bool )
534+ attention_mask [0 , :] = True # a full-length row
535+ attention_mask [1 , 0 ] = True # a single valid token
536+
537+ ids_out , packed = preprocess_packed_seqs (input_ids , attention_mask , pre_process = True )
538+ assert packed .max_seqlen_q == packed .max_seqlen_kv
539+ assert ids_out .shape [0 ] == 1
540+ # 8 valid tokens need no padding; the 1-token row is padded to align_size = 4.
541+ assert ids_out .shape [1 ] == (8 + 4 ) // cp_size
542+
543+ self .destroy_parallel_state ()
544+
510545 @pytest .mark .skipif (
511546 not torch .cuda .is_available () or int (os .environ .get ("WORLD_SIZE" , "1" )) < 2 ,
512547 reason = "Requires at least 2 GPUs" ,
0 commit comments