You can implement both (i) and (ii) like this:
import random

from torch.utils.data import DataLoader, Sampler, RandomSampler, Dataset


class MyBatchSampler(Sampler):
    def __init__(self, sampler, batch_size_list, drop_last):
        self.sampler = sampler
        self.batch_size_list = batch_size_list
        self.drop_last = drop_last

    def __iter__(self):
        batch = []
        batch_size = random.choice(self.batch_size_list)
        for idx in self.sampler:
            batch.append((idx, batch_size))
            if len(batch) == batch_size:
                yield batch
                batch = []
                batch_size = random.choice(self.batch_size_list)
        if len(batch) > 0 and not self.drop_last:
            yield batch

    def __len__(self) -> int:
        if self.drop_last:
            return len(self.sampler) // self.batch_size  # type: ignore[arg-type]
        else:
            return (len(self.sampler) + self.batch_size - 1) // self.batch_size  # type: ignore[arg-type]


class MyDataSet(Dataset):
    def __getitem__(self, item):
        index, batch_size = item
        return index, batch_size

    def __len__(self):
        return 10


my_dataset = MyDataSet()
my_sampler = MyBatchSampler(RandomSampler(my_dataset), [1, 2, 3], False)
dataloader = DataLoader(my_dataset, batch_sampler=my_sampler)
for data in dataloader:
    print(data)