code: def _save_preprocess_config(
      preprocess_fn: Callable,
      input_shape: Optional[tuple],
      filepath: Path,
  ) -> dict:
      """
      Serializes a drift detectors preprocess_fn. Artefacts are saved to disk, and a config dict containing filepaths
      to the saved artefacts is returned.
  
      Parameters
      ----------
      preprocess_fn
          The preprocess function to be serialized.
      input_shape
          Input shape for a model (if a model exists).
      filepath
          Directory to save serialized artefacts to.
  
      Returns
      -------
      The config dictionary, containing references to the serialized artefacts. The format if this dict matches that \
      of the `preprocess` field in the drift detector specification.
      """
      preprocess_cfg: Dict[str, Any] = {}
      local_path = Path("preprocess_fn")
  
      # Serialize function
      func, func_kwargs = _serialize_object(
          preprocess_fn, filepath, local_path.joinpath("function")
      )
      preprocess_cfg.update({"src": func})
  
      # Process partial function kwargs (if they exist)
      kwargs = {}
      for k, v in func_kwargs.items():
          # Model/embedding
          if isinstance(v, supported_models_all):
              cfg_model, cfg_embed = _save_model_config(
                  v, filepath, input_shape, local_path
              )
              kwargs.update({k: cfg_model})
              if cfg_embed is not None:
                  kwargs.update({"embedding": cfg_embed})
  
          # Tokenizer
          elif isinstance(v, PreTrainedTokenizerBase):
              cfg_token = _save_tokenizer_config(
                  v, filepath, local_path
              )
              kwargs.update({k: cfg_token})
  
          # torch device
          elif (
              v.__class__.__name__ == "device"
          ):  # avoiding torch import in case not installed
              kwargs.update({k: v.type})
  
          # Arbitrary function
          elif callable(v):
              src, _ = _serialize_object(
                  v, filepath, local_path.joinpath(k)
              )
              kwargs.update({k: src})
  
          # Put remaining kwargs directly into cfg
          else:
              kwargs.update({k: v})
  
      if "preprocess_drift" in func:
          preprocess_cfg.update(kwargs)
      else:
          preprocess_cfg.update({"kwargs": kwargs})
  
      return preprocess_cfg
  
imports: import logging
  import os
  import shutil
  import warnings
  from functools import partial
  from pathlib import Path
  from typing import Callable, Optional, Tuple, Union, Any, Dict, TYPE_CHECKING
  import dill
  import numpy
  import toml
  from transformers import PreTrainedTokenizerBase
  from alibi_detect.saving._typing import VALID_DETECTORS
  from alibi_detect.saving.loading import _replace, validate_config, STATE_PATH
  from alibi_detect.saving.registry import registry
  from alibi_detect.utils._types import supported_models_all, supported_models_tf, supported_models_torch, supported_models_sklearn
  from alibi_detect.base import Detector, ConfigurableDetector, StatefulDetectorOnline
  from alibi_detect.saving._tensorflow import save_detector_legacy, save_model_config_tf, save_optimizer_config_tf
  from alibi_detect.saving._pytorch import save_model_config_pt, save_device_pt
  from alibi_detect.saving._sklearn import save_model_config_sk
label_desc: Use image node_id 5 for calling a global function with example usage: _save_preprocess_config(preprocess_fn, input_shape, filepath) and returns: preprocess_cfg