Repository: UKGovernmentBEIS/inspect_ai
Issue: [Feature Request] Support returning multiple Samples for record_to_sample_fn 
Root cause: Unimplemented Feature Gap

I'm working on adding tasks that have different difficulties, each difficulty would provide different hints. It would be great that if the `record_to_sample_fn` function supports returning a list of Samples so that I don't need to duplicate the same task multiple times in the config json file where the only difference is just the hints.  

I'd be happy to contribute making this change if you agree that it makes sense.

I had exactly this scenario where I wanted to add different difficulty leves through hints. I ended up writting this code snippet, but it would maybe be nice to have something builtin. There's probably a cleaner way to do this, but in case you find it useful here's what I wrote:

```python
def _add_difficulty_levels(task: Task, difficulty_map: dict[str, dict[str, any]] | None) -> Task:
    """
    Add difficulty levels to a task.
    :param task: The task to add difficulty levels to.
    :param difficulty_map: The difficulty map.
    :return: The task with difficulty levels added.
    """
    dataset = []
    # for each sample in the dataset, create a new sample for each difficulty level
    for sample in task.dataset:
        dataset.extend(create_difficulty_dataset(sample, difficulty_map))

    task.dataset = MemoryDataset(dataset)

    return task


def add_difficulty_levels(
        tasks: list[Task] | Task,
        difficulty_map: dict[str, dict[str, any]] | None,
) -> list[Task] | Task:
    """
    Add difficulty levels to a task or list of tasks. This function will create new samples for each combination of difficulty level in the difficulty map for every sample in each task.
    :param tasks: The task or list of tasks to add difficulty levels to.
    :param difficulty_map: The difficulty map.
    :return: The task or list of tasks with difficulty levels added.
    """
    if isinstance(tasks, Task):
        return _add_difficulty_levels(tasks, difficulty_map)
    else:
        return [_add_difficulty_levels(task, difficulty_map) for task in tasks]

def generate_combinations(d: dict[str, dict[str, any]]):
    # Extract the items from each nested dictionary
    items = [
        [
            {'category': category, 'level': level, 'value': value}
            for level, value in category_levels.items()
        ]
        for category, category_levels in d.items()
    ]
    # Use product to generate all combinations
    combinations = product(*items)
    # Convert each combination to a set and put it in a list
    return combinations

def create_difficulty_dataset(sample: Sample, difficulty_map: dict[str, dict[str, any]] | None) -> MemoryDataset:
    """
    Create a dataset with a single sample and a difficulty map. The difficulty map is a dictionary where the keys are
    the difficulty category and the values are maps from difficulty levels within that category to the string to
    insert into the prompt.

    The sample's input field should be a format string with placeholders for each of the difficulty categories. For
    example, if the sample's input is "Question: ... \n\n Hint: {hint}", then the difficulty map should have a key
    "hint" with a value that is a dictionary mapping difficulty levels to strings to insert into the prompt.

    We combine every combination of difficulty levels to create a new sample for each combination. i.e. if there are
    two difficulty categories (hint and explanation) with three levels of hint difficulty and three levels of
    explanation difficulty, we will create 9 samples.

    :param sample: The sample to base the dataset on
    :param difficulty_map: The difficulty map
    :return: The dataset
    """
    # If there are no difficulty levels, return the sample as is
    if difficulty_map is None or len(difficulty_map) == 0:
        return MemoryDataset([sample])

    samples = []

    # create a new sample for each combination of difficulty levels
    for combination in generate_combinations(difficulty_map):
        substitutions = {c['category']: c['value'] for c in combination}

        metadata = sample.metadata.copy() if sample.metadata is not None else {}
        metadata.update({c['category']: c['level'] for c in combination})
        metadata['difficulty'] = {c['category']: c['level'] for c in combination}

        input = sample.input
        # first format anything that is not callable
        input = input.format(**{k: v for k, v in substitutions.items() if not callable(v)}).strip()
        # then format anything that is callable
        for key, value in substitutions.items():
            if callable(value):
                input = value(input)

        id_ = sample.id
        if id_ is not None:
            for category, level in metadata['difficulty'].items():
                id_ += f'_{category}_{level}'

        new_sample = Sample(
            id=id_,
            input=input,
            target=sample.target,
            choices=sample.choices,
            metadata=metadata,
            files=sample.files
        )
        samples.append(new_sample)

    return MemoryDataset(samples)
```

This is then used something like this:

```python
sample = Sample(input="Question: ... Hints: {hint_A}\n{hint_B}", ...)

task = ...

task = add_difficulty_levels(
    task,
    difficulty_map={
        "hint_A": {
            "easy": "...",
            "medium": "...",
            "hard": ""
        },
        "hint_B": {
            "easy": "...",
            "medium": "...",
            "hard": ""
        }
    },
)
```

Yes, this does make a lot of sense and will definitely reduce repetition in a lot of datasets. Implemented and merged to `main`. here: https://github.com/UKGovernmentBEIS/inspect_ai/pull/364