Serialize a list of experiences into a compact bytes payload. Tensor fields are packed with safetensors while non-tensor fields are packed as metadata via pickle.
(cls, experiences: List[Experience])
| 344 | |
| 345 | @classmethod |
| 346 | def serialize_many(cls, experiences: List[Experience]) -> bytes: |
| 347 | """Serialize a list of experiences into a compact bytes payload. |
| 348 | |
| 349 | Tensor fields are packed with safetensors while non-tensor fields are packed |
| 350 | as metadata via pickle. |
| 351 | """ |
| 352 | metadata = {"version": cls._SER_VERSION, "num_items": len(experiences), "items": []} |
| 353 | tensor_data = {} |
| 354 | seen_storages: set = set() |
| 355 | |
| 356 | for index, exp in enumerate(experiences): |
| 357 | item_meta = {} |
| 358 | for field_name in cls._META_FIELDS: |
| 359 | value = getattr(exp, field_name) |
| 360 | if field_name == "eid" and value is not None: |
| 361 | item_meta[field_name] = value.to_dict() if isinstance(value, EID) else value |
| 362 | else: |
| 363 | item_meta[field_name] = value |
| 364 | |
| 365 | item_meta["custom_fields"] = cls._serialize_custom_fields(exp.custom_fields) |
| 366 | |
| 367 | for field_name in cls._TENSOR_FIELDS: |
| 368 | value = getattr(exp, field_name) |
| 369 | if value is None: |
| 370 | continue |
| 371 | t = value.detach().contiguous().cpu() |
| 372 | storage_ptr = t.untyped_storage().data_ptr() |
| 373 | if storage_ptr in seen_storages: |
| 374 | t = t.clone() |
| 375 | seen_storages.add(storage_ptr) |
| 376 | tensor_data[f"{index}:{field_name}"] = t |
| 377 | |
| 378 | if exp.multi_modal_inputs is None: |
| 379 | item_meta["multi_modal_input_keys"] = [] |
| 380 | else: |
| 381 | mm_keys = list(exp.multi_modal_inputs.keys()) |
| 382 | item_meta["multi_modal_input_keys"] = mm_keys |
| 383 | for key in mm_keys: |
| 384 | value = exp.multi_modal_inputs[key] |
| 385 | t = value.detach().contiguous().cpu() |
| 386 | storage_ptr = t.untyped_storage().data_ptr() |
| 387 | if storage_ptr in seen_storages: |
| 388 | t = t.clone() |
| 389 | seen_storages.add(storage_ptr) |
| 390 | tensor_data[f"{index}:multi_modal_inputs:{key}"] = t |
| 391 | |
| 392 | metadata["items"].append(item_meta) |
| 393 | |
| 394 | metadata_bytes = pickle.dumps(metadata, protocol=pickle.HIGHEST_PROTOCOL) |
| 395 | tensor_bytes = st_save(tensor_data) |
| 396 | header = ( |
| 397 | cls._SER_MAGIC |
| 398 | + struct.pack("<B", cls._SER_VERSION) |
| 399 | + struct.pack("<Q", len(metadata_bytes)) |
| 400 | + struct.pack("<Q", len(tensor_bytes)) |
| 401 | ) |
| 402 | return header + metadata_bytes + tensor_bytes |
| 403 |