code: def _to_json_inner(ebm, detail="all"):
      """Converts the inner model to a JSONable representation.
  
      Args:
          detail: 'minimal', 'interpretable', 'mergeable', 'all'
  
      Returns:
          JSONable object
      """
  
      if detail == "minimal":
          level = 0
      elif detail == "interpretable":
          level = 1
      elif detail == "mergeable":
          level = 2
      elif detail == "all":
          level = 3
      else:
          msg = f"Unrecognized to_json detail: {detail}"
          _log.error(msg)
          raise ValueError(msg)
  
      j = {}
  
      # future-proof support for multi-output models
      outputs = []
      output = {}
      task = identify_task(ebm.link_)
      output["task"] = task
      if task == "classification":
          output["classes"] = ebm.classes_.tolist()
      elif task == "regression":
          if 3 <= level:
              min_target = getattr(ebm, "min_target_", None)
              if min_target is not None and not isnan(min_target):
                  output["min_target"] = jsonify_item(min_target)
              max_target = getattr(ebm, "max_target_", None)
              if max_target is not None and not isnan(max_target):
                  output["max_target"] = jsonify_item(max_target)
      else:
          raise ValueError(f"Unsupported link function: {ebm.link_}")
  
      output["link"] = ebm.link_
      output["link_param"] = jsonify_item(ebm.link_param_)
  
      outputs.append(output)
      j["outputs"] = outputs
  
      if isinstance(ebm.intercept_, float):
          # scikit-learn requires that we have a single float value as our intercept for compatibility with
          # RegressorMixin, but in other scenarios where we want to support things like multi-output it would be
          # easier if the regression intercept were handled identically to classification, so put it in an array
          # for our JSON format to harmonize the cross-language representation
          j["intercept"] = [jsonify_item(ebm.intercept_)]
      else:
          j["intercept"] = jsonify_lists(ebm.intercept_.tolist())
  
      bagged_intercept = getattr(ebm, "bagged_intercept_", None)
      if bagged_intercept is not None:
          j["bagged_intercept"] = jsonify_lists(
              bagged_intercept.tolist()
          )
  
      if 3 <= level:
          noise_scale_binning = getattr(
              ebm, "noise_scale_binning_", None
          )
          if noise_scale_binning is not None:
              j["noise_scale_binning"] = jsonify_item(
                  noise_scale_binning
              )
          noise_scale_boosting = getattr(
              ebm, "noise_scale_boosting_", None
          )
          if noise_scale_boosting is not None:
              j["noise_scale_boosting"] = jsonify_item(
                  noise_scale_boosting
              )
  
      if 2 <= level:
          bag_weights = getattr(ebm, "bag_weights_", None)
          if bag_weights is not None:
              j["bag_weights"] = jsonify_lists(bag_weights.tolist())
  
      if 3 <= level:
          breakpoint_iteration = getattr(
              ebm, "breakpoint_iteration_", None
          )
          if breakpoint_iteration is not None:
              j["breakpoint_iteration"] = breakpoint_iteration.tolist()
  
      if 3 <= level:
          j["implementation"] = "python"
          params = {}
  
          # TODO: we need to clean up and validate our input parameters before putting them into JSON
          # if we were pass a numpy array instead of a list or a numpy type these would fail
          # for now we can just require that anything numpy as input is illegal
  
          if hasattr(ebm, "feature_names"):
              params["feature_names"] = ebm.feature_names
  
          if hasattr(ebm, "feature_types"):
              params["feature_types"] = ebm.feature_types
  
          if hasattr(ebm, "max_bins"):
              params["max_bins"] = ebm.max_bins
  
          if hasattr(ebm, "max_interaction_bins"):
              params["max_interaction_bins"] = ebm.max_interaction_bins
  
          if hasattr(ebm, "interactions"):
              params["interactions"] = ebm.interactions
  
          if hasattr(ebm, "exclude"):
              params["exclude"] = ebm.exclude
  
          if hasattr(ebm, "validation_size"):
              params["validation_size"] = ebm.validation_size
  
          if hasattr(ebm, "outer_bags"):
              params["outer_bags"] = ebm.outer_bags
  
          if hasattr(ebm, "inner_bags"):
              params["inner_bags"] = ebm.inner_bags
  
          if hasattr(ebm, "learning_rate"):
              params["learning_rate"] = ebm.learning_rate
  
          if hasattr(ebm, "greediness"):
              params["greediness"] = ebm.greediness
  
          if hasattr(ebm, "smoothing_rounds"):
              params["smoothing_rounds"] = ebm.smoothing_rounds
  
          if hasattr(ebm, "max_rounds"):
              params["max_rounds"] = ebm.max_rounds
  
          if hasattr(ebm, "early_stopping_rounds"):
              params[
                  "early_stopping_rounds"
              ] = ebm.early_stopping_rounds
  
          if hasattr(ebm, "early_stopping_tolerance"):
              params[
                  "early_stopping_tolerance"
              ] = ebm.early_stopping_tolerance
  
          if hasattr(ebm, "min_samples_leaf"):
              params["min_samples_leaf"] = ebm.min_samples_leaf
  
          if hasattr(ebm, "max_leaves"):
              params["max_leaves"] = ebm.max_leaves
  
          if hasattr(ebm, "objective"):
              params["objective"] = ebm.objective
  
          if hasattr(ebm, "n_jobs"):
              params["n_jobs"] = ebm.n_jobs
  
          if hasattr(ebm, "random_state"):
              params["random_state"] = ebm.random_state
  
          if hasattr(ebm, "epsilon"):
              params["epsilon"] = ebm.epsilon
  
          if hasattr(ebm, "delta"):
              params["delta"] = ebm.delta
  
          if hasattr(ebm, "composition"):
              params["composition"] = ebm.composition
  
          if hasattr(ebm, "bin_budget_frac"):
              params["bin_budget_frac"] = ebm.bin_budget_frac
  
          if hasattr(ebm, "privacy_bounds"):
              params["privacy_bounds"] = ebm.privacy_bounds
  
          if hasattr(ebm, "privacy_target_min"):
              params["privacy_target_min"] = ebm.privacy_target_min
  
          if hasattr(ebm, "privacy_target_max"):
              params["privacy_target_max"] = ebm.privacy_target_max
  
          j["implementation_params"] = params
  
      unique_val_counts = getattr(ebm, "unique_val_counts_", None)
      feature_bounds = getattr(ebm, "feature_bounds_", None)
      histogram_weights = getattr(ebm, "histogram_weights_", None)
  
      features = []
      for i in range(len(ebm.bins_)):
          feature = {}
  
          feature["name"] = ebm.feature_names_in_[i]
          feature_type = ebm.feature_types_in_[i]
          feature["type"] = feature_type
  
          if 1 <= level:
              if unique_val_counts is not None:
                  feature["num_unique_vals"] = int(unique_val_counts[i])
  
          if feature_type == "nominal" or feature_type == "ordinal":
              categories = []
              for bins in ebm.bins_[i]:
                  leveled_categories = []
                  feature_categories = list(
                      map(tuple, map(reversed, bins.items()))
                  )
                  feature_categories.sort()  # groupby requires sorted data
                  for _, category_iter in groupby(
                      feature_categories, lambda x: x[0]
                  ):
                      category_group = [
                          category for _, category in category_iter
                      ]
                      if len(category_group) == 1:
                          leveled_categories.append(category_group[0])
                      else:
                          leveled_categories.append(category_group)
                  categories.append(leveled_categories)
              feature["categories"] = categories
          elif feature_type == "continuous":
              cuts = []
              for bins in ebm.bins_[i]:
                  cuts.append(bins.tolist())
              feature["cuts"] = cuts
              if 1 <= level:
                  if feature_bounds is not None:
                      feature_min = feature_bounds[i, 0]
                      if not isnan(feature_min):
                          feature["min"] = jsonify_item(feature_min)
                      feature_max = feature_bounds[i, 1]
                      if not isnan(feature_max):
                          feature["max"] = jsonify_item(feature_max)
                  if histogram_weights is not None:
                      feature_histogram_weights = histogram_weights[i]
                      if feature_histogram_weights is not None:
                          feature[
                              "histogram_weights"
                          ] = feature_histogram_weights.tolist()
          else:
              raise ValueError(
                  f"Unsupported feature type: {feature_type}"
              )
  
          features.append(feature)
      j["features"] = features
  
      standard_deviations_all = getattr(
          ebm, "standard_deviations_", None
      )
      bagged_scores_all = getattr(ebm, "bagged_scores_", None)
  
      terms = []
      for term_idx in range(len(ebm.term_features_)):
          term = {}
          # we already used "features", so use "term_features" to avoid confusion
          term["term_features"] = [
              ebm.feature_names_in_[feature_idx]
              for feature_idx in ebm.term_features_[term_idx]
          ]
          term["scores"] = jsonify_lists(
              ebm.term_scores_[term_idx].tolist()
          )
          if 1 <= level:
              if standard_deviations_all is not None:
                  standard_deviations = standard_deviations_all[
                      term_idx
                  ]
                  if standard_deviations is not None:
                      term["standard_deviations"] = jsonify_lists(
                          standard_deviations.tolist()
                      )
          if 2 <= level:
              if bagged_scores_all is not None:
                  bagged_scores = bagged_scores_all[term_idx]
                  if bagged_scores is not None:
                      term["bagged_scores"] = jsonify_lists(
                          bagged_scores.tolist()
                      )
          if 1 <= level:
              term["bin_weights"] = jsonify_lists(
                  ebm.bin_weights_[term_idx].tolist()
              )
  
          terms.append(term)
      j["terms"] = terms
  
      return j
  
file_path: interpret/python/interpret-core/interpret/glassbox/_ebm/_json.py
imports: import logging
  from warnings import warn
  from ...utils._link import identify_task
  from math import isnan
  import numpy
  from itertools import groupby
  from ._utils import generate_term_names
  from ...utils._histogram import make_all_histogram_edges
  from ...utils._clean_simple import typify_classification
label_desc: Use image node_id 3 for calling a global function with example usage: _to_json_inner(ebm, detail) and returns: j