Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 0 additions & 94 deletions tests/test_data_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import copy
import textwrap
from time import strftime

Expand All @@ -33,7 +32,6 @@
maybe_unpair_preference_dataset,
pack_dataset,
prepare_multimodal_messages,
prepare_multimodal_messages_vllm,
unpair_preference_dataset,
)

Expand Down Expand Up @@ -268,98 +266,6 @@ def test_prepared_image_blocks_without_new_images(self):
assert messages == expected


@require_vision
class TestPrepareMultimodalMessagesVLLM:
def test_single_image_conversion(self):
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": Image.new("RGB", (10, 10), color="blue")},
{"type": "text", "text": "What color is the sky?"},
],
}
]

result = prepare_multimodal_messages_vllm(messages)

# Original should remain unchanged (deepcopy test)
assert messages[0]["content"][0]["type"] == "image"

# Converted version should have correct structure
assert result[0]["content"][0]["type"] == "image_pil"
assert "image_pil" in result[0]["content"][0]
assert "image" not in result[0]["content"][0]
assert isinstance(result[0]["content"][0]["image_pil"], Image.Image)
assert result[0]["content"][1]["type"] == "text"

def test_mixed_content_conversion(self):
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "What color is the sky?"},
{"type": "image", "image": Image.new("RGB", (10, 10), color="blue")},
],
}
]

result = prepare_multimodal_messages_vllm(messages)

# The image part should be converted, text should be unchanged
assert result[0]["content"][0]["type"] == "text"
assert result[0]["content"][1]["type"] == "image_pil"

def test_no_images(self):
messages = [{"role": "user", "content": [{"type": "text", "text": "What color is the sky?"}]}]

result = prepare_multimodal_messages_vllm(messages)

# Should be identical since there are no images
assert result == messages
# And a deepcopy — not the same object
assert result is not messages
assert result[0] is not messages[0]

def test_multiple_messages(self):
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "What color is the sky?"},
{"type": "image", "image": Image.new("RGB", (10, 10), color="blue")},
],
},
{
"role": "assistant",
"content": [{"type": "text", "text": "It is blue."}],
},
]

result = prepare_multimodal_messages_vllm(messages)

assert result[0]["content"][1]["type"] == "image_pil"
assert result[1]["content"][0]["type"] == "text"
assert result[1]["content"][0]["text"] == "It is blue."

def test_deepcopy_integrity(self):
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "What color is the sky?"},
{"type": "image", "image": Image.new("RGB", (10, 10), color="blue")},
],
},
]
original = copy.deepcopy(messages)

_ = prepare_multimodal_messages_vllm(messages)

# Original should not be mutated
assert messages == original


class TestIsConversational(TrlTestCase):
# fmt: off
conversational_examples = [
Expand Down
2 changes: 0 additions & 2 deletions trl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"maybe_unpair_preference_dataset",
"pack_dataset",
"prepare_multimodal_messages",
"prepare_multimodal_messages_vllm",
"unpair_preference_dataset",
],
"models": ["create_reference_model"],
Expand Down Expand Up @@ -92,7 +91,6 @@
maybe_unpair_preference_dataset,
pack_dataset,
prepare_multimodal_messages,
prepare_multimodal_messages_vllm,
unpair_preference_dataset,
)
from .models import create_reference_model
Expand Down
34 changes: 0 additions & 34 deletions trl/data_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import copy
from collections import defaultdict, deque
from collections.abc import Callable, Sequence
from itertools import takewhile
Expand Down Expand Up @@ -124,39 +123,6 @@ def prepare_multimodal_messages(messages: list[dict[str, Any]], images: list | N
return new_messages


def prepare_multimodal_messages_vllm(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
# docstyle-ignore # because <Image> is not parsable in the code block
"""
Convert structured multimodal messages into a format compatible with vLLM. Replaces `"type": "image"` blocks with
`"type": "image_pil"` blocks, and `"image": Image` with `"image_pil": Image`.

Args:
messages (`list[dict[str, Any]]`):
Messages with `"role"` and `"content"`. Content is expected to be a list of structured blocks.

Returns:
`list[dict[str, Any]]`:
A deep-copied list of messages compatible with vLLM's expected input format.

Example:
```python
# Input
[{"role": "user", "content": [{"type": "image", "image": <PIL.Image.Image>}, {"type": "text", "text": "What's in this image?"}]}]

# Output
[{"role": "user", "content": [{"type": "image_pil", "image_pil": <PIL.Image.Image>}, {"type": "text", "text": "What's in this image?"}]}]
```
"""
messages = copy.deepcopy(messages) # avoid modifying the original messages
for message in messages:
if isinstance(message["content"], list):
for part in message["content"]:
if part["type"] == "image":
part["type"] = "image_pil" # vLLM expects 'image_pil' key for images
part["image_pil"] = part.pop("image")
return messages


def is_conversational(example: dict[str, Any]) -> bool:
r"""
Check if the example is in a conversational format.
Expand Down
Loading