code: def generate_json_ai(
      type_information: TypeInformation,
      statistical_analysis: StatisticalAnalysis,
      problem_definition: ProblemDefinition,
  ) -> JsonAI:
      """
      Given ``type_infer.TypeInformation``, ``dataprep_ml.StatisticalAnalysis``, and the ``ProblemDefinition``, generate a JSON config file with the necessary elements of the ML pipeline populated.
  
      :param TypeInformation: Specifies what data types each column within the dataset are. Generated by `mindsdb/type_infer`.
      :param statistical_analysis:
      :param problem_definition: Specifies details of the model training/building procedure, as defined by ``ProblemDefinition``
  
      :returns: JSON-AI object with fully populated details of the ML pipeline
      """  # noqaexec
      exec(IMPORTS, globals())
      exec(IMPORT_EXTERNAL_DIRS, globals())
      target = problem_definition.target
      input_cols = []
      dependency_dict = {}
      tss = problem_definition.timeseries_settings
  
      dtype_dict_override = problem_definition.dtype_dict
      dtype_dict = type_information.dtypes
  
      for k in type_information.identifiers:
          if (
              not (
                  tss.is_timeseries
                  and tss.group_by
                  and k in tss.group_by
              )
              and k != target
          ):
              del dtype_dict[k]
  
      for k, v in dtype_dict_override.items():
          dtype_dict[k] = v
  
      for col_name, col_dtype in dtype_dict.items():
          if (
              col_name not in type_information.identifiers
              and col_dtype not in (dtype.invalid, dtype.empty)
              and col_name != target
          ) or (tss.group_by is not None and col_name in tss.group_by):
              if col_name != problem_definition.target:
                  input_cols.append(col_name)
  
      is_target_predicting_encoder = False
      is_ts = problem_definition.timeseries_settings.is_timeseries
      imputers = []
  
      # Single text column classification
      if (
          len(input_cols) == 1
          and type_information.dtypes[input_cols[0]]
          in (dtype.rich_text)
          and type_information.dtypes[target]
          in (dtype.categorical, dtype.binary)
      ):
          is_target_predicting_encoder = True
  
      submodels = []
      if is_target_predicting_encoder:
          submodels.extend(
              [
                  {
                      "module": "Unit",
                      "args": {
                          "target_encoder": "$encoders[self.target]",
                          "stop_after": "$problem_definition.seconds_per_mixer",
                      },
                  }
              ]
          )
      else:
          # add neural model
          if not tss.is_timeseries:
              submodels.extend(
                  [
                      {
                          "module": "Neural",
                          "args": {
                              "fit_on_dev": True,
                              "stop_after": "$problem_definition.seconds_per_mixer",
                              "search_hyperparameters": True,
                          },
                      }
                  ]
              )
          else:
              submodels.extend(
                  [
                      {
                          "module": "NeuralTs",
                          "args": {
                              "fit_on_dev": True,
                              "stop_after": "$problem_definition.seconds_per_mixer",
                              "search_hyperparameters": True,
                          },
                      },
                  ]
              )
  
          # add other models
          if (not tss.is_timeseries or tss.horizon == 1) and dtype_dict[
              target
          ] not in (dtype.num_array, dtype.cat_array):
              submodels.extend(
                  [
                      {
                          "module": "XGBoostMixer",
                          "args": {
                              "stop_after": "$problem_definition.seconds_per_mixer",
                              "fit_on_dev": True,
                          },
                      },
                      {
                          "module": "Regression",
                          "args": {
                              "stop_after": "$problem_definition.seconds_per_mixer",
                          },
                      },
                      {
                          "module": "RandomForest",
                          "args": {
                              "stop_after": "$problem_definition.seconds_per_mixer",
                              "fit_on_dev": True,
                          },
                      },
                  ]
              )
  
          # special forecasting dispatch
          elif tss.is_timeseries:
              submodels.extend(
                  [
                      {
                          "module": "XGBoostArrayMixer",
                          "args": {},
                      },
                  ]
              )
  
      model = {
          "module": "BestOf",
          "args": {
              "submodels": submodels,
          },
      }
  
      num_ts_dtypes = (dtype.integer, dtype.float, dtype.quantity)
      if tss.is_timeseries and tss.horizon > 1:
          if dtype_dict[target] in num_ts_dtypes:
              dtype_dict[target] = dtype.num_tsarray
              problem_definition.anomaly_detection = True
          else:
              dtype_dict[target] = dtype.cat_tsarray
      elif tss.is_timeseries and dtype_dict[target] in num_ts_dtypes:
          problem_definition.anomaly_detection = True
  
      encoders = {
          target: lookup_encoder(
              dtype_dict[target],
              target,
              True,
              problem_definition,
              False,
              statistical_analysis,
          )
      }
  
      for col in input_cols:
          encoders[col] = lookup_encoder(
              dtype_dict[col],
              col,
              False,
              problem_definition,
              is_target_predicting_encoder,
              statistical_analysis,
          )
  
      # Decide on the accuracy functions to use
      output_dtype = dtype_dict[target]
      if output_dtype in [
          dtype.integer,
          dtype.float,
          dtype.date,
          dtype.datetime,
          dtype.quantity,
      ]:
          accuracy_functions = ["r2_score"]
      elif output_dtype in [
          dtype.categorical,
          dtype.tags,
          dtype.binary,
      ]:
          accuracy_functions = ["balanced_accuracy_score"]
      elif output_dtype in (dtype.num_tsarray,):
          accuracy_functions = ["complementary_smape_array_accuracy"]
      elif output_dtype in (dtype.num_array,):
          accuracy_functions = ["evaluate_num_array_accuracy"]
      elif output_dtype in (dtype.cat_array, dtype.cat_tsarray):
          accuracy_functions = ["evaluate_cat_array_accuracy"]
      else:
          raise Exception(
              f"Please specify a custom accuracy function for output type {output_dtype}"
          )
  
      if is_ts:
          if output_dtype in [
              dtype.integer,
              dtype.float,
              dtype.quantity,
          ]:
              # forces this acc fn for t+1 time series forecasters
              accuracy_functions = [
                  "complementary_smape_array_accuracy"
              ]
  
          if output_dtype in (
              dtype.integer,
              dtype.float,
              dtype.quantity,
              dtype.num_tsarray,
          ):
              imputers.append(
                  {
                      "module": "NumericalImputer",
                      "args": {
                          "value": "'zero'",
                          "target": f"'{target}'",
                      },
                  }
              )
          elif output_dtype in [
              dtype.categorical,
              dtype.tags,
              dtype.binary,
              dtype.cat_tsarray,
          ]:
              imputers.append(
                  {
                      "module": "CategoricalImputer",
                      "args": {
                          "value": "'mode'",
                          "target": f"'{target}'",
                      },
                  }
              )
  
      if problem_definition.time_aim is None:
          # 5 days
          problem_definition.time_aim = 3 * 24 * 3600
  
      # Encoders are assigned 1/3 of the time unless a user overrides this (equal time per encoder)
      if problem_definition.seconds_per_encoder is None:
          nr_trainable_encoders = len(
              [
                  x
                  for x in encoders.values()
                  if eval(x["module"]).is_trainable_encoder
              ]
          )
          if nr_trainable_encoders > 0:
              problem_definition.seconds_per_encoder = (
                  0.33
                  * problem_definition.time_aim
                  / nr_trainable_encoders
              )
  
      # Mixers are assigned 1/3 of the time aim (or 2/3 if there are no trainable encoders )\
      # unless a user overrides this (equal time per mixer)
      if problem_definition.seconds_per_mixer is None:
          if problem_definition.seconds_per_encoder is None:
              problem_definition.seconds_per_mixer = (
                  0.66
                  * problem_definition.time_aim
                  / len(model["args"]["submodels"])
              )
          else:
              problem_definition.seconds_per_mixer = (
                  0.33
                  * problem_definition.time_aim
                  / len(model["args"]["submodels"])
              )
  
      return JsonAI(
          cleaner=None,
          splitter=None,
          analyzer=None,
          explainer=None,
          encoders=encoders,
          imputers=imputers,
          dtype_dict=dtype_dict,
          dependency_dict=dependency_dict,
          model=model,
          problem_definition=problem_definition,
          identifiers=type_information.identifiers,
          timeseries_transformer=None,
          timeseries_analyzer=None,
          accuracy_functions=accuracy_functions,
      )
  
file_path: lightwood/lightwood/api/json_ai.py
imports: import inspect
  from type_infer.dtype import dtype
  from type_infer.base import TypeInformation
  from dataprep_ml import StatisticalAnalysis
  from lightwood.helpers.templating import _consolidate_analysis_blocks, _add_cls_kwarg
  from lightwood.helpers.constants import IMPORTS, IMPORT_EXTERNAL_DIRS
  from lightwood.api.types import JsonAI, ProblemDefinition
  import lightwood.ensemble
  import lightwood.encoder
label_desc: Use image node_id 2 for calling a global function with example usage: generate_json_ai(type_information, statistical_analysis, problem_definition) and returns: JsonAI