Repository: huggingface/transformers
Pull request: Fix processor chat template

# What does this PR do?

As er title, the slow tests in gemma3 were failing and the cause of issue is an incorrect indentation. We need to add exactly one sublist of images per batch, but the current code was treating each conversation turn as a new batch

Fixed and added a fast test as well

Changed files:
filename: src/transformers/processing_utils.py
patch: @@ -1572,6 +1572,7 @@ def apply_chat_template(
             batch_images, batch_videos = [], []
             batch_audios = []
             for conversation in conversations:
+                images, videos = [], []
                 for message in conversation:
                     visuals = [content for content in message["content"] if content["type"] in ["image", "video"]]
                     audio_fnames = [
@@ -1586,12 +1587,14 @@ def apply_chat_template(
                         for key in ["image", "url", "path", "base64"]
                         if key in vision_info and vision_info["type"] == "image"
                     ]
+                    images.extend(image_fnames)
                     video_fnames = [
                         vision_info[key]
                         for vision_info in visuals
                         for key in ["video", "url", "path"]
                         if key in vision_info and vision_info["type"] == "video"
                     ]
+                    videos.extend(video_fnames)
 
                     # Audio models do not accept nested list of audios (yet!) so we construct a flat input audio list
                     if not mm_load_kwargs["load_audio_from_video"]:
@@ -1601,10 +1604,10 @@ def apply_chat_template(
                         for fname in video_fnames:
                             batch_audios.append(load_audio(fname, sampling_rate=mm_load_kwargs["sampling_rate"]))
 
-                    # Currently all processors can accept nested list of batches, but not flat list of visuals
-                    # So we'll make a batched list of images and let the processor handle it
-                    batch_images.append(image_fnames)
-                    batch_videos.append(video_fnames)
+                # Currently all processors can accept nested list of batches, but not flat list of visuals
+                # So we'll make a batched list of images and let the processor handle it
+                batch_images.append(images)
+                batch_videos.append(videos)
 
         prompt, generation_indices = render_jinja_template(
             conversations=conversations,

filename: tests/test_processing_common.py
patch: @@ -1003,10 +1003,8 @@ def _test_apply_chat_template(
 
         batch_messages = [
             [
-                {
-                    "role": "user",
-                    "content": [{"type": "text", "text": "Describe this."}],
-                },
+                {"role": "system", "content": [{"type": "text", "text": "You are a helpful assistant."}]},
+                {"role": "user", "content": [{"type": "text", "text": "Describe this."}]},
             ]
         ] * batch_size
 
@@ -1053,7 +1051,7 @@ def _test_apply_chat_template(
 
         # Test that with modality URLs and `return_dict=True`, we get modality inputs in the dict
         for idx, url in enumerate(input_data[:batch_size]):
-            batch_messages[idx][0]["content"] = [batch_messages[idx][0]["content"][0], {"type": modality, "url": url}]
+            batch_messages[idx][1]["content"] = [batch_messages[idx][1]["content"][0], {"type": modality, "url": url}]
 
         out_dict = processor.apply_chat_template(
             batch_messages,