code: def run_test_with_features(
      input_features,
      output_features,
      num_examples=100,
      run_fn=run_api_experiment,
      expect_error=False,
      df_engine=None,
      dataset_type="parquet",
      predict=False,
      skip_save_processed_input=True,
      skip_save_predictions=True,
      nan_percent=0.0,
      preprocessing=None,
      first_row_none=False,
      last_row_none=False,
      nan_cols=None,
      required_metrics=None,
      backend_kwargs=None,
  ):
      preprocessing = preprocessing or {}
      config = {
          "input_features": input_features,
          "output_features": output_features,
          "combiner": {"type": "concat", "output_size": 14},
          TRAINER: {"epochs": 2, "batch_size": 8},
      }
      if preprocessing:
          config[PREPROCESSING] = preprocessing
  
      backend_kwargs = copy.deepcopy(backend_kwargs or {})
      backend_config = merge_dict(RAY_BACKEND_CONFIG, backend_kwargs)
      if df_engine:
          backend_config["processor"]["type"] = df_engine
  
      with tempfile.TemporaryDirectory() as tmpdir:
          csv_filename = os.path.join(tmpdir, "dataset.csv")
          dataset_csv = generate_data(
              input_features,
              output_features,
              csv_filename,
              num_examples=num_examples,
          )
          dataset = create_data_set_to_use(
              dataset_type, dataset_csv, nan_percent=nan_percent
          )
          dataset = augment_dataset_with_none(
              dataset, first_row_none, last_row_none, nan_cols
          )
  
          if expect_error:
              with pytest.raises(
                  (RuntimeError, ray.exceptions.RayTaskError)
              ):
                  run_fn(
                      config,
                      dataset=dataset,
                      backend_config=backend_config,
                      predict=predict,
                      skip_save_processed_input=skip_save_processed_input,
                      skip_save_predictions=skip_save_predictions,
                      required_metrics=required_metrics,
                  )
          else:
              run_fn(
                  config,
                  dataset=dataset,
                  backend_config=backend_config,
                  predict=predict,
                  skip_save_processed_input=skip_save_processed_input,
                  skip_save_predictions=skip_save_predictions,
                  required_metrics=required_metrics,
              )
  
imports: import copy
  import os
  import tempfile
  from typing import Literal, Optional, Union
  import numpy
  import pandas
  import pytest
  import torch
  from ludwig.api import LudwigModel
  from ludwig.backend import create_ray_backend, initialize_backend, LOCAL_BACKEND
  from ludwig.constants import AUDIO, BAG, BALANCE_PERCENTAGE_TOLERANCE, BATCH_SIZE, BFILL, BINARY, CATEGORY, COLUMN, DATE, H3, IMAGE, MAX_BATCH_SIZE_DATASET_FRACTION, NAME, NUMBER, PREPROCESSING, SEQUENCE, SET, SPLIT, TEXT, TIMESERIES, TRAINER, VECTOR
  from ludwig.data.preprocessing import balance_data
  from ludwig.data.split import DEFAULT_PROBABILITIES
  from ludwig.utils.data_utils import read_parquet
  from ludwig.utils.misc_utils import merge_dict
  from tests.integration_tests.utils import audio_feature, augment_dataset_with_none, bag_feature, binary_feature, category_feature, create_data_set_to_use, date_feature, generate_data, h3_feature, image_feature, number_feature, RAY_BACKEND_CONFIG, sequence_feature, set_feature, text_feature, timeseries_feature, train_with_backend, vector_feature
  import dask
  import ray
  import ray.exceptions
  from ray.air.config import DatasetConfig
  from ray.data import Dataset, DatasetPipeline
  from ray.train._internal.dataset_spec import DataParallelIngestSpec
  from ludwig.backend.ray import get_trainer_kwargs, RayBackend
  from ludwig.data.dataframe.dask import DaskEngine
label_desc: Use image node_id 8 for calling a global function with example usage: run_test_with_features(input_features, output_features, num_examples, run_fn, expect_error, df_engine, dataset_type, predict, skip_save_processed_input, skip_save_predictions, nan_percent, preprocessing, first_row_none, last_row_none, nan_cols, required_metrics, backend_kwargs) without return types