def get_eval_jobs(workdir: str,
                  config: ml_collections.ConfigDict,
                  final_num_repeats: int = 1,
                  *,
                  custom_outputs: bool = False):
  """Get evaluation jobs from checkpoint manager.

  Two possible cases:
  1. When jobs run train and eval in parallel, we knew the ckpt_dir=workdir,
    and config.
  2. For a single eval job, the user needs to specify config.eval_from and
    it will look up the information for the train_config.

  Args:
    workdir: Experiment directory for saving the checkpoint.
    config: Configurations of the experiments used in the trainer.
    final_num_repeats: Number of repeats for the last checkpoint.
    custom_outputs: Whether to load custom outputs.

  Returns:
    The train_config with updated values for the specified prefixes,
      ckpt_manager, and ckpt_list.
  """
  if 'eval_from' in config:
    # separate eval job, gets info from the train experiment
    override_prefix = ('eval', 'data', 'log', 'batch',
                       'sampling') + config.eval_from.get(
                           'override_prefix', tuple())
    train_config, ckpt_path = get_train_config(config, override_prefix)
    if not gfile.isdir(ckpt_path):
      ckpt_dir = os.path.dirname(ckpt_path)
    else:
      ckpt_dir = ckpt_path
  else:
    # during training/eval parallel jobs, use the current config.
    ckpt_dir = workdir
    train_config = config

  config = train_config

  if custom_outputs:
    result_dir = os.path.join(config.eval_from.result_dir,
                              config.eval_from.get('sub_dir', ''))
    ckpt_manager = TaskManager(
        result_dir,
        workdir,
        config.eval_from.output_pattern,
        lambda x: int(  # pylint: disable=g-long-lambda
            x.split('_')[config.eval_from.get('output_step_field', -1)]
        ),
    )
  else:
    ckpt_manager = TaskManager(ckpt_dir, workdir)
  if config.eval_from.get('step') is None:
    ckpt_list = ckpt_manager.unevaluated_checkpoints(
        timeout=config.eval_from.get('timeout', 3600 * 8))
  elif config.eval_from.step > 0:
    # Evaluates at a specified step
    ckpt_list = [
        c for c in ckpt_manager.unevaluated_checkpoints(0, return_all=True)
        if ckpt_manager.sort_key_fn(c) == config.eval_from.step
    ]
    assert ckpt_list, f'Checkpoint at step {config.eval_from.step} not found.'
  elif config.eval_from.step == -1:  # The last checkpoint.
    ckpt_list = [*ckpt_manager.unevaluated_checkpoints(0, return_all=True)][-1:]
    assert ckpt_list, 'No checkpoint found.'
  elif config.eval_from.step == 0:
    ckpt_list = [ckpt_dir]  # For unit test.
  else:
    raise ValueError(f'Invalid step to evaluate: {config.eval_from.step}')
  if final_num_repeats > 1:
    ckpt_list = _repeat_last(ckpt_list, final_num_repeats)
  return config, ckpt_manager, ckpt_list
Get evaluation jobs from checkpoint manager.

  Two possible cases:
  1. When jobs run train and eval in parallel, we knew the ckpt_dir=workdir,
    and config.
  2. For a single eval job, the user needs to specify config.eval_from and
    it will look up the information for the train_config.

  Args:
    workdir: Experiment directory for saving the checkpoint.
    config: Configurations of the experiments used in the trainer.
    final_num_repeats: Number of repeats for the last checkpoint.
    custom_outputs: Whether to load custom outputs.

  Returns:
    The train_config with updated values for the specified prefixes,
      ckpt_manager, and ckpt_list.
  
videogvt/train_lib/eval_utils.py