code: def run_test_explainer_api(
      explainer_class,
      model_type,
      output_features,
      additional_config,
      tmpdir,
      input_features=None,
      batch_size=128,
      **kwargs,
  ):
      image_dest_folder = os.path.join(tmpdir, "generated_images")
  
      if input_features is None:
          input_features = [
              # Include a non-canonical name that's not a valid key for a vanilla pytorch ModuleDict:
              # https://github.com/pytorch/pytorch/issues/71203
              {"name": "type", "type": "binary"},
              number_feature(),
              category_feature(
                  encoder={TYPE: "onehot", "reduce_output": "sum"}
              ),
              category_feature(
                  encoder={TYPE: "passthrough", "reduce_output": "sum"}
              ),
          ]
          if model_type == MODEL_ECD:
              # TODO(travis): need unit tests to test the get_embedding_layer() of every encoder to ensure it is
              #  compatible with the explainer
              input_features += [
                  category_feature(
                      encoder={"type": "dense", "reduce_output": "sum"}
                  ),
                  text_feature(encoder={"vocab_size": 3}),
                  vector_feature(),
                  timeseries_feature(),
                  image_feature(folder=image_dest_folder),
                  # audio_feature(os.path.join(tmpdir, "generated_audio")), # NOTE: works but takes a long time
                  # sequence_feature(encoder={"vocab_size": 3}),
                  date_feature(),
                  # h3_feature(),
                  set_feature(encoder={"vocab_size": 3}),
                  # bag_feature(encoder={"vocab_size": 3}),
              ]
  
      # Generate data
      csv_filename = os.path.join(tmpdir, "training.csv")
      generate_data(
          input_features,
          output_features,
          csv_filename,
          num_examples=200,
      )
      df = pd.read_csv(csv_filename)
      if "split" in additional_config.get("preprocessing", {}):
          df["split"] = np.random.randint(0, 3, df.shape[0])
  
      # Train model
      config = {
          "input_features": input_features,
          "output_features": output_features,
          "model_type": model_type,
      }
      if model_type == MODEL_ECD:
          config["trainer"] = {"epochs": 2, BATCH_SIZE: batch_size}
      else:
          # Disable feature filtering to avoid having no features due to small test dataset,
          # see https://stackoverflow.com/a/66405983/5222402
          config["trainer"] = {"feature_pre_filter": False}
      config.update(additional_config)
  
      model = LudwigModel(
          config,
          logging_level=logging.WARNING,
          backend=LocalTestBackend(),
      )
      model.train(df)
  
      # Explain model
      explainer = explainer_class(
          model,
          inputs_df=df,
          sample_df=df,
          target=output_features[0]["name"],
          **kwargs,
      )
  
      is_binary = output_features[0].get("type") == BINARY
      is_category = output_features[0].get("type") == CATEGORY
  
      vocab_size = 1
      if is_binary:
          vocab_size = 2
      elif is_category:
          vocab_size = (
              output_features[0].get("decoder", {}).get("vocab_size")
          )
  
      assert explainer.is_binary_target == is_binary
      assert explainer.is_category_target == is_category
      assert explainer.vocab_size == vocab_size
  
      explanations_result = explainer.explain()
  
      # Verify shapes.
      assert (
          explanations_result.global_explanation.to_array().shape
          == (vocab_size, len(input_features))
      )
  
      assert len(explanations_result.row_explanations) == len(df)
      for e in explanations_result.row_explanations:
          assert e.to_array().shape == (vocab_size, len(input_features))
  
      assert len(explanations_result.expected_values) == vocab_size
  
imports: import logging
  import os
  import numpy
  import pandas
  import pytest
  from ludwig.api import LudwigModel
  from ludwig.constants import BATCH_SIZE, BINARY, CATEGORY, MINIMUM_BATCH_SIZE, MODEL_ECD, MODEL_GBM, TYPE
  from ludwig.explain.captum import IntegratedGradientsExplainer
  from ludwig.explain.explainer import Explainer
  from ludwig.explain.explanation import Explanation
  from ludwig.explain.gbm import GBMExplainer
  from tests.integration_tests.utils import binary_feature, category_feature, date_feature, generate_data, image_feature, LocalTestBackend, number_feature, sequence_feature, set_feature, text_feature, timeseries_feature, vector_feature
label_desc: Use image node_id 8 for calling a global function with example usage: run_test_explainer_api(explainer_class, model_type, output_features, additional_config, tmpdir, input_features, batch_size) without return types