def make_dataclass_and_log_config(
    cfg: DictConfig,
    dataclass_constructor: Callable[..., T],
    dataclass_fields: set[str],
    transforms: Optional[Union[list[Callable[[dict[str, Any]], dict[str, Any]]],
                               list[str], str]] = None,
    icl_tasks_required: bool = False,
) -> tuple[dict[str, Any], T]:
    """Converts a DictConfig to a dataclass and creates a logged config."""
    unstructured_config = om.to_container(cfg, resolve=True)
    assert isinstance(unstructured_config, dict)
    assert all(isinstance(k, str) for k in unstructured_config.keys())
    unstructured_config = {str(k): v for k, v in unstructured_config.items()}

    # Flatten union types before creating structured config:
    if 'eval_gauntlet' in unstructured_config:
        forbid_config_key(unstructured_config, 'eval_gauntlet_str')
        if isinstance(unstructured_config['eval_gauntlet'], str):
            unstructured_config['eval_gauntlet_str'] = unstructured_config.pop(
                'eval_gauntlet',
            )
    if (loader := unstructured_config.get('eval_loader', None)) is not None:
        forbid_config_key(unstructured_config, 'eval_loaders')
        if isinstance(loader, list):
            unstructured_config['eval_loaders'] = unstructured_config.pop(
                'eval_loader',
            )
    if 'icl_tasks' in unstructured_config:
        forbid_config_key(unstructured_config, 'icl_tasks_str')
        if isinstance(unstructured_config['icl_tasks'], str):
            unstructured_config['icl_tasks_str'] = unstructured_config.pop(
                'icl_tasks',
            )
    else:
        if icl_tasks_required:
            raise MissingMandatoryValue(
                'icl_tasks must be specified in the config',
            )

    # Apply transforms to the unstructured config before constructing dataclass
    unstructured_config = apply_transforms_to_config(
        unstructured_config,
        transforms,
    )

    # Create copy of config for logging
    logged_cfg: dict[str, Any] = copy.deepcopy(unstructured_config)

    arg_config_keys = set(unstructured_config.keys())
    extraneous_keys = set.difference(arg_config_keys, dataclass_fields)

    if 'variables' not in unstructured_config:
        unstructured_config['variables'] = {}

    if len(extraneous_keys) > 0:
        raise ValueError(
            f'Unused parameters {sorted(extraneous_keys)} found in cfg. Please check your yaml to ensure these parameters are necessary. Please place any variables under the `variables` key.',
        )

    dataclass_dict_config: DictConfig = om.structured(
        dataclass_constructor(**unstructured_config),
    )

    # Error on missing mandatory values:
    for key in dataclass_fields:
        _ = dataclass_dict_config[key]

    # Convert DictConfig to dict for dataclass constructor so that child
    # configs are not DictConfigs
    dataclass_config: T = dataclass_constructor(
        **to_dict_container(dataclass_dict_config),
    )

    return logged_cfg, dataclass_config
Converts a DictConfig to a dataclass and creates a logged config.
llmfoundry/utils/config_utils.py