code: def upgrade_model_progress(model_progress: Dict) -> Dict:
      """Updates model progress info to be compatible with latest ProgressTracker implementation.
  
      Notably, we convert epoch-based stats to their step-based equivalents and reformat metrics into `TrainerMetric`
      tuples.
      """
      ret = copy.deepcopy(model_progress)
  
      if "last_improvement_epoch" in ret:
          ret["last_improvement_steps"] = (
              ret["last_improvement_epoch"] * ret["batch_size"]
          )
          del ret["last_improvement_epoch"]
  
      if "last_learning_rate_reduction_epoch" in ret:
          ret["last_learning_rate_reduction_steps"] = (
              ret["last_learning_rate_reduction_epoch"]
              * ret["batch_size"]
          )
          del ret["last_learning_rate_reduction_epoch"]
  
      if "last_increase_batch_size_epoch" in ret:
          ret["last_increase_batch_size_steps"] = (
              ret["last_increase_batch_size_epoch"] * ret["batch_size"]
          )
          del ret["last_increase_batch_size_epoch"]
  
      if "vali_metrics" in ret:
          ret["validation_metrics"] = ret["vali_metrics"]
          del ret["vali_metrics"]
  
      for metric_group in (
          "train_metrics",
          "test_metrics",
          "validation_metrics",
      ):
          if metric_group not in ret:
              continue
          for tgt in ret[metric_group]:
              for metric in ret[metric_group][tgt]:
                  if len(
                      ret[metric_group][tgt][metric]
                  ) == 0 or isinstance(
                      ret[metric_group][tgt][metric][0], (tuple, list)
                  ):
                      continue
  
                  ret[metric_group][tgt][metric] = [
                      TrainerMetric(
                          i + 1, (i + 1) * ret["batch_size"], val
                      )
                      for i, val in enumerate(
                          ret[metric_group][tgt][metric]
                      )
                  ]
  
      if "tune_checkpoint_num" not in ret:
          ret["tune_checkpoint_num"] = 0
  
      # Upgrades related to extending progress tracker with explicit bests.
      if "checkpoint_number" not in ret:
          ret["checkpoint_number"] = 0
  
      if "best_eval_metric_steps" not in ret:
          ret["best_eval_metric_steps"] = 0
  
      if "best_eval_metric_epoch" not in ret:
          ret["best_eval_metric_epoch"] = 0
  
      if "best_eval_metric_checkpoint_number" not in ret:
          ret["best_eval_metric_checkpoint_number"] = 0
  
      if "best_eval_train_metrics" not in ret:
          ret["best_eval_train_metrics"] = {}
  
      if "best_eval_validation_metrics" not in ret:
          ret["best_eval_validation_metrics"] = {}
  
      if "best_eval_test_metrics" not in ret:
          ret["best_eval_test_metrics"] = {}
  
      if "best_eval_metric" in ret:
          ret["best_eval_metric_value"] = ret["best_eval_metric"]
          del ret["best_eval_metric"]
  
      if "last_improvement" in ret:
          del ret["last_improvement"]
  
      # Delete learning-rate related fields removed in https://github.com/ludwig-ai/ludwig/pull/2877.
      if "best_reduce_learning_rate_eval_metric" in ret:
          del ret["best_reduce_learning_rate_eval_metric"]
  
      if "last_reduce_learning_rate_eval_metric_improvement" in ret:
          del ret["last_reduce_learning_rate_eval_metric_improvement"]
  
      return ret
  
imports: import copy
  import logging
  import warnings
  from typing import Any, Callable, Dict, List, Union
  from ludwig.api_annotations import DeveloperAPI
  from ludwig.constants import AUDIO, BIAS, CLASS_WEIGHTS, COLUMN, CONV_BIAS, CONV_USE_BIAS, DECODER, DEFAULT_BIAS, DEFAULT_USE_BIAS, DEFAULTS, ENCODER, EVAL_BATCH_SIZE, EXECUTOR, FORCE_SPLIT, HEIGHT, HYPEROPT, IMAGE, INPUT_FEATURES, LOSS, MISSING_VALUE_STRATEGY, MODEL_ECD, MODEL_GBM, MODEL_TYPE, NAME, NUM_SAMPLES, NUMBER, OUTPUT_FEATURES, PARAMETERS, PREPROCESSING, PROBABILITIES, RANDOM, RAY, SAMPLER, SCHEDULER, SEARCH_ALG, SEQUENCE, SPLIT, SPLIT_PROBABILITIES, STRATIFY, TEXT, TIMESERIES, TRAINER, TRAINING, TYPE, USE_BIAS, WIDTH
  from ludwig.features.feature_registries import get_base_type_registry, get_input_type_registry, get_output_type_registry
  from ludwig.globals import LUDWIG_VERSION
  from ludwig.schema.defaults.gbm import GBMDefaultsConfig
  from ludwig.schema.encoders.utils import get_encoder_cls
  from ludwig.types import FeatureConfigDict, FeatureTypeDefaultsDict, HyperoptConfigDict, ModelConfigDict, PreprocessingConfigDict, TrainerConfigDict, TrainingSetMetadataDict
  from ludwig.utils.metric_utils import TrainerMetric
  from ludwig.utils.misc_utils import get_from_registry, merge_dict
  from ludwig.utils.version_transformation import VersionTransformation, VersionTransformationRegistry
label_desc: Use image node_id 3 for calling a global function with example usage: upgrade_model_progress(model_progress) and returns: ret