code: def describe(args: Namespace) -> None:
      session = cli.setup_session(args)
      responses: List[bindings.v1GetExperimentResponse] = []
      for experiment_id in args.experiment_ids.split(","):
          r = bindings.get_GetExperiment(
              session, experimentId=experiment_id
          )
          responses.append(r)
  
      if args.json:
          determined.cli.render.print_json(
              [resp.to_json() for resp in responses]
          )
          return
      exps = [resp.experiment for resp in responses]
  
      # Display overall experiment information.
      headers = [
          "Experiment ID",
          "State",
          "Progress",
          "Started",
          "Ended",
          "Name",
          "Description",
          "Archived",
          "Resource Pool",
          "Labels",
      ]
      values: List[List] = [
          [
              exp.id,
              exp.state,
              render.format_percent(exp.progress),
              render.format_time(exp.startTime),
              render.format_time(exp.endTime),
              exp.name,
              exp.description,
              exp.archived,
              exp.resourcePool,
              ", ".join(sorted(exp.labels or [])),
          ]
          for exp in exps
      ]
      if not args.outdir:
          outfile = None
          print("Experiment:")
      else:
          outfile = args.outdir.joinpath("experiments.csv")
      render.tabulate_or_csv(headers, values, args.csv, outfile)
  
      # Display trial-related information.
  
      def get_all_trials(exp_id: int) -> List[bindings.trialv1Trial]:
          def get_with_offset(
              offset: int,
          ) -> bindings.v1GetExperimentTrialsResponse:
              return bindings.get_GetExperimentTrials(
                  session,
                  offset=offset,
                  experimentId=exp_id,
              )
  
          resps = api.read_paginated(get_with_offset)
          return [t for r in resps for t in r.trials]
  
      trials_for_experiment = {
          exp.id: get_all_trials(exp.id) for exp in exps
      }
  
      headers = [
          "Trial ID",
          "Experiment ID",
          "State",
          "Started",
          "Ended",
          "H-Params",
      ]
      values = [
          [
              trial.id,
              exp.id,
              trial.state.value.replace("STATE_", ""),
              render.format_time(trial.startTime),
              render.format_time(trial.endTime),
              json.dumps(trial.hparams, indent=4),
          ]
          for exp in exps
          for trial in trials_for_experiment[exp.id]
      ]
      if not args.outdir:
          outfile = None
          print("\nTrials:")
      else:
          outfile = args.outdir.joinpath("trials.csv")
      render.tabulate_or_csv(headers, values, args.csv, outfile)
  
      # Display workload-related information.
  
      def get_all_workloads(
          trial_id: int,
      ) -> List[bindings.v1WorkloadContainer]:
          def get_with_offset(
              offset: int,
          ) -> bindings.v1GetTrialWorkloadsResponse:
              return bindings.get_GetTrialWorkloads(
                  session,
                  offset=offset,
                  trialId=trial_id,
                  limit=500,
              )
  
          resps = api.read_paginated(get_with_offset)
          return [w for r in resps for w in r.workloads]
  
      all_workloads = {
          exp.id: {
              t.id: get_all_workloads(t.id)
              for t in trials_for_experiment[exp.id]
          }
          for exp in exps
      }
  
      t_metrics_headers: List[str] = []
      t_metrics_names: List[str] = []
      v_metrics_headers: List[str] = []
      v_metrics_names: List[str] = []
      if args.metrics:
          # Accumulate the scalar training and validation metric names from all provided experiments.
          for exp in exps:
              sample_trial = trials_for_experiment[exp.id][0]
              sample_workloads = all_workloads[exp.id][sample_trial.id]
              t_metrics_names += scalar_training_metrics_names(
                  sample_workloads
              )
              v_metrics_names += scalar_validation_metrics_names(
                  sample_workloads
              )
          t_metrics_names = sorted(set(t_metrics_names))
          t_metrics_headers = [
              f"Training Metric: {name}" for name in t_metrics_names
          ]
          v_metrics_names = sorted(set(v_metrics_names))
          v_metrics_headers = [
              f"Validation Metric: {name}" for name in v_metrics_names
          ]
  
      headers = (
          ["Trial ID", "# of Batches", "State", "Report Time"]
          + t_metrics_headers
          + [
              "Checkpoint State",
              "Checkpoint Report Time",
              "Validation State",
              "Validation Report Time",
          ]
          + v_metrics_headers
      )
  
      values = []
      for exp in exps:
          for trial in trials_for_experiment[exp.id]:
              workloads = all_workloads[exp.id][trial.id]
              wl_output: Dict[int, List[Any]] = {}
              for workload in workloads:
                  t_metrics_fields = []
                  wl_detail: Optional[
                      Union[
                          bindings.v1MetricsWorkload,
                          bindings.v1CheckpointWorkload,
                      ]
                  ] = None
                  if workload.training:
                      wl_detail = workload.training
                      for name in t_metrics_names:
                          if (
                              wl_detail.metrics
                              and wl_detail.metrics.avgMetrics
                              and (name in wl_detail.metrics.avgMetrics)
                          ):
                              t_metrics_fields.append(
                                  wl_detail.metrics.avgMetrics[name]
                              )
                          else:
                              t_metrics_fields.append(None)
                  else:
                      t_metrics_fields = [
                          None for name in t_metrics_names
                      ]
  
                  if workload.checkpoint:
                      wl_detail = workload.checkpoint
  
                  if workload.checkpoint and wl_detail:
                      assert isinstance(
                          wl_detail, bindings.v1CheckpointWorkload
                      )
                      checkpoint_state = wl_detail.state.value
                      checkpoint_end_time = wl_detail.endTime
                  else:
                      checkpoint_state = ""
                      checkpoint_end_time = None
  
                  v_metrics_fields = []
                  if workload.validation:
                      wl_detail = workload.validation
                      validation_state = "STATE_COMPLETED"
                      validation_end_time = wl_detail.endTime
                      for name in v_metrics_names:
                          if (
                              wl_detail.metrics
                              and wl_detail.metrics.avgMetrics
                              and (name in wl_detail.metrics.avgMetrics)
                          ):
                              v_metrics_fields.append(
                                  wl_detail.metrics.avgMetrics[name]
                              )
                          else:
                              v_metrics_fields.append(None)
                  else:
                      validation_state = ""
                      validation_end_time = None
                      v_metrics_fields = [
                          None for name in v_metrics_names
                      ]
  
                  if wl_detail:
                      if wl_detail.totalBatches in wl_output:
                          # condense training, checkpoints, validation workloads into one step-like
                          # row for compatibility with previous versions of describe
                          merge_row = wl_output[wl_detail.totalBatches]
                          merge_row[3] = max(
                              merge_row[3],
                              render.format_time(wl_detail.endTime),
                          )
                          for idx, tfield in enumerate(
                              t_metrics_fields
                          ):
                              if tfield and merge_row[4 + idx] is None:
                                  merge_row[4 + idx] = tfield
                          start_checkpoint = 4 + len(t_metrics_fields)
                          if checkpoint_state:
                              merge_row[
                                  start_checkpoint
                              ] = checkpoint_state.replace("STATE_", "")
                              merge_row[
                                  start_checkpoint + 1
                              ] = render.format_time(
                                  checkpoint_end_time
                              )
                          if validation_end_time:
                              merge_row[
                                  start_checkpoint + 3
                              ] = render.format_time(
                                  validation_end_time
                              )
                          if validation_state:
                              merge_row[
                                  start_checkpoint + 2
                              ] = validation_state.replace("STATE_", "")
                          for idx, vfield in enumerate(
                              v_metrics_fields
                          ):
                              if (
                                  vfield
                                  and merge_row[
                                      start_checkpoint + idx + 4
                                  ]
                                  is None
                              ):
                                  merge_row[
                                      start_checkpoint + idx + 4
                                  ] = vfield
                      else:
                          row = (
                              [
                                  trial.id,
                                  wl_detail.totalBatches,
                                  (
                                      checkpoint_state
                                      or validation_state
                                  ).replace("STATE_", ""),
                                  render.format_time(wl_detail.endTime),
                              ]
                              + t_metrics_fields
                              + [
                                  checkpoint_state.replace(
                                      "STATE_", ""
                                  ),
                                  render.format_time(
                                      checkpoint_end_time
                                  ),
                                  validation_state.replace(
                                      "STATE_", ""
                                  ),
                                  render.format_time(
                                      validation_end_time
                                  ),
                              ]
                              + v_metrics_fields
                          )
                          wl_output[wl_detail.totalBatches] = row
  
              # Done processing one trial's workloads, add to output values.
              values += sorted(
                  wl_output.values(), key=lambda a: int(a[1])
              )
  
      if not args.outdir:
          outfile = None
          print("\nWorkloads:")
      else:
          outfile = args.outdir.joinpath("workloads.csv")
      render.tabulate_or_csv(headers, values, args.csv, outfile)
  
imports: import base64
  import json
  import numbers
  import pathlib
  import sys
  import time
  from argparse import ArgumentError, FileType, Namespace
  from pathlib import Path
  from pprint import pformat
  from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
  import tabulate
  import termcolor
  import determined
  import determined.cli.render
  import determined.experimental
  import determined.load
  from determined import cli
  from determined.cli import checkpoint, render
  from determined.cli.errors import CliError
  from determined.cli.ntsc import CONFIG_DESC, parse_config_overrides
  from determined.common import api, context, set_logger, util
  from determined.common.api import authentication, bindings, logs
  from determined.common.declarative_argparse import Arg, Cmd, Group
  from determined.experimental import client
  from .checkpoint import render_checkpoint
  from .project import project_by_name
  from .trial import logs_args_description
label_desc: Use image node_id 13 for calling a global function with example usage: describe(args) and returns: unknown, unknown, bindings, bindings