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
34 changes: 34 additions & 0 deletions tests/test_protocol_on_cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from verl import DataProto
from verl.protocol import (
DataProtoItem,
deserialize_single_tensor,
deserialize_tensordict,
serialize_single_tensor,
Expand Down Expand Up @@ -152,6 +153,39 @@ def test_tensor_dict_constructor():
data = DataProto.from_dict(tensors={"obs": obs, "act": act}, num_batch_dims=3)


def test_dataproto_item_consistency():
data = DataProto.from_dict(
tensors={"obs": torch.randn(2, 3)},
non_tensors={"label": ["a", "b"]},
meta_info={"split": "train"},
)

item = data[0]

assert item.batch.batch_size == torch.Size([])
assert item.non_tensor_batch == {"label": "a"}
assert item.meta_info == {"split": "train"}


def test_dataproto_item_rejects_batched_tensordict():
batch = TensorDict({"obs": torch.randn(1, 3)}, batch_size=[1])

with pytest.raises(AssertionError, match="must represent a single item"):
DataProtoItem(batch=batch)


@pytest.mark.parametrize(
("kwargs", "message"),
[
({"non_tensor_batch": []}, "non_tensor_batch must be a dict"),
({"meta_info": []}, "meta_info must be a dict"),
],
)
def test_dataproto_item_rejects_non_dict_metadata(kwargs, message):
with pytest.raises(AssertionError, match=message):
DataProtoItem(**kwargs)


def test_tensor_dict_make_iterator():
obs = torch.randn(100, 10)
labels = [random.choice(["abc", "cde"]) for _ in range(100)]
Expand Down
17 changes: 16 additions & 1 deletion verl/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,26 @@ def collate_fn(x: list["DataProtoItem"]):

@dataclass
class DataProtoItem:
# TODO(zhangchi.usc1992) add consistency check
batch: TensorDict = None
non_tensor_batch: dict = field(default_factory=dict)
meta_info: dict = field(default_factory=dict)

def __post_init__(self):
self.check_consistency()

def check_consistency(self):
"""Validate the unbatched item contract used by ``DataProto.__getitem__``."""

if self.batch is not None:
assert len(self.batch.batch_size) == 0, (
"DataProtoItem.batch must represent a single item with num_batch_dims=0, "
f"got batch_size={self.batch.batch_size}"
)
assert isinstance(self.non_tensor_batch, dict), (
f"DataProtoItem.non_tensor_batch must be a dict, got {type(self.non_tensor_batch)}"
)
assert isinstance(self.meta_info, dict), f"DataProtoItem.meta_info must be a dict, got {type(self.meta_info)}"


@dataclass
class DataProto:
Expand Down